roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * files-xml-read.cpp
 *	 Used to read objects/rooms/creatures etc from 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 "effects.h"
#include "bans.h"
#include "guilds.h"
#include "factions.h"
#include "version.h"
#include "specials.h"
#include "calendar.h"
#include "quests.h"
#include "unique.h"
#include "magic.h"
#include "alchemy.h"

extern int objRefSaveFlags[];

// TODO: Make a function that will use an xml reader to only parse the player to find
// name and password

//*********************************************************************
//						loadPlayer
//*********************************************************************
// Attempt to load the player named 'name' into the address given
// return 0 on success, -1 on failure

bool loadPlayer(const char* name, Player** player, LoadType loadType) {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char		filename[256];
	bstring		pass = "", loadName = "";

	if(!checkWinFilename(NULL, name)) {
		printf("Failed lookup on player %s due to checkWinFilename.\n", name);
		return(false);
	}

	if(loadType == LS_BACKUP)
		sprintf(filename, "%s/%s.bak.xml", BACKUPPATH, name);
	else if(loadType == LS_CONVERT)
		sprintf(filename, "%s/convert/%s.xml", PLAYERPATH, name);
	else // LS_NORMAL
		sprintf(filename, "%s/%s.xml", PLAYERPATH, name);

	//printf("Attempting to load player %s from file %s.\n", name, filename);

	if((xmlDoc = xml::loadFile(filename, "Player")) == NULL)
		return(false);

	rootNode = xmlDocGetRootElement(xmlDoc);
	loadName = xml::getProp(rootNode, "Name");
	if(loadName != name) {
		printf("Error loading %s, found %s instead!\n", name, loadName.c_str());
		xmlFreeDoc(xmlDoc);
		xmlCleanupParser();
		return(false);
	}

	*player = new Player;
	if(!*player)
		merror("loadPlayer", FATAL);

//	strcpy((*player)->name, tmpStr);
	xml::copyPropToCString((*player)->name, rootNode, "Name");
	xml::copyPropToBString(pass, rootNode, "Password");
	(*player)->setPassword(pass);
	//xml::copyPropToBString((*player)->version, rootNode, "Version");
	(*player)->setLastLogin(xml::getIntProp(rootNode, "LastLogin"));

	// If we get here, we should be loading the correct player, so start reading them in
	(*player)->readFromXml(rootNode);

	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

//*********************************************************************
//						loadMonster
//*********************************************************************

bool loadMonster(int index, Monster ** pMonster) {
	CatRef cr;
	cr.id = index;
	return(loadMonster(cr, pMonster));
}

bool loadMonster(const CatRef cr, Monster ** pMonster) {
	if(!validMobId(cr))
		return(false);

	// Check if monster is already loaded, and if so return pointer
	if(gConfig->monsterInQueue(cr)) {
		gConfig->frontMonsterQueue(cr);
		*pMonster = new Monster;
		gConfig->getMonsterQueue(cr, pMonster);
	} else {
		// Otherwise load the monster and return a pointer to the newly loaded monster
		// Load the creature from it's file
		if(!loadMonsterFromFile(cr, pMonster))
			return(false);
		gConfig->addMonsterQueue(cr, pMonster);
	}

	(*pMonster)->fd = -1;
	(*pMonster)->lasttime[LT_TICK].ltime =
	(*pMonster)->lasttime[LT_TICK_SECONDARY].ltime =
	(*pMonster)->lasttime[LT_TICK_HARMFUL].ltime = time(0);

	(*pMonster)->lasttime[LT_TICK].interval  =
	(*pMonster)->lasttime[LT_TICK_SECONDARY].interval = 60;
	(*pMonster)->lasttime[LT_TICK_HARMFUL].interval = 30;
	(*pMonster)->getMobSave();

	return(true);
}

//*********************************************************************
//						loadObject
//*********************************************************************

bool loadObject(int index, Object** pObject) {
	CatRef cr;
	cr.id = index;
	return(loadObject(cr, pObject));
}

bool loadObject(const CatRef cr, Object** pObject) {
	if(!validObjId(cr))
		return(false);

	// Check if object is already loaded, and if so return pointer
	if(gConfig->objectInQueue(cr)) {
		gConfig->frontObjectQueue(cr);

		*pObject = new Object;
		gConfig->getObjectQueue(cr, pObject);
	}
	// Otherwise load the object and return a pointer to the newly loaded object
	else {
		// Load the object from it's file
		if(!loadObjectFromFile(cr, pObject))
			return(false);
		gConfig->addObjectQueue(cr, pObject);
	}


	if(pObject) {
		// Quest items are now auto NO-DROP
		if((*pObject)->getQuestnum()) {
			(*pObject)->setFlag(O_NO_DROP);
		}
		// cannot steal scrolls
		if((*pObject)->getType() == SCROLL) {
			(*pObject)->setFlag(O_NO_STEAL);
		}
	}
	return(true);
}

//*********************************************************************
//						loadRoom
//*********************************************************************

bool loadRoom(int index, Room **pRoom) {
	CatRef	cr;
	cr.id = index;
	return(loadRoom(cr, pRoom));
}

bool loadRoom(const CatRef cr, Room **pRoom) {
	if(!validRoomId(cr))
		return(false);

	// If the room is already loaded, return it
	if(gConfig->roomInQueue(cr)) {
		gConfig->frontRoomQueue(cr);
		gConfig->getRoomQueue(cr, pRoom);
	} else {
		// Otherwise load the room and return it
		if(!loadRoomFromFile(cr, pRoom))
			return(false);
		gConfig->addRoomQueue(cr, pRoom);
	}
	return(true);
}

//*********************************************************************
//						loadMonsterFromFile
//*********************************************************************

bool loadMonsterFromFile(const CatRef cr, Monster **pMonster, bstring filename) {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	int		num=0;

	if(filename == "")
		filename = monsterPath(cr);
	//printf("Attempting to load creature %d from %s\n", index, filename);

	if((xmlDoc = xml::loadFile(filename.c_str(), "Creature")) == NULL)
		return(false);

	if(xmlDoc == NULL) {
		printf("Error parsing file %s\n", filename.c_str());
		return(false);
	}
	rootNode = xmlDocGetRootElement(xmlDoc);
	num = xml::getIntProp(rootNode, "Num");

	if(cr.id == -1 || num == cr.id) {
		*pMonster = new Monster;
		if(!*pMonster)
			merror("loadMonsterFromFile", FATAL);
		(*pMonster)->setVersion(rootNode);

		(*pMonster)->readFromXml(rootNode);
		if((*pMonster)->flagIsSet(M_TALKS)) {
			loadCreature_tlk((*pMonster));
			(*pMonster)->convertOldTalks();
		}
	}

	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

//*********************************************************************
//						loadObjectFromFile
//*********************************************************************

bool loadObjectFromFile(const CatRef cr, Object** pObject) {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	int			num;
	char		filename[256];

	sprintf(filename, objectPath(cr));

	//printf("Attempting to load object %d from %s\n", index, filename);

	if((xmlDoc = xml::loadFile(filename, "Object")) == NULL)
		return(false);

	if(xmlDoc == NULL) {
		printf("Error parsing file %s\n", filename);
		return(false);
	}
	rootNode = xmlDocGetRootElement(xmlDoc);
	num = xml::getIntProp(rootNode, "Num");

	if(num == cr.id) {
		// BINGO: This is the object we want, read it in
		*pObject = new Object;
		if(!*pObject)
			merror("loadObjectFile", FATAL);
		xml::copyPropToBString((*pObject)->version, rootNode, "Version");

		(*pObject)->readFromXml(rootNode);
	}

	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

//*********************************************************************
//						loadRoomFromFile
//*********************************************************************
// if we're loading only from a filename, get CatRef from file

bool loadRoomFromFile(const CatRef cr, Room **pRoom, bstring filename) {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	int			num;

	if(filename == "")
		filename = roomPath(cr);

	//printf("Attempting to load room %d from file %s.\n", index, filename);
	if((xmlDoc = xml::loadFile(filename.c_str(), "Room")) == NULL)
		return(false);

	rootNode = xmlDocGetRootElement(xmlDoc);
	num = xml::getIntProp(rootNode, "Num");

	if(cr.id == -1 || num == cr.id) {
		*pRoom = new Room;
		if(!*pRoom)
			merror("loadRoomFromFile", FATAL);
		(*pRoom)->setVersion(xml::getProp(rootNode, "Version"));

		(*pRoom)->readFromXml(rootNode);
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

//*********************************************************************
//						readFromXml
//*********************************************************************
// Reads a creature from the given xml document and root node

int convertProf(Creature* player, Realm realm) {
	int skill = player->getLevel()*7 + player->getLevel()*3 * mprofic(player, realm) / 100 - 5;
	skill = MAX(0, skill);
	return(skill);
}

int Creature::readFromXml(xmlNodePtr rootNode) {
	xmlNodePtr curNode;
	int i;
	unsigned short c = 0;

	Player *pPlayer = getPlayer();
	Monster *mMonster = getMonster();

	if(mMonster) {
		mMonster->info.load(rootNode);
		mMonster->info.id = xml::getIntProp(rootNode, "Num");
	}
	xml::copyPropToBString(version, rootNode, "Version");

	curNode = rootNode->children;
	// Start reading stuff in!

	while(curNode) {
		// Name will only be loaded for Monsters
			 if(NODE_NAME(curNode, "Name")) xml::copyToCString(name, curNode);
		else if(NODE_NAME(curNode, "Description")) xml::copyToBString(description, curNode);
		else if(NODE_NAME(curNode, "Keys")) {
			loadStringArray(curNode, key, CRT_KEY_LENGTH, "Key", 3);
		}
		else if(NODE_NAME(curNode, "MoveTypes")) {
			loadStringArray(curNode, movetype, CRT_MOVETYPE_LENGTH, "MoveType", 3);
		}
		else if(NODE_NAME(curNode, "Level")) setLevel(xml::toNum<unsigned short>(curNode), true);
		else if(NODE_NAME(curNode, "Type")) setType(xml::toNum<unsigned short>(curNode));
		else if(NODE_NAME(curNode, "RoomNum")&& getVersion() < "2.34" ) xml::copyToNum(room.id, curNode);
		else if(NODE_NAME(curNode, "Room")) room.load(curNode);

		else if(NODE_NAME(curNode, "Race")) setRace(xml::toNum<unsigned short>(curNode));
		else if(NODE_NAME(curNode, "Class")) c = xml::toNum<unsigned short>(curNode);
		else if(NODE_NAME(curNode, "AttackPower")) setAttackPower(xml::toNum<unsigned int>(curNode));
		else if(NODE_NAME(curNode, "DefenseSkill")) {
			if(mMonster) {
				mMonster->setDefenseSkill(xml::toNum<int>(curNode));
			}
		}
		else if(NODE_NAME(curNode, "WeaponSkill")) {
			if(mMonster) {
				mMonster->setWeaponSkill(xml::toNum<int>(curNode));
			}
		}
		else if(NODE_NAME(curNode, "Class2")) {
			if(pPlayer) {
				pPlayer->setSecondClass(xml::toNum<unsigned short>(curNode));
			} else if(mMonster) {
				// TODO: Dom: for compatability, remove when possible
				mMonster->setMobTrade(xml::toNum<unsigned short>(curNode));
			}
		}
		else if(NODE_NAME(curNode, "Alignment")) setAlignment(xml::toNum<short>(curNode));
		else if(NODE_NAME(curNode, "Armor")) setArmor(xml::toNum<unsigned int>(curNode));
		else if(NODE_NAME(curNode, "Experience")) setExperience(xml::toNum<unsigned long>(curNode));

		else if(NODE_NAME(curNode, "Deity")) setDeity(xml::toNum<unsigned short>(curNode));

		else if(NODE_NAME(curNode, "Clan")) setClan(xml::toNum<unsigned short>(curNode));
		else if(NODE_NAME(curNode, "PoisonDuration")) setPoisonDuration(xml::toNum<unsigned short>(curNode));
		else if(NODE_NAME(curNode, "PoisonDamage")) setPoisonDamage(xml::toNum<unsigned short>(curNode));
		else if(NODE_NAME(curNode, "CurrentLanguage")) xml::copyToNum(current_language, curNode);
		else if(NODE_NAME(curNode, "Coins")) coins.load(curNode);
		else if(NODE_NAME(curNode, "Realms")) {
			xml::loadNumArray<unsigned long>(curNode, realm, "Realm", MAX_REALM-1);
		}
		else if(NODE_NAME(curNode, "Proficiencies")) {
			long proficiency[6];
			zero(proficiency, sizeof(proficiency));
			xml::loadNumArray<long>(curNode, proficiency, "Proficiency", 6);

			// monster jail rooms shouldn't be stored here
			if(mMonster && mMonster->getVersion() < "2.42b") {
				mMonster->setCastChance(proficiency[0]);
				mMonster->rescue[0].setArea("misc");
				mMonster->rescue[0].id = proficiency[1];
				mMonster->rescue[1].setArea("misc");
				mMonster->rescue[1].id = proficiency[2];
				mMonster->setMaxLevel(proficiency[3]);
				mMonster->jail.setArea("misc");
				mMonster->jail.id = proficiency[4];
			}
		}
		else if(NODE_NAME(curNode, "Dice")) damage.load(curNode);
		else if(NODE_NAME(curNode, "Skills")) {
			loadSkills(curNode);
		}
		else if(NODE_NAME(curNode, "Factions")) {
			loadFactions(curNode);
		}
		else if(NODE_NAME(curNode, "Effects")) {
			effects.load(curNode, this);
		}
		else if(NODE_NAME(curNode, "SpecialAttacks")) {
			loadAttacks(curNode);
		}
		else if(NODE_NAME(curNode, "InitialStats")) {
			// TODO: remove
			short ist[5];
			xml::loadNumArray<short>(curNode, ist, "Stat", 5);
			strength.setInitial(ist[0]);
			dexterity.setInitial(ist[1]);
			constitution.setInitial(ist[2]);
			intelligence.setInitial(ist[3]);
			piety.setInitial(ist[4]);
		}
		else if(NODE_NAME(curNode, "Stats")) {
			loadStats(curNode);
		}
		else if(NODE_NAME(curNode, "Flags")) {
			// Clear flags before loading incase we're loading a reference creature
			for(i=0; i<CRT_FLAG_ARRAY_SIZE; i++)
				flags[i] = 0;
			loadBits(curNode, flags);
		}
		else if(NODE_NAME(curNode, "Spells")) {
			loadBits(curNode, spells);
		}
		else if(NODE_NAME(curNode, "Quests")) {
			loadBits(curNode, quests);
		}
		else if(NODE_NAME(curNode, "Languages")) {
			loadBits(curNode, languages);
		}
		else if(NODE_NAME(curNode, "DailyTimers")) {
			loadDailys(curNode, daily);
		}
		else if(NODE_NAME(curNode, "LastTimes")) {
			loadLastTimes(curNode, lasttime);
		}
		else if(NODE_NAME(curNode, "SavingThrows")) {
			loadSavingThrows(curNode, saves);
		}
		else if(NODE_NAME(curNode, "Inventory")) {
			readObjectsXml(curNode, this, CRT);
		}
		else if(NODE_NAME(curNode, "Pets")) {
			readCreaturesXml(curNode, this, CRT);
		}
		else if(NODE_NAME(curNode, "AreaRoom")) gConfig->areaInit(this, curNode);
		else if(NODE_NAME(curNode, "Size")) setSize(whatSize(xml::toNum<int>(curNode)));
		else if(NODE_NAME(curNode, "Hooks")) hooks.load(curNode);


		// code for only players
		else if(pPlayer) pPlayer->readXml(curNode);

		// code for only monsters
		else if(mMonster) mMonster->readXml(curNode);

		curNode = curNode->next;
	}

	// run this here so effects are added properly
	setClass(c);

	convertOldEffects();
	convertOldFlags();


	if(isPlayer() && getVersion() < "2.43" && getClass() != BERSERKER) {
		int skill = level;
		if(isPureCaster() || isHybridCaster() || isStaff()) {
			skill *= 10;
		} else {
			skill *= 7;
		}
		skill -= 5;
		skill = MAX(0, skill);

		addSkill("abjuration", skill);
		addSkill("conjuration", skill);
		addSkill("divination", skill);
		addSkill("enchantment", skill);
		addSkill("evocation", skill);
		addSkill("illusion", skill);
		addSkill("necromancy", skill);
		addSkill("translocation", skill);
		addSkill("transmutation", skill);

		addSkill("fire", convertProf(this, FIRE));
		addSkill("water", convertProf(this, WATER));
		addSkill("earth", convertProf(this, EARTH));
		addSkill("air", convertProf(this, WIND));
		addSkill("cold", convertProf(this, COLD));
		addSkill("electric", convertProf(this, ELEC));
	}
	if(isPlayer() && getClass() == LICH && getVersion() < "2.43b" && !spellIsKnown(S_SAP_LIFE))
		learnSpell(S_SAP_LIFE);

	setVersion();


	if(size == NO_SIZE && race)
		size = gConfig->getRace(race)->getSize();

	escapeText();
	return(0);
}

//*********************************************************************
//						readXml
//*********************************************************************

void Monster::readXml(xmlNodePtr curNode) {
	xmlNodePtr childNode;

	if(NODE_NAME(curNode, "CarriedItems")) {
		loadCarryArray(curNode, carry, "Carry", 10);
	}
	else if(NODE_NAME(curNode, "LoadAggro")) setLoadAggro(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "LastMod")) xml::copyToCString(last_mod, curNode);

	// get rid of these after conversion
	else if(NODE_NAME(curNode, "StorageRoom") && getVersion() < "2.34")
		setLoadAggro(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "BoundRoom") && getVersion() < "2.34")
		setSkillLevel(xml::toNum<int>(curNode));


	else if(NODE_NAME(curNode, "SkillLevel")) setSkillLevel(xml::toNum<int>(curNode));
	else if(NODE_NAME(curNode, "ClassAggro")) {
		loadBits(curNode, cClassAggro);
	}
	else if(NODE_NAME(curNode, "RaceAggro")) {
		loadBits(curNode, raceAggro);
	}
	else if(NODE_NAME(curNode, "DeityAggro")) {
		loadBits(curNode, deityAggro);
	}
	else if(NODE_NAME(curNode, "Attacks")) {
		loadStringArray(curNode, attack, CRT_ATTACK_LENGTH, "Attack", 3);
	}
	else if(NODE_NAME(curNode, "Talk")) xml::copyToBString(talk, curNode);
	else if(NODE_NAME(curNode, "TalkResponses")) {
		childNode = curNode->children;
		TalkResponse* newTalk;
		while(childNode) {
			if(NODE_NAME(childNode, "TalkResponse")) {
				if((newTalk = new TalkResponse(childNode)) != NULL) {
					responses.push_back(newTalk);
				}
			}
			childNode = childNode->next;
		}

	}
	else if(NODE_NAME(curNode, "TradeTalk")) xml::copyToCString(ttalk, curNode);
	else if(NODE_NAME(curNode, "NumWander")) setNumWander(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "MagicResistance")) setMagicResistance(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "MobTrade")) setMobTrade(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "AssistMobs")) {
		loadCatRefArray(curNode, assist_mob, "Mob", NUM_ASSIST_MOB);
	}
	else if(NODE_NAME(curNode, "EnemyMobs")) {
		loadCatRefArray(curNode, enemy_mob, "Mob", NUM_ENEMY_MOB);
	}
	else if(NODE_NAME(curNode, "PrimeFaction")) {
		xml::copyToBString(primeFaction, curNode);
	}
	else if(NODE_NAME(curNode, "AggroString")) xml::copyToCString(aggroString, curNode);
	else if(NODE_NAME(curNode, "MaxLevel")) setMaxLevel(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "Cast")) setCastChance(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "Jail")) jail.load(curNode);
	else if(NODE_NAME(curNode, "Rescue")) loadCatRefArray(curNode, rescue, "Mob", NUM_RESCUE);

	else if(NODE_NAME(curNode, "UpdateAggro")) setUpdateAggro(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "PkIn") && getVersion() < "2.42b") setUpdateAggro(xml::toNum<unsigned short>(curNode));

	// Now handle version changes

	else if(getVersion() < "2.21") {
		//std::cout << "Loading mob pre version 2.21" << std::endl;
		// Title was changed to AggroString as of 2.21
		if(NODE_NAME(curNode, "Title")) xml::copyToCString(aggroString, curNode);
	}

}

