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/
/*
 * Quests.cpp
 *	 New questing system
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 "xml.h"
#include "quests.h"
#include "commands.h"
#include "factions.h"
#include "tokenizer.h"

QuestCatRef::QuestCatRef() {
	area = gConfig->defaultArea;
	reqNum = 1;
	curNum = 0;
	id = 0;
}

QuestCatRef::QuestCatRef(xmlNodePtr rootNode) {
	// Set up the defaults
	reqNum = 1;
	curNum = 0;
	area = gConfig->defaultArea;

	// And then read in the XML file
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
			if(NODE_NAME(curNode, "Area")) xml::copyToBString(area, curNode);
		else if(NODE_NAME(curNode, "Id")) xml::copyToNum(id, curNode);
		else if(NODE_NAME(curNode, "ReqAmt")) xml::copyToNum(reqNum, curNode);
		else if(NODE_NAME(curNode, "CurAmt")) xml::copyToNum(curNum, curNode);

		curNode = curNode->next;
	}
}
xmlNodePtr QuestCatRef::save(xmlNodePtr rootNode, bstring saveName) const {
	xmlNodePtr curNode = xml::newStringChild(rootNode, saveName.c_str());

	xml::newStringChild(curNode, "Area", area);
	xml::newNumChild(curNode, "Id", id);
	xml::newNumChild(curNode, "ReqAmt", reqNum);
	xml::newNumChild(curNode, "CurAmt", curNum);
	return(curNode);
}

QuestInfo::QuestInfo(xmlNodePtr rootNode) {
	bstring faction = "";

	questId = xml::getIntProp(rootNode, "Num");
	repeatable = sharable = false;
	expReward = minLevel = minFaction = level = 0;

	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
			if(NODE_NAME(curNode, "Name")) xml::copyToBString(name, curNode);
		else if(NODE_NAME(curNode, "Revision")) xml::copyToBString(revision, curNode);
		else if(NODE_NAME(curNode, "Description")) xml::copyToBString(description, curNode);
		else if(NODE_NAME(curNode, "ReceiveString")) xml::copyToBString(receiveString, curNode);
		else if(NODE_NAME(curNode, "CompletionString")) xml::copyToBString(completionString, curNode);
		else if(NODE_NAME(curNode, "Repeatable")) xml::copyToBool(repeatable, curNode);
		else if(NODE_NAME(curNode, "TimesRepetable")) xml::copyToNum(timesRepetable, curNode);
		else if(NODE_NAME(curNode, "Sharable")) xml::copyToBool(sharable, curNode);
		else if(NODE_NAME(curNode, "TurnIn")) turnInMob = QuestCatRef(curNode);
		else if(NODE_NAME(curNode, "Level")) xml::copyToNum(level, curNode);
		else if(NODE_NAME(curNode, "MinLevel")) xml::copyToNum(minLevel, curNode);
		else if(NODE_NAME(curNode, "MinFaction")) xml::copyToNum(minFaction, curNode);
		else if(NODE_NAME(curNode, "Prerequisites")) {
			xmlNodePtr childNode = curNode->children;
			int preReq=0;
			while(childNode) {
				if(NODE_NAME(childNode, "Prerequisite"))
					if((preReq = xml::toNum<int>(childNode)) != 0)
						preRequisites.push_back(preReq);

				childNode = childNode->next;
			}
		}
		else if(NODE_NAME(curNode, "Initial")) {
			xmlNodePtr childNode = curNode->children;
			while(childNode) {
					if(NODE_NAME(childNode, "Object")) initialItems.push_back(QuestCatRef(childNode));

				childNode = childNode->next;
			}
		}
		else if(NODE_NAME(curNode, "Requirements")) {
			xmlNodePtr childNode = curNode->children;
			while(childNode) {
					if(NODE_NAME(childNode, "Object")) itemsToGet.push_back(QuestCatRef(childNode));
				else if(NODE_NAME(childNode, "Monster")) mobsToKill.push_back(QuestCatRef(childNode));
				else if(NODE_NAME(childNode, "Room")) roomsToVisit.push_back(QuestCatRef(childNode));

				childNode = childNode->next;
			}
		}
		else if(NODE_NAME(curNode, "Rewards")) {
			xmlNodePtr childNode = curNode->children;
			while(childNode) {
					if(NODE_NAME(childNode, "Coins")) cashReward.load(childNode);
				else if(NODE_NAME(childNode, "Experience")) xml::copyToNum(expReward, childNode);
				else if(NODE_NAME(childNode, "Object")) itemRewards.push_back(QuestCatRef(childNode));
				else if(NODE_NAME(childNode, "Faction")) {
					xml::copyPropToBString(faction, childNode, "id");
					if(faction != "")
						factionRewards[faction] = xml::toNum<long>(childNode) * -1;
				}

				childNode = childNode->next;
			}
		}
		curNode = curNode->next;
	}
}


QuestCompletion::QuestCompletion(QuestInfo* parent, Player* player) {
	questId = parent->getId();
	parentQuest = parent;
	parentPlayer = player;
	revision = parent->revision;

	for(QuestCatRef & qcr : parent->mobsToKill)
		mobsKilled.push_back(qcr);

	for(QuestCatRef & qcr : parent->roomsToVisit)
		roomsVisited.push_back(qcr);
	mobsCompleted = false;
	itemsCompleted = false;
	roomsCompleted = false;
}


QuestCompletion::QuestCompletion(xmlNodePtr rootNode, Player* player) {
	questId = xml::getIntProp(rootNode, "ID");
	resetParentQuest();

	parentPlayer = player;

	mobsCompleted = false;
	itemsCompleted = false;
	roomsCompleted = false;

	xmlNodePtr curNode = rootNode->children;
	xmlNodePtr childNode;
	while(curNode) {
		if(NODE_NAME(curNode, "Revision")) xml::copyToBString(revision, curNode);
		else if(NODE_NAME(curNode, "MobsKilled")) {
			childNode = curNode->children;
			while(childNode) {
				if(NODE_NAME(childNode, "QuestCatRef")) {
					mobsKilled.push_back(QuestCatRef(childNode));
				}
				childNode = childNode->next;
			}
		}
		else if(NODE_NAME(curNode, "RoomsVisited")) {
			childNode = curNode->children;
			while(childNode) {
				if(NODE_NAME(childNode, "QuestCatRef")) {
					roomsVisited.push_back(QuestCatRef(childNode));
				}
				childNode = childNode->next;
			}
		}
		curNode = curNode->next;
	}

	checkQuestCompletion(false);
}
xmlNodePtr QuestCompletion::save(xmlNodePtr rootNode) const {
	xmlNodePtr curNode = xml::newStringChild(rootNode, "QuestCompletion");
	xml::newNumProp(curNode, "ID", questId);

	// We don't actually read this in, but we'll save it for reference incase
	// someone edits the file and wants to know this info
	xml::newStringChild(curNode, "QuestName", parentQuest->getName());
	xml::newStringChild(curNode, "Revision", revision);
	if(!mobsKilled.empty()) {
		xmlNodePtr mobsNode = xml::newStringChild(curNode, "MobsKilled");
		for(const QuestCatRef & mob : mobsKilled) {
			mob.save(mobsNode);
		}
	}
	if(!roomsVisited.empty()) {
		xmlNodePtr roomsNode = xml::newStringChild(curNode, "RoomsVisited");
		for(const QuestCatRef & rom : roomsVisited) {
			rom.save(roomsNode);
		}
	}
	return(curNode);
}
void QuestCompletion::resetParentQuest() {
	if((parentQuest = gConfig->getQuest(questId)) == NULL) {
		throw(std::runtime_error("Unable to find parent quest - " + bstring(questId)));
	}
}
QuestInfo* QuestCompletion::getParentQuest() const {
	return(parentQuest);
}

TalkResponse::TalkResponse() {

}

TalkResponse::TalkResponse(xmlNodePtr rootNode) {

	// And then read in the XML file
	xmlNodePtr curNode = rootNode->children;
	while(curNode) {
			if(NODE_NAME(curNode, "Keyword")) keywords.push_back(xml::getBString(curNode).toLower());
		else if(NODE_NAME(curNode, "Response")) xml::copyToBString(response, curNode);
		else if(NODE_NAME(curNode, "Action")) xml::copyToBString(action, curNode);

		curNode = curNode->next;
	}
}
xmlNodePtr TalkResponse::saveToXml(xmlNodePtr rootNode) const {
	xmlNodePtr talkNode = xml::newStringChild(rootNode, "TalkResponse");

	for(const bstring&  keyword : keywords) {
		xml::newStringChild(talkNode, "Keyword", keyword);
	}
	xml::newStringChild(talkNode, "Response", response);
	xml::newStringChild(talkNode, "Action", action);
	return(talkNode);
}

bool QuestInfo::isRepeatable() const {
	return(repeatable);
}
int QuestInfo::getTimesRepetable() const {
    return(timesRepetable);
}

const QuestCatRef& QuestInfo::getTurnInMob() const {
	return(turnInMob);
}
bstring QuestInfo::getName() const {
	if(!this)
		return("** NULL QUEST**");
	return(name);
}
int QuestInfo::getId() const {
	return(questId);
}
bstring QuestInfo::getDisplayName() const {
	std::ostringstream displayStr;
	displayStr << "^Y#" << questId << " - " << name << "^x";
	return(displayStr.str());
}

bstring QuestInfo::getDisplayString() const {
	std::ostringstream displayStr;
	std::map<bstring, long>::const_iterator it;
	int i = 0;

	displayStr << getDisplayName();
	if(repeatable)
		displayStr << " ^G*Repeatable*^x";
	if(sharable)
		displayStr << " ^Y*Sharable*^x";
	displayStr << std::endl;
	displayStr << "^WDescription: ^x" << description << "^x\n";
	bstring temp = "";

	temp = receiveString;
	temp.Replace("*CR", "\n");
	displayStr << "^WReceiveString: ^x" << temp << "\n";

	temp = completionString;
	temp.Replace("*CR", "\n");
	displayStr << "^WCompletionString: ^x" << temp << "\n";

	if(!preRequisites.empty()) {
		QuestInfo* q=0;
		int t = 0;
		displayStr << "^WPrerequisites:^x";
		for(const int & preReq : preRequisites) {
			q = gConfig->getQuest(preReq);
			if(q) {
				if(t++ > 0)
					displayStr << ", ";
				else displayStr << " ";
				displayStr << q->getDisplayName();
			}
		}
		displayStr << ".\n";
	}
	if(minLevel)
		displayStr << "^WMinimum Level:^x " << minLevel << "\n";

	if(minFaction)
		displayStr << "^WMinimum Faction:^x " << minFaction << "\n";

	Monster* endMonster = 0;
	displayStr << "^RTurn in Monster:^x ";
	if(loadMonster(turnInMob, &endMonster)) {
		 displayStr << endMonster->getName();
		 delete endMonster;
	} else {
		displayStr << "ERROR: Monster doesn't exit!";
	}
	displayStr << "(" << turnInMob.str() << ")\n";


	if(!initialItems.empty()) {
		Object* object;
		displayStr << "\t^WInitial Items:^x\n";
		i = 1;
		for(const QuestCatRef & obj : initialItems) {
			displayStr << "\t\t^W" << i++ << ")^x #" << obj.str() << " - ";
			if(loadObject(obj, &object)) {
				 displayStr << object->getName();
				 delete object;
			} else {
				displayStr << "ERROR: Object doesn't exist!";
			}
			displayStr << "(" << obj.reqNum << ")" << std::endl;
		}
	}


	if(!mobsToKill.empty()) {
		Monster* monster;
		displayStr << "\t^WMonsters To Kill:^x\n";
		i = 1;
		for(const QuestCatRef & mob : mobsToKill) {
			displayStr << "\t\t^W" << i++ << ")^x #" << mob.str() << " - ";
			if(loadMonster(mob, &monster)) {
				displayStr << monster->getName();
				delete monster;
			} else {
				displayStr << "ERROR: Monster doesn't exist!";
			}
			displayStr << "(" << mob.reqNum << ")" << std::endl;
		}
	}
	if(!itemsToGet.empty()) {
		Object* object;
		displayStr << "\t^WObjects To Obtain:^x\n";
		i = 1;
		for(const QuestCatRef & obj : itemsToGet) {
			displayStr << "\t\t^W" << i++ << ")^x #" << obj.str() << " - ";
			if(loadObject(obj, &object)) {
				 displayStr << object->getName();
				 delete object;
			} else {
				displayStr << "ERROR: Object doesn't exist!";
			}
			displayStr << "(" << obj.reqNum << ")" << std::endl;
		}
	}
	if(!roomsToVisit.empty()) {
		UniqueRoom* room;
		displayStr << "\t^WRooms To Visit:^x\n";
		i = 1;
		for(const QuestCatRef & rom : roomsToVisit) {
			displayStr << "\t\t^W" << i++ << ")^x #" << rom.str() << " - ";
			if(loadRoom(rom, &room)) {
				displayStr << room->getName() << std::endl;
			} else {
				 displayStr << "ERROR: Room doesn't exist!" << std::endl;
			}
		}
	}

	displayStr << "\t^WRewards:^x\n";
	displayStr.imbue(std::locale(""));
	if(!cashReward.isZero())
		displayStr << "\t\t^WCash:^x " << cashReward.str() << std::endl;
	if(expReward)
		displayStr << "\t\t^WExp:^x " << expReward << std::endl;
	if(!itemRewards.empty()) {
		Object* object;
		i = 1;
		displayStr << "\t\t^WItems:^x" << std::endl;
		for(const QuestCatRef & obj : itemRewards) {
			displayStr << "\t\t\t^W" << i++ << ")^x #" << obj.str() << " - ";
			if(loadObject(obj, &object)) {
				 displayStr << object->getName();
				 delete object;
			} else {
				displayStr << "ERROR: Object doesn't exist!";
			}
			displayStr << "(" << obj.reqNum << ")";
			if(obj.curNum == -1)
				displayStr << " ^Y*Hidden Reward^x";
			displayStr << std::endl;
		}
	}
	if(!factionRewards.empty()) {
		displayStr << "\t\t^WFactions:^x" << std::endl;
		for(it = factionRewards.begin(); it != factionRewards.end(); it++) {
			displayStr << "\t\t   " << (*it).first << ": " << (*it).second << std::endl;
		}
	}

	return(displayStr.str());
}

void Config::resetParentQuests() {
	Player* player=0;
	for(std::pair<bstring, Player*> p : gServer->players) {
		player = p.second;
		if(!player->questsInProgress.empty()) {
		    for(std::pair<int, QuestCompletion*> p : player->questsInProgress) {
		    	QuestCompletion* quest = p.second;
		    	quest->resetParentQuest();
		    }
		}
	}
}

void Config::clearQuests() {
	std::map<int, QuestInfo*>::iterator it;
	QuestInfo* quest;

	for(it = quests.begin(); it != quests.end(); it++) {
		quest = (*it).second;
		delete quest;
		//guilds.erase(it);
	}
	quests.clear();
}

bool Config::loadQuests() {
	xmlDocPtr	xmlDoc;
	char filename[256];

	sprintf(filename, "%s/quests.xml", Path::Game);
	if((xmlDoc = xml::loadFile(filename, "Quests")) == NULL)
		return(false);

	xmlNodePtr rootNode = xmlDocGetRootElement(xmlDoc);
	xmlNodePtr curNode = rootNode->children;
	int questId = 0;

	clearQuests();
	while(curNode) {
		if(NODE_NAME(curNode, "QuestInfo")) {
			questId = xml::getIntProp(curNode, "Num");
			if(questId > 0 && quests[questId] == NULL)
				quests[questId] = new QuestInfo(curNode);
		}

		curNode = curNode->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

QuestInfo* Config::getQuest(int questNum) {
	if(quests.find(questNum) != quests.end())
		return(quests[questNum]);
	else
		return(NULL);

}

bool Player::addQuest(QuestInfo* toAdd) {
	if(hasQuest(toAdd))
		return(false);
	questsInProgress[toAdd->getId()] = new QuestCompletion(toAdd, this);
	printColor("^W%s^x has been added to your quest book.\n", toAdd->getName().c_str());
	return(true);
}
bool Player::hasQuest(int questId) const {
	return(questsInProgress.find(questId) != questsInProgress.end());
}

bool Player::hasQuest(const QuestInfo *quest) const {
	return(quest && hasQuest(quest->getId()));
}

bool Player::hasDoneQuest(int questId) const {
    return(questsCompleted.find(questId) != questsCompleted.end());
}

void Player::updateMobKills(Monster* monster) {
    for(std::pair<int, QuestCompletion*> p : questsInProgress) {
    	QuestCompletion* quest = p.second;
    	quest->updateMobKills(monster);
    }
}

void Player::updateItems(Object* object) {
    for(std::pair<int, QuestCompletion*> p : questsInProgress) {
    	QuestCompletion* quest = p.second;
    	quest->updateItems(object);
    }
}
void Player::updateRooms(UniqueRoom* room) {
    for(std::pair<int, QuestCompletion*> p : questsInProgress) {
    	QuestCompletion* quest = p.second;
    	quest->updateRooms(room);
    }
}
void QuestCompletion::updateMobKills(Monster* monster) {
	//std::list<QuestCatRef> mobsKilled;
	for(QuestCatRef & qcr : mobsKilled) {
		if(qcr == monster->info) {
			if(qcr.reqNum != qcr.curNum) {
				if(++(qcr.curNum) == qcr.reqNum) {
					parentPlayer->printColor("Quest Update: %s - Required number of ^W%s^x have been killed.\n",
							parentQuest->getName().c_str(), monster->getCName());
				} else {
					parentPlayer->printColor("Quest Update: %s - Killed ^W %s / %s^x.\n",
							parentQuest->getName().c_str(), intToText(qcr.curNum).c_str(),
							monster->getCrtStr(parentPlayer, INV, qcr.reqNum).c_str());
				}
			}
		}
	}
	checkQuestCompletion();
}

// Called when adding an item to inventory, but before it is put in the list of items
void QuestCompletion::updateItems(Object* object) {
	for(QuestCatRef & qcr : parentQuest->itemsToGet) {
		if(qcr == object->info && object->isQuestOwner(parentPlayer)) {
			int curNum = parentPlayer->countItems(qcr)-1;

			if(curNum < qcr.reqNum) {
				itemsCompleted = false;
				if(++curNum == qcr.reqNum) {
					parentPlayer->printColor("Quest Update: %s - Required number of ^W%s^x have been obtained.\n",
						parentQuest->getName().c_str(), object->getCName());
				} else {
					parentPlayer->printColor("Quest Update: %s - Obtained ^W%s / %s^x.\n",
						parentQuest->getName().c_str(), intToText(curNum).c_str(),
						object->getObjStr(parentPlayer, INV, qcr.reqNum).c_str());
				}
			}
		}
	}
	checkQuestCompletion();
}
void QuestCompletion::updateRooms(UniqueRoom* room) {
	if(roomsCompleted)
		return;
	for(QuestCatRef & qcr : roomsVisited) {
		if(qcr == room->info) {
			if(qcr.curNum != 1) {
				if(++qcr.curNum == 1) {
					// ReqNum should always be one
					parentPlayer->printColor("Quest Update: %s - Visited ^W%s^x.\n",
							parentQuest->getName().c_str(), room->getCName());
				}
			}
		}
	}
	checkQuestCompletion();
}
bool QuestCompletion::checkQuestCompletion(bool showMessage) {
	bool alreadyCompleted = mobsCompleted && itemsCompleted && roomsCompleted;

	// First see if we have killed all required monsters
	if(mobsCompleted == false) {
		// Variable is false, so check the function
		if(!hasRequiredMobs())
			mobsCompleted = false;
		else
			mobsCompleted = true;
	}
	// Now check to see if we've been to all of the rooms we should have
	if(roomsCompleted == false) {
		// Nope, so check the function
		if(!hasRequiredRooms())
			roomsCompleted = false;
		else
			roomsCompleted = true;
	}

	// Now see if we still have the required items
	bool itemsDone = hasRequiredItems();
	if(itemsCompleted == true && itemsDone == true) {
		itemsCompleted = true;
	} else if(itemsDone == false) {
		itemsCompleted = false;
		return(false);
	} else { //if(itemsDone == true) {
		itemsCompleted = true;
	}

	if(mobsCompleted && roomsCompleted && itemsCompleted) {
		if(showMessage && !alreadyCompleted) {
			Monster* endMonster = 0;

			parentPlayer->printColor("You have fufilled all of the requirements for ^W%s^x!\n", parentQuest->getName().c_str());
			if(loadMonster(parentQuest->turnInMob, &endMonster)) {
				parentPlayer->printColor("Return to ^W%s^x to claim your reward.\n", endMonster->getCName());
				delete endMonster;
			}
		}
		return(true);
	}
	return(false);

}

bool QuestCompletion::hasRequiredMobs() const {
	// Assume completion, and test for non completion
	for(const QuestCatRef & mob : mobsKilled) {
		if(mob.curNum != mob.reqNum) {
			return(false);
		}
	}
	return(true);
}
bool QuestCompletion::hasRequiredItems() const {
	int curNum;
	for(const QuestCatRef & obj : parentQuest->itemsToGet) {
		curNum = parentPlayer->countItems(obj);
		curNum = MIN(curNum, obj.reqNum);
		if(curNum != obj.reqNum) {
			return(false);
		}
	}
	return(true);
}
bool QuestCompletion::hasRequiredRooms() const {
	// Assume completion, and test for non completion
	for(const QuestCatRef & rom : roomsVisited) {
		if(rom.curNum != 1) {
			return(false);
		}
	}
	return(true);
}
bstring QuestCompletion::getStatusDisplay() {
	std::ostringstream displayStr;
	int i;

	checkQuestCompletion(false);

	if(mobsCompleted && itemsCompleted && roomsCompleted)
		displayStr << "^Y*";
	else
		displayStr << "^y";
	displayStr << parentQuest->name << "^x\n";


	displayStr << "^WDescription: ^w" << parentQuest->description << "^x\n";

	Monster* endMonster = 0;
	displayStr << "^RWhen finished, return to: ^x";
	if(loadMonster(parentQuest->turnInMob, &endMonster)) {
		 displayStr << endMonster->getName();
		 delete endMonster;
	} else {
		displayStr << "ERROR: Monster doesn't exit!";
	}
	displayStr << "^x\n";

	if(!mobsKilled.empty()) {
		displayStr << "     ";
		if(mobsCompleted)
			displayStr << "^Y*";
		else
			displayStr << "^y";
		displayStr << "Monsters to Kill.^x\n";
		Monster* monster;
		i = 1;
		for(QuestCatRef & mob : mobsKilled) {
			displayStr << "\t";
			if(mob.curNum == mob.reqNum)
				displayStr << "^C*";
			else
				displayStr << "^c";

			displayStr << i++ << ") ";
			if(loadMonster(mob, &monster)) {
				displayStr << monster->getName();
				delete monster;
			} else {
				displayStr << "ERROR: Unable to load monster";
			}
			displayStr << " " << mob.curNum << "/" << mob.reqNum << "^x\n";

		}
	}
	if(!parentQuest->itemsToGet.empty()) {
		displayStr << "     ";
		if(itemsCompleted)
			displayStr << "^Y*";
		else
			displayStr << "^y";

		displayStr << "Objects to Get.^x\n";
		Object* object;
		i = 1;
		for(QuestCatRef & obj : parentQuest->itemsToGet) {
			int curNum = parentPlayer->countItems(obj);
			curNum = MIN(curNum, obj.reqNum);
			displayStr << "\t";
			if(curNum == obj.reqNum)
				displayStr << "^C*";
			else
				displayStr << "^c";

			displayStr << i++ << ") ";
			if(loadObject(obj, &object)) {
				displayStr << object->getName();
				delete object;
			} else {
				displayStr << "ERROR: Unable to load object";
			}
			displayStr << " " << curNum << "/" << obj.reqNum << "^x\n";
		}
	}
	if(!roomsVisited.empty()) {
		displayStr << "     ";
		if(roomsCompleted)
			displayStr << "^Y*";
		else
			displayStr << "^y";

		displayStr << "Rooms to Visit.^x\n";
		UniqueRoom* room;
		i = 1;
		for(QuestCatRef & rom : roomsVisited) {
			displayStr << "\t";
			if(rom.curNum == rom.reqNum)
				displayStr << "^C*";
			else
				displayStr << "^c";

			displayStr << i++ << ") ";
			if(loadRoom(rom, &room)) {
				displayStr << room->getName();
			} else {
				displayStr << "ERROR: Unable to load room";
			}
			displayStr << std::endl;
		}
	}

	displayStr << "^WRewards:^x\n";
	displayStr.imbue(std::locale(""));
	if(!parentQuest->cashReward.isZero())
		displayStr << "     ^WCash:^x " << parentQuest->cashReward.str() << std::endl;
	if(!parentQuest->itemRewards.empty()) {
		Object* object;
		i = 1;
		displayStr << "     ^WItems:^x" << std::endl;
		for(QuestCatRef & obj : parentQuest->itemRewards) {
			// Hidden Reward
			if(obj.curNum == -1)
				continue;
			displayStr << "          ^W" << i++ << ")^x ";
			if(loadObject(obj, &object)) {
				 displayStr << object->getName();
				 delete object;
			} else {
				displayStr << "ERROR: Object doesn't exist!";
			}
			if(obj.reqNum > 1)
				displayStr << "(" << obj.reqNum << ")";
			displayStr << std::endl;
		}
	}

	return(displayStr.str());

}

int cmdQuestStatus(Player* player, cmd* cmnd) {
	Player* target = player;
	int i = 1;

    for(std::pair<int, QuestCompletion*> p : target->questsInProgress) {
    	QuestCompletion* quest = p.second;
    	player->print("%d) %s\n", i++, quest->getStatusDisplay().c_str());
    }
	return(1);
}
bool Object::isQuestValid() const {
	return((type == CONTAINER && shotsCur == 0) ||
	 (type != CONTAINER && (shotsCur != 0 || shotsMax == 0)) );

}
// Count how many of a given item this player has that are non-broken
int Player::countItems(const QuestCatRef& obj) {
	int	total=0;
	for(Object* object : objects) {
		// Items only count if they're a bag and have 0 shots, or if
		// they're not a bag, and don't have 0 shots (unless shotsmax is 0)
		if(object && object->info == obj && object->isQuestValid())
			total++;

		if(object && object->getType() == CONTAINER) {
			for(Object* subObj : object->objects) {
				if(subObj->info == obj  && subObj->isQuestValid())
					total++;
			}
		}
	}
	return(total);
}

bool prepareItemList(const Player* player, std::list<Object*> &objects, Object* object, const Monster* monster, bool isTrade, bool setTradeOwner, int totalBulk);

bool QuestCompletion::complete(Monster* monster) {
	std::map<bstring, long>::const_iterator it;
	std::list<Object*> objects;
	std::list<Object*>::iterator oIt;

	if(!checkQuestCompletion()) {
		parentPlayer->print("You have not fulfilled all the requirements to complete this quest.\n");
		return(false);
	}

	// if they can't get all of the item rewards, they can't complete the quest
	for(QuestCatRef & obj : parentQuest->itemRewards) {
		int num = obj.reqNum, cur = 0;
		Object* object=0;
		if(num <= 0)
			continue;

		while(cur++ < num) {
			if(loadObject(obj, &object)) {
				if(!prepareItemList(parentPlayer, objects, object, monster, false, true, 0))
					return(false);
			}
		}
	}

	// Remove this quest from the list of quests so when we add items to the player
	// it doesn't try to update this quest.  We will delete this quest at the end of the
	// function right before we return

	parentPlayer->questsInProgress.erase(parentQuest->questId);
	parentQuest->printCompletionString(parentPlayer, monster);

	// First, remove all of the items from the player
	for(QuestCatRef & obj : parentQuest->itemsToGet) {
		bstring oName;
		Object *object;
		ObjectSet::iterator it;
		int num = obj.reqNum;

		for( it = parentPlayer->objects.begin() ; it != parentPlayer->objects.end() && num > 0 ; ) {
			object = (*it++);
			if(object->info == obj && object->isQuestValid()) {
				oName = object->getName();
				parentPlayer->delObj(object, true, false, true, false);
				delete object;
				num--;
				continue;
			}
			if(object->getType() == CONTAINER) {
				Object *subObject;
				ObjectSet::iterator sIt;
				for(sIt = object->objects.begin() ; sIt != object->objects.end() && num > 0 ; ) {
					subObject = (*sIt++);
					if(subObject->info == obj && subObject->isQuestValid()) {
						oName = subObject->getName();
						parentPlayer->delObj(subObject, true, false, true, false);
						delete subObject;
						num--;
						continue;
					}
				}
			}
		}
		if(loadObject(obj, &object)) {
			parentPlayer->printColor("%M takes ^W%s^x from you.\n", monster,
				object->getObjStr(parentPlayer, 0, obj.reqNum).c_str());
			delete object;
		}
	}

	for(oIt = objects.begin(); oIt != objects.end(); oIt++) {
		parentPlayer->printColor("%M gives you ^C%P^x.\n", monster, *oIt);
		(*oIt)->setDroppedBy(monster, "QuestCompletion");
		doGetObject(*oIt, parentPlayer);
	}

	parentPlayer->checkDarkness();

	//float multiplier = 1.0;
	// TODO: Adjust multiplier based on level difference
	if(parentQuest->level != 0) {
		// adjust multiplier here
	}

	if(!parentQuest->cashReward.isZero()) {
		parentPlayer->coins.add(parentQuest->cashReward);
		gServer->logGold(GOLD_IN, parentPlayer, parentQuest->cashReward, NULL, "QuestCompletion");
		parentPlayer->printColor("%M gives you ^C%s^x. You now have %s.\n",
			monster, parentQuest->cashReward.str().c_str(), parentPlayer->coins.str().c_str());
	}
	if(parentQuest->expReward) {
		if(!parentPlayer->halftolevel()) {
			parentPlayer->printColor("You %s ^C%ld^x experience.\n", gConfig->isAprilFools() ? "lose" : "gain", parentQuest->expReward);
			parentPlayer->addExperience(parentQuest->expReward);
		}
	}
	if(!parentQuest->factionRewards.empty())
		parentPlayer->adjustFactionStanding(parentQuest->factionRewards);


	// Only add it to the list of quests they have done, if they haven't already
	// done it.  No adding a quest twice if it's repeatable
	if(!parentPlayer->hasDoneQuest(parentQuest->questId)) {
		parentPlayer->questsCompleted.insert(std::pair<int,int>(parentQuest->questId, 1));
	} else {
		parentPlayer->questsCompleted[parentQuest->questId]++;
	}

	// INVALID AFTER THIS...DO NOT ACCESS ANY MEMBERS OR THIS QUEST FROM ANYWHERE
	delete this;

	return(true);
}


//*****************************************************************************
//						cmdTalk
//*****************************************************************************

int cmdTalk(Player* player, cmd* cmnd) {
	Monster *target=0;

	bstring question;
	bstring response;
	bstring action;

	player->clearFlag(P_AFK);

	if(!player->ableToDoCommand())
		return(0);

	if(cmnd->num < 2) {
		player->print("Talk to whom?\n");
		return(0);
	}

	target = player->getParent()->findMonster(player, cmnd);
	if(!target) {
		player->print("You don't see that here.\n");
		return(0);
	}

	player->unhide();
	if(player->checkForSpam())
		return(0);

	if(target->flagIsSet(M_AGGRESSIVE_AFTER_TALK))
		target->addEnemy(player);

	if(target->current_language && !target->languageIsKnown(player->current_language)) {
		if(!player->checkStaff("%M doesn't seem to understand anything in %s.\n", target, get_language_adj(player->current_language)))
			return(0);
	}

	if(!target->canSpeak()) {
		player->print("%M is unable to speak right now.\n", target);
		return(0);
	}

	if(target->getTalk() != "" && !Faction::willSpeakWith(player, target->getPrimeFaction())) {
		player->print("%M refuses to speak with you.\n", target);
		return(0);
	}

	if(cmnd->num == 2 || target->responses.empty()) {
		response = target->getTalk();
		if(response == "$random") {
			std::list<bstring> randomResponses;
			std::list<bstring> randomActions;
			int numResponses=0;
			for(TalkResponse * talkResponse : target->responses) {
				for(const bstring&  keyword : talkResponse->keywords) {
					if(keyword == "$random") {
						randomResponses.push_back(talkResponse->response);
						randomActions.push_back(talkResponse->action);
						numResponses++;
						break;
					}
				}
			}
			if(!numResponses)
				response = "";
			else {
				numResponses = mrand(1,numResponses);
				while(numResponses > 1) {
					randomResponses.pop_front();
					randomActions.pop_front();
					numResponses--;
				}
				response = randomResponses.front();
				action = randomActions.front();
			}
		}
		if(response != "")
			broadcast(player->getSock(), player->getParent(), "%M speaks to %N in %s.",
				player, target, get_language_adj(player->current_language));
	} else {
		question = keyTxtConvert(getFullstrText(cmnd->fullstr, 2).toLower());
		broadcast_rom_LangWc(target->current_language, player->getSock(), player->currentLocation, "%M asks %N \"%s\".^x",
			player, target, question.c_str());
		bstring key = "", keyword = "";
		for(TalkResponse * talkResponse : target->responses) {
			for(const bstring& keyWrd : talkResponse->keywords) {
				keyword = keyTxtConvert(keyWrd).toLower();
				
				if(keyword[0] == '@') {
					// We're looking for an exact match of the entire string
					const bstring& toMatch = keyword.substr(1);
					if(question == toMatch) {
						// First let's copy over the information
						key = keyword;
						response = talkResponse->response;
						action = talkResponse->action;
						// Next...lets get the hell out of here!
						goto foundresponse;
					}
				} else if(keyword[0] == '%') {
					// Now we're looking for a match of the keyword surrounded by either white space,
					// punctuation, or the end/start of the string
					const bstring& toMatch = keyword.substr(1);
					bstring::size_type idx = question.find(toMatch,0);
					if(idx != bstring::npos) {
						// Possible match
						bstring::size_type keyLen = toMatch.length();
						bstring::size_type questLen = question.length();
						bstring::size_type startIdx = idx-1;
						if(idx == 0 || (isspace(question[startIdx]) || ispunct(question[startIdx]))) {
							// The start of the string is good, now let's check the end
							bstring::size_type endIdx = idx + keyLen;
							//char ch = question[endIdx];

							if(endIdx >= questLen || (isspace(question[endIdx]) || ispunct(question[endIdx]))) {
								// We've got a good match
								// First let's copy over the information
								key = keyword;
								response = talkResponse->response;
								action = talkResponse->action;
								// Next...lets get the hell out of here!
								goto foundresponse;
							}
						}
					}
				} else if(question.find(keyword, 0) != bstring::npos) {
					// We found one of the keywords!
					// First let's copy over the information
					key = keyword;
					response = talkResponse->response;
					action = talkResponse->action;
					// Next...lets get the hell out of here!
					goto foundresponse;
				}
			}
		}
		foundresponse:

		// they can't trigger special responses by talking to them
		if(key == "" || key.getAt(0) == '$') {
			response = "";
			action = "";
		}

		if(response == "" && action == "") {
			broadcast(NULL, player->getRoomParent(), "%M shrugs.", target);
			return(0);
		}
	}


	if(response != "")
		target->sayTo(player, response);

	if(action != "")
		target->doTalkAction(player, action);

	if(response == "" && action == "")
		broadcast(NULL, player->getRoomParent(), "%M doesn't say anything.", target);

	return(0);
}

//*****************************************************************************
//						doTalkAction
//*****************************************************************************

bool Monster::doTalkAction(Player* target, bstring action) {
	if(action.empty())
		return(false);

	boost::tokenizer<> tok(action);
	boost::tokenizer<>::iterator it = tok.begin();

	if(it == tok.end())
		return(false);

	bstring cmd = *it++;

	if(cmd.equals("attack", false)) {
		addEnemy(target, true);
		return(true);
	} else if(cmd.equals("action", false)) {
		if(it != tok.end()) {
			// Need to use global namespace cmd, overriding local variable
			::cmd cm;

			bstring actionCmd = *it++;
			cm.fullstr = actionCmd;
			//strncpy(cm.fullstr, actionCmd.c_str(), 100);

			if(it != tok.end()) {
				bstring actTarget = *it++;
				if(actTarget.equals("player", false)) {
					cm.fullstr += bstring(" ") + target->getName();
				}
			}

			stripBadChars(cm.fullstr); // removes '.' and '/'
			lowercize(cm.fullstr, 0);
			parse(cm.fullstr, &cm);

			cmdProcess(this, &cm);
			return(true);
		}
	} else if(cmd.equals("cast", false)) {
		if(it != tok.end()) {
			// Need to use global namespace cmd, overriding local variable
			::cmd cm;

			action.Replace("PLAYER", target->getCName());
			cm.fullstr = action;

			stripBadChars(cm.fullstr); // removes '.' and '/'
			lowercize(cm.fullstr, 0);
			parse(cm.fullstr, &cm);

			CastResult result = doCast(this, &cm);

			if(result == CAST_RESULT_FAILURE) {
				target->print("%M apologizes that %s cannot cast that spell.\n", this, heShe());
				return(false);
			} else if(result == CAST_RESULT_CURRENT_FAILURE || result == CAST_RESULT_SPELL_FAILURE) {
				target->print("%M apologizes that %s cannot currently cast that spell.\n", this, heShe());
				return(false);
			}

			return(true);

		}
	} else if(cmd.equals("give", false)) {
		if(it != tok.end()) {
			CatRef toGive;
			// Find the area/number
			bstring area = *it++;
			int objNum = area.toInt();
			if(objNum <= 0) {
				toGive.setArea(area);

				// We didn't find a number, must be an area, look for the number now
				bstring num;

				if(it == tok.end())
					return(false);
				num = *it++;
				objNum = num.toInt();
				if(objNum <= 0)
					return(false);
			}
			toGive.id = objNum;

			Object* object = 0;
			if(loadObject(toGive, &object)) {
				if(object->flagIsSet(O_RANDOM_ENCHANT))
					object->randomEnchant();

				if(	(target->getWeight() + object->getActualWeight() > target->maxWeight()) &&
					!target->checkStaff("You can't hold anymore.\n")
				) {
					delete object;
					return(false);
				}

				if(	object->getQuestnum() && target->questIsSet(object->getQuestnum()) &&
					!target->checkStaff("You may not get that. You have already fulfilled that quest.\n")
				) {
					delete object;
					return(false);
				}

				fulfillQuest(target, object);

				target->addObj(object);
				target->printColor("%M gives you %1P\n", this, object);
				broadcast(target->getSock(), target->getRoomParent(), "%M gives %N %1P\n", this, target, object);
			} else
				target->print("%M has nothing to give you.\n", this);

			return(true);
		}
	} else if(cmd.equals("quest", false)) {
		if(isEnemy(target) && !target->checkStaff("%M refuses to talk to you about that.\n", this))
			return(false);

		if(!canSee(target) && !target->checkStaff("^m%M says, \"I'm not telling you about that unless I can see you!\"\n", this))
			return(false);

		// Give them a quest
		if(it == tok.end())
			return(false);
		// Find the quest number
		bstring num = *it++;
		int questNum = num.toInt();
		if(questNum < 1)
			return(false);

		QuestInfo* questInfo = gConfig->getQuest(questNum);
		if(!questInfo)
			return(false);

		if(!questInfo->canGetQuest(target, this))
			return(false);

		target->printColor("^m%M shares ^W%s^m with you.\n",  this, questInfo->getName().c_str());
		broadcast(target->getSock(), target->getRoomParent(), "^x%M shares ^W%s^x with %N.\n",
			this, questInfo->getName().c_str(), target);
		questInfo->printReceiveString(target, this);
		target->addQuest(questInfo);
		questInfo->giveInitialitems(this, target);
		return(true);
	}
	return(false);
}

//*****************************************************************************
//						giveInitialitems
//*****************************************************************************

void QuestInfo::giveInitialitems(const Monster* giver, Player* player) const {
	Object* object=0;
	std::list<QuestCatRef>::const_iterator it;
	int num=0, cur=0;

	for(it = initialItems.begin(); it != initialItems.end(); it++) {
		num = (*it).reqNum, cur = 0;
		if(num <= 0)
			continue;

		while(cur++ < num) {
			if(loadObject(*it, &object)) {
				object->init();
				// quest results set player ownership
				object->setQuestOwner(player);
				player->addObj(object);
			}
		}
		player->printColor("%M gives you ^C%s^x.\n", giver, object->getObjStr(player, 0, num).c_str());
	}
}

//*****************************************************************************
//						canGetQuest
//*****************************************************************************

bool QuestInfo::canGetQuest(const Player* player, const Monster* giver) const {

	if(player->hasQuest(this)) {
		// Staff still can't have it in their quest book twice
		player->printColor("^m%M says, \"I'd tell you about ^W%s^m, but you already know about it!\"\n",
			giver, getName().c_str());
		return(false);
	}

	if(!isRepeatable() && player->hasDoneQuest(questId) &&
		!player->checkStaff("^m%M says, \"I'd tell you about ^W%s^m, but you've already done that quest!\"\n",
		giver, getName().c_str())
	)
		return(false);

	if(!preRequisites.empty()) {
		for(const int & preReq : preRequisites) {
			if(!player->hasDoneQuest(preReq) &&
				!player->checkStaff("^m%M says, \"You're not ready for that information yet! Return when you have finished ^W%s^m.\"\n",
					giver, gConfig->getQuest(preReq)->getName().c_str())
			)
				return(false);
		}
	}

	if(minFaction != 0 && !giver->getPrimeFaction().empty()) {
		if(player->getFactionStanding(giver->getPrimeFaction()) < minFaction) {
			if(!player->checkStaff("^m%M says, \"I'm sorry, I don't trust you enough to tell you about that.\"\n", giver))
				return(false);
		}
	}

	if(minLevel != 0 && player->getLevel() < minLevel) {
		if(!player->checkStaff("^m%M says, \"You're not ready for that information yet! Return when you have gained more experience.\"\n", giver))
			return(false);
	}
	return(true);
}

//*****************************************************************************
//						printReceiveString
//*****************************************************************************

void QuestInfo::printReceiveString(const Player* player, const Monster* giver) const {
	if(receiveString.empty())
		return;

	bstring toPrint = receiveString;

	toPrint.Replace("*GIVER*", giver->getCrtStr(player, CAP).c_str());
	toPrint.Replace("*CR*", "\n");

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

//*****************************************************************************
//						printCompletionString
//*****************************************************************************

void QuestInfo::printCompletionString(const Player* player, const Monster* giver) const {
	if(receiveString.empty())
		return;

	bstring toPrint = completionString;

	toPrint.Replace("*GIVER*", giver->getCrtStr(player, CAP).c_str());
	toPrint.Replace("*CR*", "\n");

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

//*****************************************************************************
//						convertOldTalks
//*****************************************************************************

void Monster::convertOldTalks() {
	ttag	*tp=0, *next=0;

	if(!flagIsSet(M_TALKS))
		return;
	clearFlag(M_TALKS);
	tp = first_tlk;
	while(tp) {
		TalkResponse* newResponse = new TalkResponse();
		newResponse->keywords.push_back(tp->key);
		newResponse->response = tp->response;
		switch(tp->type) {
		case 1:
			newResponse->action = "attack";
			break;
		case 2:
			newResponse->action = bstring("action ") + tp->action + " " + tp->target;
			break;
		case 3:
			newResponse->action = bstring("cast ") + tp->action + " " + tp->target;
			break;
		case 4:
			newResponse->action = bstring("give ") + tp->action;
			break;
		}

		responses.push_back(newResponse);
		tp = tp->next_tag;
	}

	tp = first_tlk;
	first_tlk = 0;
	while(tp) {
		next = tp->next_tag;
		if(tp->key)
			delete[] tp->key;
		if(tp->response)
			delete[] tp->response;
		if(tp->action)
			delete[] tp->action;
		if(tp->target)
			delete[] tp->target;
		delete tp;
		tp = next;
	}
	saveToFile();
}

//*********************************************************************
//						cmdQuests
//*********************************************************************

int cmdQuests(Player* player, cmd* cmnd) {
	int j,i;
	char str[2048], str2[80];

	player->clearFlag(P_AFK);

	if(player->isBraindead()) {
		player->print("You are brain-dead. You can't do that.\n");
		return(0);
	}

	if(cmnd->num > 1 && (!strncmp(cmnd->str[1], "complete", strlen(cmnd->str[1])) ||
						 !strncmp(cmnd->str[1], "finish", strlen(cmnd->str[1]))) )
	{
		// We're trying to complete a quest here, lets see if we have one that matches
		// the user's input
		bstring questName = getFullstrText(cmnd->fullstr, 2);
		QuestCompletion* quest;
		for(std::pair<int, QuestCompletion*> p : player->questsInProgress) {
			quest = p.second;
			if(questName.empty() || !strncasecmp(quest->getParentQuest()->getName().c_str(), questName.c_str(), questName.length())) {

				// Make sure the quest is completed!!
				if(!quest->checkQuestCompletion(false)) {

					// No name was specified, so continue to next quest and try to complete that
					if(questName.empty()) continue;

					player->printColor("But you haven't met all of the requirements for ^W%s^x yet!\n",
						quest->getParentQuest()->getName().c_str());
					return(0);
				}

				// We've found the quest that matches the string the user put in, now lets see if we can
				// find the finishing monster
				for(Monster* mons : player->getRoomParent()->monsters) {
					if(	mons->info == quest->getParentQuest()->getTurnInMob() ) {
						// We have a turn in monster, lets complete the quest
						if(mons->isEnemy(player)) {
							player->print("%M refuses to deal with you right now!\n", mons);
							return(0);
						}

						bstring name = quest->getParentQuest()->getName();
						player->printColor("Completing quest ^W%s^x.\n", name.c_str());

						// NOTE: After quest->complete, quest is INVALID, do not attempt to access it
						if(quest->complete(mons)) {
							broadcast(player->getSock(), player->getParent(), "%M just completed ^W%s^x.",
								player, name.c_str());
						} else {
							//player->print("Quest completion failed.\n");
							broadcast(player->getSock(), player->getParent(), "%M tried to complete ^W%s^x.",
								player, name.c_str());
						}

						return(0);
					}

				}

				if(!questName.empty()) {
					// Name was specified, so stop
					player->printColor("Could not find a turn in monster!\n");
					return(0);
				}
				// No name was specified, so continue to next quest and try to complete that
			}
		}
		player->printColor("No quests were found that could be completed right now.\n");
		return(0);
	} else if(cmnd->num > 1 && (!strncmp(cmnd->str[1], "quit", strlen(cmnd->str[1])) ||
			 !strncmp(cmnd->str[1], "abandon", strlen(cmnd->str[1]))) )
	{
		// We're trying to abandon a quest here, lets see if we have one that matches
		// the user's input
		bstring questName = getFullstrText(cmnd->fullstr, 2);
		QuestCompletion* quest;
		if(questName.empty()) {
			player->print("Abandon which quest?\n");
			return(0);
		}
		for(std::pair<int, QuestCompletion*> p : player->questsInProgress) {
			quest = p.second;
			if(!strncasecmp(quest->getParentQuest()->getName().c_str(), questName.c_str(), questName.length())) {
				player->printColor("Abandoning ^W%s^x.\n", quest->getParentQuest()->getName().c_str());
				player->questsInProgress.erase(quest->getParentQuest()->getId());
				delete quest;
				return(0);
			}
		}
		player->printColor("Could not find any quests that matched the name ^W%s^x.\n", questName.c_str());
		return(0);
	}
	sprintf(str, "^WOld Quests Completed:^x\n");
	for(i=1, j=0; i<MAX_QUEST; i++)
		if(player->questIsSet(i)) {
			sprintf(str2, "%s, ", get_quest_name(i));
			strcat(str, str2);
			j++;
		}
	if(!j)
		strcat(str, "None.\n");
	else {
		str[strlen(str)-2] = '.';
		strcat(str, "\n");
	}

	player->printColor("%s", str);

	if(!player->questsCompleted.empty()) {

		std::ostringstream displayStr;

		displayStr << "^WQuests Completed:^x\n";
		for(std::pair<int,int> qc : player->questsCompleted) {
			displayStr << gConfig->getQuest(qc.first)->getName();
			if(qc.second > 1)
				displayStr << " (" << qc.second << ")";
			displayStr << "\n";
		}

		player->printColor("%s", displayStr.str().c_str());
	}


	Player* target = player;
	i = 1;
	player->printColor("^WQuests in Progress:\n");

	if(target->questsInProgress.empty())
		player->printColor("None!\n");

    for(std::pair<int, QuestCompletion*> p : target->questsInProgress) {
    	QuestCompletion* quest = p.second;
    	player->printColor("%d) %s\n", i++, quest->getStatusDisplay().c_str());
    }

	return(0);
}