/* ** AreaEditor - a program for editing SMAUG and ROM area files. ** Author: Nick Gammon ** http://www.gammon.com.au/ ** See Copyright Notice at the end of AreaEditor.h */ #include "stdafx.h" #include "AreaEditor.h" #include "MainFrm.h" #include "defaults.h" #include "SkillsListDlg.h" int get_skill (const CString & skilltype) { if ( skilltype == "Race" ) return SKILL_RACIAL; if ( skilltype == "Spell" ) return SKILL_SPELL; if ( skilltype == "Skill" ) return SKILL_SKILL; if ( skilltype == "Weapon" ) return SKILL_WEAPON; if ( skilltype == "Tongue" ) return SKILL_TONGUE; if ( skilltype == "Herb" ) return SKILL_HERB; return SKILL_UNKNOWN; } void CAreaEditorApp::fread_skill (CFileRead & FileRead) { CString buf; CString word; bool fMatch; bool got_info; CSkill *skill; skill = new CSkill; m_SkillList.AddTail (skill); while (true) { word = FileRead.fread_word (); fMatch = FALSE; switch ( UPPER(word[0]) ) { case '*': fMatch = TRUE; FileRead.fread_to_eol (); break; case 'A': if (word == "Affect") { CSkillAffect *aff; aff = new CSkillAffect; aff->duration = FileRead.fread_word (); aff->location = FileRead.fread_number (); aff->modifier = FileRead.fread_word (); aff->bitvector = FileRead.fread_number (); skill->affects.AddTail (aff); fMatch = TRUE; break; } break; case 'C': if ( word == "Class" ) { int theclass = FileRead.fread_number (); int level = FileRead.fread_number (); int adept = FileRead.fread_number (); if (theclass >= 0 && theclass < MAX_CLASS) { skill->skill_level[theclass] = level; skill->skill_adept[theclass] = adept; } fMatch = TRUE; break; } /* huh? if ( word == "Code" ) { skill->spell_skill_fun = FileRead.fread_word (); fMatch = TRUE; break; } */ KEY( "Code", skill->spell_skill_fun, FileRead.fread_word () ); KEY( "Components", skill->components, FileRead.fread_string () ); break; case 'D': KEY( "Dammsg", skill->noun_damage, FileRead.fread_string () ); KEY( "Dice", skill->dice, FileRead.fread_string () ); KEY( "Diechar", skill->die_char, FileRead.fread_string () ); KEY( "Dieroom", skill->die_room, FileRead.fread_string () ); KEY( "Dievict", skill->die_vict, FileRead.fread_string () ); KEY( "Difficulty", skill->difficulty, FileRead.fread_number () ); break; case 'E': if ( word == "End" ) return; break; case 'F': KEY( "Flags", skill->flags, FileRead.fread_number () ); break; case 'G': KEY( "Guild", skill->guild, FileRead.fread_number () ); break; case 'H': KEY( "Hitchar", skill->hit_char, FileRead.fread_string () ); KEY( "Hitdest", skill->hit_dest, FileRead.fread_string () ); KEY( "Hitroom", skill->hit_room, FileRead.fread_string () ); KEY( "Hitvict", skill->hit_vict, FileRead.fread_string () ); break; case 'I': KEY( "Immchar", skill->imm_char, FileRead.fread_string () ); KEY( "Immroom", skill->imm_room, FileRead.fread_string () ); KEY( "Immvict", skill->imm_vict, FileRead.fread_string () ); if ( !strcmp( word, "Info" ) ) { skill->info = FileRead.fread_number (); got_info = TRUE; fMatch = TRUE; break; } break; case 'M': KEY( "Mana", skill->min_mana, FileRead.fread_number () ); KEY( "Minlevel", skill->min_level, FileRead.fread_number () ); KEY( "Minpos", skill->minimum_position, FileRead.fread_number () ); KEY( "Misschar", skill->miss_char, FileRead.fread_string () ); KEY( "Missroom", skill->miss_room, FileRead.fread_string () ); KEY( "Missvict", skill->miss_vict, FileRead.fread_string () ); break; case 'N': KEY( "Name", skill->name, FileRead.fread_string () ); break; case 'P': KEY( "Participants",skill->participants, FileRead.fread_number () ); break; case 'R': KEY( "Range", skill->range, FileRead.fread_number () ); KEY( "Rounds", skill->beats, FileRead.fread_number () ); if ( !strcmp( word, "Race" ) ) { int race = FileRead.fread_number (); int level = FileRead.fread_number (); int adept = FileRead.fread_number (); if (race >= 0 && race < MAX_CLASS) { skill->race_level[race] = level; skill->race_adept[race] = adept; } fMatch = TRUE; break; } break; break; case 'S': KEY( "Slot", skill->slot, FileRead.fread_number () ); KEY( "Saves", skill->saves, FileRead.fread_number () ); break; case 'T': KEY( "Target", skill->target, FileRead.fread_number () ); KEY( "Teachers", skill->teachers, FileRead.fread_string () ); KEY( "Type", skill->type, get_skill(FileRead.fread_word ()) ); break; case 'V': KEY( "Value", skill->value, FileRead.fread_number () ); break; case 'W': KEY( "Wearoff", skill->msg_off, FileRead.fread_string () ); break; } if ( !fMatch ) ThrowErrorException ("No match: %s", (LPCTSTR) word ); } // end of processing each word } // end of CAreaEditorApp::fread_skill void CAreaEditorApp::LoadSkills (const CString strFileName) { CWaitCursor wait; try { CFileRead FileRead; // Open skills file CFile fSkills (strFileName, CFile::modeRead|CFile::shareDenyWrite); CArchive ar(&fSkills, CArchive::load); FileRead.Init ("Loading skills", &ar); try { while (true) { char letter; CString word; letter = FileRead.fread_letter(); if ( letter == '*' ) { FileRead.fread_to_eol (); continue; } if ( letter != '#' ) ThrowErrorException ( "# not found."); word = FileRead.fread_word (); if (word == "SKILL") { fread_skill (FileRead); continue; } else if (word == "END") break; else ThrowErrorException ( "Bad section."); } // end of read loop m_bSkillsDirty = false; } // end of try block catch(CException* e) { FileRead.Wrapup (); DeleteSkills (); // error on load - don't keep half of them e->ReportError (); ::AfxMessageBox (CFormat ("Error occurred in file %s at (line %ld) \"%s\"", (LPCTSTR) strFileName, FileRead.GetLineNumber (), (LPCTSTR) FileRead.GetLastLineRead ()), MB_ICONINFORMATION); e->Delete(); ar.Close(); return; } ar.Close(); FileRead.Wrapup (); } catch(CException* e) { ::AfxMessageBox ( CFormat ("Unable to open skills file: %s", (LPCTSTR) strFileName), MB_ICONEXCLAMATION); e->Delete(); return; } SortSkills (); } // end of CAreaEditorApp::LoadSkills void CAreaEditorApp::OnSkillsLoad() { CFileDialog dlg(TRUE, // TRUE for FileOpen "dat", // default extension "skills.dat", // initial file name OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, "Skills table files (*.dat)|*.dat||"); dlg.m_ofn.lpstrTitle = "Select skills file"; if (dlg.DoModal() != IDOK) return; LoadSkills (dlg.GetPathName()); } void CAreaEditorApp::OnSkillsEdit() { CSkillsListDlg dlg; dlg.DoModal (); } void CAreaEditorApp::OnUpdateSkillsEdit(CCmdUI* pCmdUI) { pCmdUI->Enable (!m_SkillList.IsEmpty ()); } void CAreaEditorApp::OnUpdateSkillsLoad(CCmdUI* pCmdUI) { pCmdUI->Enable (m_SkillList.IsEmpty ()); } void CAreaEditorApp::OnSkillsClose() { if (m_bSkillsDirty) { int iResponse = ::AfxMessageBox ("Skills file has changed. Save it?", MB_YESNOCANCEL | MB_ICONQUESTION); if (iResponse == IDCANCEL) return; if (iResponse == IDYES) OnSkillsSave (); } DeleteSkills (); } void CAreaEditorApp::OnUpdateSkillsClose(CCmdUI* pCmdUI) { pCmdUI->Enable (!m_SkillList.IsEmpty ()); } void CAreaEditorApp::OnSkillsSave() { CFileDialog dlg(FALSE, // FALSE for FileSave "dat", // default extension "skills.dat", OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST, "Data files (*.dat)|*.dat||", &Frame); dlg.m_ofn.lpstrTitle = "Skills file"; if (dlg.DoModal() != IDOK) return; try { POSITION skillPos; CSkill * skill; CFile f (dlg.GetPathName(), CFile::modeCreate|CFile::modeWrite|CFile::shareExclusive); CArchive ar(&f, CArchive::store); for (skillPos = App.m_SkillList.GetHeadPosition (); skillPos; ) { skill = App.m_SkillList.GetNext (skillPos); ar.WriteString ("#SKILL" ENDLINE); CString strType = SkillTypeList.ReturnName (skill->type, false); if (!strType.IsEmpty ()) strType.SetAt (0, toupper (strType [0])); // capitalise type #define CONDSTRING(name, field) \ do { \ if (!skill->field.IsEmpty ()) \ ar.WriteString (CFormat (name "%s~" ENDLINE, (LPCTSTR) skill->field)); \ } while (false) #define CONDWORD(name, field) \ do { \ if (!skill->field.IsEmpty ()) \ ar.WriteString (CFormat (name "%s" ENDLINE, (LPCTSTR) skill->field)); \ } while (false) #define CONDNUMBER(name, field) \ do { \ if (skill->field) \ ar.WriteString (CFormat (name "%i" ENDLINE, skill->field)); \ } while (false) // these two lines are required ar.WriteString (CFormat ("Name %s~" ENDLINE, (LPCTSTR) skill->name)); ar.WriteString (CFormat ("Type %s" ENDLINE, (LPCTSTR) strType)); ar.WriteString (CFormat ("Flags %i" ENDLINE, skill->flags)); // CONDNUMBER ("Flags ", flags); CONDNUMBER ("Guild ", guild); CONDNUMBER ("Target ", target); CONDNUMBER ("Minpos ", minimum_position); CONDNUMBER ("Slot ", slot); CONDNUMBER ("Mana ", min_mana); CONDNUMBER ("Rounds ", beats); CONDWORD ("Code ", spell_skill_fun); ar.WriteString (CFormat ("Dammsg %s~" ENDLINE, (LPCTSTR) skill->noun_damage)); CONDSTRING ("Wearoff ", msg_off); CONDSTRING ("Teachers ", teachers); CONDSTRING ("Hitchar ", hit_char); CONDSTRING ("Hitvict ", hit_vict); CONDSTRING ("Hitroom ", hit_room); CONDSTRING ("Hitdest ", hit_dest); CONDSTRING ("Misschar ", miss_char); CONDSTRING ("Missvict ", miss_vict); CONDSTRING ("Missroom ", miss_room); CONDSTRING ("Dice ", dice); CONDNUMBER ("Participants ", participants); CONDSTRING ("Components ", components); CONDSTRING ("Immchar ", imm_char); CONDSTRING ("Immroom ", imm_room); CONDSTRING ("Immvict ", imm_vict); CONDSTRING ("Diechar ", die_char); CONDSTRING ("Dieroom ", die_room); CONDSTRING ("Dievict ", die_vict); // affects for (POSITION affectPos = skill->affects.GetHeadPosition (); affectPos; ) { CSkillAffect *affect = skill->affects.GetNext (affectPos); ar.WriteString (CFormat ("Affect \'%s\' %i \'%s\' %i" ENDLINE, (LPCTSTR) affect->duration, affect->location, (LPCTSTR) affect->modifier, affect->bitvector)); } // end of affects list CONDNUMBER ("Value ", value); ar.WriteString (CFormat ("Minlevel %i" ENDLINE, skill->min_level)); CONDNUMBER ("Difficulty ", difficulty); CONDNUMBER ("Info ", info); CONDNUMBER ("Range ", range); CONDNUMBER ("Saves ", saves); // Races int x; // I don't have an example of race, but will assume we do it if nonzero for (x = 0; x < MAX_CLASS; x++ ) { if (skill->race_level[x] || skill->race_adept[x]) ar.WriteString (CFormat ("Race %i %i %i" ENDLINE, x, skill->race_level[x] , skill->race_adept[x])); } // Classs // I don't have an example of class, but will assume we do it if nonzero for (x = 0; x < MAX_CLASS; x++ ) { if (skill->skill_level[x] || skill->skill_adept[x]) ar.WriteString (CFormat ("Class %i %i %i" ENDLINE, x, skill->skill_level[x] , skill->skill_adept[x])); } ar.WriteString ("End" ENDLINE); // end of this skill ar.WriteString (ENDLINE); // blank line } // end of each skill ar.WriteString ("#END" ENDLINE); // skills file terminator ar.Close (); } catch (CException* e) { e->ReportError(); e->Delete(); } m_bSkillsDirty = false; } void CAreaEditorApp::OnUpdateSkillsSave(CCmdUI* pCmdUI) { pCmdUI->Enable (!m_SkillList.IsEmpty () && m_bSkillsDirty); } void CAreaEditorApp::DeleteSkills (void) { // ======================================================================== // delete all skills for (POSITION skillPos = m_SkillList.GetHeadPosition (); skillPos; ) delete m_SkillList.GetNext (skillPos); m_SkillList.RemoveAll (); // pointers are deleted, remove list items m_bSkillsDirty = false; } int CompareSkill (const void * elem1, const void * elem2) { // ah yes, *this* line was fun to get working :) return (*((CSkill **) elem1))->name.CompareNoCase ((*((CSkill **) elem2))->name); } void CAreaEditorApp::SortSkills (void) { POSITION SkillPos; CTypedPtrArray <CPtrArray, CSkill*> CSkillsArray; int iCount = App.m_SkillList.GetCount (); int i; try { CSkillsArray.SetSize (iCount); // extract pointers into a simple array for (i = 0, SkillPos = m_SkillList.GetHeadPosition (); SkillPos; i++) CSkillsArray.SetAt (i, m_SkillList.GetNext (SkillPos)); // sort the array qsort (CSkillsArray.GetData (), iCount, sizeof (CSkill *), CompareSkill); // get rid of old list m_SkillList.RemoveAll (); // re-add in sorted order for (i = 0; i < iCount; i++) m_SkillList.AddTail (CSkillsArray [i]); } // end of try block catch(CException* e) { e->ReportError (); e->Delete(); } }