roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * statistics.cpp
 *	 Player statistics
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */

#include "mud.h"
#include "commands.h"
#include <iomanip>

//*********************************************************************
//                      LevelInfo
//*********************************************************************

LevelInfo::LevelInfo(int pLevel, int pHp, int pMp, int pStat, int pSave, time_t pTime) {
    level = pLevel;
    hpGain = pHp;
    mpGain = pMp;
    statUp = pStat;
    saveGain = pSave;
    levelTime = pTime;
}

LevelInfo::LevelInfo(const LevelInfo* l) {
    level = l->level;;
    hpGain = l->hpGain;
    mpGain = l->mpGain;
    statUp = l->statUp;
    saveGain = l->saveGain;
    levelTime = l->levelTime;

}
LevelInfo::LevelInfo(xmlNodePtr rootNode) {

    level = 0;
    hpGain = 0;
    mpGain = 0;
    statUp = 0;
    saveGain = 0;
    levelTime = 0;

    xmlNodePtr childNode = rootNode->children;

    while(childNode) {
        if(NODE_NAME(childNode, "Level")) xml::copyToNum(level, childNode);
        else if(NODE_NAME(childNode, "HpGain")) xml::copyToNum(hpGain, childNode);
        else if(NODE_NAME(childNode, "MpGain")) xml::copyToNum(mpGain, childNode);
        else if(NODE_NAME(childNode, "StatUp")) xml::copyToNum(statUp, childNode);
        else if(NODE_NAME(childNode, "SaveGain")) xml::copyToNum(saveGain, childNode);
        else if(NODE_NAME(childNode, "LevelTime")) xml::copyToNum(levelTime, childNode);
        childNode = childNode->next;
    }

    if(level == 0)
        throw std::exception();

}
void LevelInfo::save(xmlNodePtr rootNode) {
    xmlNodePtr curNode = xml::newStringChild(rootNode, "LevelInfo");

    xml::saveNonZeroNum(curNode, "Level", level);
    xml::saveNonZeroNum(curNode, "HpGain", hpGain);
    xml::saveNonZeroNum(curNode, "MpGain", mpGain);
    xml::saveNonZeroNum(curNode, "StatUp", statUp);
    xml::saveNonZeroNum(curNode, "SaveGain", saveGain);
    xml::saveNonZeroNum(curNode, "LevelTime", levelTime);


}

void Statistics::setLevelInfo(int level, LevelInfo* levelInfo) {
    LevelInfoMap::iterator it = levelHistory.find(level);
    if(it != levelHistory.end()) {
        levelHistory.erase(it);
    }

    levelHistory.insert(LevelInfoMap::value_type(level, levelInfo));
}

LevelInfo* Statistics::getLevelInfo(int level) {
    LevelInfoMap::iterator it = levelHistory.find(level);
    if(it == levelHistory.end())
        return(NULL);

    return(it->second);
}

int LevelInfo::getLevel() { return(level); }
int LevelInfo::getHpGain() { return(hpGain); }
int LevelInfo::getMpGain() { return(mpGain); }
int LevelInfo::getStatUp() { return(statUp); }
int LevelInfo::getSaveGain() { return(saveGain); }
time_t LevelInfo::getLevelTime() { return(levelTime); }

//*********************************************************************
//						StringStatistic
//*********************************************************************

StringStatistic::StringStatistic() {
	reset();
}

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

void StringStatistic::save(xmlNodePtr rootNode, bstring nodeName) const {
	if(!value && name == "")
		return;
	xmlNodePtr curNode = xml::newStringChild(rootNode, nodeName);

	xml::saveNonZeroNum(curNode, "Value", value);
	xml::saveNonNullString(curNode, "Name", name);
}

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

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

	while(childNode) {
		if(NODE_NAME(childNode, "Value")) xml::copyToNum(value, childNode);
		else if(NODE_NAME(childNode, "Name")) xml::copyToBString(name, childNode);
		childNode = childNode->next;
	}
}

//*********************************************************************
//						update
//*********************************************************************

void StringStatistic::update(unsigned long num, bstring with) {
	if(num > value) {
		value = num;
		name = stripColor(with);
	}
}

//*********************************************************************
//						reset
//*********************************************************************

void StringStatistic::reset() {
	value = 0;
	name = "";
}

