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/
/*
 * 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-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 "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 = PUREBLOOD;
				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";

	Player* target=0;
	for(std::pair<bstring, Player*> 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->isEffected("mist") && !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->getProxyName() != "") {
		player->print("You are unable to suicide a proxied character.\n");
		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->getParent(),"%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->getCName(), player->himHer());
	//else
	//	broadcast(isWatcher, "^C### %s committed suicide! We'll miss %s dearly.", player->getCName(), player->himHer());

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

	deletePlayer(player);
	updateRecentActivity();
	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->getName();

 	gConfig->deleteUniques(player);
 	gServer->clearAsEnemy(player);

	// 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,true);
	//gServer->clearPlayer(name);
	sock->setPlayer(NULL);

	// get rid of any files the player was using
	sprintf(file, "%s/%s.xml", Path::Player, name.c_str());
	unlink(file);

	sprintf(file, "%s/%s.txt", Path::Bank, name.c_str());
	unlink(file);
	sprintf(file, "%s/%s.txt", Path::Post, name.c_str());
	unlink(file);
	sprintf(file, "%s/%s.txt", Path::History, name.c_str());
	unlink(file);

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

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


//*********************************************************************
//						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->print("Disabled\n");
    return(0);
	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.", getCName());
		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, bstring str) {
	sock->getPlayer()->changingStats(str);
}
void Player::changingStats(bstring str) {
	int		a, n, i, k, l, sum=0;
	int		vnum[5];
	vstat	nstat, sendStat;

	switch(getSock()->getState()) {
	case CON_CHANGING_STATS:
		n = str.length();
		l = 0;
		k = 0;
		for(i=0; i<=n; i++) {
			if(str[i]==' ' || str[i]==0) {
				str[i] = 0;
				//bstring tmp = str.substr(l);
				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.", getCName(), 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.", getCName());
			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;
	}
}



//*********************************************************************
//						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;
	const CatRefInfo* cri = gConfig->getCatRefInfo(player->getRoomParent(), 1);
	bstring world = "";

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

	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);
			showAbility(player, "bite", "bite", LT_PLAYER_BITE, player->lasttime[LT_PLAYER_BITE].interval);
			showAbility(player, "bloodsac", "blood sacrifice", LT_BLOOD_SACRIFICE, 600);
			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);
			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);
			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->inUniqueRoom() &&
				player->getUniqueRoomParent()->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;
		for(Monster* pet : player->pets) {
			if(pet->isMonster() && pet->isPet()) {
				i = 1;
				if(pet->isUndead())
					player->print("Time left before creature following you leaves/fades: %s\n",
						timestr(pet->lasttime[LT_ANIMATE].ltime+pet->lasttime[LT_ANIMATE].interval-t));
				else
					player->print("Time left before creature following you leaves/fades: %s\n",
						timestr(pet->lasttime[LT_INVOKE].ltime+pet->lasttime[LT_INVOKE].interval-t));

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

		// 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->inUniqueRoom() && player->getUniqueRoomParent()->info.isArea("stor"))
		gConfig->resaveRoom(player->getUniqueRoomParent()->info);

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