/* ** 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" #define SETUP_DEFAULTS #include "defaults.h" #undef SETUP_DEFAULTS #include "configuration.h" #include "DefaultFlags.h" #include "DefaultLists.h" tConfiguration GeneralDirectives [] = { { "maxlevel", cfgInteger, &iMaxLevel } , { "maxvnum", cfgInteger, &iMaxVnum }, { "maxresetfreq", cfgInteger, &iMaxResetFreq }, { "maxdescriptionlength", cfgInteger, &iMaxDescriptionLength }, }; // end of configuration directives tConfiguration MobDirectives [] = { { "minac", cfgInteger, &iMinAC } , { "maxac", cfgInteger, &iMaxAC }, { "maxdamnodice", cfgInteger, &iMaxdamnodice }, { "maxdamplus", cfgInteger, &iMaxdamplus}, { "maxdamroll", cfgInteger, &iMaxdamroll}, { "maxmananodice", cfgInteger, &iMaxmananodice }, { "maxmanaplus", cfgInteger, &iMaxmanaplus}, { "maxmanasizedice",cfgInteger, &iMaxmanasizedice}, { "maxdamsizedice", cfgInteger, &iMaxdamsizedice}, { "maxhitnodice", cfgInteger, &iMaxhitnodice }, { "maxhitplus", cfgInteger, &iMaxhitplus}, { "maxhitroll", cfgInteger, &iMaxhitroll}, { "maxhitsizedice", cfgInteger, &iMaxhitsizedice}, { "minstats", cfgInteger, &iMinStats}, { "maxstats", cfgInteger, &iMaxStats}, { "minalignment", cfgInteger, &iMinAlignment}, { "maxalignment", cfgInteger, &iMaxAlignment}, { "maxnumattacks", cfgInteger, &iMaxNumattacks}, { "minsavingthrow", cfgInteger, &iMinSavingthrow}, { "maxsavingthrow", cfgInteger, &iMaxSavingthrow}, { "defaultname", cfgString, &strMobDefaultName }, { "defaultshortdescription", cfgString, &strMobDefaultShortDescription }, { "defaultlongdescription", cfgString, &strMobDefaultLongDescription }, { "defaultdescription", cfgString, &strMobDefaultDescription }, }; // end of configuration directives tConfiguration ObjectDirectives [] = { { "defaultname", cfgString, &strObjectDefaultName }, { "defaultshortdescription", cfgString, &strObjectDefaultShortDescription }, { "defaultdescription", cfgString, &strObjectDefaultDescription }, { "mincondition", cfgInteger, &iMinCondition }, { "maxcondition", cfgInteger, &iMaxCondition }, }; // end of configuration directives tConfiguration RoomDirectives [] = { { "defaultname", cfgString, &strRoomDefaultName }, { "defaultdescription", cfgString, &strRoomDefaultDescription }, }; // end of configuration directives tConfiguration ExitDirectives [] = { { "defaultkeywords", cfgString, &strExitDefaultKeywords }, { "defaultdescription", cfgString, &strExitDefaultDescription }, }; // end of configuration directives tConfiguration RepairDirectives [] = { { "minprofitfix", cfgInteger, &iMinprofitfix } , { "maxprofitfix", cfgInteger, &iMaxprofitfix }, }; // end of configuration directives tConfiguration ShopDirectives [] = { { "minprofitbuy", cfgInteger, &iMinprofitbuy } , { "maxprofitbuy", cfgInteger, &iMaxprofitbuy }, { "minprofitsell", cfgInteger, &iMinprofitsell } , { "maxprofitsell", cfgInteger, &iMaxprofitsell }, }; // end of configuration directives // used for reading in flags in general tFlags Flags; tConfiguration FlagDirectives [64] = { { "flag01", cfgWord, &Flags.strName [0] } , { "flag02", cfgWord, &Flags.strName [1] } , { "flag03", cfgWord, &Flags.strName [2] } , { "flag04", cfgWord, &Flags.strName [3] } , { "flag05", cfgWord, &Flags.strName [4] } , { "flag06", cfgWord, &Flags.strName [5] } , { "flag07", cfgWord, &Flags.strName [6] } , { "flag08", cfgWord, &Flags.strName [7] } , { "flag09", cfgWord, &Flags.strName [8] } , { "flag10", cfgWord, &Flags.strName [9] } , { "flag11", cfgWord, &Flags.strName [10] } , { "flag12", cfgWord, &Flags.strName [11] } , { "flag13", cfgWord, &Flags.strName [12] } , { "flag14", cfgWord, &Flags.strName [13] } , { "flag15", cfgWord, &Flags.strName [14] } , { "flag16", cfgWord, &Flags.strName [15] } , { "flag17", cfgWord, &Flags.strName [16] } , { "flag18", cfgWord, &Flags.strName [17] } , { "flag19", cfgWord, &Flags.strName [18] } , { "flag20", cfgWord, &Flags.strName [19] } , { "flag21", cfgWord, &Flags.strName [20] } , { "flag22", cfgWord, &Flags.strName [21] } , { "flag23", cfgWord, &Flags.strName [22] } , { "flag24", cfgWord, &Flags.strName [23] } , { "flag25", cfgWord, &Flags.strName [24] } , { "flag26", cfgWord, &Flags.strName [25] } , { "flag27", cfgWord, &Flags.strName [26] } , { "flag28", cfgWord, &Flags.strName [27] } , { "flag29", cfgWord, &Flags.strName [28] } , { "flag30", cfgWord, &Flags.strName [29] } , { "flag31", cfgWord, &Flags.strName [30] } , { "flag32", cfgWord, &Flags.strName [31] } , { "flag33", cfgWord, &Flags.strName [32] } , { "flag34", cfgWord, &Flags.strName [33] } , { "flag35", cfgWord, &Flags.strName [34] } , { "flag36", cfgWord, &Flags.strName [35] } , { "flag37", cfgWord, &Flags.strName [36] } , { "flag38", cfgWord, &Flags.strName [37] } , { "flag39", cfgWord, &Flags.strName [38] } , { "flag40", cfgWord, &Flags.strName [39] } , { "flag41", cfgWord, &Flags.strName [40] } , { "flag42", cfgWord, &Flags.strName [41] } , { "flag43", cfgWord, &Flags.strName [42] } , { "flag44", cfgWord, &Flags.strName [43] } , { "flag45", cfgWord, &Flags.strName [44] } , { "flag46", cfgWord, &Flags.strName [45] } , { "flag47", cfgWord, &Flags.strName [46] } , { "flag48", cfgWord, &Flags.strName [47] } , { "flag49", cfgWord, &Flags.strName [48] } , { "flag50", cfgWord, &Flags.strName [49] } , { "flag51", cfgWord, &Flags.strName [50] } , { "flag52", cfgWord, &Flags.strName [51] } , { "flag53", cfgWord, &Flags.strName [52] } , { "flag54", cfgWord, &Flags.strName [53] } , { "flag55", cfgWord, &Flags.strName [54] } , { "flag56", cfgWord, &Flags.strName [55] } , { "flag57", cfgWord, &Flags.strName [56] } , { "flag58", cfgWord, &Flags.strName [57] } , { "flag59", cfgWord, &Flags.strName [58] } , { "flag60", cfgWord, &Flags.strName [59] } , { "flag61", cfgWord, &Flags.strName [60] } , { "flag62", cfgWord, &Flags.strName [61] } , { "flag63", cfgWord, &Flags.strName [62] } , { "flag64", cfgWord, &Flags.strName [63] } , }; // end of configuration directives // this lists all flag sections tFlagsEntry AllFlags [] = { {"act", &MobActFlags, act_flags, 64 }, {"affect", &MobAffectFlags, a_flags, 64 }, {"attack", &MobAttackFlags, attack_flags, 64 }, {"defense", &MobDefenseFlags, defense_flags, 64 }, {"parts", &MobPartsFlags, part_flags, 32 }, {"resist", &MobResistFlags, ris_flags, 32 }, {"language",&MobLanguageFlags,lang_names, 32 }, {"object", &ObjectFlags, o_flags, 64 }, {"wear", &WearFlags, w_flags, 32 }, {"room", &RoomFlags, r_flags, 32 }, {"exit", &ExitFlags, exit_flags, 32 }, {"area", &AreaFlags, area_flags, 32 }, // for ROM {"form", &MobFormFlags, form_flags, 32 }, {"furniture", &ObjectFurnitureFlags, furniture_flags, 32 }, {"container", &ObjectContainerFlags, container_flags, 32 }, {"weaponflags", &ObjectWeaponFlags, weapon_flags, 32 }, {"portal", &ObjectPortalFlags, portal_flags, 32 }, }; // end of list of flags tListEntry AllLists [] = { {"constants", &Constants, NULL }, // mobs {"sex", &MobSexList, sex_table }, {"size", &MobSizeList, size_table }, {"race", &MobRaceList, race_table }, {"class", &MobClassList, class_table }, {"position", &MobPositionList, position_table }, {"special", &MobSpecialList, special_functions_table }, // objects {"weapon", &ObjectWeaponList, weapon_table }, {"liquid", &ObjectLiquidList, liquid_table }, {"attacklist", &ObjectAttackList, attack_table }, {"item" , &ObjectItemList, item_table }, {"wearlist" , &ObjectWearList, wear_table }, {"affects", &ObjectAffectList, affect_table }, {"weaponcondition", &ObjectWeaponConditionList, weapon_condition_table }, {"armourcondition", &ObjectArmourConditionList, armour_condition_table }, {"foodcondition", &ObjectFoodConditionList, food_condition_table }, {"affectwhere", &ObjectAffectWhereList, affect_where_table }, // rooms {"sector", &RoomSectorList, sector_table }, {"direction", &RoomDirectionList, direction_table }, // general {"skilltype", &SkillTypeList, skill_types_table }, {"spell", &SpellNameList, spell_names_table }, {"smaugskill", &SMAUGSkillNameList,SMAUG_skill_names_table }, {"romskill", &ROMSkillNameList, ROM_skill_names_table }, {"program", &ProgramNameList, program_table }, }; // end of list of lists // this lists all non-flag sections tOtherEntry AllSections [] = { { "general", GeneralDirectives, NUMITEMS (GeneralDirectives) }, { "mobiles", MobDirectives, NUMITEMS (MobDirectives) }, { "objects", ObjectDirectives, NUMITEMS (ObjectDirectives) }, { "rooms", RoomDirectives, NUMITEMS (RoomDirectives) }, { "exits", ExitDirectives, NUMITEMS (ExitDirectives) }, { "repairs", RepairDirectives, NUMITEMS (RepairDirectives) }, { "shops", ShopDirectives, NUMITEMS (ShopDirectives) }, }; // end of list of other things void ProcessSection (CFileRead & FileRead, tConfiguration * ConfigurationDirectives, const int iNumber) { CString word; CString buf; bool bMatch; for (int i = 0; i < iNumber; i++) ConfigurationDirectives [i].bFound = false; while (true) { word = FileRead.fread_word (); word.MakeLower (); bMatch = false; if (word == "*") { FileRead.fread_to_eol (); bMatch = true; } else if (word == "end") { // check all required directives found for (int i = 0; i < iNumber; i++) if (ConfigurationDirectives [i].bRequired && !ConfigurationDirectives [i].bFound) ThrowErrorException ("Required entry \"%s\" not found.", ConfigurationDirectives [i].pName); return; } else for (int i = 0; i < iNumber; i++) { if (word == ConfigurationDirectives [i].pName) { switch (ConfigurationDirectives [i].type) { case cfgString: // string delimited by ~ *((CString *) ConfigurationDirectives [i].pData) = FileRead.fread_string (); break; case cfgWord: // single word *((CString *) ConfigurationDirectives [i].pData) = FileRead.fread_word (); break; case cfgInteger: // number *((int *) ConfigurationDirectives [i].pData) = FileRead.fread_number (); break; case cfgBoolean: // boolean buf = FileRead.fread_word (); buf.MakeLower (); if (buf == "yes" || buf == "y" || buf == "true") *((bool *) ConfigurationDirectives [i].pData) = true; else if (buf == "no" || buf == "n" || buf == "false") *((bool *) ConfigurationDirectives [i].pData) = false; else ThrowErrorException ("Directive \"%s\" value \"%s\" should be YES or NO", ConfigurationDirectives [i].pName, (LPCTSTR) buf); break; default: ThrowErrorException ("Unknown configuration type for directive \"%s\"", ConfigurationDirectives [i].pName); } // end of switch on type ConfigurationDirectives [i].bFound = true; // note we found this one bMatch = true; FileRead.fread_to_eol (); // skip rest of line so it can be a comment break; } // end of a match } // end of looping through all directives if ( !bMatch ) ThrowErrorException ("Configuration directive \"%s\" unknown.", (LPCTSTR) word); } // end of looping until End found } // end of ProcessSection static void GetFlags (CFileRead & FileRead, tFlags & DestinationFlags, const int iMaxFlags) { // process the flags section into our general-purpose array ProcessSection (FileRead, FlagDirectives, NUMITEMS (FlagDirectives)); // copy from general-purpose array to specific flags for (int i = 0; i < MAX_BITS; i++) if (FlagDirectives [i].bFound) { if (i > iMaxFlags) ThrowErrorException ("Too many flags for this section"); DestinationFlags.strName [i] = *((CString *) FlagDirectives [i].pData); } else DestinationFlags.strName [i].Empty (); } // end of GetFlags static void AddListItem (const CString & sWord, CConfigList & DestinationList, int & iLastValue) { CString strName; CString strValue; int iValue; int iEquals; iEquals = sWord.Find ('='); if (iEquals == -1) { // take next value iLastValue++; DestinationList.list.AddTail (tList (sWord, iLastValue)); } // end of no value specified else { // list items can be: fred=22 // isolate name and value strName = sWord.Left (iEquals); strValue = sWord.Mid (iEquals + 1); strName.TrimLeft (); strName.TrimRight (); strValue.TrimLeft (); strValue.TrimRight (); // name cannot be empty if (strName.IsEmpty ()) ThrowErrorException ("Invalid list item format \"%s\" - null name", (LPCTSTR) sWord); // check for duplicates int iExistingValue; if (DestinationList.FindValue (strName, iExistingValue)) ThrowErrorException ("List item \"%s\" is already in this list with value %i", (LPCTSTR) strName, iExistingValue); // value cannot be empty if (strValue.IsEmpty ()) ThrowErrorException ("Invalid list item format \"%s\" - null value", (LPCTSTR) sWord); // if value is numeric, use it if (isdigit (strValue [0]) || strValue [0] == '+' || strValue [0] == '-') iValue = atoi (strValue); else { // lookup constants list, so you can cross-reference to a list of constants if (!Constants.FindValue (strValue, iValue)) // lookup an earlier entry in this list, so you can equate values if (!DestinationList.FindValue (strValue, iValue)) // not a number, or existing list item - must be an error ThrowErrorException ("Invalid list item format \"%s\" - bad value", (LPCTSTR) sWord); } iLastValue = iValue; DestinationList.list.AddTail (tList (strName, iValue)); } // end of value specified } // end of AddListItem static void GetList(CFileRead & FileRead, CConfigList & DestinationList) { CString word; int iLastValue = -1; // clear old contents of list DestinationList.list.RemoveAll (); while (true) { word = FileRead.fread_line (); word.MakeLower (); if (word [0] == '*') continue; // ignore line starting with * (comment) else if (word == "end") return; // the word "end" ends the list else AddListItem (word, DestinationList, iLastValue); } // end of looping until End found } // end of GetFlags void CAreaEditorApp::LoadConfig (const CString strFileName) { int i; CWaitCursor wait; try { // they are (re)processing the config file - (re)setup all default flags SetUpDefaultFlags (); SetUpDefaultLists (); // and lists CFileRead FileRead; // Open Config file CFile fConfig (strFileName, CFile::modeRead|CFile::shareDenyWrite); CArchive ar(&fConfig, CArchive::load); FileRead.Init ("Loading Config", &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 (); word.MakeLower (); // look for configuration sections like "general", "mobiles" for (i = 0; i < NUMITEMS (AllSections); i++) { if (word == AllSections [i].pName) { ProcessSection (FileRead, AllSections [i].pSection, AllSections [i].nItems); break; } } if (i < NUMITEMS (AllSections)) continue; // found section, don't need to look at others // look for "flag" sections (eg. "act", "room") for (i = 0; i < NUMITEMS (AllFlags); i++) { if (word == AllFlags [i].pName) { GetFlags (FileRead, *(AllFlags [i].pFlags), AllFlags [i].iMaxFlags); break; } } if (i < NUMITEMS (AllFlags)) continue; // found flag, don't need to look at others // look for "list" sections (eg. "sex", "race") for (i = 0; i < NUMITEMS (AllLists); i++) { if (word == AllLists [i].pName) { GetList (FileRead, *(AllLists [i].pList)); break; } } if (i < NUMITEMS (AllLists)) continue; // found list, don't need to look at others if (word == "end") break; else ThrowErrorException ( "Bad section: %s.", (LPCTSTR) word); } // end of read loop } // end of try block catch(CException* e) { FileRead.Wrapup (); 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 Config file: %s", (LPCTSTR) strFileName), MB_ICONEXCLAMATION); e->Delete(); return; } } void CAreaEditorApp::SetUpDefaultFlags (void) { int i, j; // initialise all default flag names for (j = 0; j < NUMITEMS (AllFlags); j++) for (i = 0; i < MAX_BITS; i++) AllFlags [j].pFlags->strName [i] = AllFlags [j].sDefaultFlags [i]; } void CAreaEditorApp::SetUpDefaultLists (void) { int i, j; int iLastValue; // initialise all default list entries for (j = 0; j < NUMITEMS (AllLists); j++) { iLastValue = -1; // new list - starts at zero AllLists [j].pList->list.RemoveAll (); if (AllLists [j].sDefaultList) // null list means no defaults for (i = 0; AllLists [j].sDefaultList [i]; i++) AddListItem (AllLists [j].sDefaultList [i], *(AllLists [j].pList), iLastValue); } // end of doing each list } // end of CAreaEditorApp::SetUpDefaultLists // finds a corresponding name for a given value bool CConfigList::FindName (const int iValue, CString & strName, const bool bDefaultToFirst) const { if (list.IsEmpty () && bDefaultToFirst) { strName = "0"; // cannot show first item, show zero return false; } for (POSITION pos = list.GetHeadPosition (); pos; ) { tList listItem = list.GetNext (pos); if (listItem.iValue == iValue) { strName = listItem.strName; return true; } } // end of loop // if not found, default to first item in list if (bDefaultToFirst) strName = list.GetHead ().strName; else strName = CFormat ("Unknown=%i", iValue); return false; // not found } // end of CConfigList::FindName // returns a corresponding name for a given value CString CConfigList::ReturnName (const EXT_BV iValue, const bool bDefaultToFirst) const { if (list.IsEmpty () && bDefaultToFirst) return "0"; for (POSITION pos = list.GetHeadPosition (); pos; ) { tList listItem = list.GetNext (pos); if (listItem.iValue == iValue) return listItem.strName; } // end of loop // if not found, default to first item in list or show offending item if (bDefaultToFirst) return list.GetHead ().strName; // not found else return CFormat ("Unknown=%i", iValue); } // end of CConfigList::ReturnName bool CConfigList::IsValueInList (const int iValue) const // true if found { for (POSITION pos = list.GetHeadPosition (); pos; ) if (list.GetNext (pos).iValue == iValue) return true; return false; // not found } // finds a corresponding value for a given name bool CConfigList::FindValue (const CString strName, int & iValue, const bool bPartialMatch) const { for (POSITION pos = list.GetHeadPosition (); pos; ) { tList listItem = list.GetNext (pos); if (bPartialMatch) { if (str_prefix (strName, listItem.strName) == 0) { iValue = listItem.iValue; return true; } // end of partial match } // end of partial match OK else if (listItem.strName.CompareNoCase (strName) == 0) { iValue = listItem.iValue; return true; } } // end of loop iValue = 0; return false; // not found } // end of CConfigList::FindValue // returns a corresponding value for a given name int CConfigList::ReturnValue (const CString strName, const bool bPartialMatch) const { for (POSITION pos = list.GetHeadPosition (); pos; ) { tList listItem = list.GetNext (pos); if (bPartialMatch) { if (str_prefix (strName, listItem.strName) == 0) return listItem.iValue; } // end of partial match OK else if (listItem.strName.CompareNoCase (strName) == 0) return listItem.iValue; } // end of loop return 0; // not found } // end of CConfigList::ReturnValue bool CConfigList::IsNameInList (const CString strName, const bool bPartialMatch) const { for (POSITION pos = list.GetHeadPosition (); pos; ) { tList listItem = list.GetNext (pos); if (bPartialMatch) { if (str_prefix (strName, listItem.strName) == 0) return true; } // end of partial match OK else if (listItem.strName.CompareNoCase (strName) == 0) return true; } // end of loop return false; // not found }