//*********************************************************************
//						Statistics
//*********************************************************************

Statistics::Statistics() {
	track = true;
	numPkWon = numPkIn = 0;
	reset();
}
Statistics::Statistics(Statistics& cr) {
	doCopy(cr);
}

Statistics::Statistics(const Statistics& cr) {
	doCopy(cr);
}

Statistics& Statistics::operator=(const Statistics& cr) {
	doCopy(cr);
	return(*this);
}

Statistics::~Statistics() {
	reset();
}

void Statistics::doCopy(const Statistics& st) {
	start = st.start;

	// combat
	numSwings = st.numSwings;
	numHits = st.numHits;
	numMisses = st.numMisses;
	numFumbles = st.numFumbles;
	numDodges = st.numDodges;
	numCriticals = st.numCriticals;
	numTimesHit = st.numTimesHit;
	numTimesMissed = st.numTimesMissed;
	numTimesFled = st.numTimesFled;
	numPkIn = st.numPkIn;
	numPkWon = st.numPkWon;
	// magic
	numCasts = st.numCasts;
	numOffensiveCasts = st.numOffensiveCasts;
	numHealingCasts = st.numHealingCasts;
	numWandsUsed = st.numWandsUsed;
	numTransmutes = st.numTransmutes;
	numPotionsDrank = st.numPotionsDrank;
	// death
	numKills = st.numKills;
	numDeaths = st.numDeaths;
	expLost = st.expLost; // New
	lastExpLoss = st.lastExpLoss; // New

	// other
	numThefts = st.numThefts;
	numAttemptedThefts = st.numAttemptedThefts;
	numSaves = st.numSaves;
	numAttemptedSaves = st.numAttemptedSaves;
	numRecalls = st.numRecalls;
	numLagouts = st.numLagouts;
	numFishCaught = st.numFishCaught;
	numItemsCrafted = st.numItemsCrafted;
	numCombosOpened = st.numCombosOpened;
	levelHistoryStart = st.levelHistoryStart;

	// most
	mostGroup = st.mostGroup;
	mostExperience = st.mostExperience; // New
	mostMonster = st.mostMonster;
	mostAttackDamage = st.mostAttackDamage;
	mostMagicDamage = st.mostMagicDamage;

	// so we can reference
	parent = st.parent;
	LevelInfoMap::const_iterator it;
	LevelInfo* lInfo;
	for(it = st.levelHistory.begin() ; it != st.levelHistory.end() ; it++) {
		lInfo = new LevelInfo((*it).second);
		levelHistory[(*it).first] = lInfo;
	}
}
//*********************************************************************
//						reset
//*********************************************************************

void Statistics::reset() {
	numSwings = numHits = numMisses = numFumbles = numDodges =
		numCriticals = numTimesHit = numTimesMissed = numTimesFled =
		numCasts = numOffensiveCasts = numHealingCasts = numKills =
		numDeaths = numThefts = numAttemptedThefts = numSaves =
		numAttemptedSaves = numRecalls = numLagouts = numWandsUsed =
		numPotionsDrank = numItemsCrafted = numFishCaught = numCombosOpened =
		mostGroup = numPkIn = numPkWon = numTransmutes = lastExpLoss = expLost = 0;
	levelHistoryStart = 0;
	mostMonster.reset();
	mostAttackDamage.reset();
	mostMagicDamage.reset();
	mostExperience.reset();
	long t = time(0);
	start = ctime(&t);
	start = start.trim();

	LevelInfoMap::iterator it;
	for(it = levelHistory.begin() ; it != levelHistory.end() ; ) {
		delete (*it++).second;
	}
	levelHistory.clear();
}

//*********************************************************************
//						StartLevelHistoryTracking
//*********************************************************************
// Timing of level history after this point in time should be accurate

void Statistics::startLevelHistoryTracking() {
	levelHistoryStart = time(0);
}
//*********************************************************************
//						getLevelHistoryStart
//*********************************************************************

time_t Statistics::getLevelHistoryStart() {
	return(levelHistoryStart);
}
//*********************************************************************
//						displayLevelHistory
//*********************************************************************

