roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * 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-2012 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 "alchemy.h"
#include "socials.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,
	O_RECLAIMED,
	-1
};

//*********************************************************************
//						saveToFile
//*********************************************************************
// This function will write the supplied player to 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( !getName().empty() );
	ASSERTLOG( isPlayer() );

	if(getName()[0] == '\0') {
		printf("Invalid player passed to save\n");
		return(-1);
	}

	gServer->saveIds();

	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) {
		sprintf(filename, "%s/%s.bak.xml", Path::PlayerBackup, getCName());
	} else {
		sprintf(filename, "%s/%s.xml", Path::Player, getCName());
	}

	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
	return(0);
}

//*********************************************************************
//						saveToFile
//*********************************************************************
// This function will write the supplied room to ROOM_PATH/r#####.xml
// It will most likely only be called from *save r

int UniqueRoom::saveToFile(int permOnly, LoadType saveType) {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char		filename[256];

	ASSERTLOG( this != NULL );
	ASSERTLOG( info.id >= 0 );
	if(saveType == LS_BACKUP)
		Path::checkDirExists(info.area, roomBackupPath);
	else
		Path::checkDirExists(info.area, roomPath);

	gServer->saveIds();

	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);
	Path::checkDirExists(info.area, monsterPath);

	gServer->saveIds();

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Creature", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);

	escapeText();
	bstring idTemp = id;
	id = "-1";
	saveToXml(rootNode, ALLITEMS, LS_FULL);
	id = idTemp;

	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);
	Path::checkDirExists(info.area, objectPath);

	gServer->saveIds();

	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_PROTOTYPE, false);

	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;
	const Guild *guild;
	for(it = guilds.begin() ; it != guilds.end() ; it++) {
		guild = (*it).second;
		if(guild->getNum() == 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", Path::PlayerData);
	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", Path::Config);
	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, bool saveID) const {
//	xmlNodePtr	rootNode;
	xmlNodePtr		curNode;
	xmlNodePtr		childNode;
	int i;

	if(getName()[0] == '\0' || rootNode == NULL)
		return(-1);

	const Player	*pPlayer = getAsConstPlayer();
	const Monster	*mMonster = getAsConstMonster();

	if(pPlayer) {
		xml::newProp(rootNode, "Name", pPlayer->getName());
		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->getName());
		xml::newProp(rootNode, "Area", mMonster->info.area);
	}

	if(saveID == true)
		xml::saveNonNullString(rootNode, "Id", getId());
	else
		xml::newProp(rootNode, "ID", "-1");

	// 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->inAreaRoom()) {
			curNode = xml::newStringChild(rootNode, "AreaRoom");
			pPlayer->getConstAreaRoomParent()->mapmarker.save(curNode);
			//pPlayer->currentLocation.mapmarker.save(curNode);
		} else
			pPlayer->currentLocation.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");

	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);

		hooks.save(rootNode, "Hooks");

		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, objects, 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;

	// record monsters saved during swap
	if(gConfig->swapIsInteresting(this))
		gConfig->swapLog((bstring)"m" + info.rstr(), false);

	xml::saveNonNullString(curNode, "Plural", plural);
	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");
	for(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 swap
	if(gConfig->swapIsInteresting(this))
		gConfig->swapLog((bstring)"p" + getName(), false);

	bank.save("Bank", curNode);
	xml::saveNonZeroNum(curNode, "WeaponTrains", weaponTrains);
	xml::saveNonNullString(curNode, "Surname", surname);
	xml::saveNonNullString(curNode, "Forum", forum);
	xml::newNumChild(curNode, "Wrap", wrap);
	bound.save(curNode, "BoundRoom");
	previousRoom.save(curNode, "PreviousRoom"); // monster do not save 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, "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(!storesRefunded.empty()) {
	    childNode = xml::newStringChild(curNode, "StoresRefunded");
	    for(it = storesRefunded.begin() ; it != storesRefunded.end() ; it++) {
	        (*it).save(childNode, "Store", true);
	    }
	}

	if(!roomExp.empty()) {
		childNode = xml::newStringChild(curNode, "RoomExp");
		for(it = roomExp.begin() ; it != roomExp.end() ; it++) {
			(*it).save(childNode, "Room", true);
		}
	}
	if(!objIncrease.empty()) {
		childNode = xml::newStringChild(curNode, "ObjIncrease");
		for(it = objIncrease.begin() ; it != objIncrease.end() ; it++) {
			(*it).save(childNode, "Object", 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");
	savePets(childNode);

	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 {
	xmlNodePtr questNode;

	questNode = xml::newStringChild(rootNode, "QuestsInProgress");
	for(std::pair<int, QuestCompletion*> p : questsInProgress) {
		p.second->save(questNode);
	}

	questNode = xml::newStringChild(rootNode, "QuestsCompleted");
	if(!questsCompleted.empty()) {
		xmlNodePtr completionNode;
		for(std::pair<int,int> qp : questsCompleted) {
			completionNode = xml::newStringChild(questNode, "Quest");
			xml::newNumChild(completionNode, "Id", qp.first);
			xml::newNumChild(completionNode, "Num", qp.second);
		}
	}
//	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, Skill*>::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);
	EffectList::const_iterator eIt;
	for(eIt = effectList.begin() ; eIt != effectList.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);
	xml::newNumChild(effectNode, "PulseModifier", pulseModifier);
}

//*********************************************************************
//						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 Skill::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, bool saveId, std::list<bstring> *idList) const {
//	xmlNodePtr	rootNode;
	xmlNodePtr		curNode;
	xmlNodePtr		childNode;

	int i=0;

	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 && saveType != LS_PROTOTYPE) {
		// 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;
	}

	// record objects saved during swap
	if(gConfig->swapIsInteresting(this))
		gConfig->swapLog((bstring)"o" + info.rstr(), false);

	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);
		curNode = xml::newStringChild(rootNode, "IdList");
		if(idList != 0) {
			std::list<bstring>::iterator idIt = idList->begin();
			while(idIt != idList->end()) {
				xml::newStringChild(curNode, "Id", (*idIt++));
			}
		}
	} else {
		if(saveId == true)
			xml::saveNonNullString(rootNode, "Id", getId());
		else
			xml::newProp(rootNode, "ID", "-1");
	}

	// These are saved for full and reference
	xml::saveNonNullString(rootNode, "Name", getName());
	if(saveType != LS_PROTOTYPE) {
		droppedBy.save(rootNode);
		xml::saveNonZeroNum(rootNode, "ShopValue", shopValue);
	}


	xml::saveNonNullString(rootNode, "Plural", plural);
	xml::saveNonZeroNum(rootNode, "Adjustment", adjustment);
	xml::saveNonZeroNum(rootNode, "ShotsMax", shotsMax);
	xml::saveNonZeroNum(rootNode, "ChargesMax", chargesMax);
	// NOTE: This is saved even if zero so that it loads broken items
	// properly.
	xml::newNumChild(rootNode, "ShotsCur", shotsCur);
	xml::newNumChild(rootNode, "ChargesCur", chargesCur);
	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 && saveType != LS_PROTOTYPE) {
		// Save only a subset of flags for obj references
		saveObjRefFlags(rootNode, "Flags", MAX_OBJECT_FLAGS, flags);
	}

	if(saveType == LS_FULL || saveType == LS_PROTOTYPE) {
		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");

			for(std::pair<int, AlchemyEffect> 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, "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);
	}
	if(increase) {
		curNode = xml::newStringChild(rootNode, "ObjIncrease");
		increase->save(curNode);
	}

	// Save contained items for both full and reference saves
	// Also check for container flag
	if(saveType != LS_PROTOTYPE && !objects.empty()) {
		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, objects, permOnly);
	}
	return(0);
}

