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/
/*
 * config.cpp
 *   Handles config options for the entire mud
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 "os.h"
#include "mud.h"
#include "bans.h"
#include "factions.h"
#include "guilds.h"
#include "calendar.h"
#include "effects.h"
#include "msdp.h"

//#include "config.h"

extern bool listing;

// Globals
Config *gConfig = NULL;

// Static initialization
Config* Config::myInstance = NULL;

Config::Config() {
	reset();
	inUse = true;
}

Config::~Config() {
	if(inUse)
		throw(std::runtime_error("Error, trying to destroy config\n"));
	else
		std::cout << "Properly deconstructing Config class";
}

//--------------------------------------------------------------------
// Instance Functions

//********************************************************************
// Get Instance - Return the static instance of config
//********************************************************************
Config* Config::getInstance() {
	if(myInstance == NULL)
		myInstance = new Config;
	return(myInstance);
}
//********************************************************************
// Destroy Instance - Destroy the static instance
//********************************************************************
void Config::destroyInstance() {
	if(myInstance != NULL) {
		myInstance->cleanUp();
		delete myInstance;
	}
	myInstance = NULL;
}

// End - Instance Functions
//--------------------------------------------------------------------





//--------------------------------------------------------------------
// Memory Functions
void Config::cleanUp() {
	clearClans();
	clearGuildList();
	clearBanList();
	clearFactionList();
	clearSkills();
	calendar->clear();
	clearShips();
	clearClasses();
	clearRaces();
	clearRecipes();
	clearDeities();
	clearAreas();
	clearStartLoc();
	clearLimited();
	clearProperties();
	clearCatRefInfo();
	clearQuests();
	clearAlchemy();
	clearCommands();
	clearSocials();
    clearSpells();
	clearMsdpVariables();
	clearMxpElements();
	clearEffects();
	clearSongs();
	clearProxyAccess();
	inUse = false;
}
void Config::clearProxyAccess() {
	proxyManager->clear();
	proxyManager = 0;
}

//********************************************************************
// resetConfig
//********************************************************************
// Sets all config options to their default value.

void Config::reset(bool reload) {
	mudName = "Default Mud Name";
	saveOnDrop = true;
	checkDouble = true;
	getHostByName = true;
	logSuicide = true;

	logDeath = true;
	autoShutdown = false;
	doAprilFools = false;
	charCreationDisabled = false;
	lessExpLoss = false;
	pkillInCombatDisabled = false;
	recordAll = false;

	flashPolicyPort = 0;
	shopNumObjects = shopNumLines = 0;

	dmPass = "default_dm_pw";
	webserver = qs = userAgent = reviewer = "";

	if(!bHavePort)
		portNum = 3333;

	memset(customColors, CUSTOM_COLOR_DEFAULT, sizeof(customColors));
	customColors[CUSTOM_COLOR_SIZE-1] = 0;

	customColors[CUSTOM_COLOR_BROADCAST] = 'x';
	customColors[CUSTOM_COLOR_GOSSIP] = 'M';
	customColors[CUSTOM_COLOR_PTEST] = 'Y';
	customColors[CUSTOM_COLOR_NEWBIE] = 'M';
	customColors[CUSTOM_COLOR_DM] = 'g';
	customColors[CUSTOM_COLOR_ADMIN] = 'g';
	customColors[CUSTOM_COLOR_SEND] = 'Y';
	customColors[CUSTOM_COLOR_MESSAGE] = 'G';
	customColors[CUSTOM_COLOR_WATCHER] = 'C';
	customColors[CUSTOM_COLOR_CLASS] = 'y';
	customColors[CUSTOM_COLOR_RACE] = 'c';
	customColors[CUSTOM_COLOR_CLAN] = 'm';
	customColors[CUSTOM_COLOR_TELL] = 'x';
	customColors[CUSTOM_COLOR_GROUP] = 'g';
	customColors[CUSTOM_COLOR_DAMAGE] = 'm';
	customColors[CUSTOM_COLOR_SELF] = 'w';
	customColors[CUSTOM_COLOR_GUILD] = 'g';

	lotteryEnabled = true;
	lotteryTicketPrice = 100;
	lotteryTicketsSold = 0;
	lotteryJackpot = 500000;
	lotteryWon = 0;
	lotteryWinnings = 0;
	lotteryRunTime = 0;

	lotteryNumbers[0] =
	lotteryNumbers[1] =
	lotteryNumbers[2] =
	lotteryNumbers[3] =
	lotteryNumbers[4] =
	lotteryNumbers[5] = 0;
	lotteryCycle = 1;

	for(LottoTicket* ticket : tickets) {
		delete ticket;
	}

    logDbType="";
    logDbUser="";
    logDbPass="";
    logDbDatabase="";

	
	// the following settings aren't cleared on reload
	if(reload)
		return;


	
	roomHead =
	roomTail =
	monsterHead =
	monsterTail =
	objectHead =
	objectTail = 0;

	conjureDisabled = false;

	// Numerical defaults
	crashes = 0;
	supportRequiredForGuild = 2;
	minBroadcastLevel = 2;
	//maxGuilds = 8;
	numGuilds = 0;


	calendar = NULL;
	proxyManager = NULL;

	tickets.clear();
	defaultArea = "misc";
	swapping = roomSearchFailure = txtOnCrash = false;
}


bool Config::loadBeforePython() {
	printf("Checking Directories...%s.\n", Path::checkPaths() ? "done" : "*** FAILED ***");
	printf("Initializing command table...%s.\n", initCommands() ? "done" : "*** FAILED ***");

	printf("Loading Config...%s.\n", loadConfig() ? "done" : "*** FAILED ***");

	printf("Loading Socials...%s.\n", loadSocials() ? "done" : "*** FAILED ***");
	
	printf("Loading Recipes...%s.\n", loadRecipes() ? "done" : "*** FAILED ***");
	printf("Loading Flags...%s.\n", loadFlags() ? "done" : "*** FAILED ***");
    printf("Loading Effects...%s\n", loadEffects() ? "done" : "*** FAILED ***");
	printf("Writing Help Files...%s.\n", writeHelpFiles() ? "done" : "*** FAILED ***");
    printf("Loading Spell List...%s.\n", loadSpells() ? "done" : "*** FAILED ***");
    printf("Loading Song List...%s.\n", loadSongs() ? "done" : "*** FAILED ***");
	printf("Loading Quest Table...%s.\n", loadQuestTable() ? "done" : "*** FAILED ***");
	printf("Loading New Quests...%s.\n", loadQuests() ? "done" : "*** FAILED ***");
	printf("Loading StartLocs...%s.\n", loadStartLoc() ? "done" : "*** FAILED ***");
	printf("Loading CatRefInfo...%s.\n", loadCatRefInfo() ? "done" : "*** FAILED ***");

	printf("Loading Bans...%s.\n", loadBans() ? "done" : "*** FAILED ***");
	printf("Loading Fishing...%s.\n", loadFishing() ? "done" : "*** FAILED ***");
	printf("Loading Guilds...%s.\n", loadGuilds() ? "done" : "*** FAILED ***");

	std::cout << "Loading Skills...";
	if(loadSkills())
		std::cout << "done." << std::endl;
	else {
		std::cout << "*** FAILED *** " << std::endl;
		exit(-15);
	}

	printf("Loading Deities...%s.\n", loadDeities() ? "done" : "*** FAILED ***");
	printf("Loading Clans...%s.\n", loadClans() ? "done" : "*** FAILED ***");
	printf("Loading Classes...%s.\n", loadClasses() ? "done" : "*** FAILED ***");
	printf("Loading Races...%s.\n", loadRaces() ? "done" : "*** FAILED ***");
	printf("Loading Factions...%s.\n", loadFactions() ? "done" : "*** FAILED ***");
	printf("Loading Alchemy...%s.\n", loadAlchemy() ? "done" : "*** FAILED ***");
	printf("Loading MSDP Variables...%s.\n", loadMsdpVariables() ? "done" : "*** FAILED ***");
	printf("Loading MXP Elements...%s.\n", loadMxpElements() ? "done" : "*** FAILED ***");
	printf("Loading Limited Items...%s.\n", loadLimited() ? "done" : "*** FAILED ***");
	printf("Loading Double Log Info...%s.\n", loadDoubleLog() ? "done" : "*** FAILED ***");

	printf("Loading Calendar...");
	loadCalendar();
	printf("done.\n");

	std::cout << "Loading Proxy Access...";
	loadProxyAccess();
	std::cout << "done." << std::endl;

	return(true);
}

// These items depend on python so load them after python has been initialized
bool Config::loadAfterPython() {
	printf("Loading Areas...%s.\n", loadAreas() ? "done" : "*** FAILED ***");
	if(!listing)
		printf("Loading Ships...%s.\n", loadShips() ? "done" : "*** FAILED ***");
	printf("Loading Properties...%s.\n", loadProperties() ? "done" : "*** FAILED ***");
	return (true);
}
bool Config::startFlashPolicy() const {
	return(false);
	char script[256], policy[256], cmd[256];

	if(!flashPolicyPort) {
		broadcast(isDm, "^oNo flash policy port specified.");
		return(false);
	}
	if(flashPolicyPort == portNum) {
		broadcast(isDm, "^oFlash policy port equals main mud port.");
		return(false);
	}

	sprintf(script, "%s/flashpolicyd.py", Path::UniqueRoom);
	if(!file_exists(script)) {
		broadcast(isDm, "^oUnable to find flash policy server.");
		return(false);
	}

	sprintf(policy, "%s/flashpolicy.xml", Path::Config);
	if(!file_exists(policy)) {
		broadcast(isDm, "^oUnable to find flash policy file.");
		return(false);
	}

	if(!fork()) {
		sprintf(cmd, "python %s --file=%s --port=%d", script, policy, flashPolicyPort);
		system(cmd);
		exit(0);
	}
	return(true);
}

bool Config::loadConfig(bool reload) {
	char filename[256];
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	xmlNodePtr	curNode;

	sprintf(filename, "%s/config.xml", Path::Config);

	if(!file_exists(filename))
		return(false);

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

	rootNode = xmlDocGetRootElement(xmlDoc);
	curNode = rootNode->children;

	// Reset current config
	reset(reload);
	while(curNode) {
			 if(NODE_NAME(curNode, "General")) loadGeneral(curNode);
		else if(NODE_NAME(curNode, "Lottery")) loadLottery(curNode);

		curNode = curNode->next;
	}

	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();

	//gConfig->numGuilds = gConfig->numGuilds;
	return(true);
}

void Config::loadGeneral(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;

	while(curNode) {
			 if(NODE_NAME(curNode, "AutoShutdown")) xml::copyToBool(autoShutdown, curNode);
		else if(NODE_NAME(curNode, "AprilFools")) xml::copyToBool(doAprilFools, curNode);
		else if(NODE_NAME(curNode, "FlashPolicyPort")) xml::copyToNum(flashPolicyPort, curNode);
		else if(NODE_NAME(curNode, "CharCreationDisabled")) xml::copyToBool(charCreationDisabled, curNode);
		else if(NODE_NAME(curNode, "CheckDouble")) xml::copyToBool(checkDouble, curNode);
		else if(NODE_NAME(curNode, "GetHostByName")) xml::copyToBool(getHostByName, curNode);
		else if(NODE_NAME(curNode, "LessExpLoss")) xml::copyToBool(lessExpLoss, curNode);
		else if(NODE_NAME(curNode, "LogDatabaseType")) xml::copyToBString(logDbType, curNode);
		else if(NODE_NAME(curNode, "LogDatabaseUser")) xml::copyToBString(logDbUser, curNode);
		else if(NODE_NAME(curNode, "LogDatabasePassword")) xml::copyToBString(logDbPass, curNode);
		else if(NODE_NAME(curNode, "LogDatabaseDatabase")) xml::copyToBString(logDbDatabase, curNode);
		else if(NODE_NAME(curNode, "LogDeath")) xml::copyToBool(logDeath, curNode);
		else if(NODE_NAME(curNode, "PkillInCombatDisabled")) xml::copyToBool(pkillInCombatDisabled, curNode);
		else if(NODE_NAME(curNode, "RecordAll")) xml::copyToBool(recordAll, curNode);
		else if(NODE_NAME(curNode, "LogSuicide")) xml::copyToBool(logSuicide, curNode);
		else if(NODE_NAME(curNode, "SaveOnDrop")) xml::copyToBool(saveOnDrop, curNode);
		//else if(NODE_NAME(curNode, "MaxGuild")) xml::copyToNum(maxGuilds, curNode);
		else if(NODE_NAME(curNode, "DmPass")) xml::copyToBString(dmPass, curNode);
		else if(NODE_NAME(curNode, "MudName")) xml::copyToBString(mudName, curNode);
		else if(NODE_NAME(curNode, "Webserver")) xml::copyToBString(webserver, curNode);
		else if(NODE_NAME(curNode, "QS")) { xml::copyToBString(qs, curNode);
			std::cout << "Loaded QS: " << qs << std::endl;
		}
		else if(NODE_NAME(curNode, "UserAgent")) xml::copyToBString(userAgent, curNode);
		else if(NODE_NAME(curNode, "Reviewer")) xml::copyToBString(reviewer, curNode);
		else if(NODE_NAME(curNode, "ShopNumObjects")) xml::copyToNum(shopNumObjects, curNode);
		else if(NODE_NAME(curNode, "ShopNumLines")) xml::copyToNum(shopNumLines, curNode);
		else if(NODE_NAME(curNode, "CustomColors")) xml::copyToCString(customColors, curNode);
		else if(!bHavePort && NODE_NAME(curNode, "Port")) xml::copyToNum(portNum, curNode);

		curNode = curNode->next;
	}
}


void Config::loadLottery(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;

	while(curNode) {
			 if(NODE_NAME(curNode, "Enabled")) xml::copyToBool(lotteryEnabled, curNode);
		else if(NODE_NAME(curNode, "curNoderentCycle")) xml::copyToNum(lotteryCycle, curNode);
		else if(NODE_NAME(curNode, "curNoderentJackpot")) xml::copyToNum(lotteryJackpot, curNode);
		else if(NODE_NAME(curNode, "TicketPrice")) xml::copyToNum(lotteryTicketPrice, curNode);
		else if(NODE_NAME(curNode, "LotteryWon")) xml::copyToBool(lotteryWon, curNode);
		else if(NODE_NAME(curNode, "TicketsSold")) xml::copyToNum(lotteryTicketsSold, curNode);
		else if(NODE_NAME(curNode, "WinningsThisCycle")) xml::copyToNum(lotteryWinnings, curNode);
		else if(NODE_NAME(curNode, "LotteryRunTime")) xml::copyToNum(lotteryRunTime, curNode);

		else if(NODE_NAME(curNode, "WinningNumbers")) {
			xml::loadNumArray<short>(curNode, lotteryNumbers, "LotteryNum", 6);
		}
		else if(NODE_NAME(curNode, "Tickets")) {
			loadTickets(curNode);
		}

		curNode = curNode->next;
	}
}

void Config::loadTickets(xmlNodePtr rootNode) {
	xmlNodePtr curNode = rootNode->children;
	LottoTicket* ticket=0;

	while(curNode) {
		if(NODE_NAME(curNode, "Ticket")) {
			 if((ticket = new LottoTicket(curNode)) != NULL) {
				 tickets.push_back(ticket);
			 }
		}

		curNode = curNode->next;
	}
}

bool Config::save() const {
	saveConfig();
	return(true);
}

// Functions to get configured options
bool Config::saveConfig() const {
	xmlDocPtr	xmlDoc;
	xmlNodePtr		rootNode, curNode;
	char			filename[256];

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

	// Make general section
	curNode = xmlNewChild(rootNode, NULL, BAD_CAST "General", NULL);
	if(!bHavePort)
		xml::saveNonZeroNum(curNode, "Port", portNum);

	xml::saveNonNullString(curNode, "MudName", mudName);
	xml::saveNonNullString(curNode, "DmPass", dmPass);
	xml::saveNonNullString(curNode, "Webserver", webserver);
	xml::saveNonNullString(curNode, "QS", qs);
	xml::saveNonNullString(curNode, "UserAgent", userAgent);
	xml::saveNonNullString(curNode, "CustomColors", customColors);
	xml::saveNonNullString(curNode, "Reviewer", reviewer);

    xml::newStringChild(curNode, "LogDatabaseType", logDbType);
    xml::newStringChild(curNode, "LogDatabaseUser", logDbUser);
    xml::newStringChild(curNode, "LogDatabasePassword", logDbPass);
    xml::newStringChild(curNode, "LogDatabaseDatabase", logDbDatabase);

	xml::newBoolChild(curNode, "AprilFools", doAprilFools);
	xml::saveNonZeroNum(curNode, "FlashPolicyPort", flashPolicyPort);
	xml::newBoolChild(curNode, "AutoShutdown", autoShutdown);
	xml::newBoolChild(curNode, "CharCreationDisabled", charCreationDisabled);
	xml::newBoolChild(curNode, "CheckDouble", checkDouble);
	xml::newBoolChild(curNode, "GetHostByName", getHostByName);
	xml::newBoolChild(curNode, "LessExpLoss", lessExpLoss);
	xml::newBoolChild(curNode, "LogDeath", logDeath);
	xml::newBoolChild(curNode, "PkillInCombatDisabled", pkillInCombatDisabled);
	xml::newBoolChild(curNode, "RecordAll", recordAll);
	xml::newBoolChild(curNode, "LogSuicide", logSuicide);
	xml::newBoolChild(curNode, "SaveOnDrop", saveOnDrop);
	//xml::newBoolChild(curNode, "MaxGuild", maxGuilds);

	xml::saveNonZeroNum(curNode, "ShopNumObjects", shopNumObjects);
	xml::saveNonZeroNum(curNode, "ShopNumLines", shopNumLines);

	// Lottery Section
	curNode = xmlNewChild(rootNode, NULL, BAD_CAST "Lottery", NULL);
	xml::saveNonZeroNum(curNode, "CurrentCycle", lotteryCycle);
	xml::saveNonZeroNum(curNode, "CurrentJackpot", lotteryJackpot);
	xml::newBoolChild(curNode, "Enabled", BAD_CAST iToYesNo(lotteryEnabled));
	xml::saveNonZeroNum(curNode, "TicketPrice", lotteryTicketPrice);
	xml::saveNonZeroNum(curNode, "TicketsSold", lotteryTicketsSold);
	xml::saveNonZeroNum(curNode, "LotteryWon", lotteryWon);
	xml::saveNonZeroNum(curNode, "WinningsThisCycle", lotteryWinnings);
	xml::saveNonZeroNum(curNode, "LotteryRunTime", lotteryRunTime);
	saveShortIntArray(curNode, "WinningNumbers", "LotteryNum", lotteryNumbers, 6);
	xmlNodePtr ticketsNode = xml::newStringChild(curNode, "Tickets");
	for(LottoTicket* ticket : tickets) {
		ticket->saveToXml(ticketsNode);
	}

	sprintf(filename, "%s/config.xml", Path::Config);
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);

	return(true);
}

int Config::getPortNum() const {
	return(portNum);
}
void Config::setPortNum(int pPort) {
	portNum = pPort;
}
int getPkillInCombatDisabled() {
	return(gConfig->pkillInCombatDisabled);
}

bstring Config::getMonthDay() const {
	long	t = time(0);
	bstring str = ctime(&t);
	return(str.substr(4,6));
}

unsigned long Config::expNeeded(int level) {
	if(level < 1)
		return(0);
	if(level > MAXALVL)
		return(MAXINT);
	return(needed_exp[level-1]);
}

bool Config::isAprilFools() const { return(doAprilFools && getMonthDay() == "Apr  1"); }
bool Config::willAprilFools() const { return(doAprilFools); }
int Config::getFlashPolicyPort() const { return(flashPolicyPort); }

bool Config::sendTxtOnCrash() const { return(txtOnCrash); }
void Config::toggleTxtOnCrash() { txtOnCrash = !txtOnCrash; }

int Config::getShopNumObjects() const { return(shopNumObjects ? shopNumObjects : 400); }
int Config::getShopNumLines() const { return(shopNumLines ? shopNumLines : 150); }

const cWeather* Config::getWeather() const { return(calendar->getCurSeason()->getWeather()); }

bstring Config::getDmPass() const { return(dmPass); }
bstring Config::getWebserver() const { return(webserver); }
bstring Config::getQS() const { return(qs); }
bstring Config::getUserAgent() const { return(userAgent); }
bstring Config::getReviewer() const { return(reviewer); }

#include "specials.h"

bstring Config::getSpecialFlag(int index) {
	if(index >= SA_MAX_FLAG || index <= SA_NO_FLAG )
		return("Invalid Flag");

	return(specialFlags[index].name);
}
#include "version.h"
bstring Config::getVersion() {
	return(VERSION);
}
bstring Config::getMudName() {
	return(mudName);
}

bstring Config::getMudNameAndVersion() {
	return(getMudName() + " v" + getVersion());
}
// End Config Functions
//--------------------------------------------------------------------

// **************
//   Save Lists
// **************

template<class Type>
bool saveList(bstring xmlDocName, bstring fName, const std::map<bstring, Type*>& sMap) {
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char		filename[80];

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST xmlDocName.c_str(), NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);

	for(std::pair<bstring, Type*> sp : sMap) {
		Type* curItem = sp.second;
		curItem->save(rootNode);
	}

	snprintf(filename, 80, "%s/%s", Path::Config, fName.c_str());
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
	return(true);

}

bool Config::saveSpells() const {
    return(saveList<Spell>("Spells", "spelllist.xml", spells));
}

bool Config::saveSongs() const {
    return(saveList<Song>("Songs", "songlist.xml", songs));
}

// **************
//   Load Lists
// **************

template<class Type>
bool loadList(bstring xmlDocName, bstring xmlNodeName, bstring fName, std::map<bstring, Type*>& sMap) {
    xmlDocPtr xmlDoc;
    xmlNodePtr curNode;

    char filename[80];
    snprintf(filename, 80, "%s/%s", Path::Code, fName.c_str());
	xmlDoc = xml::loadFile(filename, xmlDocName.c_str());
	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);
	}

	while(curNode != NULL) {
		if(NODE_NAME(curNode, xmlNodeName.c_str())) {
			Type* curItem = new Type(curNode);
			sMap[curItem->getName()] = curItem;
		}
		curNode = curNode->next;
	}

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

bool Config::loadEffects() {
    clearEffects();
    return(loadList<Effect>("Effects", "Effect", "effects.xml", effects));
}

bool Config::loadSpells() {
	clearSpells();
    return(loadList<Spell>("Spells", "Spell", "spelllist.xml", spells));
}

bool Config::loadSongs() {
	clearSongs();
    return(loadList<Song>("Songs", "Song", "songlist.xml", songs));
}


//**********************************************************************
//						writeHelpFiles
//**********************************************************************
// commands are written in initCommands
// socials are written in initCommands
// flags are written in loadFlags

bool Config::writeHelpFiles() const {
	bool success = true;

	success &= writeSpellFiles();
	success &= writeSocialFile();


	return(success);
}

namespace Path {
	const char* Bin = "../bin/";
	const char* Log = "../log/";
	const char* BugLog = "../log/bug/";
	const char* StaffLog = "../log/staff/";
	const char* BankLog = "../log/bank/";
	const char* GuildBankLog = "../log/guildbank/";

	const char* UniqueRoom = "../rooms/";
	const char* AreaRoom = "../rooms/area/";
	const char* Monster = "../monsters/";
	const char* Object = "../objects/";
	const char* Player = "../player/";
	const char* PlayerBackup = "../player/backup/";

	const char* Config = "../config/";

	const char* Code = "../config/code/";
	const char* Python = "../config/code/python/";
	const char* Game = "../config/game/";
	const char* AreaData = "../config/game/area/";
	const char* Talk = "../config/game/talk/";
	const char* Desc = "../config/game/ddesc/";
	const char* Sign = "../config/game/signs/";

	const char* PlayerData = "../config/player/";
	const char* Bank = "../config/player/bank/";
	const char* GuildBank = "../config/player/guildbank/";
	const char* History = "../config/player/history/";
	const char* Post = "../config/player/post/";

	const char* BaseHelp = "../help/";
	const char* Help = "../help/help/";
	const char* CreateHelp = "../help/create/";
	const char* Wiki = "../help/wiki/";
	const char* DMHelp = "../help/dmhelp/";
	const char* BuilderHelp = "../help/bhelp/";
	const char* HelpTemplate = "../help/template/";
}
//*********************************************************************
//						path functions
//*********************************************************************

char* objectPath(const CatRef cr) {
	static char filename[256];
	if(cr.id < 0)
		sprintf(filename, "%s/%s/", Path::Object, cr.area.c_str());
	else
		sprintf(filename, "%s/%s/o%05d.xml", Path::Object, cr.area.c_str(), cr.id);
	return(filename);
}
char* monsterPath(const CatRef cr) {
	static char filename[256];
	if(cr.id < 0)
		sprintf(filename, "%s/%s/", Path::Monster, cr.area.c_str());
	else
		sprintf(filename, "%s/%s/m%05d.xml", Path::Monster, cr.area.c_str(), cr.id);
	return(filename);
}
char* roomPath(const CatRef cr) {
	static char filename[256];
	if(cr.id < 0)
		sprintf(filename, "%s/%s/", Path::UniqueRoom, cr.area.c_str());
	else
		sprintf(filename, "%s/%s/r%05d.xml", Path::UniqueRoom, cr.area.c_str(), cr.id);
	return(filename);
}
char* roomBackupPath(const CatRef cr) {
	static char filename[256];
	if(cr.id < 0)
		sprintf(filename, "%s/%s/backup/", Path::UniqueRoom, cr.area.c_str());
	else
		sprintf(filename, "%s/%s/backup/r%05d.xml", Path::UniqueRoom, cr.area.c_str(), cr.id);
	return(filename);
}

//*********************************************************************
//						checkPaths
//*********************************************************************

bool Path::checkPaths() {
	bool ok = true;

	ok = Path::checkDirExists(Path::Bin) && ok;
	ok = Path::checkDirExists(Path::Log) && ok;
	ok = Path::checkDirExists(Path::BugLog) && ok;
	ok = Path::checkDirExists(Path::StaffLog) && ok;
	ok = Path::checkDirExists(Path::BankLog) && ok;
	ok = Path::checkDirExists(Path::GuildBankLog) && ok;

	ok = Path::checkDirExists(Path::UniqueRoom) && ok;
	ok = Path::checkDirExists(Path::AreaRoom) && ok;
	ok = Path::checkDirExists(Path::Monster) && ok;
	ok = Path::checkDirExists(Path::Object) && ok;
	ok = Path::checkDirExists(Path::Player) && ok;
	ok = Path::checkDirExists(Path::PlayerBackup) && ok;

	ok = Path::checkDirExists(Path::Config) && ok;

	ok = Path::checkDirExists(Path::Code) && ok;
	ok = Path::checkDirExists(Path::Python) && ok;
	ok = Path::checkDirExists(Path::Game) && ok;
	ok = Path::checkDirExists(Path::AreaData) && ok;
	ok = Path::checkDirExists(Path::Talk) && ok;
	ok = Path::checkDirExists(Path::Desc) && ok;
	ok = Path::checkDirExists(Path::Sign) && ok;

	ok = Path::checkDirExists(Path::PlayerData) && ok;
	ok = Path::checkDirExists(Path::Bank) && ok;
	ok = Path::checkDirExists(Path::GuildBank) && ok;
	ok = Path::checkDirExists(Path::History) && ok;
	ok = Path::checkDirExists(Path::Post) && ok;

	ok = Path::checkDirExists(Path::BaseHelp) && ok;
	ok = Path::checkDirExists(Path::Help) && ok;
	ok = Path::checkDirExists(Path::Wiki) && ok;
	ok = Path::checkDirExists(Path::DMHelp) && ok;
	ok = Path::checkDirExists(Path::BuilderHelp) && ok;
	ok = Path::checkDirExists(Path::HelpTemplate) && ok;

	return(ok);
}

//*********************************************************************
//						checkDirExists
//*********************************************************************

bool Path::checkDirExists(const char* filename) {
	struct stat     f_stat;
	if(stat(filename, &f_stat)) {
		return(!mkdir(filename, 0755));
	}
	return(true);
}

bool Path::checkDirExists(bstring area, char* (*fn)(const CatRef cr)) {
	char	filename[256];
	CatRef	cr;

	// this will trigger the dir-only mode
	cr.setArea(area.c_str());
	cr.id = -1;
	strcpy(filename, (*fn)(cr));

	return(checkDirExists(filename));
}




//*********************************************************************
//						reloadRoom
//*********************************************************************
// This function reloads a room from disk, if it's already loaded. This
// allows you to make changes to a room, and then reload it, even if it's
// already in the memory room queue.

bool Config::reloadRoom(BaseRoom* room) {
	UniqueRoom*	uRoom = room->getAsUniqueRoom();

	if(uRoom) {
		bstring str = uRoom->info.str();
		if(	roomQueue.find(str) != roomQueue.end() &&
			roomQueue[str].rom &&
			reloadRoom(uRoom->info) >= 0
		) {
			roomQueue[str].rom->addPermCrt();
			return(true);
		}
	} else {

		AreaRoom* aRoom = room->getAsAreaRoom();
		char	filename[256];
		sprintf(filename, "%s/%d/%s", Path::AreaRoom, aRoom->area->id,
			aRoom->mapmarker.filename().c_str());

		if(file_exists(filename)) {
			xmlDocPtr	xmlDoc;
			xmlNodePtr	rootNode;

			if((xmlDoc = xml::loadFile(filename, "AreaRoom")) == NULL)
				merror("Unable to read arearoom file", FATAL);

			rootNode = xmlDocGetRootElement(xmlDoc);

			Area *area = aRoom->area;
			aRoom->reset();
			aRoom->area = area;
			aRoom->load(rootNode);
			return(true);
		}
	}
	return(false);
}

int Config::reloadRoom(CatRef cr) {
	UniqueRoom	*room=0;

	bstring str = cr.str();
	if(roomQueue.find(str) == roomQueue.end())
		return(0);
	if(!roomQueue[str].rom)
		return(0);

//	roomQueue[str].rom->purge();

	if(!loadRoomFromFile(cr, &room))
		return(-1);
	// Move any current players & monsters into the new room
	for(Player* ply : roomQueue[str].rom->players) {
		room->players.insert(ply);
		ply->setParent(room);
	}
	roomQueue[str].rom->players.clear();

	if(room->monsters.empty()) {
		for(Monster* mons : roomQueue[str].rom->monsters) {
			room->monsters.insert(mons);
			mons->setParent(room);
		}
		roomQueue[str].rom->monsters.clear();
	}
	if(room->objects.empty()) {
		for(Object* obj : roomQueue[str].rom->objects) {
			room->objects.insert(obj);
			obj->setParent(room);
		}
		roomQueue[str].rom->objects.clear();
	}


	delete roomQueue[str].rom;
	roomQueue[str].rom = room;

	room->registerMo();

	return(0);
}

//*********************************************************************
//						resaveRoom
//*********************************************************************
// This function saves an already-loaded room back to memory without
// altering its position on the queue.

int saveRoomToFile(UniqueRoom* pRoom, int permOnly);

int Config::resaveRoom(CatRef cr) {
	bstring str = cr.str();
	if(roomQueue[str].rom)
		roomQueue[str].rom->saveToFile(ALLITEMS);
	return(0);
}


int Config::saveStorage(UniqueRoom* uRoom) {
	if(uRoom->flagIsSet(R_SHOP))
		saveStorage(shopStorageRoom(uRoom));
	return(saveStorage(uRoom->info));
}
int Config::saveStorage(CatRef cr) {
	bstring str = cr.str();
	if(!roomQueue[str].rom)
		return(0);

	if(roomQueue[str].rom->saveToFile(ALLITEMS) < 0)
		return(-1);

	return(0);
}

//********************************************************************
//						resaveAllRooms
//********************************************************************
// This function saves all memory-resident rooms back to disk.  If the
// permonly parameter is non-zero, then only permanent items in those
// rooms are saved back.

void Config::resaveAllRooms(char permonly) {
	qtag 	*qt = roomHead;
	while(qt) {
		if(roomQueue[qt->str].rom) {
			if(roomQueue[qt->str].rom->saveToFile(permonly) < 0)
				return;
		}
		qt = qt->next;
	}
}

//*********************************************************************
//						replaceMonsterInQueue
//*********************************************************************
void Config::replaceMonsterInQueue(CatRef cr, Monster *monster) {
	if(this->monsterInQueue(cr))
		*monsterQueue[cr.str()].mob = *monster;
	else
		addMonsterQueue(cr, &monster);
}

//*********************************************************************
//						replaceObjectInQueue
//*********************************************************************
void Config::replaceObjectInQueue(CatRef cr, Object* object) {
	if(objectInQueue(cr))
		*objectQueue[cr.str()].obj = *object;
	else
		addObjectQueue(cr, &object);
}