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/
/*
 * data.cpp
 *	 Data storage file
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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
 *
 */
#include "mud.h"
#include "calendar.h"
//#include "factions.h"
//#include "commands.h"

#include <sstream>
#include <iomanip>
#include <locale>

//*********************************************************************
//						moCopy
//*********************************************************************

void MudObject::moCopy(const MudObject& mo) {
	strcpy(name, mo.name);
	hooks = mo.hooks;
}

//*********************************************************************
//						Hook
//*********************************************************************
// Available Hooks:
//		onSocialInteraction
//		beforeAddObject				beforeAddRoom
//		afterAddObject				afterAddRoom
//		beforeDeleteFromObject		beforeDeleteFromRoom
//		afterDeleteFromObject		afterDeleteFromRoom

Hooks::Hooks() {
	parent = 0;
}

//*********************************************************************
//						Hook
//*********************************************************************

void Hooks::setParent(MudObject* target) {
	parent = target;
}

//*********************************************************************
//						addHook
//*********************************************************************

void Hooks::addHook(const bstring& event, const bstring& code) {
	hooks.insert(MultiMap::value_type(event, code));
}

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

void Hooks::load(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;
	bstring event, code;
	while(childNode) {
		if(NODE_NAME(curNode, "Hook")) {
			xml::copyPropToBString(event, childNode, "event");
			xml::copyToBString(code, childNode);
			addHook(event, code);
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						save
//*********************************************************************

void Hooks::save(xmlNodePtr curNode, const char* name) const {
	xmlNodePtr childNode, subNode;
	MultiMap::const_iterator it = hooks.begin();
	if(it == hooks.end())
		return;
	childNode = xml::newStringChild(curNode, name);

	for(; it != hooks.end(); it++) {
		subNode = xml::newStringChild(childNode, "Hook", (*it).second);
		xml::newProp(subNode, "event", (*it).first);
	}
}

//*********************************************************************
//						execute
//*********************************************************************

bool Hooks::execute(const bstring& event, MudObject* target) const {
	std::pair<MultiMap::const_iterator, MultiMap::const_iterator> p = hooks.equal_range(event);
	bool ran = false;
	//broadcast(isDm, "^orunning hook %s: %s on %s", event.c_str(), parent->name, target->name);
	for(MultiMap::const_iterator it = p.first; it != p.second; ++it) {
		ran = true;
		//broadcast(isDm, "  :: %s", (*it).second.c_str());
		//gServer->runPython((*it).second);
	}
	return(ran);
}

//*********************************************************************
//						MudFlag
//*********************************************************************

MudFlag::MudFlag() {
	id = 0;
	name = desc = "";
}

//*********************************************************************
//						CatRefInfo
//*********************************************************************

CatRefInfo::CatRefInfo() { clear(); }
CatRefInfo::~CatRefInfo() { clear(); }

void CatRefInfo::clear() {
	id=0;
	parent = name = worldName = area = yearsSince = fishing = "";
	limbo = recall = 0;
	teleportWeight = teleportZone = trackZone = 0;
	yearOffset = 0;

	std::map<Season,cSeason*>::iterator st;
	cSeason *season=0;
	for(st = seasons.begin() ; st != seasons.end() ; st++) {
		season = (*st).second;
		delete season;
	}
	seasons.clear();
}

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

void CatRefInfo::load(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;

	id = xml::getIntProp(rootNode, "id");
	while(curNode) {
			 if(NODE_NAME(curNode, "Area")) xml::copyToBString(area, curNode);
		else if(NODE_NAME(curNode, "Name")) xml::copyToBString(name, curNode);
		else if(NODE_NAME(curNode, "Fishing")) xml::copyToBString(fishing, curNode);
		else if(NODE_NAME(curNode, "WorldName")) xml::copyToBString(worldName, curNode);
		else if(NODE_NAME(curNode, "YearsSince")) xml::copyToBString(yearsSince, curNode);
		else if(NODE_NAME(curNode, "Parent")) xml::copyToBString(parent, curNode);
		else if(NODE_NAME(curNode, "Limbo")) xml::copyToNum(limbo, curNode);
		else if(NODE_NAME(curNode, "YearOffset")) xml::copyToNum(yearOffset, curNode);
		else if(NODE_NAME(curNode, "Recall")) xml::copyToNum(recall, curNode);
		else if(NODE_NAME(curNode, "TeleportWeight")) xml::copyToNum(teleportWeight, curNode);
		else if(NODE_NAME(curNode, "TeleportZone")) xml::copyToNum(teleportZone, curNode);
		else if(NODE_NAME(curNode, "TrackZone")) xml::copyToNum(trackZone, curNode);
		else if(NODE_NAME(curNode, "Seasons")) loadSeasons(curNode);
		curNode = curNode->next;
	}

	// this would make an infinite loop
	if(parent == area)
		parent = "";
}

//*********************************************************************
//						save
//*********************************************************************

void CatRefInfo::save(xmlNodePtr curNode) const {
	xmlNodePtr deepNode,subNode,childNode = xml::newStringChild(curNode, "Info");
	xml::newNumProp(childNode, "id", id);

	xml::saveNonNullString(childNode, "Area", area);
	xml::saveNonNullString(childNode, "Name", name);
	xml::saveNonNullString(childNode, "WorldName", worldName);
	xml::saveNonNullString(childNode, "YearsSince", yearsSince);
	xml::saveNonNullString(childNode, "Parent", parent);
	xml::saveNonZeroNum(childNode, "Limbo", limbo);
	xml::saveNonZeroNum(childNode, "YearOffset", yearOffset);
	xml::saveNonZeroNum(childNode, "Recall", recall);
	xml::saveNonZeroNum(childNode, "TeleportWeight", teleportWeight);
	xml::saveNonZeroNum(childNode, "TeleportZone", teleportZone);
	xml::saveNonZeroNum(childNode, "Track", trackZone);

	subNode = xml::newStringChild(childNode, "Seasons");
	std::map<Season,cSeason*>::const_iterator st;
	for(st = seasons.begin() ; st != seasons.end() ; st++) {
		deepNode = xml::newStringChild(subNode, "Season");
		(*st).second->save(deepNode);
	}
}

//*********************************************************************
//						loadSeasons
//*********************************************************************

void CatRefInfo::loadSeasons(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;
	cSeason* season=0;

	while(childNode) {
		if(NODE_NAME(childNode, "Season")) {
			season = new cSeason;
			season->load(childNode);
			if(seasons[season->getId()])
				delete seasons[season->getId()];
			seasons[season->getId()] = season;
		}
		childNode = childNode->next;
	}
}

//*********************************************************************
//						getWeather
//*********************************************************************

const cWeather* CatRefInfo::getWeather() const {
	std::map<Season,cSeason*>::const_iterator st;
	st = seasons.find(gConfig->getCalendar()->getCurSeason()->getId());
	if(st != seasons.end())
		return((*st).second->getWeather());
	return(0);
}

//*********************************************************************
//						str
//*********************************************************************

bstring CatRefInfo::str() const {
	std::ostringstream oStr;
	oStr << area;
	if(area == "area")
		oStr << " " << id;
	return(oStr.str());
}


//*********************************************************************
//						loadCatRefInfo
//*********************************************************************

bool Config::loadCatRefInfo() {
	xmlDocPtr xmlDoc;
	xmlNodePtr curNode;
	CatRefInfo *cri=0;
	const CatRefInfo *parent=0;

	xmlDoc = xml::loadFile(CONFPATH "catRefInfo.xml", "CatRefInfo");

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

	curNode = xmlDocGetRootElement(xmlDoc);

	bstring d = "";
	xml::copyPropToBString(d, curNode, "default");
	if(d != "")
		gConfig->defaultArea = d;

	curNode = curNode->children;
	while(curNode && xmlIsBlankNode(curNode))
		curNode = curNode->next;

	if(curNode == 0) {
		xmlFreeDoc(xmlDoc);
		return(false);
	}

	clearCatRefInfo();
	while(curNode != NULL) {
		if(NODE_NAME(curNode, "Info")) {
			cri = new CatRefInfo;
			cri->load(curNode);
			catRefInfo.push_back(cri);
		}
		curNode = curNode->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();

	// propogate: this happens once, so if we ever need to know info from the
	// parent, we never need to look for it
	std::list<CatRefInfo*>::iterator it;
	for(it = catRefInfo.begin() ; it != catRefInfo.end() ; it++) {
		cri = (*it);
		if(cri->parent != "") {
			parent = getCatRefInfo(cri->parent, 0, true);
			if(parent)
				cri->copyVars(parent);
		}
	}
	return(true);
}

//*********************************************************************
//						copyVars
//*********************************************************************
// only copy the vars it inherits from the parent

void CatRefInfo::copyVars(const CatRefInfo* cri) {
	teleportZone = cri->teleportZone;
	trackZone = cri->trackZone;
}

//*********************************************************************
//						copyVars
//*********************************************************************

void Config::clearCatRefInfo() {
	CatRefInfo* cr;

	while(!catRefInfo.empty()) {
		cr = catRefInfo.front();
		delete cr;
		catRefInfo.pop_front();
	}
	catRefInfo.clear();
}


//*********************************************************************
//						saveCatRefInfo
//*********************************************************************

void Config::saveCatRefInfo() const {
	std::list<CatRefInfo*>::const_iterator it;
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char			filename[80];

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "CatRefInfo", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);
	xml::newProp(rootNode, "default", defaultArea.c_str());

	for(it = catRefInfo.begin() ; it != catRefInfo.end() ; it++)
		(*it)->save(rootNode);

	sprintf(filename, "%s/catRefInfo.xml", CONFPATH);
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
}

//*********************************************************************
//						getCatRefInfo
//*********************************************************************

const CatRefInfo* Config::getCatRefInfo(bstring area, int id, int getParent) const {
	std::list<CatRefInfo*>::const_iterator it;

	// prevent infinite loops from bad config files
	if(getParent > 30) {
		broadcast(isCt, "^yConfig::getCatRefInfo is terminating because it is looping too long.\nCheck the catrefinfo config file for possible looping issues.\n");
		return(0);
	}
	for(it = catRefInfo.begin() ; it != catRefInfo.end() ; it++) {
		if(area == (*it)->area && id == (*it)->id) {
			if(getParent && (*it)->parent != "")
				return(getCatRefInfo((*it)->parent, 0, getParent+1));
			return(*it);
		}
	}

	return(0);
}

//*********************************************************************
//						getCatRefInfo
//*********************************************************************

const CatRefInfo* Config::getCatRefInfo(const BaseRoom* room, int getParent) const {
	const AreaRoom* aRoom=0;
	const Room* uRoom=0;
	int id = 0;
	bstring area = "";

	if(room) {
		uRoom = room->getConstUniqueRoom();
		aRoom = room->getConstAreaRoom();
	}

	if(uRoom) {
		area = uRoom->info.area;
	} else if(aRoom) {
		area = "area";
		id = aRoom->area->id;
	} else {
		area = "misc";
	}

	return(getCatRefInfo(area, id, getParent));
}

//*********************************************************************
//						getRandomCatRefInfo
//*********************************************************************
// used by teleport spell, so check teleportZone

const CatRefInfo* Config::getRandomCatRefInfo(int zone) const {
	std::list<CatRefInfo*>::const_iterator it;
	int total=0, pick=0;

	for(it = catRefInfo.begin() ; it != catRefInfo.end() ; it++) {
		if(!zone || zone == (*it)->teleportZone)
			total += (*it)->teleportWeight;
	}
	if(!total)
		return(0);

	pick = mrand(1, total);
	total = 0;

	for(it = catRefInfo.begin() ; it != catRefInfo.end() ; it++) {
		if(!zone || zone == (*it)->teleportZone) {
			total += (*it)->teleportWeight;
			if(total >= pick)
				return(*it);
		}
	}
	return(0);
}

//*********************************************************************
//						dmCatRefInfo
//*********************************************************************

int dmCatRefInfo(Player* player, cmd* cmnd) {
	std::list<CatRefInfo*>::const_iterator it;
	std::ostringstream oStr;
	const CatRefInfo *cri=0;
	int i=0;

	oStr.setf(std::ios::left, std::ios::adjustfield);
	oStr << "^yCatRefInfo List^x\n";

	for(it = gConfig->catRefInfo.begin() ; it != gConfig->catRefInfo.end() ; it++) {
		cri = (*it);
		i = 20;
		if(cri->name.getAt(0) == '^')
			i += 2;
		oStr << "  Area: ^c" << std::setw(10) << cri->str()
			 << "^x Name: ^c" << std::setw(i)
			 << cri->name << "^x ";
		if(cri->parent == "") {
			oStr << "TeleportZone: ^c" << std::setw(4) << cri->teleportZone << "^x "
				 << "TrackZone: ^c" << std::setw(4) << cri->trackZone << "^x "
				 << "TeleportWeight: ^c" << cri->teleportWeight;
			if(cri->getFishing() != "")
				oStr << " Fishing: ^c" << cri->getFishing();
			oStr << "^x\n"
				 << "                                              "
				 << "Limbo: ^c" << cri->limbo << "^x    Recall: ^c" << cri->recall << "^x\n";
		} else {
			oStr << "Parent: ^c" << cri->parent << "^x "
				 << "TeleportWeight: ^c" << cri->teleportWeight;
			if(cri->getFishing() != "")
				oStr << " Fishing: ^c" << cri->getFishing();
			oStr << "^x\n";
		}
	}

	player->printColor("%s\n", oStr.str().c_str());
	return(0);
}

//*********************************************************************
//						Range
//*********************************************************************

Range::Range() {
	high = 0;
}

Range& Range::operator=(const Range& r) {
	low = r.low;
	high = r.high;
	return(*this);
}

bool Range::operator==(const Range& r) const {
	if(	low != r.low ||
		high != r.high
	)
		return(false);
	return(true);
}
bool Range::operator!=(const Range& r) const {
	return(!(*this==r));
}

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

void Range::load(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;

	while(childNode) {
			 if(NODE_NAME(childNode, "Low")) low.load(childNode);
		else if(NODE_NAME(childNode, "High")) xml::copyToNum(high, childNode);

		childNode = childNode->next;
	}
	if(!high && low.id)
		high = low.id;
}

//*********************************************************************
//						save
//*********************************************************************

xmlNodePtr Range::save(xmlNodePtr rootNode, const char* childName, int pos) const {
	if(!low.id && !high)
		return(NULL);
	xmlNodePtr curNode = xml::newStringChild(rootNode, childName);
	if(pos)
		xml::newNumProp(curNode, "Num", pos);

	low.save(curNode, "Low", false);
	xml::newNumChild(curNode, "High", high);

	return(curNode);
}

//*********************************************************************
//						belongs
//*********************************************************************

bool Range::belongs(CatRef cr) const {
	return(	low.isArea(cr.area) && (
			(cr.id >= low.id && cr.id <= high) ||
			(low.id == -1 && high == -1)
	));
}

//*********************************************************************
//						str
//*********************************************************************

bstring Range::str() const {
	std::ostringstream oStr;
	oStr << low.area << ":";
	if(high == -1 && low.id == -1)
		oStr << "entire range";
	else if(high == low.id)
		oStr << low.id;
	else
		oStr << low.id << "-" << high;
	return(oStr.str());
}

//*********************************************************************
//						isArea
//*********************************************************************

bool Range::isArea(bstring c) const {
	return(low.isArea(c));
}

//*********************************************************************
//						CatRef
//*********************************************************************

CatRef::CatRef() {
	clear();
	if(gConfig)
		setArea(gConfig->defaultArea);
	else
		setArea("misc");
}

void CatRef::setDefault(const Creature* target) {
	clear();
	setArea(gConfig->defaultArea);

	if(target) {
		if(target->parent_rom) {
			setArea(target->parent_rom->info.area);
		} else if(target->area_room) {
			const CatRefInfo* cri = gConfig->getCatRefInfo("area", target->area_room->area->id, true);
			if(cri)
				setArea(cri->area);
		}
	}
}

void CatRef::clear() {
	area = "";
	id = 0;
}

CatRef& CatRef::operator=(const CatRef& cr) {
	area = cr.area;
	id = cr.id;
	return(*this);
}

bool CatRef::operator==(const CatRef& cr) const {
	return(area == cr.area && id == cr.id);
}
bool CatRef::operator!=(const CatRef& cr) const {
	return(!(*this == cr));
}

//*********************************************************************
//						str
//*********************************************************************

// version of string for reloading
bstring CatRef::rstr() const {
	std::ostringstream oStr;
	oStr << area << "." << id;
	return(oStr.str());
}

bstring CatRef::str(bstring current, char color) const {
	std::ostringstream oStr;
	// if we're in an area already, we can chop off some text because they
	// already know what the area is
	if(	id &&
		area != "" &&
		(current == "" || area != current)
	) {
		if(color)
			oStr << "^" << color << area << ":^x" << id;
		else
			oStr << id << " - " << area;
	} else
		oStr << id;
	return(oStr.str());
}

//*********************************************************************
//						setArea
//*********************************************************************

void CatRef::setArea(bstring c) {
	if(c.getLength() > 20)
		c = c.left(20);
	area = c.toLower();
}

//*********************************************************************
//						isArea
//*********************************************************************

bool CatRef::isArea(bstring c) const {
	return(area == c);
}

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

void CatRef::load(xmlNodePtr curNode) {
	xml::copyPropToBString(area, curNode, "Area");
	xml::copyToNum(id, curNode);
}

//*********************************************************************
//						save
//*********************************************************************

xmlNodePtr CatRef::save(xmlNodePtr curNode, const char* childName, bool saveNonZero, int pos) const {
	if(!saveNonZero && !id)
		return(NULL);
	curNode = xml::newNumChild(curNode, childName, id);
	xml::newProp(curNode, "Area", area.c_str());
	if(pos)
		xml::newNumProp(curNode, "Num", pos);
	return(curNode);
}

//*********************************************************************
//						Carry
//*********************************************************************

Carry::Carry() {
	numTrade = 0;
}

Carry& Carry::operator=(const Carry& cry) {
	info = cry.info;
	numTrade = cry.numTrade;
	return(*this);
}

bool Carry::operator==(const Carry& cry) const {
	return(info == cry.info && numTrade == cry.numTrade);
}
bool Carry::operator!=(const Carry& cry) const {
	return(!(*this == cry));
}
//*********************************************************************
//						load
//*********************************************************************

void Carry::load(xmlNodePtr curNode) {
	info.load(curNode);
	numTrade = xml::getIntProp(curNode, "NumTrade");
}

//*********************************************************************
//						save
//*********************************************************************

xmlNodePtr Carry::save(xmlNodePtr curNode, const char* childName, bool saveNonZero, int pos) const {
	curNode = info.save(curNode, childName, saveNonZero, pos);
	if(curNode && numTrade)
		xml::newNumProp(curNode, "NumTrade", numTrade);
	return(curNode);
}

//*********************************************************************
//						str
//*********************************************************************

bstring Carry::str(bstring current, char color) const {
	std::ostringstream oStr;
	oStr << info.str(current, color);
	if(numTrade)
		oStr << " (" << numTrade << ")";
	return(oStr.str());
}

//*********************************************************************
//						Location
//*********************************************************************

Location::Location() { }

void Location::save(xmlNodePtr rootNode, bstring name) const {
	xmlNodePtr curNode = xml::newStringChild(rootNode, name);
	room.save(curNode, "Room", false);
	if(mapmarker.getArea()) {
		xmlNodePtr childNode = xml::newStringChild(curNode, "MapMarker");
		mapmarker.save(childNode);
	}
}

void Location::load(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;

	while(childNode) {
		if(NODE_NAME(childNode, "Room")) room.load(childNode);
		else if(NODE_NAME(childNode, "MapMarker")) mapmarker.load(childNode);
		childNode = childNode->next;
	}
}

bstring Location::str() const {
	if(room.id)
		return(room.str());
	return(mapmarker.str());
}

bool Location::operator==(const Location& l) const {
	return(	(	(room.id == 0 && l.room.id == 0) ||	// if rooms are empty, don't care about area
				(room == l.room)					// same room
			) &&
			mapmarker == l.mapmarker
	);
}
bool Location::operator!=(const Location& l) const {
	return(!(*this == l));
}

BaseRoom* Location::loadRoom(Player* player) const {
	Room* uRoom=0;
	if(room.id && ::loadRoom(room, &uRoom))
		return(uRoom);

	Area* area = gConfig->getArea(mapmarker.getArea());
	if(area) {
		AreaRoom* aRoom = area->loadRoom(player, &mapmarker, false);
		return(aRoom);
	}

	return(0);
}

short Location::getId() const {
	if(room.id)
		return(room.id);
	return(mapmarker.getArea());
}

//*********************************************************************
//						PlayerTitle
//*********************************************************************

PlayerTitle::PlayerTitle() {
	male = "";
	female = "";
}
void PlayerTitle::load(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
			 if(NODE_NAME(curNode, "Female")) xml::copyToBString(female, curNode);
		else if(NODE_NAME(curNode, "Male")) xml::copyToBString(male, curNode);
		curNode = curNode->next;
	}
}
bstring PlayerTitle::getTitle(bool sexMale) const {
	return(sexMale ? male : female);
}

//*********************************************************************
//						raceCount
//*********************************************************************

unsigned short Config::raceCount() {
	return(races.size());
}

unsigned short Config::getPlayableRaceCount() {
	std::pair<int, RaceData*> rp;
	RaceData* curRace;
	int numPlayableRaces = 0;
	foreach(rp, races) {
		curRace = rp.second;
		if(!curRace) continue;

		if(curRace->isPlayable())
			numPlayableRaces++;
	}
	return(numPlayableRaces);
}

//*********************************************************************
//						RaceData
//*********************************************************************

RaceData::RaceData(xmlNodePtr rootNode) {
	Sex		sex;
	Dice	dice;
	char	sizeTxt[20];
	int		i=0;
	short	n=0;
	bstring str;

	id = 0;
	name = adjective = abbr = "";
	infra = bonus = 0;
	startAge = parentRace = porphyriaResistance = 0;
	size = NO_SIZE;
	isParentRace = false;
	playable = gendered = true;
	zero(stats, sizeof(stats));
	zero(saves, sizeof(saves));
	zero(classes, sizeof(classes));

	zero(multiClerDeities, sizeof(multiClerDeities));
	zero(clerDeities, sizeof(clerDeities));
	zero(palDeities, sizeof(palDeities));
	zero(dkDeities, sizeof(dkDeities));

	id = xml::getIntProp(rootNode, "id");
	xml::copyPropToBString(name, rootNode, "name");

	xmlNodePtr subNode, childNode, curNode = rootNode->children;

	while(curNode) {
			 if(NODE_NAME(curNode, "Adjective")) xml::copyToBString(adjective, curNode);
		else if(NODE_NAME(curNode, "Abbr")) xml::copyToBString(abbr, curNode);
		else if(NODE_NAME(curNode, "NotPlayable")) playable = false;
		else if(NODE_NAME(curNode, "ParentRace")) {
			xml::copyToNum(parentRace, curNode);
			if(gConfig->races.find(parentRace) != gConfig->races.end())
				gConfig->races[parentRace]->makeParent();
		}
		else if(NODE_NAME(curNode, "Data")) {
			childNode = curNode->children;
			while(childNode) {
				if(NODE_NAME(childNode, "Size")) {
					xml::copyToCString(sizeTxt, childNode);
					size = ::getSize(sizeTxt);
				}
				else if(NODE_NAME(childNode, "BaseHeight")) {
					sex = (Sex)xml::getIntProp(childNode, "Sex");
					xml::copyToNum(n, childNode);
					baseHeight[sex] = n;
				}
				else if(NODE_NAME(childNode, "BaseWeight")) {
					sex = (Sex)xml::getIntProp(childNode, "Sex");
					xml::copyToNum(n, childNode);
					baseWeight[sex] = n;
				}
				else if(NODE_NAME(childNode, "VarHeight")) {
					sex = (Sex)xml::getIntProp(childNode, "Sex");
					dice.load(childNode);
					varHeight[sex] = dice;
				}
				else if(NODE_NAME(childNode, "VarWeight")) {
					sex = (Sex)xml::getIntProp(childNode, "Sex");
					dice.load(childNode);
					varWeight[sex] = dice;
				}
				else if(NODE_NAME(childNode, "Gendered")) xml::copyToBool(gendered, childNode);
				else if(NODE_NAME(childNode, "BonusStat")) {
					xml::copyToNum(i, childNode);
					if(i)
						bonus = true;
				}
				else if(NODE_NAME(childNode, "StartAge")) xml::copyToNum(startAge, childNode);
				else if(NODE_NAME(childNode, "PorphyriaResistance")) xml::copyToNum(porphyriaResistance, childNode);
				else if(NODE_NAME(childNode, "Infravision")) xml::copyToBool(infra, childNode);
				else if(NODE_NAME(childNode, "Stats")) {
					subNode = childNode->children;
					while(subNode) {
						if(NODE_NAME(subNode, "Stat")) {
							xml::copyPropToBString(str, subNode, "id");
							xml::copyToNum(stats[gConfig->stattoNum(str)], subNode);
						}
						subNode = subNode->next;
					}
				}
				else if(NODE_NAME(childNode, "Classes")) {
					subNode = childNode->children;
					while(subNode) {
						if(NODE_NAME(subNode, "Class")) {
							xml::copyPropToBString(str, subNode, "id");
							classes[gConfig->classtoNum(str)] = true;
						}
						subNode = subNode->next;
					}
				}
				else if(NODE_NAME(childNode, "MultiClericDeities")) {
					subNode = childNode->children;
					while(subNode) {
						if(NODE_NAME(subNode, "Deity")) {
							xml::copyPropToBString(str, subNode, "id");
							multiClerDeities[gConfig->deitytoNum(str)] = true;
						}
						subNode = subNode->next;
					}
				}
				else if(NODE_NAME(childNode, "ClericDeities")) {
					subNode = childNode->children;
					while(subNode) {
						if(NODE_NAME(subNode, "Deity")) {
							xml::copyPropToBString(str, subNode, "id");
							clerDeities[gConfig->deitytoNum(str)] = true;
						}
						subNode = subNode->next;
					}
				}
				else if(NODE_NAME(childNode, "PaladinDeities")) {
					subNode = childNode->children;
					while(subNode) {
						if(NODE_NAME(subNode, "Deity")) {
							xml::copyPropToBString(str, subNode, "id");
							palDeities[gConfig->deitytoNum(str)] = true;
						}
						subNode = subNode->next;
					}
				}
				else if(NODE_NAME(childNode, "DeathknightDeities")) {
					subNode = childNode->children;
					while(subNode) {
						if(NODE_NAME(subNode, "Deity")) {
							xml::copyPropToBString(str, subNode, "id");
							dkDeities[gConfig->deitytoNum(str)] = true;
						}
						subNode = subNode->next;
					}
				}
				else if(NODE_NAME(childNode, "SavingThrows")) {
					subNode = childNode->children;
					while(subNode) {
						if(NODE_NAME(subNode, "SavingThrow")) {
							xml::copyPropToBString(str, subNode, "id");
							xml::copyToNum(saves[gConfig->savetoNum(str)], subNode);
						}
						subNode = subNode->next;
					}
				}
				else if(NODE_NAME(childNode, "Effects")) {
					subNode = childNode->children;
					while(subNode) {
						if(NODE_NAME(subNode, "Effect")) {
							xml::copyToBString(str, subNode);
							effects.push_back(str);
						}
						subNode = subNode->next;
					}
				} else if(NODE_NAME(childNode, "Skills")) {
					xmlNodePtr skillNode = childNode->children;
					while(skillNode) {
						if(NODE_NAME(skillNode, "Skill")) {
							SkillGain* skillGain = new SkillGain(skillNode);
							baseSkills.push_back(skillGain);
						}
						skillNode = skillNode->next;
					}
				}
				childNode = childNode->next;
			}
		}
		curNode = curNode->next;
	}
}

int Config::stattoNum(bstring str) {
	if(str == "Str") return(STR);
	if(str == "Con") return(CON);
	if(str == "Dex") return(DEX);
	if(str == "Int") return(INT);
	if(str == "Pty") return(PTY);
	return(0);
}
int Config::savetoNum(bstring str) {
	if(str == "Poi") return(POI);
	if(str == "Dea") return(DEA);
	if(str == "Bre") return(BRE);
	if(str == "Men") return(MEN);
	if(str == "Spl") return(SPL);
	return(0);
}
int Config::classtoNum(bstring str) {
	if(str == "Assassin") return(ASSASSIN);
	if(str == "Berserker") return(BERSERKER);
	if(str == "Cleric") return(CLERIC);
	if(str == "Fighter") return(FIGHTER);
	if(str == "Mage") return(MAGE);
	if(str == "Paladin") return(PALADIN);
	if(str == "Ranger") return(RANGER);
	if(str == "Thief") return(THIEF);
	if(str == "Vampire") return(VAMPIRE);
	if(str == "Monk") return(MONK);
	if(str == "Deathknight") return(DEATHKNIGHT);
	if(str == "Druid") return(DRUID);
	if(str == "Lich") return(LICH);
	if(str == "Werewolf") return(WEREWOLF);
	if(str == "Bard") return(BARD);
	if(str == "Rogue") return(ROGUE);
	if(str == "Fighter/Mage") return(MULTI_BASE+0);
	if(str == "Fighter/Thief") return(MULTI_BASE+1);
	if(str == "Cleric/Assassin") return(MULTI_BASE+2);
	if(str == "Mage/Thief") return(MULTI_BASE+3);
	if(str == "Thief/Mage") return(MULTI_BASE+4);
	if(str == "Cleric/Fighter") return(MULTI_BASE+5);
	if(str == "Mage/Assassin") return(MULTI_BASE+6);
	return(0);
}
int Config::racetoNum(bstring str) {
	for(unsigned i=0; i<races.size(); i++) {
		if(!races[i])
			continue;
		if(str == races[i]->getName() || str == races[i]->getAdjective())
			return(i);
	}
	return(-1);
}
int Config::deitytoNum(bstring str) {
	for(unsigned i=0; i<deities.size(); i++) {
		if(!deities[i])
			continue;
		if(str == deities[i]->getName())
			return(i);
	}
	return(-1);
}

//**********************************************************************
//						loadRaces
//**********************************************************************

bool Config::loadRaces() {
	xmlDocPtr xmlDoc;
	xmlNodePtr curNode;
	int		i=0;

	xmlDoc = xml::loadFile(CONFPATH "races.xml", "Races");

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

	curNode = xmlDocGetRootElement(xmlDoc);

	curNode = curNode->children;
	while(curNode && xmlIsBlankNode(curNode))
		curNode = curNode->next;

	if(curNode == 0) {
		xmlFreeDoc(xmlDoc);
		return(false);
	}

	clearRaces();
	while(curNode != NULL) {
		if(NODE_NAME(curNode, "Race")) {
			i = xml::getIntProp(curNode, "id");

			if(races.find(i) == races.end())
				races[i] = new RaceData(curNode);
		}
		curNode = curNode->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

//**********************************************************************
//						loadDeities
//**********************************************************************

bool Config::loadDeities() {
	xmlDocPtr xmlDoc;
	xmlNodePtr curNode;
	int		i=0;

	xmlDoc = xml::loadFile(CONFPATH "deities.xml", "Deities");

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

	curNode = xmlDocGetRootElement(xmlDoc);

	curNode = curNode->children;
	while(curNode && xmlIsBlankNode(curNode))
		curNode = curNode->next;

	if(curNode == 0) {
		xmlFreeDoc(xmlDoc);
		return(false);
	}

	clearDeities();
	while(curNode != NULL) {
		if(NODE_NAME(curNode, "Deity")) {
			i = xml::getIntProp(curNode, "id");

			if(deities.find(i) == deities.end())
				deities[i] = new DeityData(curNode);
		}
		curNode = curNode->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}


//**********************************************************************
//						loadFlags
//**********************************************************************

bool Config::loadFlags() {
	xmlDocPtr xmlDoc;
	xmlNodePtr curNode, childNode;

	xmlDoc = xml::loadFile(CONFPATH "flags.xml", "Flags");

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

	curNode = xmlDocGetRootElement(xmlDoc);

	curNode = curNode->children;
	while(curNode && xmlIsBlankNode(curNode))
		curNode = curNode->next;

	if(curNode == 0) {
		xmlFreeDoc(xmlDoc);
		return(false);
	}

	clearFlags();
	while(curNode != NULL) {
		childNode = curNode->children;
		while(childNode) {
			if(NODE_NAME(childNode, "Flag")) {
				MudFlag f;
				f.id = xml::getIntProp(childNode, "id");
				xml::copyPropToBString(f.name, childNode, "Name");
				xml::copyToBString(f.desc, childNode);

				if(NODE_NAME(curNode, "Rooms")) {
					rflags[f.id] = f;
				} else if(NODE_NAME(curNode, "Exits")) {
					xflags[f.id] = f;
				} else if(NODE_NAME(curNode, "Players")) {
					pflags[f.id] = f;
				} else if(NODE_NAME(curNode, "Monsters")) {
					mflags[f.id] = f;
				} else if(NODE_NAME(curNode, "Objects")) {
					oflags[f.id] = f;
				} else if(NODE_NAME(curNode, "SpecialAttacks")) {
					specialFlags[f.id] = f;
				} else if(NODE_NAME(curNode, "PropStorage")) {
					propStorFlags[f.id] = f;
				} else if(NODE_NAME(curNode, "PropShop")) {
					propShopFlags[f.id] = f;
				} else if(NODE_NAME(curNode, "PropHouse")) {
					propHouseFlags[f.id] = f;
				} else if(NODE_NAME(curNode, "PropGuild")) {
					propGuildFlags[f.id] = f;
				}
			}
			childNode = childNode->next;
		}
		curNode = curNode->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

//**********************************************************************
//						clearFlags
//**********************************************************************

void Config::clearFlags() {
	rflags.clear();
	xflags.clear();
	pflags.clear();
	mflags.clear();
	oflags.clear();
	specialFlags.clear();
	propStorFlags.clear();
	propShopFlags.clear();
	propHouseFlags.clear();
	propGuildFlags.clear();
}


//**********************************************************************
//						clearRaces
//**********************************************************************

void Config::clearRaces() {
	std::map<int, RaceData*>::iterator it;

	for(it = races.begin() ; it != races.end() ; it++) {
		RaceData* r = (*it).second;
		delete r;
	}
	races.clear();
}

//**********************************************************************
//						clearDeities
//**********************************************************************

void Config::clearDeities() {
	std::map<int, DeityData*>::iterator it;
	std::map<int, PlayerTitle*>::iterator tt;

	for(it = deities.begin() ; it != deities.end() ; it++) {
		DeityData* d = (*it).second;
		for(tt = d->titles.begin() ; tt != d->titles.end() ; tt++) {
			PlayerTitle* p = (*tt).second;
			delete p;
		}
		d->titles.clear();
		delete d;
	}
	deities.clear();
}

//**********************************************************************
//						getRace
//**********************************************************************

const RaceData* Config::getRace(bstring race) const {
	std::map<int, RaceData*>::const_iterator it;
	RaceData* data=0;
	int len = race.getLength();
	race = race.toLower();

	for(it = races.begin() ; it != races.end() ; it++) {
		// exact match
		if((*it).second->getName().toLower() == race)
			return((*it).second);
		if((*it).second->getName().substr(0, len) == race) {
			// not unique
			if(data)
				return(0);
			data = (*it).second;
		}
	}

	return(data);
}

//**********************************************************************
//						getRace
//**********************************************************************

const RaceData* Config::getRace(int id) const {
	std::map<int, RaceData*>::const_iterator it = races.find(id);

	if(it == races.end())
		it = races.begin();

	return((*it).second);
}

//**********************************************************************
//						getDeity
//**********************************************************************

const DeityData* Config::getDeity(int id) const {
	std::map<int, DeityData*>::const_iterator it = deities.find(id);

	if(it == deities.end())
		it = deities.begin();

	return((*it).second);
}

//*********************************************************************
//						dmShowRaces
//*********************************************************************

int dmShowRaces(Player* player, cmd* cmnd) {
	std::map<int, RaceData*>::const_iterator it;
	RaceData* data=0;
	bool	all = player->isDm() && cmnd->num > 1 && !strcmp(cmnd->str[1], "all");
	std::map<Sex, short>::const_iterator bIt;
	std::map<Sex, Dice>::const_iterator dIt;

	player->printColor("Displaying Races:%s\n",
		player->isDm() && !all ? "  Type ^y*racelist all^x to view all information." : "");
	for(it = gConfig->races.begin() ; it != gConfig->races.end() ; it++) {
		data = (*it).second;
		player->printColor("Id: ^c%-2d^x   Name: ^c%s\n", data->getId(), data->getName().c_str());
		if(all) {
			if(data->bonusStat())
				player->printColor("   ^mThis race picks a bonus and a penalty stat.\n");
			else
				player->printColor("   Str: ^m%-2d^x  Dex: ^m%-2d^x  Con: ^m%-2d^x  Int: ^m%-2d^x  Pty: ^m%-2d^x\n",
					data->getStatAdj(STR), data->getStatAdj(DEX), data->getStatAdj(CON),
					data->getStatAdj(INT), data->getStatAdj(PTY));
			player->printColor("   Infravision: %-3s^x  Size: %s  isParent: %-3s\n",
				data->hasInfravision() ? "^gYes" : "^rNo",
				getSizeName(data->getSize()).c_str(),
				data->isParent() ? "^gYes" : "^rNo");
			if(data->getParentRace())
				player->print("   Parent Race: %s", gConfig->getRace(data->getParentRace())->getName().c_str());
			if(data->getPorphyriaResistance())
				player->print("   Porphyria Resistance: %d", data->getPorphyriaResistance());
			player->printColor("   Playable: %s\n", data->isPlayable() ? "^gYes" : "^rNo");

			// base weight for all genders
			for(bIt = data->baseHeight.begin() ; bIt != data->baseHeight.end() ; bIt++) {
				player->printColor("   BaseHeight: %s: ^c%d", getSexName((*bIt).first).c_str(), (*bIt).second);
			}
			if(data->baseHeight.size())
				player->print("\n");
			// variable weight for all genders
			for(dIt = data->varHeight.begin() ; dIt != data->varHeight.end() ; dIt++) {
				player->printColor("   VarHeight: %s: ^c%s", getSexName((*dIt).first).c_str(), (*dIt).second.str().c_str());
			}
			if(data->varHeight.size())
				player->print("\n");

			// base height for all genders
			for(bIt = data->baseWeight.begin() ; bIt != data->baseWeight.end() ; bIt++) {
				player->printColor("   BaseWeight: %s: ^c%d", getSexName((*bIt).first).c_str(), (*bIt).second);
			}
			if(data->baseWeight.size())
				player->print("\n");
			// variable height for all genders
			for(dIt = data->varWeight.begin() ; dIt != data->varWeight.end() ; dIt++) {
				player->printColor("   VarWeight: %s: ^c%s", getSexName((*dIt).first).c_str(), (*dIt).second.str().c_str());
			}
			if(data->varWeight.size())
				player->print("\n");
		}
	}
	player->print("\n");
	return(0);
}

//*********************************************************************
//						dmShowDeities
//*********************************************************************

int dmShowDeities(Player* player, cmd* cmnd) {
	std::map<int, DeityData*>::iterator it;
	std::map<int, PlayerTitle*>::iterator tt;
	DeityData* data=0;
	PlayerTitle* title=0;
	bool	all = player->isDm() && cmnd->num > 1 && !strcmp(cmnd->str[1], "all");


	player->printColor("Displaying Deities:%s\n",
		player->isDm() && !all ? "  Type ^y*deitylist all^x to view all information." : "");
	for(it = gConfig->deities.begin() ; it != gConfig->deities.end() ; it++) {
		data = (*it).second;
		player->printColor("Id: ^c%-2d^x   Name: ^c%s\n", data->getId(), data->getName().c_str());
		if(all) {
			for(tt = data->titles.begin() ; tt != data->titles.end(); tt++) {
				title = (*tt).second;
				player->printColor("   Level: ^c%-2d^x   Male: ^c%s^x   Female: ^c%s\n",
					(*tt).first, title->getTitle(true).c_str(), title->getTitle(false).c_str());
			}
			player->print("\n");
		}
	}
	player->print("\n");
	return(0);
}

std::list<SkillGain*>::const_iterator RaceData::getSkillBegin() const {
	return(baseSkills.begin());
}
std::list<SkillGain*>::const_iterator RaceData::getSkillEnd() const {
	return(baseSkills.end());
}

int RaceData::getId() const { return(id); }
bstring RaceData::getName(int tryToShorten) const {
	if(!tryToShorten)
		return(name);
	bstring txt = name;
	if(txt.length() > (unsigned)tryToShorten)
		txt.Replace("Half-", "H-");
	return(txt);
}
bstring RaceData::getAdjective() const { return(adjective); }
bstring RaceData::getAbbr() const { return(abbr); }
int RaceData::getParentRace() const { return(parentRace); }
short RaceData::getPorphyriaResistance() const { return(porphyriaResistance); }
void RaceData::makeParent() { isParentRace = true; }
bool RaceData::isParent() const { return(isParentRace); }
bool RaceData::isPlayable() const { return(playable); }
bool RaceData::isGendered() const { return(gendered); }
bool RaceData::hasInfravision() const { return(infra); }
bool RaceData::bonusStat() const { return(bonus); }
Size RaceData::getSize() const { return(size); }
int RaceData::getStartAge() const { return(startAge); }
int RaceData::getStatAdj(int stat) const { return(stats[stat]); }
int RaceData::getSave(int save) const { return(saves[save]); }
bool RaceData::allowedClass(int cls) const { return(classes[cls]); }
bool RaceData::allowedDeity(int cls, int cls2, int dty) const {
	return(	(cls == CLERIC && !cls2 && allowedClericDeity(dty)) ||
			(cls == PALADIN && !cls2 && allowedPaladinDeity(dty)) ||
			(cls == DEATHKNIGHT && !cls2 && allowedDeathknightDeity(dty)) ||
			(cls == CLERIC && cls2 && allowedMultiClericDeity(dty))
	);
}
bool RaceData::allowedMultiClericDeity(int dty) const { return(multiClerDeities[dty]); }
bool RaceData::allowedClericDeity(int dty) const { return(clerDeities[dty]); }
bool RaceData::allowedPaladinDeity(int dty) const { return(palDeities[dty]); }
bool RaceData::allowedDeathknightDeity(int dty) const { return(dkDeities[dty]); }



DeityData::DeityData(xmlNodePtr rootNode) {
	int lvl=0;

	id = 0;
	name = "";
	xmlNodePtr curNode, childNode;

	id = xml::getIntProp(rootNode, "id");
	xml::copyPropToBString(name, rootNode, "name");

	curNode = rootNode->children;
	while(curNode) {
		if(NODE_NAME(curNode, "Titles")) {
			childNode = curNode->children;
			while(childNode) {
				if(NODE_NAME(childNode, "Title")) {
					lvl = xml::getIntProp(childNode, "level");
					titles[lvl] = new PlayerTitle;
					titles[lvl]->load(childNode);
				}
				childNode = childNode->next;
			}
		}
		curNode = curNode->next;
	}
}

int DeityData::getId() const {
	return(id);
}
bstring DeityData::getName() const {
	return(name);
}


bstring getTitle(const std::map<int, PlayerTitle*>& titles, int lvl, bool male, bool ignoreCustom) {
	std::map<int, PlayerTitle*>::const_iterator it;
	if(lvl < 1)
		lvl = 1;
	if(lvl > MAXALVL)
		lvl = MAXALVL;
	while(	lvl > 1 && (
				(it = titles.find(lvl)) == titles.end() ||
				(	ignoreCustom &&
					(*it).second->getTitle(male) == "[custom]"
				)
			)
	)
		lvl--;
	it = titles.find(lvl);
	return((*it).second->getTitle(male));
}

bstring DeityData::getTitle(int lvl, bool male, bool ignoreCustom) const {
	return(::getTitle(titles, lvl, male, ignoreCustom));
}
bstring PlayerClass::getTitle(int lvl, bool male, bool ignoreCustom) const {
	return(::getTitle(titles, lvl, male, ignoreCustom));
}