//*********************************************************************
//						saveToXml
//*********************************************************************

int UniqueRoom::saveToXml(xmlNodePtr rootNode, int permOnly) const {
	std::map<int, crlasttime>::const_iterator it;
	xmlNodePtr		curNode;
	int i;

	if(!this || getName()[0] == '\0' || rootNode == NULL)
		return(-1);

	// record rooms saved during swap
	if(gConfig->swapIsInteresting(this))
		gConfig->swapLog((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", getName());
	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(rootNode, "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, objects, permOnly);

	// Save Creatures
	curNode = xml::newStringChild(rootNode, "Creatures");
	saveCreaturesXml(curNode, monsters, permOnly);

	// Save Exits
	curNode = xml::newStringChild(rootNode, "Exits");
	saveExitsXml(curNode);
	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", getName());

	// 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::saveNonZeroNum(rootNode, "Direction", (int)direction);
	xml::saveNonNullString(rootNode, "PassPhrase", passphrase);
	xml::saveNonZeroNum(rootNode, "PassLang", passlang);
	xml::saveNonNullString(rootNode, "Description", description);
	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(rootNode, "Hooks");

	return(0);
}

//*********************************************************************
//						saveExitsXml
//*********************************************************************

int BaseRoom::saveExitsXml(xmlNodePtr curNode) const {
	for(Exit* exit : exits) {
		exit->saveToXml(curNode);
	}
	return(0);
}

//*********************************************************************
//						saveObjectsXml
//*********************************************************************

int saveObjectsXml(xmlNodePtr parentNode, const ObjectSet& set, int permOnly) {
	xmlNodePtr curNode;
	int quantity=0;
	LoadType lt;
	ObjectSet::const_iterator it;
	const Object* obj;
	std::list<bstring> *idList = 0;
	for( it = set.begin() ; it != set.end() ; ) {
		obj = (*it++);
		if(	obj &&
			(	permOnly == ALLITEMS ||
				(permOnly == PERMONLY && obj->flagIsSet(O_PERM_ITEM))
			))
		{
			if(obj->flagIsSet(O_CUSTOM_OBJ) || obj->flagIsSet(O_SAVE_FULL) || !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;
			}

			// TODO: Modify this to work with object ids
			// quantity code reduces filesize for player shops, storage rooms, and
			// inventories (which tend of have a lot of identical items in them)
			quantity = 1;
			idList = new std::list<bstring>;
			idList->push_back(obj->getId());
			while(it != set.end() && *(*it) == *obj) {
				idList->push_back((*it)->getId());
				quantity++;
				it++;
			}

			obj->saveToXml(curNode, permOnly, lt, quantity, true, idList);
			delete idList;
			idList = 0;
		}
	}
	return(0);
}

//*********************************************************************
//						saveCreaturesXml
//*********************************************************************

int saveCreaturesXml(xmlNodePtr parentNode, const MonsterSet& set, int permOnly) {
	xmlNodePtr curNode;
	for(const Monster* mons : set) {
        if( mons && mons->isMonster() &&
            (   (permOnly == ALLITEMS && !mons->isPet()) ||
                (permOnly == PERMONLY && mons->flagIsSet(M_PERMENANT_MONSTER))
            ) )
        {
            if(mons->flagIsSet(M_SAVE_FULL)) {
                // Save a fully copy of the mob to the node
                curNode = xml::newStringChild(parentNode, "Creature");
                mons->saveToXml(curNode, permOnly, LS_FULL);
            } else {
                // Just save a reference
                curNode = xml::newStringChild(parentNode, "CrtRef");
                mons->saveToXml(curNode, permOnly, LS_REF);
            }
        }

	}
	return(0);
}

//*********************************************************************
//						savePets
//*********************************************************************

void Creature::savePets(xmlNodePtr parentNode) const {
	xmlNodePtr curNode;

	for(const Monster* pet : pets) {
		// Only save pets, not creatures just following
		if(!pet->isPet()) continue;
	    curNode = xml::newStringChild(parentNode, "Creature");
	    pet->saveToXml(curNode, ALLITEMS, LS_FULL);
	}
	return;
}

//*********************************************************************
//						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);
}


bool Config::saveSocials() {
    xmlDocPtr   xmlDoc;
    xmlNodePtr  rootNode;

    xmlDoc = xmlNewDoc(BAD_CAST "1.0");
    rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Socials", NULL);
    xmlDocSetRootElement(xmlDoc, rootNode);

    for(SocialMap::value_type p : socials) {
        p.second->saveToXml(rootNode);
    }
    bstring filename = bstring(Path::Code) + "/" + "socials.xml";
    xml::saveFile(filename.c_str(), xmlDoc);
    xmlFreeDoc(xmlDoc);
    return(true);

}

bool SocialCommand::saveToXml(xmlNodePtr rootNode) const {
    xmlNodePtr curNode;

    curNode = xml::newStringChild(rootNode, "Social");
    xml::newStringChild(curNode, "Name", name);

    if(wakeTarget)
        xml::newBoolChild(curNode, "WakeTarget", wakeTarget);
    if(rudeWakeTarget)
        xml::newBoolChild(curNode, "RudeWakeTarget", rudeWakeTarget);
    if(wakeRoom)
        xml::newBoolChild(curNode, "WakeRoom", wakeRoom);

    xml::saveNonNullString(curNode, "Description", description);
    xml::saveNonZeroNum(curNode, "Priority", priority);

    xml::saveNonNullString(curNode, "SelfNoTarget", selfNoTarget);
    xml::saveNonNullString(curNode, "RoomNoTarget", roomNoTarget);
    xml::saveNonNullString(curNode, "SelfOnTarget", selfOnTarget);
    xml::saveNonNullString(curNode, "RoomOnTarget", roomOnTarget);
    xml::saveNonNullString(curNode, "VictimOnTarget", victimOnTarget);
    xml::saveNonNullString(curNode, "SelfOnSelf", selfOnSelf);
    xml::saveNonNullString(curNode, "RoomOnSelf", roomOnSelf);

    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);
}


void DroppedBy::save(xmlNodePtr rootNode) const {
	xmlNodePtr curNode = xml::newStringChild(rootNode, "DroppedBy");
	xml::newStringChild(curNode, "Name", name);
	xml::saveNonNullString(curNode, "Index", index);
	xml::saveNonNullString(curNode, "ID", id);
	xml::newStringChild(curNode, "Type", type);
}


#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);
	xmlNodePtr modNode = xml::newStringChild(curNode, "Modifiers");
	for(ModifierMap::value_type p: modifiers) {
		p.second->save(modNode);
	}
}

void StatModifier::save(xmlNodePtr parentNode) {
	xmlNodePtr curNode = xml::newStringChild(parentNode, "StatModifier");
	xml::newStringChild(curNode, "Name", name);
	xml::newNumChild(curNode, "ModAmt", modAmt);
	xml::newNumChild(curNode, "ModType", modType);
}
//*********************************************************************
//						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