//*********************************************************************
//						readXml
//*********************************************************************

void Player::readXml(xmlNodePtr curNode) {
	xmlNodePtr childNode;

	if(NODE_NAME(curNode, "Birthday")) {
		birthday = new cDay;
		birthday->load(curNode);
	}
	else if(NODE_NAME(curNode, "BoundRoom")) {
		if(getVersion() < "2.42h")
			bound.room.load(curNode);
		else
			bound.load(curNode);
	}
	else if(NODE_NAME(curNode, "PreviousRoom")) previousRoom.load(curNode);
	else if(NODE_NAME(curNode, "Statistics")) statistics.load(curNode);

	else if(NODE_NAME(curNode, "Bank")) bank.load(curNode);
	else if(NODE_NAME(curNode, "Surname")) xml::copyToBString(surname, curNode);
	else if(NODE_NAME(curNode, "Forum")) xml::copyToBString(forum, curNode);
	else if(NODE_NAME(curNode, "Ranges"))
		loadRanges(curNode, this);
	else if(NODE_NAME(curNode, "Wimpy")) setWimpy(xml::toNum<unsigned short>(curNode));

	else if(NODE_NAME(curNode, "Songs")) {
		loadBits(curNode, songs);
	}
	else if(NODE_NAME(curNode, "Anchors")) {
		loadAnchors(curNode);
	}

	else if(NODE_NAME(curNode, "LastPassword")) xml::copyToBString(lastPassword, curNode);
	else if(NODE_NAME(curNode, "PoisonedBy")) xml::copyToBString(poisonedBy, curNode);
	else if(NODE_NAME(curNode, "AfflictedBy")) xml::copyToBString(afflictedBy, curNode);
	else if(NODE_NAME(curNode, "ActualLevel")) setActualLevel(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "WeaponTrains")) setWeaponTrains(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "LastMod")) xml::copyToBString(oldCreated, curNode);
	else if(NODE_NAME(curNode, "Created")) {
		if(getVersion() < "2.43c")
			xml::copyToBString(oldCreated, curNode);
		else
			xml::copyToNum(created, curNode);
	}
	else if(NODE_NAME(curNode, "Guild")) setGuild(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "GuildRank")) setGuildRank(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "TickDmg")) setTickDamage(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "NegativeLevels")) setNegativeLevels(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "LostExperience")) setLostExperience(xml::toNum<unsigned long>(curNode));
	else if(NODE_NAME(curNode, "LastInterest")) setLastInterest(xml::toNum<long>(curNode));
	else if(NODE_NAME(curNode, "Title")) setTitle(xml::getBString(curNode));
	else if(NODE_NAME(curNode, "CustomColors")) xml::copyToCString(customColors, curNode);
	else if(NODE_NAME(curNode, "Thirst")) setThirst(xml::toNum<unsigned short>(curNode));
	else if(NODE_NAME(curNode, "RoomExp")) {
		childNode = curNode->children;
		while(childNode) {
			if(NODE_NAME(childNode, "Room")) {
				CatRef cr;
				cr.load(childNode);
				roomExp.push_back(cr);
			}
			childNode = childNode->next;
		}
	}
	else if(NODE_NAME(curNode, "Lore")) {
		childNode = curNode->children;
		while(childNode) {
			if(NODE_NAME(childNode, "Info")) {
				CatRef cr;
				cr.load(childNode);
				lore.push_back(cr);
			}
			childNode = childNode->next;
		}
	}
	else if(NODE_NAME(curNode, "Recipes")) {
		childNode = curNode->children;
		while(childNode) {
			if(NODE_NAME(childNode, "Recipe")) {
				int i=0;
				xml::copyToNum(i, childNode);
				recipes.push_back(i);
			}
			childNode = childNode->next;
		}
	}
	else if(NODE_NAME(curNode, "QuestsInProgress")) {
		childNode = curNode->children;
		QuestCompletion* qc;
		while(childNode) {
			if(NODE_NAME(childNode, "QuestCompletion")) {
				qc = new QuestCompletion(childNode, this);
				questsInProgress[qc->getParentQuest()->getId()] = qc;
			}
			childNode = childNode->next;
		}
	}
	else if(NODE_NAME(curNode, "QuestsCompleted")) {
		childNode = curNode->children;
		while(childNode) {
			if(NODE_NAME(childNode, "Quest")) {
				questsCompleted.push_back(xml::toNum<int>(childNode));
			}
			childNode = childNode->next;
		}
	}
	else if(NODE_NAME(curNode, "Minions")) {
		childNode = curNode->children;
		while(childNode) {
			if(NODE_NAME(childNode, "Minion")) {
				minions.push_back(xml::getBString(childNode));
			}
			childNode = childNode->next;
		}
	}
	else if(getVersion() < "2.42i") {
			 if(NODE_NAME(curNode, "PkWon")) statistics.setPkwon(xml::toNum<unsigned long>(curNode));
		else if(NODE_NAME(curNode, "PkIn")) statistics.setPkin(xml::toNum<unsigned long>(curNode));
	}
}

