/* * files-xml-save.cpp * Used to serialize structures (object, creature, room, etc) to xml files * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * Permission to use, modify and distribute is granted via the * Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License * http://creativecommons.org/licenses/by-nc-sa/3.0/ * * Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell * Contributions by Tim Callahan, Jonathan Hseu * Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman * */ /* Web Editor * _ _ ____ _______ ______ * | \ | |/ __ \__ __| ____| * | \| | | | | | | | |__ * | . ` | | | | | | | __| * | |\ | |__| | | | | |____ * |_| \_|\____/ |_| |______| * * If you change anything here, make sure the changes are reflected in the web * editor! Either edit the PHP yourself or tell Dominus to make the changes. */ #include "mud.h" #include "version.h" #include "effects.h" #include "bans.h" #include "guilds.h" #include "factions.h" #include "specials.h" #include "calendar.h" #include "quests.h" #include "unique.h" #include "magic.h" #include "alchemy.h" xmlNodePtr saveObjRefFlags(xmlNodePtr parentNode, const char* name, int maxBit, const char *bits); // Object flags to be saved for object refs int objRefSaveFlags[] = { O_PERM_ITEM, O_HIDDEN, O_CURSED, O_WORN, O_TEMP_ENCHANT, O_WAS_SHOPLIFTED, O_ENVENOMED, O_JUST_BOUGHT, O_NO_DROP, O_BROKEN_BY_CMD, O_BEING_PREPARED, O_UNIQUE, O_KEEP, O_DARKNESS, -1 }; //********************************************************************* // saveToFile //********************************************************************* // This function will write the supplied player to PLAYERPATH/Player.xml // NOTE: For now, it will ignore equiped equipment, so be sure to // remove the equiped equipment and put it in the inventory before // calling this function otherwise it will be lost int Player::saveToFile(LoadType saveType) { xmlDocPtr xmlDoc; xmlNodePtr rootNode; char filename[256]; ASSERTLOG( this != NULL ); ASSERTLOG( name[0] != '\0' ); ASSERTLOG( isPlayer() ); if(name[0] == '\0') { printf("Invalid player passed to save\n"); return(-1); } xmlDoc = xmlNewDoc(BAD_CAST "1.0"); rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Player", NULL); xmlDocSetRootElement(xmlDoc, rootNode); escapeText(); saveToXml(rootNode, ALLITEMS, LS_FULL); if(saveType == LS_BACKUP) { checkDirExists(BACKUPPATH, true); sprintf(filename, "%s/%s.bak.xml", BACKUPPATH, name); } else { checkDirExists(PLAYERPATH, true); sprintf(filename, "%s/%s.xml", PLAYERPATH, name); } xml::saveFile(filename, xmlDoc); xmlFreeDoc(xmlDoc); return(0); } //********************************************************************* // saveToFile //********************************************************************* // This function will write the supplied room to ROOMPATH/r#####.xml // It will most likely only be called from *save r int Room::saveToFile(int permOnly, LoadType saveType) { xmlDocPtr xmlDoc; xmlNodePtr rootNode; char filename[256]; ASSERTLOG( this != NULL ); ASSERTLOG( info.id >= 0 ); if(saveType == LS_BACKUP) checkDirExists(info.area, roomBackupPath); else checkDirExists(info.area, roomPath); xmlDoc = xmlNewDoc(BAD_CAST "1.0"); rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Room", NULL); xmlDocSetRootElement(xmlDoc, rootNode); escapeText(); saveToXml(rootNode, permOnly); if(saveType == LS_BACKUP) strcpy(filename, roomBackupPath(info)); else strcpy(filename, roomPath(info)); xml::saveFile(filename, xmlDoc); xmlFreeDoc(xmlDoc); return(0); } //********************************************************************* // saveToFile //********************************************************************* // This function will write the supplied creature to the proper // m##.xml file - To accomplish this it parse the proper m##.xml file if // available and walk down the tree to the proper place in numerical order // if the file does not exist it will create it with only that monster in it. // It will most likely only be called from *save c int Monster::saveToFile() { xmlDocPtr xmlDoc; xmlNodePtr rootNode; char filename[256]; // If we can't clean the monster properly, don't save anything if(cleanMobForSaving() != 1) return(-1); // Invalid Number if(info.id < 0) return(-1); checkDirExists(info.area, monsterPath); xmlDoc = xmlNewDoc(BAD_CAST "1.0"); rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Creature", NULL); xmlDocSetRootElement(xmlDoc, rootNode); escapeText(); saveToXml(rootNode, ALLITEMS, LS_FULL); strcpy(filename, monsterPath(info)); xml::saveFile(filename, xmlDoc); xmlFreeDoc(xmlDoc); return(0); } //********************************************************************* // saveObject //********************************************************************* // This function will write the supplied object to the proper // o##.xml file - To accomplish this it parse the proper o##.xml file if // available and walk down the tree to the proper place in numerical order // if the file does not exist it will create it with only that object in it. // It will most likely only be called from *save o int Object::saveToFile() { xmlDocPtr xmlDoc; xmlNodePtr rootNode; char filename[256]; ASSERTLOG( this ); // Invalid Number if(info.id < 0) return(-1); checkDirExists(info.area, objectPath); xmlDoc = xmlNewDoc(BAD_CAST "1.0"); rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Object", NULL); xmlDocSetRootElement(xmlDoc, rootNode); // make sure uniqueness stays intact setFlag(O_UNIQUE); if(!gConfig->getUnique(this)) clearFlag(O_UNIQUE); escapeText(); saveToXml(rootNode, ALLITEMS, LS_FULL); strcpy(filename, objectPath(info)); xml::saveFile(filename, xmlDoc); xmlFreeDoc(xmlDoc); return(0); } //********************************************************************* // saveGuilds //********************************************************************* // Causes the guilds structure to be generated into an xml tree and saved bool Config::saveGuilds() const { GuildCreation * gcp; xmlDocPtr xmlDoc; xmlNodePtr rootNode; //xmlNodePtr curNode, bankNode, membersNode; char filename[80]; //int i; xmlDoc = xmlNewDoc(BAD_CAST "1.0"); rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Guilds", NULL); xmlDocSetRootElement(xmlDoc, rootNode); xml::newNumProp(rootNode, "NextGuildId", nextGuildId); std::map<int, Guild*>::const_iterator it; Guild *guild; for(it = guilds.begin() ; it != guilds.end() ; it++) { guild = (*it).second; if(guild->num == 0) continue; guild->saveToXml(rootNode); } std::list<GuildCreation*>::const_iterator gcIt; for(gcIt = guildCreations.begin() ; gcIt != guildCreations.end() ; gcIt++) { gcp = (*gcIt); gcp->saveToXml(rootNode); } sprintf(filename, "%s/guilds.xml", CONFPATH); xml::saveFile(filename, xmlDoc); xmlFreeDoc(xmlDoc); return(true); } //********************************************************************* // saveBans //********************************************************************* bool Config::saveBans() const { int found=0; xmlDocPtr xmlDoc; xmlNodePtr rootNode; xmlNodePtr curNode; char filename[80]; xmlDoc = xmlNewDoc(BAD_CAST "1.0"); rootNode = xmlNewDocNode(xmlDoc, NULL,BAD_CAST "Bans", NULL); xmlDocSetRootElement(xmlDoc, rootNode); std::list<Ban*>::const_iterator it; Ban* ban; for(it = bans.begin() ; it != bans.end() ; it++) { found++; ban = (*it); curNode = xmlNewChild(rootNode, NULL, BAD_CAST "Ban", NULL); // Site xmlNewChild(curNode, NULL, BAD_CAST "Site", BAD_CAST ban->site.c_str()); // Duration //sprintf(buf, "%d", ban->duration); xml::newNumChild(curNode, "Duration", ban->duration); // Unban Time //sprintf(buf, "%ld", ban->unbanTime); xml::newNumChild(curNode, "UnbanTime", ban->unbanTime); // Banned By xmlNewChild(curNode, NULL, BAD_CAST "BannedBy", BAD_CAST ban->by.c_str()); // Ban Time xmlNewChild(curNode, NULL, BAD_CAST "BanTime", BAD_CAST ban->time.c_str()); // Reason xmlNewChild(curNode, NULL, BAD_CAST "Reason", BAD_CAST ban->reason.c_str()); // Password xmlNewChild(curNode, NULL, BAD_CAST "Password", BAD_CAST ban->password.c_str()); // Suffix xmlNewChild(curNode, NULL, BAD_CAST "Suffix", BAD_CAST iToYesNo(ban->isSuffix)); // Prefix xmlNewChild(curNode, NULL, BAD_CAST "Prefix", BAD_CAST iToYesNo(ban->isPrefix)); } sprintf(filename, "%s/bans.xml", CONFPATH); xml::saveFile(filename, xmlDoc); xmlFreeDoc(xmlDoc); return(true); } //********************************************************************* // saveToXml //********************************************************************* // This will save the entire creature with anything non zero to the given // node which should be a creature or player node int Creature::saveToXml(xmlNodePtr rootNode, int permOnly, LoadType saveType) const { // xmlNodePtr rootNode; xmlNodePtr curNode; xmlNodePtr childNode; int i; if(name[0] == '\0' || rootNode == NULL) return(-1); const Player *pPlayer = getConstPlayer(); const Monster *mMonster = getConstMonster(); if(pPlayer) { xml::newProp(rootNode, "Name", pPlayer->name); xml::newProp(rootNode, "Password", pPlayer->getPassword()); xml::newNumProp(rootNode, "LastLogin", pPlayer->getLastLogin()); } else if(mMonster) { // Saved for LS_REF and LS_FULL xml::newNumProp(rootNode, "Num", mMonster->info.id); xml::saveNonNullString(rootNode, "Name", mMonster->name); xml::newProp(rootNode, "Area", mMonster->info.area); } // For the future, when we change things, can read them in based on the version they // were saved in before or do modifications based on the new version xml::newProp(rootNode, "Version", VERSION); if(pPlayer) { if(pPlayer->area_room) { curNode = xml::newStringChild(rootNode, "AreaRoom"); pPlayer->area_room->mapmarker.save(curNode); } else pPlayer->room.save(rootNode, "Room", true); } // Saved for LS_REF and LS_FULL xml::saveNonZeroNum(rootNode, "Race", race); xml::saveNonZeroNum(rootNode, "Class", cClass); if(pPlayer) { xml::saveNonZeroNum(rootNode, "Class2", pPlayer->getSecondClass()); } else if(mMonster) { // TODO: Dom: for compatability, remove when possible xml::saveNonZeroNum(rootNode, "Class2", mMonster->getMobTrade()); xml::saveNonZeroNum(rootNode, "MobTrade", mMonster->getMobTrade()); } xml::saveNonZeroNum(rootNode, "Level", level); xml::saveNonZeroNum(rootNode, "Type", (int)type); xml::saveNonZeroNum(rootNode, "Experience", experience); coins.save("Coins", rootNode); xml::saveNonZeroNum(rootNode, "Alignment", alignment); curNode = xml::newStringChild(rootNode, "Stats"); strength.save(curNode, "Strength"); dexterity.save(curNode, "Dexterity"); constitution.save(curNode, "Constitution"); intelligence.save(curNode, "Intelligence"); piety.save(curNode, "Piety"); hp.save(curNode, "Hp"); mp.save(curNode, "Mp"); hooks.save(curNode, "Hooks"); if(pPlayer) pPlayer->focus.save(curNode, "Focus"); // Only saved for LS_FULL saves, or player saves if(saveType == LS_FULL || pPlayer) { xml::saveNonNullString(rootNode, "Description", description); // Keys curNode = xml::newStringChild(rootNode, "Keys"); for(i=0; i<3; i++) { if(key[i][0] == 0) continue; childNode = xml::newStringChild(curNode, "Key", key[i]); xml::newNumProp(childNode, "Num", i); } // MoveTypes curNode = xml::newStringChild(rootNode, "MoveTypes"); for(i=0; i<3; i++) { if(movetype[i][0] == 0) continue; childNode = xml::newStringChild(curNode, "MoveType", movetype[i]); xml::newNumProp(childNode, "Num", i); } xml::saveNonZeroNum(rootNode, "Armor", armor); xml::saveNonZeroNum(rootNode, "Deity", deity); saveULongArray(rootNode, "Realms", "Realm", realm, MAX_REALM-1); saveLongArray(rootNode, "Proficiencies", "Proficiency", proficiency, 6); damage.save(rootNode, "Dice"); xml::saveNonZeroNum(rootNode, "Clan", clan); xml::saveNonZeroNum(rootNode, "PoisonDuration", poison_dur); xml::saveNonZeroNum(rootNode, "PoisonDamage", poison_dmg); xml::saveNonZeroNum(rootNode, "Size", size); if(pPlayer) pPlayer->saveXml(rootNode); else if(mMonster) mMonster->saveXml(rootNode); saveFactions(rootNode); saveSkills(rootNode); effects.save(rootNode, "Effects"); saveAttacks(rootNode); if(!minions.empty()) { curNode = xml::newStringChild(rootNode, "Minions"); std::list<bstring>::const_iterator mIt; for(mIt = minions.begin() ; mIt != minions.end() ; mIt++) { childNode = xml::newStringChild(curNode, "Minion", (*mIt)); } } // saveShortIntArray(rootNode, "Factions", "Faction", faction, MAX_FACTION); // Save dailys curNode = xml::newStringChild(rootNode, "DailyTimers"); for(i=0; i<DAILYLAST+1; i++) saveDaily(curNode, i, daily[i]); // Save saving throws curNode = xml::newStringChild(rootNode, "SavingThrows"); for(i=0; i<MAX_SAVE; i++) saveSavingThrow(curNode, i, saves[i]); // Perhaps change this into saveInt/CharArray saveBits(rootNode, "Spells", MAXSPELL, spells); saveBits(rootNode, "Quests", MAXSPELL, quests); xml::saveNonZeroNum(rootNode, "CurrentLanguage", current_language); saveBits(rootNode, "Languages", LANGUAGE_COUNT, languages); } // Saved for LS_FULL and LS_REF saveBits(rootNode, "Flags", pPlayer ? MAX_PLAYER_FLAGS : MAX_MONSTER_FLAGS, flags); // Save lasttimes for(i=0; i<TOTAL_LTS; i++) { // this nested loop means we won't create an xml node if we don't have to if(lasttime[i].interval || lasttime[i].ltime || lasttime[i].misc) { curNode = xml::newStringChild(rootNode, "LastTimes"); for(; i<TOTAL_LTS; i++) saveLastTime(curNode, i, lasttime[i]); } } curNode = xml::newStringChild(rootNode, "Inventory"); saveObjectsXml(curNode, first_obj, permOnly); // We want quests saved after inventory so when they are loaded we can calculate // if the quest is complete or not and store it in the appropriate variables if(pPlayer) pPlayer->saveQuests(rootNode); return(0); } //********************************************************************* // saveXml //********************************************************************* void Monster::saveXml(xmlNodePtr curNode) const { xmlNodePtr childNode, subNode; xml::saveNonZeroNum(curNode, "SkillLevel", skillLevel); xml::saveNonZeroNum(curNode, "UpdateAggro", updateAggro); xml::saveNonZeroNum(curNode, "LoadAggro", loadAggro); // Attacks childNode = xml::newStringChild(curNode, "Attacks"); for(int i=0; i<3; i++) { if(attack[i][0] == 0) continue; subNode = xml::newStringChild(childNode, "Attack", attack[i]); xml::newNumProp(subNode, "Num", i); } xml::saveNonNullString(curNode, "LastMod", last_mod); xml::saveNonNullString(curNode, "Talk", talk); xmlNodePtr talkNode = xml::newStringChild(curNode, "TalkResponses"); foreach(TalkResponse * talkResponse, responses) talkResponse->saveToXml(talkNode); xml::saveNonNullString(curNode, "TradeTalk", ttalk); xml::saveNonZeroNum(curNode, "NumWander", numwander); xml::saveNonZeroNum(curNode, "MagicResistance", magicResistance); xml::saveNonZeroNum(curNode, "DefenseSkill", defenseSkill); xml::saveNonZeroNum(curNode, "AttackPower", attackPower); xml::saveNonZeroNum(curNode, "WeaponSkill", weaponSkill); saveCarryArray(curNode, "CarriedItems", "Carry", carry, 10); saveCatRefArray(curNode, "AssistMobs", "Mob", assist_mob, NUM_ASSIST_MOB); saveCatRefArray(curNode, "EnemyMobs", "Mob", enemy_mob, NUM_ENEMY_MOB); xml::saveNonNullString(curNode, "PrimeFaction", primeFaction); xml::saveNonNullString(curNode, "AggroString", aggroString); jail.save(curNode, "Jail", false); xml::saveNonZeroNum(curNode, "WeaponSkill", maxLevel); xml::saveNonZeroNum(curNode, "Cast", cast); saveCatRefArray(curNode, "Rescue", "Mob", rescue, NUM_RESCUE); saveBits(curNode, "ClassAggro", 32, cClassAggro); saveBits(curNode, "RaceAggro", 32, raceAggro); saveBits(curNode, "DeityAggro", 32, deityAggro); } //********************************************************************* // saveXml //********************************************************************* void Player::saveXml(xmlNodePtr curNode) const { xmlNodePtr childNode; int i; // record people logging off during roomMove if(gConfig->isMovingRoom()) gConfig->moveRoomLog((bstring)"p" + name, false); bank.save("Bank", curNode); xml::saveNonZeroNum(curNode, "WeaponTrains", weaponTrains); xml::saveNonNullString(curNode, "Surname", surname); xml::saveNonNullString(curNode, "Forum", forum); bound.save(curNode, "BoundRoom"); previousRoom.save(curNode, "PreviousRoom"); statistics.save(curNode, "Statistics"); xml::saveNonZeroNum(curNode, "ActualLevel", actual_level); xml::saveNonZeroNum(curNode, "Created", created); xml::saveNonNullString(curNode, "OldCreated", oldCreated); xml::saveNonZeroNum(curNode, "Guild", guild); if(title != " ") xml::saveNonNullString(curNode, "Title", title); xml::saveNonZeroNum(curNode, "GuildRank", guildRank); if(isStaff()) { childNode = xml::newStringChild(curNode, "Ranges"); for(i=0; i<MAX_BUILDER_RANGE; i++) { // empty range, skip it if(!bRange[i].low.id && !bRange[i].high) continue; bRange[i].save(childNode, "Range", i); } } xml::saveNonZeroNum(curNode, "NegativeLevels", negativeLevels); xml::saveNonZeroNum(curNode, "LostExperience", lostExperience); xml::saveNonZeroNum(curNode, "LastInterest", lastInterest); xml::saveNonZeroNum(curNode, "TickDmg", tickDmg); xml::saveNonNullString(curNode, "CustomColors", customColors); xml::saveNonZeroNum(curNode, "Thirst", thirst); saveBits(curNode, "Songs", gConfig->getMaxSong(), songs); std::list<CatRef>::const_iterator it; if(!roomExp.empty()) { childNode = xml::newStringChild(curNode, "RoomExp"); for(it = roomExp.begin() ; it != roomExp.end() ; it++) { (*it).save(childNode, "Room", true); } } if(!lore.empty()) { childNode = xml::newStringChild(curNode, "Lore"); for(it = lore.begin() ; it != lore.end() ; it++) { (*it).save(childNode, "Info", true); } } std::list<int>::const_iterator rt; if(!recipes.empty()) { childNode = xml::newStringChild(curNode, "Recipes"); for(rt = recipes.begin() ; rt != recipes.end() ; rt++) { xml::newNumChild(childNode, "Recipe", (*rt)); } } // Save any Anchors for(i=0; i<MAX_DIMEN_ANCHORS; i++) { if(anchor[i]) { childNode = xml::newStringChild(curNode, "Anchors"); for(; i<MAX_DIMEN_ANCHORS; i++) if(anchor[i]) { xmlNodePtr subNode = xml::newStringChild(childNode, "Anchor"); xml::newNumProp(subNode, "Num", i); anchor[i]->save(subNode); } break; } } xml::saveNonZeroNum(curNode, "Wimpy", wimpy); childNode = xml::newStringChild(curNode, "Pets"); savePetsXml(childNode, first_fol); if(birthday) { childNode = xml::newStringChild(curNode, "Birthday"); birthday->save(childNode); } // childNode = xml::newStringChild(curNode, "Deaths", NULL); // for(i=0; i<LUCKY_DEATHS; i++) { // if(lastDeath[i]) { // subNode = xml::newStringChild(childNode, "Death", NULL); // xml::saveNonZeroInt(subNode, "Time", lastDeath[i]); // xml::saveNonNullString(subNode, "Text", killedBy[i]); // } // } xml::saveNonNullString(curNode, "LastPassword", lastPassword); xml::saveNonNullString(curNode, "PoisonedBy", poisonedBy); xml::saveNonNullString(curNode, "AfflictedBy", afflictedBy); } //********************************************************************* // saveQuests //********************************************************************* void Player::saveQuests(xmlNodePtr rootNode) const { std::pair<int, QuestCompletion*> p; xmlNodePtr questNode; questNode = xml::newStringChild(rootNode, "QuestsInProgress"); foreach(p, questsInProgress) { p.second->save(questNode); } questNode = xml::newStringChild(rootNode, "QuestsCompleted"); std::list<int>::const_iterator it; for(it = questsCompleted.begin() ; it != questsCompleted.end() ; it++) { xml::newNumChild(questNode, "Quest", *it); } } //********************************************************************* // saveFactions //********************************************************************* void Creature::saveFactions(xmlNodePtr rootNode) const { xmlNodePtr curNode = xml::newStringChild(rootNode, "Factions"); xmlNodePtr factionNode; std::map<bstring, long>::const_iterator fIt; for(fIt = factions.begin() ; fIt != factions.end() ; fIt++) { factionNode = xml::newStringChild(curNode, "Faction"); xml::newStringChild(factionNode, "Name", (*fIt).first); xml::newNumChild(factionNode, "Regard", (*fIt).second); } } //********************************************************************* // saveSkills //********************************************************************* void Creature::saveSkills(xmlNodePtr rootNode) const { xmlNodePtr curNode = xml::newStringChild(rootNode, "Skills"); std::map<bstring, CrtSkill*>::const_iterator sIt; for(sIt = skills.begin() ; sIt != skills.end() ; sIt++) { (*sIt).second->save(curNode); } } //********************************************************************* // save //********************************************************************* void Effects::save(xmlNodePtr rootNode, const char* name) const { xmlNodePtr curNode = xml::newStringChild(rootNode, name); std::list<EffectInfo*>::const_iterator eIt; for(eIt = list.begin() ; eIt != list.end() ; eIt++) { (*eIt)->save(curNode); } } //********************************************************************* // save //********************************************************************* void EffectInfo::save(xmlNodePtr rootNode) const { xmlNodePtr effectNode = xml::newStringChild(rootNode, "Effect"); xml::newStringChild(effectNode, "Name", name); xml::newNumChild(effectNode, "Duration", duration); xml::newNumChild(effectNode, "Strength", strength); xml::newNumChild(effectNode, "Extra", extra); } //********************************************************************* // saveAttacks //********************************************************************* void Creature::saveAttacks(xmlNodePtr rootNode) const { xmlNodePtr curNode = xml::newStringChild(rootNode, "SpecialAttacks"); std::list<SpecialAttack*>::const_iterator eIt; for(eIt = specials.begin() ; eIt != specials.end() ; eIt++) { (*eIt)->save(curNode); } } //********************************************************************* // save //********************************************************************* void CrtSkill::save(xmlNodePtr rootNode) const { xmlNodePtr skillNode = xml::newStringChild(rootNode, "Skill"); xml::newStringChild(skillNode, "Name", getName()); xml::newNumChild(skillNode, "Gained", getGained()); xml::newNumChild(skillNode, "GainBonus", getGainBonus()); } int AlchemyEffect::saveToXml(xmlNodePtr rootNode) { if(rootNode == NULL) return(-1); xml::newStringChild(rootNode, "Effect", effect); xml::newNumChild(rootNode, "Duration", duration); xml::newNumChild(rootNode, "Strength", strength); xml::newNumChild(rootNode, "Quality", quality); return(0); } //********************************************************************* // saveToXml //********************************************************************* // This function will save the entire object except 0 values to the // given node which should be an object node unless saveType is REF // in which case it will only save fields that are changed in the // ordinary course of the game int Object::saveToXml(xmlNodePtr rootNode, int permOnly, LoadType saveType, int quantity) const { // xmlNodePtr rootNode; xmlNodePtr curNode; xmlNodePtr childNode; int i; if(rootNode == NULL) return(-1); ASSERTLOG( info.id >= 0 ); ASSERTLOG( info.id < OMAX ); // If the object's index is 0, then we have to do a full save if(!info.id && saveType != LS_FULL) { // We should never get here...if it's a 0 index, should have O_SAVE_FULL set printf("ERROR: Forcing full save.\n"); saveType = LS_FULL; } //rootNode = xml::newStringChild(parentNode, "Object", NULL); xml::newNumProp(rootNode, "Num", info.id); xml::newProp(rootNode, "Area", info.area); xml::newProp(rootNode, "Version", VERSION); if(quantity > 1) xml::newNumProp(rootNode, "Quantity", quantity); // These are saved for full and reference xml::saveNonNullString(rootNode, "Name", name); xml::saveNonZeroNum(rootNode, "ShopValue", shopValue); xml::saveNonZeroNum(rootNode, "Adjustment", adjustment); xml::saveNonZeroNum(rootNode, "ShotsMax", shotsmax); // NOTE: This is saved even if zero so that it loads broken items // properly. xml::newNumChild(rootNode, "ShotsCur", shotscur); xml::saveNonZeroNum(rootNode, "Armor", armor); xml::saveNonZeroNum(rootNode, "NumAttacks", numAttacks); xml::saveNonZeroNum(rootNode, "Delay", delay); xml::saveNonZeroNum(rootNode, "Extra", extra); xml::saveNonZeroNum(rootNode, "LotteryCycle", lotteryCycle); if(type == LOTTERYTICKET) { // Lottery tickets have a custom description so be sure to save it xml::saveNonNullString(rootNode, "Description", description); saveShortIntArray(rootNode, "LotteryNumbers", "LotteryNum", lotteryNumbers, 6); } damage.save(rootNode, "Dice"); value.save("Value", rootNode); hooks.save(rootNode, "Hooks"); // Save the keys in case they were modified // Keys curNode = xml::newStringChild(rootNode, "Keys"); for(i=0; i<3; i++) { if(key[i][0] == 0) continue; childNode = xml::newStringChild(curNode, "Key", key[i]); xml::newNumProp(childNode, "Num", i); } for(i=0; i<4; i++) { // this nested loop means we won't create an xml node if we don't have to if(lasttime[i].interval || lasttime[i].ltime || lasttime[i].misc) { curNode = xml::newStringChild(rootNode, "LastTimes"); for(; i<4; i++) saveLastTime(curNode, i, lasttime[i]); } } xml::saveNonNullString(rootNode, "Effect", effect); xml::saveNonZeroNum(rootNode, "EffectDuration", effectDuration); xml::saveNonZeroNum(rootNode, "EffectStrength", effectStrength); xml::saveNonZeroNum(rootNode, "Recipe", recipe); xml::saveNonZeroNum(rootNode, "Made", made); xml::saveNonNullString(rootNode, "Owner", questOwner); if(saveType != LS_FULL) { // Save only a subset of flags for obj references saveObjRefFlags(rootNode, "Flags", MAX_OBJECT_FLAGS, flags); } if(saveType == LS_FULL) { saveBits(rootNode, "Flags", MAX_OBJECT_FLAGS, flags); // These are only saved for full objects if(type != LOTTERYTICKET) xml::saveNonNullString(rootNode, "Description", description); if(!alchemyEffects.empty()) { curNode = xml::newStringChild(rootNode, "AlchemyEffects"); std::pair<int, AlchemyEffect> p; foreach(p, alchemyEffects) { childNode = xml::newStringChild(curNode, "AlchemyEffect"); p.second.saveToXml(childNode); xml::newNumProp(childNode, "Num", p.first); } } xml::saveNonNullString(rootNode, "UseOutput", use_output); xml::saveNonNullString(rootNode, "UseAttack", use_attack); xml::saveNonNullString(rootNode, "LastMod", lastMod); xml::saveNonZeroNum(rootNode, "Weight", weight); xml::saveNonZeroNum(rootNode, "Type", type); xml::saveNonNullString(rootNode, "SubType", subType); xml::saveNonZeroNum(rootNode, "WearFlag", wearflag); xml::saveNonZeroNum(rootNode, "MagicPower", magicpower); xml::saveNonZeroNum(rootNode, "Level", level); xml::saveNonZeroNum(rootNode, "Quality", quality); xml::saveNonZeroNum(rootNode, "RequiredSkill", requiredSkill); xml::saveNonZeroNum(rootNode, "Clan", clan); xml::saveNonZeroNum(rootNode, "Special", special); xml::saveNonZeroNum(rootNode, "QuestNum", questnum); xml::saveNonZeroNum(rootNode, "Bulk", bulk); xml::saveNonZeroNum(rootNode, "Size", size); xml::saveNonZeroNum(rootNode, "MaxBulk", maxbulk); xml::saveNonZeroNum(rootNode, "MaxUnique", max_unique); xml::saveNonZeroNum(rootNode, "CoinCost", coinCost); deed.save(rootNode, "Deed", false); xml::saveNonZeroNum(rootNode, "KeyVal", keyVal); xml::saveNonZeroNum(rootNode, "Material", (int)material); xml::saveNonZeroNum(rootNode, "MinStrength", minStrength); saveCatRefArray(rootNode, "InBag", "Obj", in_bag, 3); std::list<CatRef>::const_iterator it; if(!randomObjects.empty()) { curNode = xml::newStringChild(rootNode, "RandomObjects"); for(it = randomObjects.begin() ; it != randomObjects.end() ; it++) { (*it).save(curNode, "RandomObject", false); } } } if(compass) { curNode = xml::newStringChild(rootNode, "Compass"); compass->save(curNode); } // Save contained items for both full and reference saves // Also check for container flag if(first_obj) { curNode = xml::newStringChild(rootNode, "SubItems"); // If we're a permenant container, always save the items inside of it if(type == CONTAINER && flagIsSet(O_PERM_ITEM)) permOnly = ALLITEMS; saveObjectsXml(curNode, first_obj, permOnly); } return(0); } //********************************************************************* // saveToXml //********************************************************************* int Room::saveToXml(xmlNodePtr rootNode, int permOnly) const { std::map<int, crlasttime>::const_iterator it; xmlNodePtr curNode; int i; if(!this || name[0] == '\0' || rootNode == NULL) return(-1); // record rooms saved during roomMove if(gConfig->isMovingRoom()) gConfig->moveRoomLog((bstring)"r" + info.rstr(), false); xml::newNumProp(rootNode, "Num", info.id); xml::newProp(rootNode, "Version", VERSION); xml::newProp(rootNode, "Area", info.area); xml::saveNonNullString(rootNode, "Name", name); xml::saveNonNullString(rootNode, "ShortDescription", short_desc); xml::saveNonNullString(rootNode, "LongDescription", long_desc); xml::saveNonNullString(rootNode, "Fishing", fishing); xml::saveNonNullString(rootNode, "Faction", faction); xml::saveNonNullString(rootNode, "LastModBy", last_mod); // TODO: Change this into a TimeStamp xml::saveNonNullString(rootNode, "LastModTime", lastModTime); xml::saveNonNullString(rootNode, "LastPlayer", lastPly); // TODO: Change this into a TimeStamp xml::saveNonNullString(rootNode, "LastPlayerTime", lastPlyTime); xml::saveNonZeroNum(rootNode, "LowLevel", lowLevel); xml::saveNonZeroNum(rootNode, "HighLevel", highLevel); xml::saveNonZeroNum(rootNode, "MaxMobs", maxmobs); xml::saveNonZeroNum(rootNode, "Trap", trap); trapexit.save(rootNode, "TrapExit", false); xml::saveNonZeroNum(rootNode, "TrapWeight", trapweight); xml::saveNonZeroNum(rootNode, "TrapStrength", trapstrength); xml::saveNonZeroNum(rootNode, "BeenHere", beenhere); xml::saveNonZeroNum(rootNode, "RoomExp", roomExp); saveBits(rootNode, "Flags", MAX_ROOM_FLAGS, flags); curNode = xml::newStringChild(rootNode, "Wander"); wander.save(curNode); if(track.getDirection() != "") { curNode = xml::newStringChild(rootNode, "Track"); track.save(curNode); } xml::saveNonZeroNum(rootNode, "Size", size); effects.save(rootNode, "Effects"); hooks.save(curNode, "Hooks"); // Save Perm Mobs curNode = xml::newStringChild(rootNode, "PermMobs"); for(it = permMonsters.begin(); it != permMonsters.end() ; it++) { saveCrLastTime(curNode, (*it).first, (*it).second); } // Save Perm Objs curNode = xml::newStringChild(rootNode, "PermObjs"); for(it = permObjects.begin(); it != permObjects.end() ; it++) { saveCrLastTime(curNode, (*it).first, (*it).second); } // Save LastTimes -- Dunno if we need this? for(i=0; i<16; i++) { // this nested loop means we won't create an xml node if we don't have to if(lasttime[i].interval || lasttime[i].ltime || lasttime[i].misc) { curNode = xml::newStringChild(rootNode, "LastTimes"); for(; i<16; i++) saveLastTime(curNode, i, lasttime[i]); } } // Save Objects curNode = xml::newStringChild(rootNode, "Objects"); saveObjectsXml(curNode, first_obj, permOnly); // Save Creatures curNode = xml::newStringChild(rootNode, "Creatures"); saveCreaturesXml(curNode, first_mon, permOnly); // Save Exits curNode = xml::newStringChild(rootNode, "Exits"); saveExitsXml(curNode, first_ext); return(0); } //********************************************************************* // saveToXml //********************************************************************* int Exit::saveToXml(xmlNodePtr parentNode) const { xmlNodePtr rootNode; xmlNodePtr curNode; xmlNodePtr childNode; int i; if(this == NULL || parentNode == NULL || flagIsSet(X_PORTAL)) return(-1); rootNode = xml::newStringChild(parentNode, "Exit"); xml::newProp(rootNode, "Name", name); // Exit Keys curNode = xml::newStringChild(rootNode, "Keys"); for(i=0; i<3; i++) { if(desc_key[i][0] == 0) continue; childNode = xml::newStringChild(curNode, "Key", desc_key[i]); xml::newNumProp(childNode, "Num",i); } target.save(rootNode, "Target"); xml::saveNonZeroNum(rootNode, "Toll", toll); xml::saveNonZeroNum(rootNode, "Level", level); xml::saveNonZeroNum(rootNode, "Trap", trap); xml::saveNonZeroNum(rootNode, "Key", key); xml::saveNonNullString(rootNode, "KeyArea", keyArea); xml::saveNonZeroNum(rootNode, "Size", size); xml::saveNonNullString(rootNode, "PassPhrase", passphrase); xml::saveNonZeroNum(rootNode, "PassLang", passlang); xml::saveNonNullString(rootNode, "Enter", enter); xml::saveNonNullString(rootNode, "Open", open); effects.save(rootNode, "Effects"); saveBits(rootNode, "Flags", MAX_EXIT_FLAGS, flags); saveLastTime(curNode, 0, ltime); hooks.save(curNode, "Hooks"); return(0); } //********************************************************************* // saveExitsXml //********************************************************************* int saveExitsXml(xmlNodePtr curNode, xtag *xp) { while(xp) { if(xp->ext) xp->ext->saveToXml(curNode); xp = xp->next_tag; } return(0); } //********************************************************************* // saveObjectsXml //********************************************************************* int saveObjectsXml(xmlNodePtr parentNode, otag* op, int permOnly) { xmlNodePtr curNode; int quantity=0; LoadType lt; while(op) { //if(ot->obj && ot->obj->index != 0 && (permOnly == ALLITEMS || (permOnly == PERMONLY && ot->obj->flagIsSet(O_PERM_ITEM)))) { if( op->obj && !op->obj->flagIsSet(O_PORTAL) && ( permOnly == ALLITEMS || (permOnly == PERMONLY && op->obj->flagIsSet(O_PERM_ITEM)) ) ) { if(op->obj->flagIsSet(O_CUSTOM_OBJ) || op->obj->flagIsSet(O_SAVE_FULL) || !op->obj->info.id) { // If it's a custom or has the save flag set, save the entire object curNode = xml::newStringChild(parentNode, "Object"); lt = LS_FULL; } else { // Just save a reference and any changed fields curNode = xml::newStringChild(parentNode, "ObjRef"); lt = LS_REF; } // quantity code reduces filesize for player shops, storage rooms, and // inventories (which tend of have a lot of identical items in them) quantity = 1; while(op->next_tag && *op->next_tag->obj == *op->obj) { quantity++; op = op->next_tag; } op->obj->saveToXml(curNode, permOnly, lt, quantity); } op = op->next_tag; } return(0); } //********************************************************************* // saveCreaturesXml //********************************************************************* int saveCreaturesXml(xmlNodePtr parentNode, ctag* cp, int permOnly) { xmlNodePtr curNode; while(cp) { if( cp->crt && cp->crt->isMonster() && ( (permOnly == ALLITEMS && !cp->crt->isPet()) || (permOnly == PERMONLY && cp->crt->flagIsSet(M_PERMENANT_MONSTER)) ) ) { if(cp->crt->flagIsSet(M_SAVE_FULL)) { // Save a fully copy of the mob to the node curNode = xml::newStringChild(parentNode, "Creature"); cp->crt->saveToXml(curNode, permOnly, LS_FULL); } else { // Just save a reference curNode = xml::newStringChild(parentNode, "CrtRef"); cp->crt->saveToXml(curNode, permOnly, LS_REF); } } cp = cp->next_tag; } return(0); } //********************************************************************* // savePetsXml //********************************************************************* int savePetsXml(xmlNodePtr parentNode, ctag* cp) { xmlNodePtr curNode; while(cp) { if(cp->crt && cp->crt->isPet()) { curNode = xml::newStringChild(parentNode, "Creature"); cp->crt->saveToXml(curNode, ALLITEMS, LS_FULL); } cp = cp->next_tag; } return(0); } //********************************************************************* // saveToXml //********************************************************************* bool Guild::saveToXml(xmlNodePtr rootNode) const { xmlNodePtr curNode, membersNode; curNode = xml::newStringChild(rootNode, "Guild"); // xml::newStringChild(curNode, "", ); // xml::newIntChild(curNode, "", guild->); xml::newNumProp(curNode, "ID", num); xml::newStringChild(curNode, "Name", name); xml::newStringChild(curNode, "Leader", leader); xml::newNumChild(curNode, "Level", level); xml::newNumChild(curNode, "NumMembers", numMembers); membersNode = xml::newStringChild(curNode, "Members"); std::list<bstring>::const_iterator mIt; for(mIt = members.begin() ; mIt != members.end() ; mIt++) { xml::newStringChild(membersNode, "Member", *mIt); } xml::newNumChild(curNode, "PkillsWon", pkillsWon); xml::newNumChild(curNode, "PkillsIn", pkillsIn); xml::newNumChild(curNode,"Points", points); bank.save("Bank", curNode); return(true); } //********************************************************************* // saveToXml //********************************************************************* bool GuildCreation::saveToXml(xmlNodePtr rootNode) const { xmlNodePtr childNode, curNode = xml::newStringChild(rootNode, "GuildCreation"); xml::newStringChild(curNode, "Name", name); xml::newStringChild(curNode, "Leader", leader); xml::newStringChild(curNode, "LeaderIp", leaderIp); xml::newNumChild(curNode, "Status", status); // Supporters std::map<bstring, bstring>::const_iterator sIt; for(sIt = supporters.begin() ; sIt != supporters.end() ; sIt++) { childNode = xml::newStringChild(curNode, "Supporter", (*sIt).first); xml::newProp(childNode, "Ip", (*sIt).second); } return(true); } #define BIT_ISSET(p,f) ((p)[(f)/8] & 1<<((f)%8)) #define BIT_SET(p,f) ((p)[(f)/8] |= 1<<((f)%8)) #define BIT_CLEAR(p,f) ((p)[(f)/8] &= ~(1<<((f)%8))) //********************************************************************* // save //********************************************************************* void Stat::save(xmlNodePtr parentNode, const char* statName) const { xmlNodePtr curNode = xml::newStringChild(parentNode, "Stat"); xml::newProp(curNode, "Name", statName); xml::newNumChild(curNode, "Current", cur); xml::newNumChild(curNode, "Max", max); xml::newNumChild(curNode, "Initial", initial); xml::newNumChild(curNode, "TempMax", tmpMax); } //********************************************************************* // saveDaily //********************************************************************* xmlNodePtr saveDaily(xmlNodePtr parentNode, int i, struct daily pDaily) { // Avoid writing un-used daily timers if(pDaily.max == 0 && pDaily.cur == 0 && pDaily.ltime == 0) return(NULL); xmlNodePtr curNode = xml::newStringChild(parentNode, "Daily"); xml::newNumProp(curNode, "Num", i); xml::newNumChild(curNode, "Max", pDaily.max); xml::newNumChild(curNode, "Current", pDaily.cur); xml::newNumChild(curNode, "LastTime", pDaily.ltime); return(curNode); } //********************************************************************* // saveCrLastTime //********************************************************************* xmlNodePtr saveCrLastTime(xmlNodePtr parentNode, int i, struct crlasttime pCrLastTime) { // Avoid writing un-used last times if(!pCrLastTime.interval && !pCrLastTime.ltime && !pCrLastTime.cr.id) return(NULL); if(i < 0 || i >= NUM_PERM_SLOTS) return(NULL); xmlNodePtr curNode = xml::newStringChild(parentNode, "LastTime"); xml::newNumProp(curNode, "Num", i); xml::newNumChild(curNode, "Interval", pCrLastTime.interval); xml::newNumChild(curNode, "LastTime", pCrLastTime.ltime); pCrLastTime.cr.save(curNode, "Misc", false); return(curNode); } //********************************************************************* // saveLastTime //********************************************************************* xmlNodePtr saveLastTime(xmlNodePtr parentNode, int i, struct lasttime pLastTime) { // Avoid writing un-used last times if(!pLastTime.interval && !pLastTime.ltime && !pLastTime.misc) return(NULL); xmlNodePtr curNode = xml::newStringChild(parentNode, "LastTime"); xml::newNumProp(curNode, "Num", i); xml::newNumChild(curNode, "Interval", pLastTime.interval); xml::newNumChild(curNode, "LastTime", pLastTime.ltime); xml::newNumChild(curNode, "Misc", pLastTime.misc); return(curNode); } //********************************************************************* // saveSavingThrow //********************************************************************* xmlNodePtr saveSavingThrow(xmlNodePtr parentNode, int i, struct saves pSavingThrow) { xmlNodePtr curNode = xml::newStringChild(parentNode, "SavingThrow"); xml::newNumProp(curNode, "Num", i); xml::newNumChild(curNode, "Chance", pSavingThrow.chance); xml::newNumChild(curNode, "Gained", pSavingThrow.gained); xml::newNumChild(curNode, "Misc", pSavingThrow.misc); return(curNode); } //********************************************************************* // saveObjRefFlags //********************************************************************* xmlNodePtr saveObjRefFlags(xmlNodePtr parentNode, const char* name, int maxBit, const char *bits) { xmlNodePtr curNode=NULL; // this nested loop means we won't create an xml node if we don't have to for(int i=0; objRefSaveFlags[i] != -1; i++) { if(BIT_ISSET(bits, objRefSaveFlags[i])) { curNode = xml::newStringChild(parentNode, name); for(; objRefSaveFlags[i] != -1; i++) { if(BIT_ISSET(bits, objRefSaveFlags[i])) saveBit(curNode, objRefSaveFlags[i]); } return(curNode); } } return(curNode); } //********************************************************************* // saveBits //********************************************************************* xmlNodePtr saveBits(xmlNodePtr parentNode, const char* name, int maxBit, const char *bits) { xmlNodePtr curNode=NULL; // this nested loop means we won't create an xml node if we don't have to for(int i=0; i<maxBit; i++) { if(BIT_ISSET(bits, i)) { curNode = xml::newStringChild(parentNode, name); for(; i<maxBit; i++) { if(BIT_ISSET(bits, i)) saveBit(curNode, i); } return(curNode); } } return(curNode); } //********************************************************************* // saveBit //********************************************************************* xmlNodePtr saveBit(xmlNodePtr parentNode, int bit) { xmlNodePtr curNode; curNode = xml::newStringChild(parentNode, "Bit"); xml::newNumProp(curNode, "Num", bit); return(curNode); } //********************************************************************* // saveLongArray //********************************************************************* xmlNodePtr saveLongArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const long array[], int arraySize) { xmlNodePtr childNode, curNode=NULL; // this nested loop means we won't create an xml node if we don't have to for(int i=0; i<arraySize; i++) { if(!array[i]) { curNode = xml::newStringChild(parentNode, rootName); for(; i<arraySize; i++) { if(array[i]) { childNode = xml::newNumChild(curNode, childName, array[i]); xml::newNumProp(childNode, "Num", i); } } return(curNode); } } return(curNode); } //********************************************************************* // saveULongArray //********************************************************************* xmlNodePtr saveULongArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const unsigned long array[], int arraySize) { xmlNodePtr childNode, curNode=NULL; // this nested loop means we won't create an xml node if we don't have to for(int i=0; i<arraySize; i++) { if(array[i]) { curNode = xml::newStringChild(parentNode, rootName); for(; i<arraySize; i++) { if(array[i]) { childNode = xml::newNumChild(curNode, childName, array[i]); xml::newNumProp(childNode, "Num", i); } } return(curNode); } } return(curNode); } //********************************************************************* // saveCatRefArray //********************************************************************* xmlNodePtr saveCatRefArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const std::map<int, CatRef>& array, int arraySize) { xmlNodePtr curNode=NULL; std::map<int, CatRef>::const_iterator it; // this nested loop means we won't create an xml node if we don't have to for(it = array.begin(); it != array.end() ; it++) { if((*it).second.id) { curNode = xml::newStringChild(parentNode, rootName); for(; it != array.end() ; it++) { if((*it).second.id) (*it).second.save(curNode, childName, false, (*it).first); } return(curNode); } } return(curNode); } //********************************************************************* // saveCatRefArray //********************************************************************* xmlNodePtr saveCatRefArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const CatRef array[], int arraySize) { xmlNodePtr curNode=NULL; // this nested loop means we won't create an xml node if we don't have to for(int i=0; i<arraySize; i++) { if(array[i].id) { curNode = xml::newStringChild(parentNode, rootName); for(; i<arraySize; i++) { if(array[i].id) array[i].save(curNode, childName, false, i); } return(curNode); } } return(curNode); } //********************************************************************* // saveCarryArray //********************************************************************* xmlNodePtr saveCarryArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const Carry array[], int arraySize) { xmlNodePtr curNode=NULL; // this nested loop means we won't create an xml node if we don't have to for(int i=0; i<arraySize; i++) { if(array[i].info.id) { curNode = xml::newStringChild(parentNode, rootName); for(; i<arraySize; i++) { if(array[i].info.id) array[i].save(curNode, childName, false, i); } return(curNode); } } return(curNode); } //********************************************************************* // saveShortIntArray //********************************************************************* xmlNodePtr saveShortIntArray(xmlNodePtr parentNode, const char* rootName, const char* childName, const short array[], int arraySize) { xmlNodePtr childNode, curNode=NULL; // this nested loop means we won't create an xml node if we don't have to for(int i=0; i<arraySize; i++) { if(array[i]) { curNode = xml::newStringChild(parentNode, rootName); for(; i<arraySize; i++) { if(array[i]) { childNode = xml::newNumChild(curNode, childName, array[i]); xml::newNumProp(childNode, "Num", i); } } return(curNode); } } return(curNode); } #undef BIT_ISSET #undef BIT_SET #undef BIT_CLEAR