void Statistics::displayLevelHistory(const Player* viewer) {
	bstring padding;
	std::ostringstream oStr;
	// set left aligned
	oStr.setf(std::ios::right, std::ios::adjustfield);
	oStr.imbue(std::locale(""));

	oStr << "Leveling history times valid since ^C" << ctime(&levelHistoryStart) << "^x\n";

	LevelInfo *lInfo;
	for(LevelInfoMap::value_type p : levelHistory) {
	    lInfo = p.second;
	    time_t levelTime = lInfo->getLevelTime();
	    bstring levelTimeStr = ctime(&levelTime);
	    levelTimeStr.Remove('\n');

	    oStr << "Level ^C" << std::setfill('0') << std::setw(2) << lInfo->getLevel() << "^x Date: ^C" << levelTimeStr << "^x";
	    oStr << " Gains - HP: ^C" << std::setfill('0') << std::setw(2) << lInfo->getHpGain() << "^x MP: ^C";
	    oStr << std::setfill('0') << std::setw(2) << lInfo->getMpGain() << "^x Stat: ^C";
	    oStr << getStatName(lInfo->getStatUp()) << "^x Save: ^C" << getSaveName(lInfo->getSaveGain()) << "^x\n";
	}

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


//*********************************************************************
//						display
//*********************************************************************

void Statistics::display(const Player* viewer, bool death) {
	bstring padding;
	std::ostringstream oStr;
	// set left aligned
	oStr.setf(std::ios::left, std::ios::adjustfield);
	oStr.imbue(std::locale(""));

	if(death) {
		// if death = true, player will always be the owner
		oStr << "^WGeneral player statistics:^x\n"
			 << "  Level:            ^C" << parent->getLevel() << "^x\n"
			 << "  Total Experience: ^C" << parent->getExperience() << "^x\n"
			 << "  Time Played:      ^C" << parent->getTimePlayed() << "^x\n"
			 << "\n";
	}

	oStr << "Tracking statistics since: ^C" << start << "\n";
	if(	numSwings ||
		numDodges ||
		numTimesHit ||
		numTimesMissed ||
		numDeaths ||
		numKills ||
		numPkIn
	)
		oStr << "^WCombat^x\n";
	if(numSwings) {
		oStr << "  Swings:       ^C" << numSwings << "^x\n";
		if(numHits)
			oStr << "  Hits:         ^C" << numHits << " (" << (numHits * 100 / numSwings) << "%)^x\n";
		if(numMisses)
			oStr << "  Misses:       ^C" << numMisses << " (" << (numMisses * 100 / numSwings) << "%)^x\n";
		if(numFumbles)
			oStr << "  Fumbles:      ^C" << numFumbles << " (" << (numFumbles * 100 / numSwings) << "%)^x\n";
		if(numCriticals)
			oStr << "  Criticals:    ^C" << numCriticals << " (" << (numCriticals * 100 / numSwings) << "%)^x\n";
	}
	if(numDodges)
		oStr << "  Dodges:       ^C" << numDodges << "^x\n";
	if(numTimesHit)
		oStr << "  Times Hit:    ^C" << numTimesHit << "^x\n";
	if(numTimesMissed)
		oStr << "  Times Missed: ^C" << numTimesMissed << "^x\n";
	// Don't want to see this info after all
	//if(numTimesFled)
	//	oStr << "  Times Fled:   ^C" << numTimesFled << "^x\n";
	if(numDeaths)
		oStr << "  Deaths:       ^C" << numDeaths << "^x\n";
	if(numKills)
		oStr << "  Kills:        ^C" << numKills << "^x\n";
	if(numPkIn)
		oStr << "  Player kills: ^C" << numPkWon << "/" << numPkIn << " (" << (numPkWon * 100 / numPkIn) << "%)^x\n";


	padding = (numTransmutes > 0 ? "     " : "");

	if(	numCasts ||
		numOffensiveCasts ||
		numHealingCasts ||
		numWandsUsed ||
		numPotionsDrank ||
		numTransmutes
	)
		oStr << "\n^WMagic^x\n";
	if(numHealingCasts)
		oStr << "  Spells cast: ^C" << padding << numCasts << "^x\n";
	if(numCasts) {
		if(numOffensiveCasts)
			oStr << "     Offensive Spells: ^C" << numOffensiveCasts << " (" << (numOffensiveCasts * 100 / numCasts) << "%)^x\n";
		if(numHealingCasts)
			oStr << "     Healing Spells:   ^C" << numHealingCasts << " (" << (numHealingCasts * 100 / numCasts) << "%)^x\n";
	}
	if(numWandsUsed)
		oStr << "  Wands used:       ^C" << padding << numWandsUsed << "^x\n";
	if(numTransmutes)
		oStr << "  Wands transmuted: ^C" << padding << numTransmutes << "^x\n";
	if(numPotionsDrank)
		oStr << "  Potions used:     ^C" << padding << numPotionsDrank << "^x\n";


	if(	mostGroup ||
		mostMonster.value ||
		mostAttackDamage.value ||
		mostMagicDamage.value
	)
		oStr << "\n^WMost / Largest^x\n";
	if(mostGroup)
		oStr << "  Largest group:               ^C" << mostGroup << "^x\n";
	if(mostMonster.value)
		oStr << "  Toughest monster killed:     ^C" << mostMonster.name << "^x\n";
	if(mostExperience.value)
	    oStr << "  Highest experience gained:   ^C" << mostExperience.value << " from " << mostExperience.name << "^x\n";
	if(mostAttackDamage.value)
		oStr << "  Most damage in one attack:   ^C" << mostAttackDamage.value << " with " << mostAttackDamage.name << "^x\n";
	if(mostMagicDamage.value)
		oStr << "  Most damage in one spell:    ^C" << mostMagicDamage.value << " with " << mostMagicDamage.name << "^x\n";
    if(expLost)
        oStr << "  Experience Lost:             ^C" << expLost << "^x\n";


	int rooms = parent->numDiscoveredRooms();
	int numRecipes = parent->recipes.size();
	if(	(numThefts && numAttemptedThefts) ||
		(numSaves && numAttemptedSaves) ||
		numRecalls ||
		numLagouts ||
		numFishCaught ||
		numItemsCrafted ||
		numRecipes ||
		numCombosOpened ||
		rooms
	)
		oStr << "\n^WOther^x\n";
	if(numThefts && numAttemptedThefts)
		oStr << "  Items stolen:                ^C" << numThefts << "/" << numAttemptedThefts << " (" << (numThefts * 100 / numAttemptedThefts) << "%)^x\n";
	if(numSaves && numAttemptedSaves)
		oStr << "  Saving throws made:          ^C" << numSaves << "/" << numAttemptedSaves << " (" << (numSaves * 100 / numAttemptedSaves) << "%)^x\n";
	if(numRecalls)
		oStr << "  Hazies/word-of-recalls used: ^C" << numRecalls << "^x\n";
	if(numLagouts)
		oStr << "  Lagouts:                     ^C" << numLagouts << "^x\n";
	if(numFishCaught)
		oStr << "  Fish caught:                 ^C" << numFishCaught << "^x\n";
	if(numItemsCrafted)
		oStr << "  Items crafted:               ^C" << numItemsCrafted << "^x\n";
	if(numRecipes)
		oStr << "  Recipes known:               ^C" << numRecipes << "^x\n";
	if(numCombosOpened)
		oStr << "  Combinations opened:         ^C" << numCombosOpened << "^x\n";
	if(rooms)
		oStr << "  Special rooms discovered:    ^C" << rooms << "^x\n";

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

//*********************************************************************
//						calcToughness
//*********************************************************************

unsigned long Statistics::calcToughness(Creature* target) {
	unsigned long t = 0;

	if(target->isMonster()) {

		const Monster* monster = target->getAsConstMonster();
		t += target->hp.getMax();
		t += target->getAttackPower();
		t += target->getWeaponSkill();
		t += target->getDefenseSkill();
		// level is weighted
		t += target->getLevel() * 2;
		// only a percentage of their mana counts
		t += (target->mp.getMax() * monster->getCastChance() / 100);

	} else {

		t = target->getLevel() * 10;

	}

	return(t);
}

//*********************************************************************
//						damageWith
//*********************************************************************

bstring Statistics::damageWith(const Player* player, const Object* weapon) {
	if(weapon)
		return(weapon->getObjStr(NULL, INV | MAG, 1));
	return((bstring)"your " + player->getUnarmedWeaponSkill() + "s");
}

//*********************************************************************
//						pkDemographics
//*********************************************************************

unsigned long Statistics::pkDemographics() const {
	if(numPkIn == numPkWon)
		return(0);
	return(pkRank());
}

//*********************************************************************
//						getTime
//*********************************************************************

bstring Statistics::getTime() {
	return(start);
}

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

void Statistics::save(xmlNodePtr rootNode, bstring nodeName) const {
	xmlNodePtr curNode = xml::newStringChild(rootNode, nodeName);

	xml::newNumChild(curNode, "Track", track);
	xml::saveNonNullString(curNode, "Start", start);

	xml::saveNonZeroNum(curNode, "NumSwings", numSwings);
	xml::saveNonZeroNum(curNode, "NumHits", numHits);
	xml::saveNonZeroNum(curNode, "NumMisses", numMisses);
	xml::saveNonZeroNum(curNode, "NumFumbles", numFumbles);
	xml::saveNonZeroNum(curNode, "NumDodges", numDodges);
	xml::saveNonZeroNum(curNode, "NumCriticals", numCriticals);
	xml::saveNonZeroNum(curNode, "NumTimesHit", numTimesHit);
	xml::saveNonZeroNum(curNode, "NumTimesMissed", numTimesMissed);
	xml::saveNonZeroNum(curNode, "NumTimesFled", numTimesFled);
	xml::saveNonZeroNum(curNode, "NumPkIn", numPkIn);
	xml::saveNonZeroNum(curNode, "NumPkWon", numPkWon);
	xml::saveNonZeroNum(curNode, "NumCasts", numCasts);
	xml::saveNonZeroNum(curNode, "NumOffensiveCasts", numOffensiveCasts);
	xml::saveNonZeroNum(curNode, "NumHealingCasts", numHealingCasts);
	xml::saveNonZeroNum(curNode, "NumKills", numKills);
	xml::saveNonZeroNum(curNode, "NumDeaths", numDeaths);
	xml::saveNonZeroNum(curNode, "NumThefts", numThefts);
	xml::saveNonZeroNum(curNode, "NumAttemptedThefts", numAttemptedThefts);
	xml::saveNonZeroNum(curNode, "NumSaves", numSaves);
	xml::saveNonZeroNum(curNode, "NumAttemptedSaves", numAttemptedSaves);
	xml::saveNonZeroNum(curNode, "NumRecalls", numRecalls);
	xml::saveNonZeroNum(curNode, "NumLagouts", numLagouts);
	xml::saveNonZeroNum(curNode, "NumWandsUsed", numWandsUsed);
	xml::saveNonZeroNum(curNode, "NumTransmutes", numTransmutes);
	xml::saveNonZeroNum(curNode, "NumPotionsDrank", numPotionsDrank);
	xml::saveNonZeroNum(curNode, "NumFishCaught", numFishCaught);
	xml::saveNonZeroNum(curNode, "NumItemsCrafted", numItemsCrafted);
	xml::saveNonZeroNum(curNode, "NumCombosOpened", numCombosOpened);
	xml::saveNonZeroNum(curNode, "MostGroup", mostGroup);
	xml::saveNonZeroNum(curNode, "ExpLost", expLost);
	xml::saveNonZeroNum(curNode, "LastExpLoss", lastExpLoss);
	mostMonster.save(curNode, "MostMonster");
	mostAttackDamage.save(curNode, "MostAttackDamage");
	mostMagicDamage.save(curNode, "MostMagicDamage");
	mostExperience.save(curNode, "MostExperience");

	xml::saveNonZeroNum(curNode, "LevelHistoryStart", levelHistoryStart);
	xmlNodePtr historyNode = xml::newStringChild(curNode, "LevelHistory", "");
	LevelInfoMap::const_iterator it;
	for( it = levelHistory.begin() ; it != levelHistory.end() ; it++) {
		(*it).second->save(historyNode);
	}
//	for(LevelInfoMap::value_type p : levelHistory) {
//	    p.second->save(historyNode);
//	}
}

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

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

	while(childNode) {
			 if(NODE_NAME(childNode, "Track")) xml::copyToBool(track, childNode);
		else if(NODE_NAME(childNode, "Start")) xml::copyToBString(start, childNode);
		else if(NODE_NAME(childNode, "NumSwings")) xml::copyToNum(numSwings, childNode);
		else if(NODE_NAME(childNode, "NumHits")) xml::copyToNum(numHits, childNode);
		else if(NODE_NAME(childNode, "NumMisses")) xml::copyToNum(numMisses, childNode);
		else if(NODE_NAME(childNode, "NumFumbles")) xml::copyToNum(numFumbles, childNode);
		else if(NODE_NAME(childNode, "NumDodges")) xml::copyToNum(numDodges, childNode);
		else if(NODE_NAME(childNode, "NumCriticals")) xml::copyToNum(numCriticals, childNode);
		else if(NODE_NAME(childNode, "NumTimesHit")) xml::copyToNum(numTimesHit, childNode);
		else if(NODE_NAME(childNode, "NumTimesMissed")) xml::copyToNum(numTimesMissed, childNode);
		else if(NODE_NAME(childNode, "NumTimesFled")) xml::copyToNum(numTimesFled, childNode);
		else if(NODE_NAME(childNode, "NumPkIn")) xml::copyToNum(numPkIn, childNode);
		else if(NODE_NAME(childNode, "NumPkWon")) xml::copyToNum(numPkWon, childNode);
		else if(NODE_NAME(childNode, "NumCasts")) xml::copyToNum(numCasts, childNode);
		else if(NODE_NAME(childNode, "NumOffensiveCasts")) xml::copyToNum(numOffensiveCasts, childNode);
		else if(NODE_NAME(childNode, "NumHealingCasts")) xml::copyToNum(numHealingCasts, childNode);
		else if(NODE_NAME(childNode, "NumKills")) xml::copyToNum(numKills, childNode);
		else if(NODE_NAME(childNode, "NumDeaths")) xml::copyToNum(numDeaths, childNode);
		else if(NODE_NAME(childNode, "NumThefts")) xml::copyToNum(numThefts, childNode);
		else if(NODE_NAME(childNode, "NumAttemptedThefts")) xml::copyToNum(numAttemptedThefts, childNode);
		else if(NODE_NAME(childNode, "NumSaves")) xml::copyToNum(numSaves, childNode);
		else if(NODE_NAME(childNode, "NumAttemptedSaves")) xml::copyToNum(numAttemptedSaves, childNode);
		else if(NODE_NAME(childNode, "NumRecalls")) xml::copyToNum(numRecalls, childNode);
		else if(NODE_NAME(childNode, "NumLagouts")) xml::copyToNum(numLagouts, childNode);
		else if(NODE_NAME(childNode, "NumWandsUsed")) xml::copyToNum(numWandsUsed, childNode);
		else if(NODE_NAME(childNode, "NumTransmutes")) xml::copyToNum(numTransmutes, childNode);
		else if(NODE_NAME(childNode, "NumPotionsDrank")) xml::copyToNum(numPotionsDrank, childNode);
		else if(NODE_NAME(childNode, "NumFishCaught")) xml::copyToNum(numFishCaught, childNode);
		else if(NODE_NAME(childNode, "NumItemsCrafted")) xml::copyToNum(numItemsCrafted, childNode);
		else if(NODE_NAME(childNode, "NumCombosOpened")) xml::copyToNum(numCombosOpened, childNode);
		else if(NODE_NAME(childNode, "MostGroup")) xml::copyToNum(mostGroup, childNode);
		else if(NODE_NAME(childNode, "MostMonster")) mostMonster.load(childNode);
		else if(NODE_NAME(childNode, "MostAttackDamage")) mostAttackDamage.load(childNode);
		else if(NODE_NAME(childNode, "MostMagicDamage")) mostMagicDamage.load(childNode);
		else if(NODE_NAME(childNode, "MostExperience")) mostExperience.load(childNode);
		else if(NODE_NAME(childNode, "ExpLost")) xml::copyToNum(expLost, childNode);
	    else if(NODE_NAME(childNode, "LastExpLoss")) xml::copyToNum(lastExpLoss, childNode);
	    else if(NODE_NAME(childNode, "LevelHistoryStart")) xml::copyToNum(levelHistoryStart, childNode);
	    else if(NODE_NAME(childNode, "LevelHistory")) {
	        xmlNodePtr infoNode = childNode->children;
	        while(infoNode) {
	            try {
	                LevelInfo *info = new LevelInfo(infoNode);
	                levelHistory.insert(LevelInfoMap::value_type(info->getLevel(), info));
	            } catch (...) {

	            }
	            infoNode = infoNode->next;
	        }
	    }

		childNode = childNode->next;
	}
}

//*********************************************************************
//						swing
//*********************************************************************

void Statistics::swing() { if(track) numSwings++; }

//*********************************************************************
//						hit
//*********************************************************************

void Statistics::hit() { if(track) numHits++; }

//*********************************************************************
//						miss
//*********************************************************************

void Statistics::miss() { if(track) numMisses++; }

//*********************************************************************
//						fumble
//*********************************************************************

void Statistics::fumble() { if(track) numFumbles++; }

//*********************************************************************
//						dodge
//*********************************************************************

void Statistics::dodge() { if(track) numDodges++; }

//*********************************************************************
//						critical
//*********************************************************************

void Statistics::critical() { if(track) numCriticals++; }

//*********************************************************************
//						wasHit
//*********************************************************************

void Statistics::wasHit() { if(track) numTimesHit++; }

//*********************************************************************
//						wasMissed
//*********************************************************************

void Statistics::wasMissed() { if(track) numTimesMissed++; }

//*********************************************************************
//						flee
//*********************************************************************

void Statistics::flee() { if(track) numTimesFled++; }

//*********************************************************************
//						winPk
//*********************************************************************
// because these two stats are older, they're always tracked

void Statistics::winPk() { numPkIn++; numPkWon++; }

//*********************************************************************
//						losePk
//*********************************************************************

void Statistics::losePk() { numPkIn++; }

//*********************************************************************
//						cast
//*********************************************************************

void Statistics::cast() { if(track) numCasts++; }

//*********************************************************************
//						offensiveCast
//*********************************************************************

void Statistics::offensiveCast() { if(track) numOffensiveCasts++; }

//*********************************************************************
//						healingCast
//*********************************************************************

void Statistics::healingCast() { if(track) numHealingCasts++; }

//*********************************************************************
//						kill
//*********************************************************************

void Statistics::kill() { if(track) numKills++; }

//*********************************************************************
//						die
//*********************************************************************

void Statistics::die() { if(track) numDeaths++; }

//*********************************************************************
//                      experienceLost
//*********************************************************************

void Statistics::experienceLost(unsigned long amt) {
    // We only track the total if they're tracking statistics
    if (track)
        expLost += amt;
    // However, we always track the last exp loss (For resurrect)
    lastExpLoss = amt;
}
void Statistics::setExperienceLost(unsigned long amt) {
    expLost = amt;
}

//*********************************************************************
//						steal
//*********************************************************************

void Statistics::steal() { if(track) numThefts++; }

//*********************************************************************
//						attemptSteal
//*********************************************************************

void Statistics::attemptSteal() { if(track) numAttemptedThefts++; }

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

void Statistics::save() { if(track) numSaves++; }

//*********************************************************************
//						attemptSave
//*********************************************************************

void Statistics::attemptSave() { if(track) numAttemptedSaves++; }

//*********************************************************************
//						recall
//*********************************************************************

void Statistics::recall() { if(track) numRecalls++; }

//*********************************************************************
//						lagout
//*********************************************************************

void Statistics::lagout() { if(track) numLagouts++; }

//*********************************************************************
//						wand
//*********************************************************************

void Statistics::wand() { if(track) numWandsUsed++; }

//*********************************************************************
//						transmute
//*********************************************************************

void Statistics::transmute() { if(track) numTransmutes++; }

//*********************************************************************
//						potion
//*********************************************************************

void Statistics::potion() { if(track) numPotionsDrank++; }

//*********************************************************************
//						fish
//*********************************************************************

void Statistics::fish() { if(track) numFishCaught++; }

//*********************************************************************
//						craft
//*********************************************************************

void Statistics::craft() { if(track) numItemsCrafted++; }

//*********************************************************************
//						combo
//*********************************************************************

void Statistics::combo() { if(track) numCombosOpened++; }

//*********************************************************************
//						group
//*********************************************************************

void Statistics::group(unsigned long num) { if(track) mostGroup = MAX(num, mostGroup); }

//*********************************************************************
//						monster
//*********************************************************************

void Statistics::monster(Monster* monster) {
	if(!track || monster->isPet())
		return;
	mostMonster.update(calcToughness(monster), monster->getCName());
}

//*********************************************************************
//                      experience
//*********************************************************************

void Statistics::experience(unsigned long num, bstring with) {
    if(track) mostExperience.update(num, with);
}

//*********************************************************************
//						attackDamage
//*********************************************************************

void Statistics::attackDamage(unsigned long num, bstring with) {
	if(track) mostAttackDamage.update(num, with);
}

//*********************************************************************
//						magicDamage
//*********************************************************************

void Statistics::magicDamage(unsigned long num, bstring with) {
	if(track) mostMagicDamage.update(num, with);
}

//*********************************************************************
//						setParent
//*********************************************************************

void Statistics::setParent(Player* player) { parent = player; }

//*********************************************************************
//						pkRank
//*********************************************************************
// This function returns the rank of the player based on how many
// pkills they has been in, and how many they have won

unsigned long Statistics::pkRank() const {
	if(!numPkWon || !numPkIn)
		return(0);
	return(numPkWon * 100 / numPkIn);
}

//*********************************************************************
//						getPkin
//*********************************************************************

unsigned long Statistics::getPkin() const { return(numPkIn); }

//*********************************************************************
//						getPkwon
//*********************************************************************

unsigned long Statistics::getPkwon() const { return(numPkWon); }

//*********************************************************************
//                      getLostExperience
//*********************************************************************

unsigned long Statistics::getLostExperience() const { return(expLost); }

//*********************************************************************
//                      getLastExperienceLoss
//*********************************************************************

unsigned long Statistics::getLastExperienceLoss() const { return(lastExpLoss); }


//*********************************************************************
//						setPkin
//*********************************************************************
// remove when all players are up to 2.42i

void Statistics::setPkin(unsigned long p) { numPkIn = p; }

//*********************************************************************
//						setPkwon
//*********************************************************************

void Statistics::setPkwon(unsigned long p) { numPkWon = p; }


//*********************************************************************
//						cmdLevelHistory
//*********************************************************************

int cmdLevelHistory(Player* player, cmd* cmnd) {
	Player* target = player;
	bool online=true;

	if(player->isDm() && cmnd->num > 1) {
		cmnd->str[1][0] = up(cmnd->str[1][0]);
		target = gServer->findPlayer(cmnd->str[1]);
		if(!target) {
			loadPlayer(cmnd->str[1], &target);
			online = false;
			// If the player is offline, init() won't be run and the statistics object won't
			// get its parent set. Do so now.
			if(target)
				target->statistics.setParent(target);
		}
		if(!target) {
			player->print("That player does not exist.\n");
			return(0);
		}
		player->printColor("^W%s's Level History\n", target->getCName());
	}

	target->statistics.displayLevelHistory(player);

	if(!online)
		free_crt(target);
	return(0);
}

//*********************************************************************
//						cmdStatistics
//*********************************************************************

int cmdStatistics(Player* player, cmd* cmnd) {
	Player* target = player;
	bool online=true;

	if(!strcmp(cmnd->str[1], "reset")) {
		player->statistics.reset();
		player->print("Your statistics have been reset.\n");
		player->print("Note that player kill statistics are always tracked and cannot be reset.\n");
		return(0);
	}

	if(player->isDm() && cmnd->num > 1) {
		cmnd->str[1][0] = up(cmnd->str[1][0]);
		target = gServer->findPlayer(cmnd->str[1]);
		if(!target) {
			loadPlayer(cmnd->str[1], &target);
			online = false;
			// If the player is offline, init() won't be run and the statistics object won't
			// get its parent set. Do so now.
			if(target)
				target->statistics.setParent(target);
		}
		if(!target) {
			player->print("That player does not exist.\n");
			return(0);
		}
		player->printColor("^W%s's Statistics\n", target->getCName());
	}

	target->statistics.display(player);

	if(target == player) {
		*player << ColorOn << "You may type ^Wstats reset^x to reset your statistic counts to zero.\n" << ColorOff;
		*player << "You may use the set, clear, and toggle commands to control tracking of statistics.\n";
	}

	if(!online)
		free_crt(target);
	return(0);
}