//*********************************************************************
//						load
//*********************************************************************

bool CrtSkill::load(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
		if(NODE_NAME(curNode, "Name")) {
			xml::copyToBString(name, curNode);
		} else if(NODE_NAME(curNode, "Gained")) {
			xml::copyToNum(gained, curNode);
		} else if(NODE_NAME(curNode, "GainBonus")) {
			xml::copyToNum(gainBonus, curNode);
		}
		curNode = curNode->next;
	}
	if(name == "" || gained == 0) {
		return(false);
	}
	return(true);
}

//*********************************************************************
//						loadSkill
//*********************************************************************

bool Creature::loadSkill(xmlNodePtr rootNode) {
	bstring name = "";
	int			gained=0;
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
		if(NODE_NAME(curNode, "Name")) {
			xml::copyToBString(name, curNode);
		} else if(NODE_NAME(curNode, "Gained")) {
			xml::copyToNum(gained, curNode);
		}
		curNode = curNode->next;
	}
	if(name != "" && gained != 0) {
		CrtSkill* skill = new CrtSkill(name, gained);
		skills[name] = skill;
		return(true);
	}
	return(false);
}

//*********************************************************************
//						loadFaction
//*********************************************************************

bool Creature::loadFaction(xmlNodePtr rootNode) {
	bstring name = "";
	int			regard=0;
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
		if(NODE_NAME(curNode, "Name")) {
			xml::copyToBString(name, curNode);
		} else if(NODE_NAME(curNode, "Regard")) {
			xml::copyToNum(regard, curNode);
		}
		curNode = curNode->next;
	}
	// players; load faction even if 0
	if(name != "" && (regard != 0 || isPlayer())) {
		factions[name] = regard;
		return(true);
	}
	return(false);
}

