roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * command5.cpp
 *	 Command handling/parsing routines.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include "mud.h"
#include "login.h"
#include "commands.h"
#include "guilds.h"
#include "web.h"
#include "calendar.h"


//*********************************************************************
//						who
//*********************************************************************
// This function outputs a list of all the players who are currently
// logged into the game.

int cmdWho(Player* player, cmd* cmnd) {
	int		cClass=0, chaos=0, clan=0, pledge=0;
	int		race=0, cls=0, law=0, guild=0;
	bool	found=false;

	player->clearFlag(P_AFK);

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

	if(player->isBlind()) {
		player->printColor("^CYou're blind!\n");
		return(0);
	}


	if(cmnd->num > 1) {

		lowercize(cmnd->str[1],0);

		switch (cmnd->str[1][0]) {
		case 'a':
			switch (cmnd->str[1][1]) {

			case 'd':
				cClass = -2;
				break;
			case 's':
				cClass = ASSASSIN;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;
			break;
		case 'b':
			switch (cmnd->str[1][3]) {
			case 'b':
				race = BARBARIAN;
				break;
			case 's':
				cClass = BERSERKER;
				break;
			case 'd':
				cClass = BARD;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;
		case 'c':
			switch (cmnd->str[1][1]) {
			case 'a':
				switch(cmnd->str[1][2]) {
				case 'm':
					race = CAMBION;
					break;
				case 'r':
					if(!player->isCt()) {
						player->print("You do not currently have that privilege.\n");
						return(0);
					} else
						cClass = CARETAKER;
					break;
				default:
					player->print("Parameter not unique.\n");
					return(0);
					break;
				}
				break;
			case 'h':
				chaos = 1;
				break;
			case 'l':
				switch (cmnd->str[1][2]) {
				case 'e':
					cClass = CLERIC;
					break;
				case 'a':
					switch(cmnd->str[1][3]) {
					case 's':
						cClass = player->getClass();
						break;
					case 'n':
						clan = 1;
						break;

					}
					break;
				default:
					player->print("Parameter not unique.\n");
					return(0);
					break;
				}
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;

		case 'd':
			switch(cmnd->str[1][1]) {
			case 'a':
				race = DARKELF;
				break;
			case 'e':
				cClass = DEATHKNIGHT;
				break;
			case 'r':
				cClass = DRUID;
				break;
			case 'w':
				race = DWARF;
				break;
			case 'u':
				if(!player->isDm()) {
					player->print("You do not currently have that privilege.\n");
					return(0);
				} else
					cClass = DUNGEONMASTER;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;

		case 'e':
			race = ELF;
			break;
		case 'f':
			cClass = FIGHTER;
			break;
		case 'g':
			switch(cmnd->str[1][1]) {
			case 'n':
				race = GNOME;
				break;
			case 'o':
				race = GOBLIN;
				break;
			case 'u':
				guild = 1;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;

		case 'h':
			switch(cmnd->str[1][1]) {
			case 'u':
				race = HUMAN;
				break;
			case 'a':
				switch(cmnd->str[1][4]) {
				case 'e':
					race = HALFELF;
					break;
				case 'g':
					race = HALFGIANT;
					break;
				case 'l':
					race = HALFLING;
					break;
				case 'o':
					race = HALFORC;
					break;
				default:
					player->print("Parameter not unique.\n");
					return(0);
					break;
				}
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;

		case 'k':
			switch(cmnd->str[1][1]) {
			case 'a':
				race = KATARAN;
				break;
			case 'o':
				race = KOBOLD;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;

		case 'l':
			switch(cmnd->str[1][1]) {
			case 'a':
				law = 1;
				break;
			case 'i':
				cClass = LICH;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;


		case 'm':
			switch(cmnd->str[1][1]) {
			case 'a':
				cClass = MAGE;
				break;
			case 'i':
				race = MINOTAUR;
				break;
			case 'o':
				cClass = MONK;
				break;
			default:
				player->print("Unknown parameter.\n");
				return(0);
				break;
			}
			break;

		case 'o':
			switch(cmnd->str[1][1]) {
			case 'g':
				race = OGRE;
				break;
			case 'r':
				race = ORC;
				break;
			default:
				player->print("Unknown parameter.\n");
				return(0);
				break;
			}
			break;

		case 'p':
			switch(cmnd->str[1][1]) {
			case 'a':
				cClass = PALADIN;
				break;
			case 'l':
				pledge = 1;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;

		case 'r':
			switch(cmnd->str[1][1]) {
			case 'a':
				cClass = RANGER;
				break;
			case 'o':
				cClass = ROGUE;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;

		case 's':
			race = SERAPH;
			break;
		case 't':
			switch(cmnd->str[1][1]) {
			case 'h':
				cClass = THIEF;
				break;
			case 'r':
				race = TROLL;
				break;
			case 'i':
				race = TIEFLING;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;

		case 'v':
			switch (cmnd->str[1][1]) {
			case 'a':
				cClass = VAMPIRE;
				break;
			case 'e':
				cClass = -3;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;
			break;
		case 'w':
			switch(cmnd->str[1][1]) {
			case 'a':
				cClass = -1;
				break;
			case 'e':
				cClass = WEREWOLF;
				break;
			default:
				player->print("Parameter not unique.\n");
				return(0);
				break;
			}
			break;
		case '.':
			cls = 1;
			break;

		default:
			player->print("Parameter not unique.\n");
			return(0);
			break;
		}// end main switch

	} // end if cmnd->num > 1

	std::ostringstream whoStr;
	bstring curStr;

	whoStr << "\n^BPlayers currently online:\n";
	whoStr << "-------------------------------------------------------------------------------^x\n";

	std::pair<bstring, Player*> p;
	Player* target=0;
	foreach(p, gServer->players) {
		target = p.second;

		if(!target->isConnected())
			continue;
		if(!player->canSee(target))
			continue;

		if(cls == 1)
			if(target->getClass() != player->getClass())
				continue;
		if(clan == 1)
			if(!target->getClan())
				continue;
		if(guild == 1)
			if(player->getGuild() && target->getGuild() != player->getGuild())
				continue;
		if(chaos == 1)
			if(!target->flagIsSet(P_CHAOTIC))
				continue;
		if(law == 1)
			if(target->flagIsSet(P_CHAOTIC))
				continue;
		if(pledge == 1)
			if((target->getClan() && target->getClan() == player->getClan()) || !target->getClan())
				continue;
		if(race > 0)
			if((player->willIgnoreIllusion() ? target->getRace() : target->getDisplayRace()) != race)
				continue;
		if(cClass == -1)
			if(!target->isPublicWatcher())
				continue;
		if(cClass == -2)
			if(!target->flagIsSet(P_ADULT))
				continue;
		if(cClass == -3)
			if(!target->flagIsSet(P_VETERAN))
				continue;
		if(cClass > 0)
			if(target->getClass() != cClass)
				continue;
		if(target->flagIsSet(P_MISTED) && !player->isEffected("true-sight") && !player->isCt() )
			continue;
		if(target->isInvisible() && !player->isEffected("detect-invisible") && !player->isCt() )
			continue;
		if(target->flagIsSet(P_GLOBAL_GAG) && player != target && !player->isCt())
			continue;

		found = true;

		curStr = target->getWhoString(false, true, player->willIgnoreIllusion());
		whoStr << curStr;
	}
	if(!found)
		player->print("Nobody found!\n");
	else {
		curStr = whoStr.str();
		player->printColor("%s\n", curStr.c_str());
	}

	return(0);
}

//*********************************************************************
//				classwho
//*********************************************************************
// This function outputs a list of all the players who are currently
// logged into the game which are the same class as the player doing
// the command.

int cmdClasswho(Player* player, cmd* cmnd) {
	cmnd->num = 2;
	strcpy(cmnd->str[0], "who");
	strcpy(cmnd->str[1], "class");
	return(cmdWho(player, cmnd));
}


//*********************************************************************
//						cmdWhois
//*********************************************************************
// The whois function displays a selected player's name, class, level
// title, age and gender

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

	player->clearFlag(P_AFK);

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

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

	lowercize(cmnd->str[1], 1);
	target = gServer->findPlayer(cmnd->str[1]);

	if(!target || !player->canSee(target)) {
		player->print("That player is not logged on.\n");
		return(0);
	}

	player->printColor("%s", target->getWhoString(true, true, player->willIgnoreIllusion()).c_str());
	return(0);
}

//*********************************************************************
//						cmdSuicide
//*********************************************************************
// This function is called whenever the player explicitly asks to
// commit suicide.

int cmdSuicide(Player* player, cmd* cmnd) {

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

	if(player->flagIsSet(P_NO_SUICIDE)) {
		player->print("You cannot suicide right now. You are in a 24 hour cooling-off period.\n");
		return(0);
	}
	// Prevents players from suiciding in jail
	if(player->inJail()) {
		player->print("You attempt to kill yourself and fail. It sure hurts though!\n");
		broadcast(player->getSock(), player->getRoom(),"%M tries to kill %sself.", player, player->himHer());
		return(0);
	}

	if(cmnd->num < 2 || strcasecmp(cmnd->str[1], "yes")) {
		player->printColor("^rWARNING:^x This will completely erase your character!\n");
		player->print("To prevent accidental suicides you must confirm you want to suicide;\nto do so type 'suicide yes'\n");
		if(player->getGuild() && player->getGuildRank() == GUILD_MASTER)
			player->printColor("^yNote:^x if you want to control who takes over your guild after you suicide,\nbe sure to abdicate leadership first.\n");
		return(0);
	}

	//if(player->getLevel() > 2 && !player->isStaff())
		broadcast("### %s committed suicide! We'll miss %s dearly.", player->name, player->himHer());
	//else
	//	broadcast(isWatcher, "^C### %s committed suicide! We'll miss %s dearly.", player->name, player->himHer());

	logn("log.suicide", "%s(%s)-%d (%s)\n", player->name, player->getPassword().c_str(), player->getLevel(), player->getSock()->getHostname().c_str());

	deletePlayer(player);
	return(0);
}


//*********************************************************************
//						deletePlayer
//*********************************************************************
// Does a true delete of a player and their files

void deletePlayer(Player* player) {
	char	file[80];
	bool hardcore = player->isHardcore();
	// cache the name because we will be deleting the player object
 	bstring name = player->name;

 	gConfig->deleteUniques(player);
	player->clearEnemyPlayer();

	// remove minions before backup, since this affects other players as well
	player->clearMinions();

	// they are no longer the owner of any effects they have created
	gServer->removeEffectsOwner(player);

	// unassociate
	if(player->getForum() != "") {
		player->setForum("");
		webUnassociate(name);
	}

	// take the player out of any guilds
	updateGuild(player, GUILD_REMOVE);

	// save a backup - this will be the only copy of the player!
	if(player->getLevel() >= 7)
		player->save(false, LS_BACKUP);

	if(player->flagIsSet(P_CREATING_GUILD)) {
		// If they are the founder, this will return text, and we
		// need go no further. Otherwise, they are a supporter, and we
		// have to search for them.
		if(gConfig->removeGuildCreation(name) != "") {
			std::list<GuildCreation*>::iterator gcIt;
			for(gcIt = gConfig->guildCreations.begin(); gcIt != gConfig->guildCreations.end(); gcIt++) {
				// they were supporting this guild - stop
				if((*gcIt)->removeSupporter(name))
					break;
			}
			gConfig->saveGuilds();
		}
	}

	// this deletes the player object
	Socket* sock = player->getSock();
	player->uninit();
	free_crt(player);
	zero(player, sizeof(player));
	gServer->clearPlayer(name);
	sock->setPlayer(NULL);

	// get rid of any files the player was using
	sprintf(file, "%s/%s.xml", PLAYERPATH, name.c_str());
	unlink(file);
	sprintf(file, "%s/%s.txt", BANKDIR, name.c_str());
	unlink(file);
	sprintf(file, "%s/%s.txt", POSTPATH, name.c_str());
	unlink(file);
	sprintf(file, "%s/%s.txt", HISTPATH, name.c_str());
	unlink(file);

	// handle removing and property this player owned
	gConfig->destroyProperties(name);

	if(hardcore)
		sock->reconnect(true);
	else
		sock->disconnect();
}


//*********************************************************************
//						cmdConvert
//*********************************************************************
// This function allows a player to convert from chaotic to lawful alignment.

int cmdConvert(Player* player, cmd* cmnd) {
	if(!player->ableToDoCommand())
		return(0);

	if(!player->flagIsSet(P_CHAOTIC)) {
		player->print("You cannot convert. You're lawful.\n");
		return(0);
	} else if(player->getRace() == TIEFLING) {
		player->print("Tieflings are required to be chaotic.\n");
		return(0);
	} else if(player->isNewVampire()) {
		player->print("Vampires are required to be chaotic.\n");
		return(0);
	}

	if(cmnd->num < 2 || strcasecmp(cmnd->str[1], "yes")) {
		player->print("To prevent accidental conversion you must confirm you want to convert,\n");
		player->print("to do so type 'convert yes'\n");
		player->print("Remember, you will NEVER be able to be chaotic again.\n");
		return(0);
	}

	if(player->getClass() == BUILDER) {
		broadcast(isStaff, "^G### %s just converted to lawful alignment.", player->getName());
		logn("log.convert","%s converted to lawful.", player->getName());
	} else
		broadcast("^G### %s just converted to lawful alignment.", player->getName());
	player->clearFlag(P_CHAOTIC);

	if(player->getClass() == CLERIC)
		player->clearFlag(P_PLEDGED);

	return(0);
}


// having a pref that starts with a hyphen (-) is instead a category
typedef struct prefInfo {
	bstring name;
	int		flag;
	bool	(*canUse)(const Creature *);
	bstring desc;
	bool	invert;					// reverses the language
} prefInfo, *prefPtr;

prefInfo prefList[] =
{
	// name			flag					canUse	desc
	{ "-Staff Preferences",	0, isStaff, "", 0 },
	{ "eaves",		P_EAVESDROPPER,			isCt,	"eavesdropper mode",		0 },
	{ "supereaves",	P_SUPER_EAVESDROPPER,	isCt,	"super eavesdropper mode",	0 },
	{ "msg",		P_NO_MSG,				isCt,	"*msg channel",				true },
	{ "mobtick",	P_NO_TICK_MSG,			isCt,	"See mob tick broadcasts",	true },
	{ "mobaggro",	P_NO_AGGRO_MSG,			isCt,	"See mob aggro broadcasts",	true },
	{ "mobdeath",	P_NO_DEATH_MSG,			isCt,	"See mob death broadcasts",	true },
	{ "ttobound",	P_T_TO_BOUND,			isStaff,"*t to bound room",			0 },
	{ "wts",		P_NO_WTS,				isCt,	"*wts channel",				true },
	{ "zones",		P_VIEW_ZONES,			isCt,	"view overland zones",		0 },

	{ "-Color",		0, 0, "", 0 },
	{ "mirc",		P_MIRC,					0,		"mirc colors",				0 },
	{ "ansi",		P_ANSI_COLOR,			0,		"ansi colors",				0 },
	{ "langcolor",	P_LANGUAGE_COLORS,		0,		"language colors",			0 },
	{ "extracolor",	P_NO_EXTRA_COLOR,		0,		"extra color options",		true },
	{ "areacolor",	P_INVERT_AREA_COLOR,	0,		"invert area colors",		0 },
	{ "mxpcolors",	P_MXP_ENABLED,			0,		"use MXP for more than 16 colors",		0 },

	{ "-Channels",	0, 0, "", 0 },
	{ "cls",		P_IGNORE_CLASS_SEND,	0,		"class channel",			true },
	{ "race",		P_IGNORE_RACE_SEND,		0,		"race channel",				true },
	{ "broad",		P_NO_BROADCASTS,		0,		"broadcast channel",		true },
	{ "newbie",		P_IGNORE_NEWBIE_SEND,	0,		"newbie channel",			true },
	{ "gossip",		P_IGNORE_GOSSIP,		0,		"gossip channel",			true },
	{ "clan",		P_IGNORE_CLAN,			0,		"clan channel",				true },
	{ "tells",		P_NO_TELLS,				0,		"send/tell/whisper/sign",	true },
	{ "sms",		P_IGNORE_SMS,			0,		"receive text messages",	true },
	{ "ignore",		0,						0,		"ignore all channels",		0 },
	// do something with P_IGNORE_ALL

	{ "-Notifications",	0, 0, "", 0 },
	{ "duel",		P_NO_DUEL_MESSAGES,		0,		"duel messages",			true },
	{ "login",		P_NO_LOGIN_MESSAGES,	0,		"login messages",			true },
	{ "shopprofit",	P_DONT_SHOW_SHOP_PROFITS,0,		"shop profit notifications",true },
	{ "mail",		P_NO_SHOW_MAIL,			0,		"mudmail notifications",	true },
	{ "permdeath",	P_PERM_DEATH,			0,		"perm death broadcasts",	0 },
	{ "auction",	P_NO_AUCTIONS,			0,		"player auctions",			true },
	{ "notifications",0,					0,		"show all notifications",	0 },

	{ "-Combat",	0, 0, "", 0 },
	{ "autoattack",	P_NO_AUTO_ATTACK,		0,		"auto attack",				true },
	{ "lagprotect",	P_LAG_PROTECTION_SET,	0,		"lag protection",			0 },
	{ "lagrecall",	P_NO_LAG_HAZY,			0,		"recall potion if below half hp",true },
	{ "wimpy",		P_WIMPY,				0,		"flee when HP below this number",0 },
	{ "killaggros",	P_KILL_AGGROS,			0,		"attack aggros first",		0 },
	{ "mobnums",	P_NO_NUMBERS,			0,		"monster ordinal numbers",	true },

	{ "-Group",		0, 0, "", 0 },
	{ "group",		P_IGNORE_GROUP_BROADCAST,0,		"group combat messages",	true },
	{ "xpsplit",	P_XP_DIVIDE,			0,		"group experience split",	0 },
	{ "split",		P_GOLD_SPLIT,			0,		"split gold among group",	0 },
	{ "stats",		P_NO_SHOW_STATS,		0,		"show group your stats",	true },
	{ "follow",		P_NO_FOLLOW,			0,		"can be followed",			true },

	{ "-Miscellaneous",	0, 0, "", 0 },
	{ "autowear",	P_NO_AUTO_WEAR,			0,		"wear all on login",		true },
	{ "short",		P_NO_SHORT_DESCRIPTION,	0,		"short description",		true },
	{ "long",		P_NO_LONG_DESCRIPTION,	0,		"long description",			true },
	{ "prompt",		P_PROMPT,				0,		"descriptive prompt",		0 },
	{ "nlprompt",	P_NEWLINE_AFTER_PROMPT,	0,		"newline after prompt",		0 },
	{ "summon",		P_NO_SUMMON,			0,		"can be summoned",			true },
	{ "xpprompt",	P_SHOW_XP_IN_PROMPT,	0,		"show exp in prompt",		0 },
	//{ "pkill",		P_NO_PKILL_PERCENT,		0,		"show pkill percentage",	true },
	{ "afk",		P_AFK,					0,		"away from keyboard",		0 },
	{ "statistics",	P_NO_TRACK_STATS,		0,		"track statistics",			true },
	{ "skillprogress", P_SHOW_SKILL_PROGRESS,0,		"show skill progress bar",	false },

	// handle wimpy below
	// handle afk below

	{ "", 0, 0, "" }
};


//*********************************************************************
//						cmdPrefs
//*********************************************************************

int cmdPrefs(Player* player, cmd* cmnd) {
	prefPtr	pref = NULL;
	int		i=0, match=0, len=strlen(cmnd->str[1]);
	bool	set = cmnd->str[0][0]=='s';
	bool	toggle = cmnd->str[0][0]=='t' || cmnd->str[0][0]=='p';
	bool	show=false, all = !strcmp(cmnd->str[1], "-all");
	bstring prefName="";

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

	// display a list of preferences
	if(cmnd->num == 1 || cmnd->str[1][0] == '-') {

		if(!len) {
			player->print("Below is a list of preference categories. To view a cateogry, type\n");
			player->print("\"pref -[category]\". To view all preferences, type \"pref -all\".\n\n");
		}

		while(prefList[i].name != "") {
			if(!prefList[i].canUse || prefList[i].canUse(player)) {

				if(prefList[i].name.at(0) == '-') {
					// previous category was shown? newline
					if(show)
						player->print("\n");
					// no parameters? indent
					if(!len)
						player->print("  ");

					prefName = prefList[i].name;
					show = len && (all || !strncmp(cmnd->str[1], prefName.toLower().c_str(), len));

					player->printColor("^%s%s^w\n", show ? "C" : "c", prefList[i].name.substr(1, prefList[i].name.length() - 1).c_str());
				} else {
					if(!show) {
						i++;
						continue;
					}
					player->print("  %-13s ::  %-31s ::  ", prefList[i].name.c_str(), prefList[i].desc.c_str());
					// wimpy gets its own special format
					if(prefList[i].name == "wimpy" && player->flagIsSet(prefList[i].flag))
						player->print("%d", player->getWimpy());
					// print everything exp ignore
					else if(prefList[i].flag)
						player->printColor("%s^w", player->flagIsSet(prefList[i].flag) ^ !prefList[i].invert ? "^roff" : "^gon");
					player->print("\n");
				}

			}
			i++;
		}
		player->print("\n");
		return(0);
	}


	// everyone is used to nosummon - let them keep it that way
	if(!strcmp(cmnd->str[1], "nosummon")) {
		if(!toggle)
			set = !set;
		strcpy(cmnd->str[1], "summon");
	}


	// we're setting or clearing a preference
	// find out which one
	while(prefList[i].name != "") {
		if(	prefList[i].name.at(0) != '-' &&
			(!prefList[i].canUse || prefList[i].canUse(player))
		) {

			if(!strcmp(cmnd->str[1], prefList[i].name.c_str())) {
				match = 1;
				pref = &prefList[i];
				break;
			} else if(!strncmp(cmnd->str[1], prefList[i].name.c_str(), len)) {
				match++;
				pref = &prefList[i];
			}

		}
		i++;
	}

	if(match > 1) {
		player->print("Preference not unique.\n");
		return(0);
	}
	if(!pref) {
		player->print("Unknown preference.\n");
		return(0);
	}


	// if any channels are being ignored, toggle = stop ignorning
	if(toggle && pref->name == "ignore") {
		toggle = false;
		set = (!player->flagIsSet(P_IGNORE_CLASS_SEND) &&
			!player->flagIsSet(P_IGNORE_RACE_SEND) &&
			!player->flagIsSet(P_NO_BROADCASTS) &&
			!player->flagIsSet(P_IGNORE_NEWBIE_SEND) &&
			!player->flagIsSet(P_IGNORE_GOSSIP) &&
			!player->flagIsSet(P_IGNORE_CLAN) &&
			!player->flagIsSet(P_NO_TELLS)
		);
	}

	// if any notifications are off, toggle = turn them all one
	if(toggle && pref->name == "notifications") {
		toggle = false;
		set = (player->flagIsSet(P_NO_DUEL_MESSAGES) ||
			player->flagIsSet(P_NO_LOGIN_MESSAGES) ||
			player->flagIsSet(P_DONT_SHOW_SHOP_PROFITS) ||
			player->flagIsSet(P_NO_SHOW_MAIL) ||
			player->flagIsSet(P_IGNORE_GROUP_BROADCAST) ||
			!player->flagIsSet(P_PERM_DEATH) ||
			player->flagIsSet(P_NO_AUCTIONS)
		);
	}

	// set
	//---------------

	//NORMAL
	// toggle
	// !flag

	// toggle
	// invert && flag

	// are we setting or clearing?
	if(	((set ^ pref->invert) && !toggle) ||
		(toggle && !player->flagIsSet(pref->flag))
	) {

		if(pref->flag == P_MXP_ENABLED) {
			if(!player->getSock()->getMxp()) {
				player->print("Your client does not support MXP!\nYou will not be able to view extra colors.\n");
				return(0);
			}
			player->defineMXP();
		}

		if(pref->name == "ignore") {
			player->setFlag(P_IGNORE_CLASS_SEND);
			player->setFlag(P_IGNORE_RACE_SEND);
			player->setFlag(P_NO_BROADCASTS);
			player->setFlag(P_IGNORE_NEWBIE_SEND);
			player->setFlag(P_IGNORE_GOSSIP);
			player->setFlag(P_IGNORE_CLAN);
			player->setFlag(P_NO_TELLS);
		} else if(pref->name == "notifications") {
			player->clearFlag(P_NO_DUEL_MESSAGES);
			player->clearFlag(P_NO_LOGIN_MESSAGES);
			player->clearFlag(P_DONT_SHOW_SHOP_PROFITS);
			player->clearFlag(P_NO_SHOW_MAIL);
			player->clearFlag(P_IGNORE_GROUP_BROADCAST);
			player->setFlag(P_PERM_DEATH);
			player->clearFlag(P_NO_AUCTIONS);
		} else
			player->setFlag(pref->flag);

		if(pref->name == "afk") {
			// broadcast for going afk
			if(!player->flagIsSet(P_DM_INVIS))
				broadcast(player->getSock(), player->getRoom(), "%M has gone afk.", player);
			else
				broadcast(isCt, player->getSock(), player->getRoom(), "*DM* %M has gone afk.", player);
		}
		if(pref->name == "wimpy") {
			player->setWimpy(cmnd->val[1] == 1L ? 10 : cmnd->val[1]);
			player->setWimpy(MAX(player->getWimpy(), 2));
		}
		set = true;

	} else {

		if(pref->name == "ignore") {
			player->clearFlag(P_IGNORE_CLASS_SEND);
			player->clearFlag(P_IGNORE_RACE_SEND);
			player->clearFlag(P_NO_BROADCASTS);
			player->clearFlag(P_IGNORE_NEWBIE_SEND);
			player->clearFlag(P_IGNORE_GOSSIP);
			player->clearFlag(P_IGNORE_CLAN);
			player->clearFlag(P_NO_TELLS);
		} else if(pref->name == "notifications") {
			player->setFlag(P_NO_DUEL_MESSAGES);
			player->setFlag(P_NO_LOGIN_MESSAGES);
			player->setFlag(P_DONT_SHOW_SHOP_PROFITS);
			player->setFlag(P_NO_SHOW_MAIL);
			player->setFlag(P_IGNORE_GROUP_BROADCAST);
			player->clearFlag(P_PERM_DEATH);
			player->setFlag(P_NO_AUCTIONS);
		} else
			player->clearFlag(pref->flag);

		if(pref->name == "wimpy")
			player->setWimpy(0);

		set = false;

	}


	// give them the message
	player->print("Preference : %s : ", pref->desc.c_str());
	if(pref->name == "wimpy" && player->flagIsSet(pref->flag))
		player->print("%d", player->getWimpy());
	else
		player->printColor("%s^w", set ^ !pref->invert ? "^roff" : "^gon");
	player->print(".\n");

	if(pref->flag == P_NO_TRACK_STATS)
		player->print("Note that player kill statistics are always tracked and cannot be reset.\n");

	return(0);
}


//*********************************************************************
//						cmdQuit
//*********************************************************************
// This function checks to see if a player is allowed to quit yet. It
// checks to make sure the player isn't in the middle of combat, and if
// so, the player is not allowed to quit (and 0 is returned).

int cmdQuit(Player* player, cmd* cmnd) {
	long	i=0, t=0;

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

	t = time(0);
	if(player->inCombat())
		i = player->getLTAttack() + 20;
	else
		i = player->getLTAttack() + 2;
	if(t < i) {
		player->pleaseWait(i-t);
		return(0);
	}
	if(player->inCombat())
		i = LT(player, LT_SPELL) + 20;
	else
		i = LT(player, LT_SPELL) + 2;

	if(t < i) {
		player->pleaseWait(i-t);
		return(0);
	}
	player->update();


	return(DISCONNECT);
}


//*********************************************************************
//						changeStats
//*********************************************************************

int cmdChangeStats(Player* player, cmd* cmnd) {
	player->changeStats();
	return(0);
}

void Player::changeStats() {
	int a=0;

	if(!flagIsSet(P_CAN_CHANGE_STATS) && !isCt()) {
		print("You cannot change your stats at this time.\n");
		return;
	} else {

		for(a=0;a<5;a++) {
			tnum[a] = 0;
			tstat.num[a] = 0;
		}

		tstat.hp = 0;
		tstat.mp = 0;
		tstat.pp = 0;
		tstat.rp = 0;
		tstat.race = 0;
		tstat.cls = 0;
		tstat.cls2 = 0;
		tstat.level = 0;


		broadcast(::isCt, "^y### %s is choosing new stats.", name);
		printColor("^yPlease enter a new set of initial stats (56 points):\n");
		getSock()->setState(CON_CHANGING_STATS);
	}
}


//********************************************************************
//						changingStats
//********************************************************************
// This function allows a player to change their stats

void changingStats(Socket* sock, char *str) {
	sock->getPlayer()->changingStats(str);
}
void Player::changingStats(char *str) {
	int		a, n, i, k, l, sum=0;
	int		vnum[5];
	vstat	nstat, sendStat;

	switch(getSock()->getState()) {
	case CON_CHANGING_STATS:
		n = strlen(str);
		l = 0;
		k = 0;
		for(i=0; i<=n; i++) {
			if(str[i]==' ' || str[i]==0) {
				str[i] = 0;
				vnum[k++] = atoi(&str[l]);
				l = i+1;
			}
			if(k>4)
				break;
		}
		if(k<5) {
			print("Please enter all 5 numbers.\n");
			print("Aborted.\n");
			getSock()->setState(CON_PLAYING);
			return;
		}
		sum = 0;
		for(i=0; i<5; i++) {
			if(vnum[i] < 3 || vnum[i] > 18) {
				print("No stats < 3 or > 18 please.\n");
				print("Aborted.\n");
				getSock()->setState(CON_PLAYING);
				return;
			}
			sum += vnum[i];
		}
		if(sum != 56) {
			print("Stat total must equal 56 points.\n");
			print("Aborted.\n");
			getSock()->setState(CON_PLAYING);
			return;
		}

		for(a=0;a<5;a++)
			tnum[a] = vnum[a];

		if(race == HUMAN) {
			print("Raise which stat?:\n[A] Strength, [B] Dexterity, [C] Constitution, [D] Intelligence, or [E] Piety.\n");
			getSock()->setState(CON_CHANGING_STATS_RAISE);
			return;
		}

		print("Your stats have been calculated.\n");
		print("Please press [ENTER].\n");
		getSock()->setState(CON_CHANGING_STATS_CALCULATE);
		return;
		break;
	case CON_CHANGING_STATS_CALCULATE:

		for(a=0;a<5;a++) {
			sendStat.num[a] = tnum[a];
		}


		calcStats(sendStat, &nstat);


		print("Your resulting stats will be as follows:\n");
		print("STR: %d   DEX: %d   CON: %d   INT: %d   PIE: %d\n", nstat.num[0], nstat.num[1], nstat.num[2], nstat.num[3], nstat.num[4]);
		print("Hit Points: %d\n", nstat.hp);
		print("Are these the stats you want? (Y/N)\n");


		for(a=0; a<5; a++)
			tstat.num[a] = nstat.num[a];
		tstat.hp = nstat.hp;

		getSock()->setState(CON_CHANGING_STATS_CONFIRM);

		// confirm y/n
		break;
	case CON_CHANGING_STATS_CONFIRM:
		if(low(str[0]) == 'y') {
			broadcast(::isCt, "^y### %s has chosen %s stats.", name, hisHer());

			print("New stats set.\n");

			strength.setCur((short)tstat.num[0]);
			dexterity.setCur((short)tstat.num[1]);
			constitution.setCur((short)tstat.num[2]);
			intelligence.setCur((short)tstat.num[3]);
			piety.setCur((short)tstat.num[4]);

			hp.setMax(tstat.hp);

			clearFlag(P_CAN_CHANGE_STATS);
			getSock()->setState(CON_PLAYING);
			return;

		} else {
			broadcast(::isCt,"^y### %s aborted choosing new stats.", name);
			print("Aborted.\n");
			getSock()->setState(CON_PLAYING);
			return;
		}
		break;
	case CON_CHANGING_STATS_RAISE:
		switch (low(str[0])) {
		case 'a':
			tnum[0]++;
			break;
		case 'b':
			tnum[1]++;
			break;
		case 'c':
			tnum[2]++;
			break;
		case 'd':
			tnum[3]++;
			break;
		case 'e':
			tnum[4]++;
			break;
		default:
			print("\nPlease choose one.\n");
			return;
			break;
		}

		print("Lower which stat?:\n[A] Strength, [B] Dexterity, [C] Constitution, [D] Intelligence, or [E] Piety.\n");
		getSock()->setState(CON_CHANGING_STATS_LOWER);
		return;
		break;
	case CON_CHANGING_STATS_LOWER:
		switch (low(str[0])) {
		case 'a':
			tnum[0]--;
			tnum[0] = MAX(1, tnum[0]);
			break;
		case 'b':
			tnum[1]--;
			tnum[1] = MAX(1, tnum[1]);
			break;
		case 'c':
			tnum[2]--;
			tnum[2] = MAX(1, tnum[2]);
			break;
		case 'd':
			tnum[3]--;
			tnum[3] = MAX(1, tnum[3]);
			break;
		case 'e':
			tnum[4]--;
			tnum[4] = MAX(1, tnum[4]);
			break;
		default:
			print("\nPlease choose one.");
			return;
			break;
		}

		print("Your stats have been calculated.\n");
		print("Please press [ENTER].\n");
		getSock()->setState(CON_CHANGING_STATS_CALCULATE);
		break;
	}
}


//*********************************************************************
//						doTrain
//*********************************************************************

void doTrain(Player* player) {
	player->upLevel();
	player->setFlag(P_JUST_TRAINED);
	broadcast("### %s just made a level!", player->name);
	player->print("Congratulations, you made a level!\n\n");

	if(player->canChooseCustomTitle()) {
		player->setFlag(P_CAN_CHOOSE_CUSTOM_TITLE);
		player->print("You may now choose a custom title. Type ""help title"" for more details.\n");
	}

	logn("log.train", "%s just trained to level %d in room %s.\n",
		player->name, player->getLevel(), player->getRoom()->fullName().c_str());
}


//*********************************************************************
//						cmdTrain
//*********************************************************************
// This function allows a player to train if they are in the correct
// training location and has enough gold and experience.  If so, the
// character goes up a level.

int cmdTrain(Player* player, cmd* cmnd) {
	unsigned long goldneeded=0, expneeded=0, bankneeded=0, maxgold=0;

	if(player->getClass() == BUILDER) {
		player->print("You don't need to do that!\n");
		return(0);
	}

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

	if(player->isBlind()) {
		player->printColor("^CYou can't do that! You're blind!\n");
		return(0);
	}
	if(!player->flagIsSet(P_SECURITY_CHECK_OK)) {
		player->print("You are not allowed to do that.\n");
		return(0);
	}

	if(!player->flagIsSet(P_PASSWORD_CURRENT)) {
		player->print("Your password is no longer current.\n");
		player->print("In order to train, you must change it.\n");
		player->print("Use the \"password\" command to do this.\n");
		return(0);
	}

	if(!player->flagIsSet(P_CHOSEN_ALIGNMENT) && player->getLevel() == ALIGNMENT_LEVEL) {
		player->print("You must choose your alignment before you can train to level %d.\n", ALIGNMENT_LEVEL+1);
		player->print("Use the 'alignment' command to do so. HELP ALIGNMENT.\n");
		return(0);
	}


	if(player->getNegativeLevels()) {
		player->print("To train, all of your life essence must be present.\n");
		return(0);
	}

	expneeded = gConfig->expNeeded(player->getLevel());

	if(player->getLevel() < 19)
		maxgold = 750000;
	else if(player->getLevel() < 22)
		maxgold = 2000000;
	else
		maxgold = ((player->getLevel()-22)*500000) + 3000000;

	goldneeded = MIN(maxgold, expneeded / 2L);

	if(player->getRace() == HUMAN)
		goldneeded += goldneeded/3/10; // Humans have +10% training costs.

	// Leveling cost temporarily suspended. -TC
	if(player->getLevel() <= 3)  // Free for levels 1-3 to train.
		goldneeded = 0;

	if(expneeded > player->getExperience()) {
		player->print("You need %s more experience.\n", player->expToLevel(false).c_str());
		return(0);
	}


	if((goldneeded > (player->coins[GOLD] + player->bank[GOLD])) && !player->flagIsSet(P_FREE_TRAIN)) {
		player->print("You don't have enough gold.\n");
		player->print("You need %ld gold to train.\n", goldneeded);
		return(0);
	}

	if(player->getRoom()->whatTraining() != player->getClass()) {
		player->print("This is not your training location.\n");
		return(0);
	}


	if(player->getLevel() >= MAXALVL) {
		player->print("You couldn't possibly train any more!\n");
		return(0);
	}

	// Prevent power leveling!
	if(player->flagIsSet(P_JUST_TRAINED)) {
		player->print("You just trained! You must leave the room and re-enter.\n");
		return(0);
	}



	if(cmnd->num >= 2 && !strcmp(cmnd->str[1], "dispel")) {
		player->print("Dispelling stat-modifying magic and abilities.\nPlease wait one moment.\n");
		player->removeEffect("strength");
		player->removeEffect("haste");
		player->removeEffect("fortitude");
		player->removeEffect("insight");
		player->removeEffect("prayer");

		player->loseRage();
		player->loseFrenzy();
		player->losePray();

		return(0);
	}

	if(	player->underStatSpell() ||
		player->flagIsSet(P_BERSERKED) ||
		player->flagIsSet(P_FRENZY) ||
		player->flagIsSet(P_PRAYED)
	) {
		player->print("You cannot train while under the effects of stat-modifying magic or abilities.\n");
		player->print("Type \"train dispel\" to dispel this magic.\n");
		return(0);
	}



	if(!player->flagIsSet(P_FREE_TRAIN)) {
		if(goldneeded > player->coins[GOLD]) {
			bankneeded = goldneeded - player->coins[GOLD];
			player->coins.set(0, GOLD);
			player->bank.sub(bankneeded, GOLD);
			player->print("You use %ld gold coins from your bank account.\n", bankneeded);
			banklog(player->name, "WITHDRAW (train) %ld [Balance: %ld]\n",
				bankneeded, player->bank[GOLD]);
		} else
			player->coins.sub(goldneeded, GOLD);
	}

	doTrain(player);
	return(0);
}

//*********************************************************************
//						timestr
//*********************************************************************

char *timestr(long t) {
	static char buf[BUFSIZ];
	if(t >= 60) {
		sprintf(buf, "%01ld:%02ld", t / 60, t % 60);
	} else {
		sprintf(buf, "%ld seconds", t);
	}
	return(buf);
}

#define TIMELEFT(name,ltflag) \
	tmp = MAX(0,(LT(player, (ltflag)) - t)); \
	player->print("Time left on current %s: %s.\n", name, timestr(tmp));
#define TIMEUNTIL(name,ltflag,time) \
	u = (time) - t + player->lasttime[(ltflag)].ltime;\
	if(u < 0) u = 0;\
	player->print("Time left before next %s: %s.\n", name, timestr(u));

//*********************************************************************
//						showAbility
//*********************************************************************

void showAbility(Player* player, const char *skill, const char *display, int lt, int tu, int flag = -1) {
	long	t = time(0), u=0, tmp=0;

	if(player->knowsSkill(skill)) {
		if(flag != -1 && player->flagIsSet(flag)) {
			TIMELEFT(display, lt);
		} else {
			TIMEUNTIL(display, lt, tu);
		}
	}
}


//*********************************************************************
//						cmdTime
//*********************************************************************
// This function outputs the time of day (realtime) to the player.

int cmdTime(Player* player, cmd* cmnd) {
	long	t = time(0), u=0, tmp=0, i=0;
	ctag	*cp=0;
	const CatRefInfo* cri = gConfig->getCatRefInfo(player->getRoom(), 1);
	bstring world = "";

	if(cri && cri->worldName != "") {
		world += " of ";
		world += cri->worldName;
	}

	player->clearFlag(P_AFK);

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

	player->printColor("^cThe current time in the world%s:\n", world.c_str());
	gConfig->getCalendar()->printtime(player);

	if(gConfig->getCalendar()->isBirthday(player))
		player->printColor("^yToday is your birthday!\n");

	if(player->flagIsSet(P_PTESTER))
		player->print("Tick Interval: %d.\n", player->lasttime[LT_TICK].interval);

	if(cmnd->num < 2) {

		// for ceris/aramon when they resurrect dead people
		tmp = MAX(0,(LT(player, LT_NOMPTICK) - t));
		if(tmp > 0)
			player->print("Time until vitality is restored: %s.\n", timestr(tmp));


		// CT+ doesn't need to see all this info
		if(!player->isCt()) {
			if(player->getClass() == BARD && player->getLevel() >= 4) {
				TIMEUNTIL("bard song", LT_SING, player->lasttime[LT_SING].interval);
			}
			showAbility(player, "barkskin", "barkskin", LT_BARKSKIN, 600);
			showAbility(player, "berserk", "berserk", LT_BERSERK, 600, P_BERSERKED);
			showAbility(player, "bite", "bite", LT_PLAYER_BITE, player->lasttime[LT_PLAYER_BITE].interval);
			showAbility(player, "bloodsac", "blood sacrifice", LT_BLOOD_SACRIFICE, 600, P_BLOODSAC);
			showAbility(player, "commune", "commune", LT_PRAY, 45);
			showAbility(player, "charm", "charm", LT_HYPNOTIZE, player->lasttime[LT_HYPNOTIZE].interval);
			showAbility(player, "creeping-doom", "creeping-doom", LT_SMOTHER, player->lasttime[LT_SMOTHER].interval);
			showAbility(player, "disarm", "disarm", LT_DISARM, player->lasttime[LT_DISARM].interval);
			showAbility(player, "drain", "drain life", LT_DRAIN_LIFE, player->lasttime[LT_DRAIN_LIFE].interval);
			showAbility(player, "smother", "earth-smother", LT_SMOTHER, player->lasttime[LT_SMOTHER].interval);
			showAbility(player, "enthrall", "enthrall", LT_HYPNOTIZE, player->lasttime[LT_HYPNOTIZE].interval);
			showAbility(player, "focus", "focus", LT_FOCUS, 600, P_FOCUSED);
			showAbility(player, "frenzy", "frenzy", LT_FRENZY, 600, P_FRENZY);
			showAbility(player, "harm", "harm touch", LT_LAY_HANDS, player->lasttime[LT_LAY_HANDS].interval);
			showAbility(player, "holyword", "holyword", LT_SMOTHER, player->lasttime[LT_SMOTHER].interval);
			showAbility(player, "howl", "howl", LT_HOWLS, 240);
			showAbility(player, "hypnotize", "hypnotize", LT_HYPNOTIZE, player->lasttime[LT_HYPNOTIZE].interval);
			if(	player->knowsSkill("identify") ||
				player->canBuildObjects()
			) {
				TIMEUNTIL("identify", LT_IDENTIFY, player->lasttime[LT_IDENTIFY].interval);
			}
			showAbility(player, "hands", "lay on hands", LT_LAY_HANDS, player->lasttime[LT_LAY_HANDS].interval);
			showAbility(player, "maul", "maul", LT_KICK, player->lasttime[LT_KICK].interval);
			showAbility(player, "meditate", "meditate", LT_MEDITATE, 90);
			showAbility(player, "mistbane", "mistbane", LT_FOCUS, 600, P_MISTBANE);
			showAbility(player, "poison", "poison", LT_DRAIN_LIFE, player->lasttime[LT_DRAIN_LIFE].interval);
			showAbility(player, "pray", "pray", LT_PRAY, 600, P_PRAYED);
			showAbility(player, "regenerate", "regenerate", LT_REGENERATE, player->lasttime[LT_REGENERATE].interval);
			showAbility(player, "renounce", "renounce", LT_RENOUNCE, player->lasttime[LT_RENOUNCE].interval);
			showAbility(player, "touch", "touch of death", LT_TOUCH_OF_DEATH, player->lasttime[LT_TOUCH_OF_DEATH].interval);
			showAbility(player, "turn", "turn undead", LT_TURN, player->lasttime[LT_TURN].interval);
			showAbility(player, "scout", "scout", LT_SCOUT, player->lasttime[LT_SCOUT].interval);
		}

		if(player->flagIsSet(P_OUTLAW)) {
			i = LT(player, LT_OUTLAW);
			player->print("Outlaw time remaining: ");
			if(i - t > 3600)
				player->print("%02d:%02d:%02d more hours.\n",(i - t) / 3600L, ((i - t) % 3600L) / 60L, (i - t) % 60L);
			else if((i - t > 60) && (i - t < 3600))
				player->print("%d:%02d more minutes.\n", (i - t) / 60L, (i - t) % 60L);
			else
				player->print("%d more seconds.\n", MAX((i - t),0));

		}

		if(player->flagIsSet(P_JAILED)) {
			if(	player->parent_rom &&
				player->parent_rom->flagIsSet(R_MOB_JAIL)
			)
				i = LT(player, LT_MOB_JAILED);
			else
				i = LT(player, LT_JAILED);

			player->print("NOTE: 0 Seconds means you're out any second.\n");
			player->print("Jailtime remaining(Approx): ");

			if(i - t > 3600)
				player->print("%02d:%02d:%02d more hours.\n",(i - t) / 3600L, ((i - t) % 3600L) / 60L, (i - t) % 60L);
			else if((i - t > 60) && (i - t < 3600))
				player->print("%d:%02d more minutes.\n", (i - t) / 60L, (i - t) % 60L);
			else
				player->print("%d more seconds.\n", MAX((i - t),0));
		}

		// All those confusing defines, i'll make up my own code
		i = 0;
		cp = player->first_fol;
		while(cp) {
			if(cp->crt->isMonster() && cp->crt->isPet()) {
				i = 1;
				if(cp->crt->isUndead())
					player->print("Time left before creature following you leaves/fades: %s\n",
						timestr(cp->crt->lasttime[LT_ANIMATE].ltime+cp->crt->lasttime[LT_ANIMATE].interval-t));
				else
					player->print("Time left before creature following you leaves/fades: %s\n",
						timestr(cp->crt->lasttime[LT_INVOKE].ltime+cp->crt->lasttime[LT_INVOKE].interval-t));

				//TIMEUNTIL("hire",LT_INVOKE,player->lasttime[LT_INVOKE].interval);
			}
			cp = cp->next_tag;
		}

		// only show how long until next if they don't have a pet already
		if(!i) {
			if(	player->getClass() == DRUID ||
				(player->getClass() == CLERIC && player->getDeity() == GRADIUS)
			) {
				TIMEUNTIL("conjure", LT_INVOKE, player->lasttime[LT_INVOKE].interval);
			}
			if(	player->getClass() == CLERIC &&
				player->getDeity() == ARAMON &&
				player->getAdjustedAlignment() <= REDDISH
			) {
				TIMEUNTIL("animate dead", LT_ANIMATE, player->lasttime[LT_ANIMATE].interval);
			}
		}
	}
	return(0);
}


//*********************************************************************
//						cmdSave
//*********************************************************************
// This is the function that is called when a players types save
// It calls save() (the real save function) and then tells the user
// the player was saved.  See save() for more details.

int cmdSave(Player* player, cmd* cmnd) {
	ASSERTLOG( player );

	player->clearFlag(P_AFK);

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

	player->save(true);
	// Save storage rooms
	if(player->parent_rom && player->parent_rom->info.isArea("stor"))
		gConfig->resaveRoom(player->parent_rom->info);

	player->print("Player saved.\n");
	return(0);
}