//*********************************************************************
//						loadSkills
//*********************************************************************

void Creature::loadSkills(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
		if(NODE_NAME(curNode, "Skill")) {
			CrtSkill *skill = new CrtSkill();
			if(skill->load(curNode)) {
				skills[skill->getName()] = skill;
			} else {
				delete skill;
			}
		}
		curNode = curNode->next;
	}
}

//*********************************************************************
//						loadFactions
//*********************************************************************

void Creature::loadFactions(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
		if(NODE_NAME(curNode, "Faction")) {
			loadFaction(curNode);
		}
		curNode = curNode->next;
	}
}

//*********************************************************************
//						load
//*********************************************************************

void Effects::load(xmlNodePtr rootNode, Creature* cParent, BaseRoom* rParent, Exit* xParent) {
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
		if(NODE_NAME(curNode, "Effect")) {
			try {
				EffectInfo* newEffect = new EffectInfo(curNode, Effects::getParentType(cParent, rParent, xParent));
				if(newEffect) {
					newEffect->setParentCreature(cParent);
					newEffect->setParentRoom(rParent);
					newEffect->setParentExit(xParent);
					list.push_back(newEffect);
				}
			} catch(bstring err) {
				std::cout << "Error adding effect: " << err << std::endl;
			}
		}
		curNode = curNode->next;
	}
}

//*********************************************************************
//						readFromXml
//*********************************************************************
// Reads an object from the given xml document and root node

int Object::readFromXml(xmlNodePtr rootNode) {
	xmlNodePtr curNode, childNode;
	info.load(rootNode);
	info.id = xml::getIntProp(rootNode, "Num");
	xml::copyPropToBString(version, rootNode, "Version");
	curNode = rootNode->children;
	// Start reading stuff in!

	while(curNode) {
		if(NODE_NAME(curNode, "Name")) xml::copyToCString(name, curNode);
		else if(NODE_NAME(curNode, "Description")) xml::copyToBString(description, curNode);
		else if(NODE_NAME(curNode, "LotteryNumbers")) {
			xml::loadNumArray<short>(curNode, lotteryNumbers, "LotteryNum", 6);
		}
		else if(NODE_NAME(curNode, "UseOutput")) xml::copyToCString(use_output, curNode);
		else if(NODE_NAME(curNode, "UseAttack")) xml::copyToCString(use_attack, curNode);
		else if(NODE_NAME(curNode, "LastMod")) xml::copyToBString(lastMod, curNode);
		else if(NODE_NAME(curNode, "Keys")) {
			loadStringArray(curNode, key, OBJ_KEY_LENGTH, "Key", 3);
		}
		else if(NODE_NAME(curNode, "Weight")) xml::copyToNum(weight, curNode);
		else if(NODE_NAME(curNode, "Type")) xml::copyToNum(type, curNode);
		else if(NODE_NAME(curNode, "SubType")) xml::copyToBString(subType, curNode);
		else if(NODE_NAME(curNode, "Adjustment")) xml::copyToNum(adjustment, curNode);
		else if(NODE_NAME(curNode, "ShotsMax")) xml::copyToNum(shotsmax, curNode);
		else if(NODE_NAME(curNode, "ShotsCur")) xml::copyToNum(shotscur, curNode);
		else if(NODE_NAME(curNode, "Armor")) xml::copyToNum(armor, curNode);
		else if(NODE_NAME(curNode, "WearFlag")) xml::copyToNum(wearflag, curNode);
		else if(NODE_NAME(curNode, "MagicPower")) xml::copyToNum(magicpower, curNode);
		else if(NODE_NAME(curNode, "Effect")) xml::copyToBString(effect, curNode);
		else if(NODE_NAME(curNode, "EffectDuration")) xml::copyToNum(effectDuration, curNode);
		else if(NODE_NAME(curNode, "EffectStrength")) xml::copyToNum(effectStrength, curNode);
		else if(NODE_NAME(curNode, "Level")) xml::copyToNum(level, curNode);
		else if(NODE_NAME(curNode, "Quality")) xml::copyToNum(quality, curNode);
		else if(NODE_NAME(curNode, "RequiredSkill")) xml::copyToNum(requiredSkill, curNode);
		else if(NODE_NAME(curNode, "Clan")) xml::copyToNum(clan, curNode);
		else if(NODE_NAME(curNode, "Special")) xml::copyToNum(special, curNode);
		else if(NODE_NAME(curNode, "QuestNum")) xml::copyToNum(questnum, curNode);
		else if(NODE_NAME(curNode, "Bulk")) xml::copyToNum(bulk, curNode);
		else if(NODE_NAME(curNode, "Size")) size = whatSize(xml::toNum<int>(curNode));
		else if(NODE_NAME(curNode, "MaxBulk")) xml::copyToNum(maxbulk, curNode);
		else if(NODE_NAME(curNode, "LotteryCycle")) xml::copyToNum(lotteryCycle, curNode);
		else if(NODE_NAME(curNode, "MaxUnique")) xml::copyToNum(max_unique, curNode);
		else if(NODE_NAME(curNode, "CoinCost")) coinCost = xml::toNum<unsigned long>(curNode);

		else if(NODE_NAME(curNode, "Deed")) deed.load(curNode);

		else if(NODE_NAME(curNode, "ShopValue")) shopValue = xml::toNum<unsigned long>(curNode);
		else if(NODE_NAME(curNode, "Made")) xml::copyToNum(made, curNode);
		else if(NODE_NAME(curNode, "KeyVal")) xml::copyToNum(keyVal, curNode);
		else if(NODE_NAME(curNode, "Material")) material = (Material)xml::toNum<int>(curNode);
		else if(NODE_NAME(curNode, "MinStrength")) xml::copyToNum(minStrength, curNode);
		else if(NODE_NAME(curNode, "NumAttacks")) xml::copyToNum(numAttacks, curNode);
		else if(NODE_NAME(curNode, "Delay")) xml::copyToNum(delay, curNode);
		else if(NODE_NAME(curNode, "Extra")) xml::copyToNum(extra, curNode);
		else if(NODE_NAME(curNode, "Recipe")) xml::copyToNum(recipe, curNode);
		else if(NODE_NAME(curNode, "Value")) value.load(curNode);
		else if(NODE_NAME(curNode, "InBag")) {
			loadCatRefArray(curNode, in_bag, "Obj", 3);
		}
		else if(NODE_NAME(curNode, "Dice")) damage.load(curNode);
		else if(NODE_NAME(curNode, "Flags")) {
			loadBits(curNode, flags);
		}
		else if(NODE_NAME(curNode, "LastTimes")) {
			loadLastTimes(curNode, lasttime);
		}
		else if(NODE_NAME(curNode, "SubItems")) {
			readObjectsXml(curNode, this, OBJ);
		}
		else if(NODE_NAME(curNode, "Compass")) {
			if(!compass)
				compass = new MapMarker;
			compass->load(curNode);
		}
		else if(NODE_NAME(curNode, "AlchemyEffects")) {
			loadAlchemyEffects(curNode);
		}
		else if(NODE_NAME(curNode, "Hooks")) hooks.load(curNode);
		else if(NODE_NAME(curNode, "RandomObjects")) {
			childNode = curNode->children;
			while(childNode) {
				if(NODE_NAME(childNode, "RandomObject")) {
					CatRef cr;
					cr.load(childNode);
					randomObjects.push_back(cr);
				}
				childNode = childNode->next;
			}
		}
		else if(NODE_NAME(curNode, "Owner")) xml::copyToBString(questOwner, curNode);

		// Now handle version changes

		else if(getVersion() < "2.41" ) {

				 if(NODE_NAME(curNode, "DeedLow")) deed.low.load(curNode);
			else if(NODE_NAME(curNode, "DeedHigh")) xml::copyToNum(deed.high, curNode);

		} else if(getVersion() < "2.42b") {
			if(NODE_NAME(curNode, "SpecialThree")) xml::copyToNum(effectDuration, curNode);
		}

		curNode = curNode->next;
	}

	if(getType() == KEY && getVersion() < "2.43b") {
		setKey(damage.getNumber());
		damage.setNumber(0);
	}

	// make sure uniqueness stays intact
	setFlag(O_UNIQUE);
	if(!gConfig->getUnique(this))
		clearFlag(O_UNIQUE);

	escapeText();
	return(0);
}

//*********************************************************************
//						readFromXml
//*********************************************************************
// Reads a room from the given xml document and root node

int Room::readFromXml(xmlNodePtr rootNode) {
	xmlNodePtr curNode;
//	xmlNodePtr childNode;

	info.load(rootNode);
	info.id = xml::getIntProp(rootNode, "Num");
	xml::copyPropToBString(version, rootNode, "Version");
	curNode = rootNode->children;
	// Start reading stuff in!
	while(curNode) {
			 if(NODE_NAME(curNode, "Name")) xml::copyToCString(name, curNode);
		else if(NODE_NAME(curNode, "ShortDescription")) xml::copyToBString(short_desc, curNode);
		else if(NODE_NAME(curNode, "LongDescription")) xml::copyToBString(long_desc, curNode);
		else if(NODE_NAME(curNode, "Fishing")) xml::copyToBString(fishing, curNode);
		else if(NODE_NAME(curNode, "Faction")) xml::copyToBString(faction, curNode);
		else if(NODE_NAME(curNode, "LastModBy")) xml::copyToCString(last_mod, curNode);
		else if(NODE_NAME(curNode, "LastModTime")) xml::copyToCString(lastModTime, curNode);
		else if(NODE_NAME(curNode, "LastPlayer")) xml::copyToCString(lastPly, curNode);
		else if(NODE_NAME(curNode, "LastPlayerTime")) xml::copyToCString(lastPlyTime, curNode);
		else if(NODE_NAME(curNode, "LowLevel")) xml::copyToNum(lowLevel, curNode);
		else if(NODE_NAME(curNode, "HighLevel")) xml::copyToNum(highLevel, curNode);
		else if(NODE_NAME(curNode, "MaxMobs")) xml::copyToNum(maxmobs, curNode);
		else if(NODE_NAME(curNode, "Trap")) xml::copyToNum(trap, curNode);
		else if(NODE_NAME(curNode, "TrapExit")) trapexit.load(curNode);
		else if(NODE_NAME(curNode, "TrapWeight")) xml::copyToNum(trapweight, curNode);
		else if(NODE_NAME(curNode, "TrapStrength")) xml::copyToNum(trapstrength, curNode);

		else if(NODE_NAME(curNode, "BeenHere")) xml::copyToNum(beenhere, curNode);
		else if(NODE_NAME(curNode, "Track")) track.load(curNode);
		else if(NODE_NAME(curNode, "Wander")) wander.load(curNode);
		else if(NODE_NAME(curNode, "RoomExp")) xml::copyToNum(roomExp, curNode);
		else if(NODE_NAME(curNode, "Flags")) {
			// No need to clear flags, no room refs
			loadBits(curNode, flags);
		}
		else if(NODE_NAME(curNode, "PermMobs")) {
			loadCrLastTimes(curNode, permMonsters);
		}
		else if(NODE_NAME(curNode, "PermObjs")) {
			loadCrLastTimes(curNode, permObjects);
		}
		else if(NODE_NAME(curNode, "LastTimes")) {
			loadLastTimes(curNode, lasttime);
		}
		else if(NODE_NAME(curNode, "Objects")) {
			readObjectsXml(curNode, this, ROOM);
		}
		else if(NODE_NAME(curNode, "Creatures")) {
			readCreaturesXml(curNode, this, ROOM);
		}
		else if(NODE_NAME(curNode, "Exits")) {
			readExitsXml(curNode);
		}
		else if(NODE_NAME(curNode, "Effects")) effects.load(curNode, 0, this);
		else if(NODE_NAME(curNode, "Size")) size = whatSize(xml::toNum<int>(curNode));
		else if(NODE_NAME(curNode, "Hooks")) hooks.load(curNode);


		// load old tracks
		if(getVersion() < "2.32d") {
				 if(NODE_NAME(curNode, "TrackExit")) track.setDirection(xml::getBString(curNode));
			else if(NODE_NAME(curNode, "TrackSize")) track.setSize(whatSize(xml::toNum<int>(curNode)));
			else if(NODE_NAME(curNode, "TrackNum")) track.setNum(xml::toNum<int>(curNode));
		}
		// load old wander info
		if(getVersion() < "2.33b") {
				 if(NODE_NAME(curNode, "Traffic")) wander.setTraffic(xml::toNum<int>(curNode));
			else if(NODE_NAME(curNode, "RandomMonsters")) {
				loadCatRefArray(curNode, wander.random, "Mob", NUM_RANDOM_SLOTS);
			}
		}
		if(getVersion() < "2.42e") {
			if(NODE_NAME(curNode, "Special")) xml::copyToNum(maxmobs, curNode);
		}
		curNode = curNode->next;
	}

	escapeText();
	addEffectsIndex();
	return(0);
}

//*********************************************************************
//						readFromXml
//*********************************************************************
// Reads a exit from the given xml document and root node

int Exit::readFromXml(xmlNodePtr rootNode, BaseRoom* room) {
	xmlNodePtr curNode;

	xml::copyPropToCString(name, rootNode, "Name");

	curNode = rootNode->children;
	// Start reading stuff in!
	while(curNode) {
			 if(NODE_NAME(curNode, "Name")) xml::copyToCString(name, curNode);
		else if(NODE_NAME(curNode, "Keys")) {
			loadStringArray(curNode, desc_key, EXIT_KEY_LENGTH, "Key", 3);
		}
		else if(NODE_NAME(curNode, "Level")) xml::copyToNum(level, curNode);
		else if(NODE_NAME(curNode, "Trap")) xml::copyToNum(trap, curNode);
		else if(NODE_NAME(curNode, "Key")) xml::copyToNum(key, curNode);
		else if(NODE_NAME(curNode, "KeyArea")) xml::copyToBString(keyArea, curNode);
		else if(NODE_NAME(curNode, "Size")) size = whatSize(xml::toNum<int>(curNode));
		else if(NODE_NAME(curNode, "Toll")) xml::copyToNum(toll, curNode);
		else if(NODE_NAME(curNode, "PassPhrase")) xml::copyToBString(passphrase, curNode);
		else if(NODE_NAME(curNode, "PassLang")) xml::copyToNum(passlang, curNode);
		else if(NODE_NAME(curNode, "Open")) xml::copyToBString(open, curNode);
		else if(NODE_NAME(curNode, "Enter")) { xml::copyToBString(enter, curNode); }
		else if(NODE_NAME(curNode, "Flags")) {
			// No need to clear flags, no exit refs
			loadBits(curNode, flags);
		}
		else if(NODE_NAME(curNode, "Target")) target.load(curNode);

		// depreciated, but there's no version function
		else if(NODE_NAME(curNode, "Room")) target.room.load(curNode);
		else if(NODE_NAME(curNode, "AreaRoom")) target.mapmarker.load(curNode);
		else if(NODE_NAME(curNode, "Effects")) effects.load(curNode, 0, room, this);
		else if(NODE_NAME(curNode, "Hooks")) hooks.load(curNode);

		curNode = curNode->next;
	}

	escapeText();
	return(0);
}

//*********************************************************************
//						readObjectsXml
//*********************************************************************
// Reads in objects and objrefs and adds them to parent of given type

void readObjectsXml(xmlNodePtr curNode, void* parent, int type) {
	xmlNodePtr childNode = curNode->children;
	Object* object=0, *object2=0;
	CatRef	cr;
	int		i=0,quantity=0;

	while(childNode) {
		object = 0;
		quantity = xml::getIntProp(childNode, "Quantity");
		if(quantity < 1)
			quantity = 1;
		if(NODE_NAME(childNode, "Object")) {
			// If it's a full object, read it in
			object = new Object;
			if(!object)
				merror("loadObjectsXml", FATAL);
			object->readFromXml(childNode);
		} else if(NODE_NAME(childNode, "ObjRef")) {
			// If it's an object reference, we want to first load the parent object
			// and then use readObjectXml to update any fields that may have changed
			cr.load(childNode);
			cr.id = xml::getIntProp(childNode, "Num");
			if(!validObjId(cr)) {
				printf("Invalid object %s\n", cr.str().c_str());
			} else {
				if(loadObject(cr, &object)) {
					// These two flags might be cleared on the reference object, so let
					// that object set them if it wants to
					object->clearFlag(O_HIDDEN);
					object->clearFlag(O_CURSED);
					object->readFromXml(childNode);
				} else {
					//printf("Error loading object %d.\n", num);
				}
			}
		}
		if(object && type == ROOM && object->flagIsSet(O_PORTAL)) {
			delete object;
			object = 0;
		}
		if(object) {
			for(i=0; i<quantity; i++) {
				if(!i) {
					object2 = object;  // just a reference
				} else {
					object2 = new Object;
					*object2 = *object;
				}
				// Add it to the appropriate parent
				if(type == OBJ) {
					((Object*)parent)->addObj(object2);
				} else if(type == PLY || type == CRT) {
					((Creature *)parent)->addObj(object2);
				} else if(type == ROOM) {
					object2->addToRoom((Room*)parent);
				}
			}
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						readCreaturesXml
//*********************************************************************
// Reads in creatures and crts refs and adds them to parent of given type

void readCreaturesXml(xmlNodePtr curNode, void* parent, int type) {
	xmlNodePtr childNode = curNode->children;
	Monster *mob=0;
	CatRef	cr;

	while(childNode) {
		mob = NULL;
		if(NODE_NAME( childNode , "Creature")) {
			// If it's a full creature, read it in
			mob = new Monster;
			if(!mob)
				merror("loadCreaturesXml", FATAL);
			mob->readFromXml(childNode);
		} else if(NODE_NAME( childNode, "CrtRef")) {
			// If it's an creature reference, we want to first load the parent creature
			// and then use readCreatureXml to update any fields that may have changed
			cr.load(childNode);
			cr.id = xml::getIntProp( childNode, "Num" );

			if(loadMonster(cr, &mob)) {
				mob->readFromXml(childNode);
			} else {
				//printf("Unable to load creature %d\n", num);
			}
		}
		if(mob != NULL) {
			// Add it to the appropriate parent
			if(type == PLY || type == CRT) {
				addFollower((Creature *) parent, mob, FALSE);
				//add_crt_crt(crt, (Creature *)parent);
			} else if(type == ROOM) {
				mob->addToRoom((Room*)parent, 0);
			} else if(type == OBJ) {
				// Umm, not implemented? Lets put this big ass goblin in my ivory coffer...yah....
			}
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						readExitsXml
//*********************************************************************

void BaseRoom::readExitsXml(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;
	Exit* ext=0;
	AreaRoom* aRoom = getAreaRoom();

	while(childNode) {
		if(NODE_NAME(childNode , "Exit")) {
			ext = new Exit;
			if(!ext)
				merror("loadExitsXml", FATAL);
			ext->readFromXml(childNode, this);

			if(!ext->flagIsSet(X_PORTAL)) {
				// moving cardinal exit on the overland?
				if(ext->flagIsSet(X_MOVING) && aRoom && aRoom->updateExit(ext->name))
					delete ext;
				else
					addExit(ext);
			} else
				delete ext;
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadAlchemy
//*********************************************************************

bool Config::loadAlchemy() {
	xmlDocPtr	xmlDoc;

	xmlNodePtr	curNode;
	char		filename[80];

	// build an XML tree from a the file
	sprintf(filename, "%s/alchemy.xml", CONFPATH);

	xmlDoc = xml::loadFile(filename, "Alchemy");
	if(xmlDoc == NULL)
		return(false);

	curNode = xmlDocGetRootElement(xmlDoc);

	curNode = curNode->children;
	while(curNode && xmlIsBlankNode(curNode)) {
		curNode = curNode->next;
	}
	if(curNode == 0) {
		xmlFreeDoc(xmlDoc);
		xmlCleanupParser();
		return(false);
	}

	clearAlchemy();
	while(curNode != NULL) {
		if(NODE_NAME(curNode, "AlchemyInfo")) {
			AlchemyInfo* alcInfo = new AlchemyInfo(curNode);
			if(alcInfo)
				alchemy.push_back(alcInfo);
		}
		curNode = curNode->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

AlchemyInfo::AlchemyInfo(xmlNodePtr rootNode) {
	rootNode = rootNode->children;
	while(rootNode != NULL)
	{
		if(NODE_NAME(rootNode, "Name")) xml::copyToBString(name, rootNode);
		else if(NODE_NAME(rootNode, "Action")) xml::copyToBString(action, rootNode);
		else if(NODE_NAME(rootNode, "PythonScript")) xml::copyToBString(pythonScript, rootNode);
		else if(NODE_NAME(rootNode, "Positive")) xml::copyToBool(positive, rootNode);
		else if(NODE_NAME(rootNode, "Throwable")) xml::copyToBool(throwable, rootNode);

		rootNode = rootNode->next;
	}
}

//*********************************************************************
//						loadBans
//*********************************************************************

bool Config::loadBans() {
	xmlDocPtr xmlDoc;
	Ban *curBan=0;
	xmlNodePtr cur;
	//char	filename[80];

	xmlDoc = xml::loadFile(CONFPATH "bans.xml", "Bans");

	if(xmlDoc == NULL)
		return(false);

	cur = xmlDocGetRootElement(xmlDoc);


	//	numBans = 0;
	//	memset(banTable, 0, sizeof(banTable));

	// First level we expect a Ban
	cur = cur->children;
	while(cur && xmlIsBlankNode(cur)) {
		cur = cur->next;
	}
	if(cur == 0) {
		xmlFreeDoc(xmlDoc);
		return(false);
	}

	clearBanList();
	while(cur != NULL) {
		if((!strcmp((char*) cur->name, "Ban"))) {
			curBan = new Ban(cur);
			if(curBan != NULL)
				bans.push_back(curBan);
		}
		cur = cur->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

//*********************************************************************
//						Ban
//*********************************************************************

Ban::Ban(xmlNodePtr curNode) {

	curNode = curNode->children;
	while(curNode != NULL)
	{
		if(NODE_NAME(curNode, "Site")) xml::copyToBString(site, curNode);
		else if(NODE_NAME(curNode, "Duration")) xml::copyToNum(duration, curNode);
		else if(NODE_NAME(curNode, "UnbanTime")) xml::copyToNum(unbanTime, curNode);
		else if(NODE_NAME(curNode, "BannedBy")) xml::copyToBString(by, curNode);
		else if(NODE_NAME(curNode, "BanTime")) xml::copyToBString(time, curNode);
		else if(NODE_NAME(curNode, "Reason")) xml::copyToBString(reason, curNode);
		else if(NODE_NAME(curNode, "Password")) xml::copyToBString(password, curNode);
		else if(NODE_NAME(curNode, "Suffix")) xml::copyToBool(isSuffix, curNode);
		else if(NODE_NAME(curNode, "Prefix")) xml::copyToBool(isPrefix, curNode);

		curNode = curNode->next;
	}
}


//*********************************************************************
//						loadGuilds
//*********************************************************************
// Causes the xml guild file to be parsed

bool Config::loadGuilds() {
	xmlDocPtr	xmlDoc;
	GuildCreation *curCreation=0;
	xmlNodePtr	cur;
	char		filename[80];

	// build an XML tree from a the file
	sprintf(filename, "%s/guilds.xml", CONFPATH);

	xmlDoc = xml::loadFile(filename, "Guilds");
	if(xmlDoc == NULL)
		return(false);

	cur = xmlDocGetRootElement(xmlDoc);
	gConfig->nextGuildId = xml::getIntProp(cur, "NextGuildId");
//	xml::copyToNum(gConfig->nextGuildId, = toNum<int>((char *)xmlGetProp(cur, BAD_CAST "NextGuildId"));

	//	numGuilds = 0;
	//	memset(guildTable, 0, sizeof(guildTable));

	// First level we expect a Guild
	cur = cur->children;
	while(cur && xmlIsBlankNode(cur))
		cur = cur->next;

	if(cur == 0) {
		// DOH! Forgot to clean up stuff here...not that it happened, but would have been leaky
		xmlFreeDoc(xmlDoc);
		xmlCleanupParser();
		return(false);
	}

	clearGuildList();
	Guild* guild;
	while(cur != NULL) {
		if(!strcmp((char *)cur->name, "Guild")) {
			guild = new Guild(cur);
			if(guild)
				addGuild(guild);
		}
		if(!strcmp((char *)cur->name, "GuildCreation")) {
			curCreation = parseGuildCreation(cur);
			if(curCreation != NULL)
				addGuildCreation(curCreation);
		}
		cur = cur->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

//*********************************************************************
//						parseGuild
//*********************************************************************
// Parse an individual guild from a valid xml file

Guild::Guild(xmlNodePtr curNode) {
	int guildId = xml::getIntProp(curNode, "ID");
	if(guildId == 0)
		throw new bstring("Invalid GuildID");

	num = guildId;
	gConfig->numGuilds = MAX(gConfig->numGuilds, guildId);
	curNode = curNode->children;
	while(curNode != NULL) {
			 if(NODE_NAME(curNode, "Name")) xml::copyToBString(name, curNode);
		else if(NODE_NAME(curNode, "Leader")) xml::copyToBString(leader, curNode);
		else if(NODE_NAME(curNode, "Level")) xml::copyToNum(level, curNode);
		else if(NODE_NAME(curNode, "NumMembers")) xml::copyToNum(numMembers, curNode);
		else if(NODE_NAME(curNode, "Members")) parseGuildMembers(curNode);
		else if(NODE_NAME(curNode, "PkillsWon")) xml::copyToNum(pkillsWon, curNode);
		else if(NODE_NAME(curNode, "PkillsIn")) xml::copyToNum(pkillsIn, curNode);
		else if(NODE_NAME(curNode, "Points")) xml::copyToNum(points, curNode);
		else if(NODE_NAME(curNode, "Bank")) bank.load(curNode);

		curNode = curNode->next;
	}
}

//*********************************************************************
//						parseGuildCreation
//*********************************************************************
// Parse an individual guild from a valid xml file

GuildCreation * parseGuildCreation(xmlNodePtr cur) {
	GuildCreation* gc = new GuildCreation;
	if(gc == NULL) {
		loge("Guild_Load: out of memory\n");
		return(NULL);
	}

	bstring temp = "";
	cur = cur->children;
	while(cur != NULL) {
			 if(NODE_NAME(cur, "Name")) xml::copyToBString(gc->name, cur);
		else if(NODE_NAME(cur, "Leader")) xml::copyToBString(gc->leader, cur);
		else if(NODE_NAME(cur, "LeaderIp")) xml::copyToBString(gc->leaderIp, cur);
		else if(NODE_NAME(cur, "Status")) xml::copyToNum(gc->status, cur);
		else if(NODE_NAME(cur, "Supporter")) {
			xml::copyToBString(temp, cur);
			gc->supporters[temp] = xml::getProp(cur, "Ip");
			gc->numSupporters++;
		}
		cur = cur->next;
	}
	return(gc);
}

//*********************************************************************
//						parseGuildMembers
//*********************************************************************

void Guild::parseGuildMembers(xmlNodePtr cur) {
	cur = cur->children;
	while(cur != NULL) {
		if(NODE_NAME(cur, "Member")) {
			char *tmp = xml::getCString(cur);
			addMember(tmp);
			free(tmp);
		}
		cur = cur->next;
	}
}

//*********************************************************************
//						loadSkills
//*********************************************************************

bool Config::loadSkills() {
	xmlDocPtr	xmlDoc;
	//GuildCreation * curCreation;
	xmlNodePtr	cur;
	char		filename[80];

	// build an XML tree from a the file
	sprintf(filename, "%s/skills.xml", CONFPATH);

	xmlDoc = xml::loadFile(filename, "Skills");
	if(xmlDoc == NULL)
		return(false);

	cur = xmlDocGetRootElement(xmlDoc);

	cur = cur->children;
	while(cur && xmlIsBlankNode(cur)) {
		cur = cur->next;
	}
	if(cur == 0) {
		xmlFreeDoc(xmlDoc);
		xmlCleanupParser();
		return(false);
	}

	clearSkills();
	while(cur != NULL) {
		if(NODE_NAME(cur, "SkillGroups")) {
			loadSkillGroups(cur);
		} else if(NODE_NAME(cur, "Skills")) {
			loadSkills(cur);
		}
		cur = cur->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);

}

//*********************************************************************
//						loadSkillGroups
//*********************************************************************

void Config::loadSkillGroups(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	while(curNode != NULL) {
		if(NODE_NAME(curNode, "SkillGroup")) {
			loadSkillGroup(curNode);
		}

		curNode = curNode->next;
	}
}

//*********************************************************************
//						loadSkillGroup
//*********************************************************************

void Config::loadSkillGroup(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	bstring name;
	bstring displayName;
	while(curNode != NULL) {
		if(NODE_NAME(curNode, "Name")) {
			xml::copyToBString(name, curNode);
		} else if(NODE_NAME(curNode, "DisplayName")) {
			xml::copyToBString(displayName, curNode);
		}
		curNode = curNode->next;
	}
	if(name != "" && displayName != "") {
		skillGroups[name] = displayName;
	}
	return;
}

//*********************************************************************
//						loadSkills
//*********************************************************************

void Config::loadSkills(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	Skill* skill=0;
	while(curNode != NULL) {
		if(NODE_NAME(curNode, "Skill")) {
			skill = new Skill;
			if(skill->load(curNode) ) {
				skills[skill->getName()] = skill;
			} else {
				delete skill;
			}
			skill = 0;
		}

		curNode = curNode->next;
	}
}

//*********************************************************************
//						load
//*********************************************************************

bool Skill::load(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	bstring group;
	bstring description;

	while(curNode != NULL) {
		if(NODE_NAME(curNode, "Name")) {
			xml::copyToBString(name, curNode);
		} else if(NODE_NAME(curNode, "DisplayName")) {
			xml::copyToBString(displayName, curNode);
		} else if(NODE_NAME(curNode, "Group")) {
			xml::copyToBString(group, curNode);
			if(!setGroup(group)) {
				printf("Error setting skill '%s' to group '%s'", name.c_str(), group.c_str());
				abort();
			}
		} else if(NODE_NAME(curNode, "Description")) {
			xml::copyToBString(description, curNode);
		} else if(NODE_NAME(curNode, "GainType")) {
			bstring strGainType;
			xml::copyToBString(strGainType, curNode);
			if(strGainType == "Medium") {
				gainType = SKILL_MEDIUM;
			} else if(strGainType == "Hard") {
				gainType = SKILL_HARD;
			} else if(strGainType == "Normal") {
				gainType = SKILL_NORMAL;
			} else if(strGainType == "Easy") {
				gainType = SKILL_EASY;
			}
		} else if(NODE_NAME(curNode, "KnownOnly")) {
			xml::copyToBool(knownOnly, curNode);
		}
		curNode = curNode->next;
	}
	if(name == "" || displayName == "") {
		printf("Invalid skill (Name:%s, DisplayName:%s)", name.c_str(), displayName.c_str());
		return(false);
	}
	return(true);
}

//*********************************************************************
//						loadCatRefArray
//*********************************************************************

void loadCatRefArray(xmlNodePtr curNode, std::map<int, CatRef>& array, const char* name, int maxProp) {
	xmlNodePtr childNode = curNode->children;
	int i=0;

	while(childNode) {
		if(NODE_NAME(childNode , name)) {
			i = xml::getIntProp(childNode, "Num");
			if(i >= 0 && i < maxProp)
				array[i].load(childNode);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadCatRefArray
//*********************************************************************

void loadCatRefArray(xmlNodePtr curNode, CatRef array[], const char* name, int maxProp) {
	xmlNodePtr childNode = curNode->children;
	int i=0;

	while(childNode) {
		if(NODE_NAME(childNode , name)) {
			i = xml::getIntProp(childNode, "Num");
			if(i >= 0 && i < maxProp)
				array[i].load(childNode);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadCarryArray
//*********************************************************************

void loadCarryArray(xmlNodePtr curNode, Carry array[], const char* name, int maxProp) {
	xmlNodePtr childNode = curNode->children;
	int i=0;

	while(childNode) {
		if(NODE_NAME(childNode , name)) {
			i = xml::getIntProp(childNode, "Num");
			if(i >= 0 && i < maxProp)
				array[i].load(childNode);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadString Array
//*********************************************************************
// Since we can't easily pass in variable length arrays, we pass in a pointer to the array
// as well as the size of the array. Based on the size and current number being read in
// we do some pointer arithmetic and then memcpy to the appropriate location

// Note: Size MUST be accurate or you will get unpredictable results

void loadStringArray(xmlNodePtr curNode, void* array, int size, const char* name, int maxProp) {
	int i=0, j=0;
	char *temp;
	xmlNodePtr childNode = curNode->children;

	while(childNode) {
		if(NODE_NAME(childNode , name)) {
			i = xml::getIntProp(childNode, "Num");
			if(i < maxProp) {
				// Note: The return from xml::getCString must be freed!
				temp = xml::getCString(childNode);
				if(temp) {
					j = strlen(temp);
					if(j > (size - 1))
						j = (size - 1);
					temp[j] = '\0';
					memcpy((char*)array + ( i * sizeof(char) * size), temp, j + 1);

					free(temp);
				}
			}
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadAlchemyEffects
//*********************************************************************
AlchemyEffect::AlchemyEffect(xmlNodePtr curNode) {
	duration = strength = 1;
	xmlNodePtr childNode = curNode->children;
	while(childNode) {
		if(NODE_NAME(childNode, "Effect")) {
			effect = xml::getBString(childNode);
		} else if(NODE_NAME(childNode, "Duration")) {
			xml::copyToNum(duration, childNode);
		} else if(NODE_NAME(childNode, "Strength")) {
			xml::copyToNum(strength, childNode);
		} else if(NODE_NAME(childNode, "Quality")) {
			xml::copyToNum(quality, childNode);
		}
		childNode = childNode->next;
	}
}
void Object::loadAlchemyEffects(xmlNodePtr curNode) {
	int i=0;
	xmlNodePtr childNode = curNode->children;
	while(childNode) {
		if(NODE_NAME(childNode, "AlchemyEffect")) {
			i = xml::getIntProp(childNode, "Num");
			if(i>0) {
				alchemyEffects[i] = AlchemyEffect(childNode);
			}
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadBits
//*********************************************************************
// Sets all bits it finds into the given bit array

void loadBits(xmlNodePtr curNode, char *bits) {
	xmlNodePtr childNode = curNode->children;
	int bit=0;

	while(childNode) {
		if(NODE_NAME(childNode, "Bit")) {
			bit = xml::getIntProp(childNode, "Num");
			// TODO: Sanity check bit
			BIT_SET(bits, bit);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadDailys
//*********************************************************************
// This function will load all daily timers into the given dailys array

void loadDailys(xmlNodePtr curNode, struct daily* pDailys) {
	xmlNodePtr childNode = curNode->children;
	int i=0;

	while(childNode) {
		if(NODE_NAME(childNode, "Daily")) {
			i = xml::getIntProp(childNode, "Num");
			// TODO: Sanity check i
			loadDaily(childNode, &pDailys[i]);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadDaily
//*********************************************************************
// This loads an individual daily into the provided pointer

void loadDaily(xmlNodePtr curNode, struct daily* pDaily) {
	xmlNodePtr childNode = curNode->children;

	while(childNode) {
		if(NODE_NAME(childNode, "Max")) xml::copyToNum(pDaily->max, childNode);
		else if(NODE_NAME(childNode, "Current")) xml::copyToNum(pDaily->cur, childNode);
		else if(NODE_NAME(childNode, "LastTime")) xml::copyToNum(pDaily->ltime, childNode);

		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadCrLastTimes
//*********************************************************************
// This function will load all crlasttimer into the given crlasttime array

void loadCrLastTimes(xmlNodePtr curNode, std::map<int, crlasttime>& pCrLastTimes) {
	xmlNodePtr childNode = curNode->children;
	int i=0;

	while(childNode) {
		if(NODE_NAME(childNode, "LastTime")) {
			i = xml::getIntProp(childNode, "Num");
			if(i >= 0 && i < NUM_PERM_SLOTS) {
				struct crlasttime cr;
				loadCrLastTime(childNode, &cr);
				if(cr.cr.id)
					pCrLastTimes[i] = cr;
			}
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadCrLastTime
//*********************************************************************
// Loads a single crlasttime into the given lasttime

void loadCrLastTime(xmlNodePtr curNode, struct crlasttime* pCrLastTime) {
	xmlNodePtr childNode = curNode->children;

	while(childNode) {
		if(NODE_NAME(childNode, "Interval")) xml::copyToNum(pCrLastTime->interval, childNode);
		else if(NODE_NAME(childNode, "LastTime")) xml::copyToNum(pCrLastTime->ltime, childNode);
		else if(NODE_NAME(childNode, "Misc")) pCrLastTime->cr.load(childNode);

		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadLastTimes
//*********************************************************************
// This function will load all lasttimer into the given lasttime array

void loadLastTimes(xmlNodePtr curNode, struct lasttime* pLastTimes) {
	xmlNodePtr childNode = curNode->children;
	int i=0;

	while(childNode) {
		if(NODE_NAME(childNode, "LastTime")) {
			i = xml::getIntProp(childNode, "Num");
			// TODO: Sanity check i
			loadLastTime(childNode, &pLastTimes[i]);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadLastTime
//*********************************************************************
// Loads a single lasttime into the given lasttime

void loadLastTime(xmlNodePtr curNode, struct lasttime* pLastTime) {
	xmlNodePtr childNode = curNode->children;

	while(childNode) {
		if(NODE_NAME(childNode, "Interval")) xml::copyToNum(pLastTime->interval, childNode);
		else if(NODE_NAME(childNode, "LastTime")) xml::copyToNum(pLastTime->ltime, childNode);
		else if(NODE_NAME(childNode, "Misc")) xml::copyToNum(pLastTime->misc, childNode);

		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadAnchors
//*********************************************************************
// Loads all anchors into the given anchors array

void Player::loadAnchors(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;
	int i=0;

	while(childNode) {
		if(NODE_NAME(childNode, "Anchor")) {
			i = xml::getIntProp(childNode, "Num");
			if(i >= 0 && i < MAX_DIMEN_ANCHORS && !anchor[i]) {
				anchor[i] = new Anchor;
				anchor[i]->load(childNode);
			}
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadSavingThrows
//*********************************************************************
// Loads all saving throws into the given array of saving throws

void loadSavingThrows(xmlNodePtr curNode, struct saves* pSavingThrows) {
	xmlNodePtr childNode = curNode->children;
	int i=0;

	while(childNode) {
		if(NODE_NAME(childNode, "SavingThrow")) {
			i = xml::getIntProp(childNode, "Num");
			// TODO: Sanity check i
			loadSavingThrow(childNode, &pSavingThrows[i]);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadSavingThrows
//*********************************************************************
// Loads an individual saving throw

void loadSavingThrow(xmlNodePtr curNode, struct saves* pSavingThrow) {
	xmlNodePtr childNode = curNode->children;

	while(childNode) {
		if(NODE_NAME(childNode, "Chance")) xml::copyToNum(pSavingThrow->chance, childNode);
		else if(NODE_NAME(childNode, "Gained")) xml::copyToNum(pSavingThrow->gained, childNode);
		else if(NODE_NAME(childNode, "Misc")) xml::copyToNum(pSavingThrow->misc, childNode);

		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadRanges
//*********************************************************************
// Loads builder ranges into the provided creature

void loadRanges(xmlNodePtr curNode, Player *pPlayer) {
	xmlNodePtr childNode = curNode->children;
	int i=0;

	while(childNode) {
		if(NODE_NAME(childNode, "Range")) {
			i = xml::getIntProp(childNode, "Num");
			if(i >= 0 && i < MAX_BUILDER_RANGE)
				pPlayer->bRange[i].load(childNode);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						loadStats
//*********************************************************************
// Loads all stats (strength, intelligence, hp, etc) into the given creature

void Creature::loadStats(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;
	bstring stat = "";

	while(childNode) {
		if(NODE_NAME(childNode, "Stat")) {
			stat = xml::getProp(childNode, "Name");

			if(stat == "")
				continue;

				 if(stat == "Strength") strength.load(childNode);
			else if(stat == "Dexterity") dexterity.load(childNode);
			else if(stat == "Constitution") constitution.load(childNode);
			else if(stat == "Intelligence") intelligence.load(childNode);
			else if(stat == "Piety") piety.load(childNode);
			else if(stat == "Hp") hp.load(childNode);
			else if(stat == "Mp") mp.load(childNode);
			else if(stat == "Focus") {
				Player* player = getPlayer();
				if(player)
					player->focus.load(childNode);
			}
		}

		childNode = childNode->next;
	}
}

//*********************************************************************
//						load
//*********************************************************************
// Loads a single stat into the given stat pointer

bool Stat::load(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;

	while(childNode) {
		if(NODE_NAME(childNode, "Current")) xml::copyToNum(cur, childNode);
		else if(NODE_NAME(childNode, "Max")) xml::copyToNum(max, childNode);
		else if(NODE_NAME(childNode, "TempMax")) xml::copyToNum(tmpMax, childNode);
		else if(NODE_NAME(childNode, "Initial")) xml::copyToNum(initial, childNode);

		childNode = childNode->next;
	}
	return(true);
}

//*********************************************************************
//						loadAttacks
//*********************************************************************

void Creature::loadAttacks(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
		if(NODE_NAME(curNode, "SpecialAttack")) {
			SpecialAttack* newAttack = new SpecialAttack(curNode);
			if(newAttack) {
				specials.push_back(newAttack);
			}
		}
		curNode = curNode->next;
	}
}