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/
/*
 * command1.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 "factions.h"
#include "calendar.h"
#include <sys/stat.h>
#include <sstream>
#include <iomanip>
#include <locale>

//static int	newline;
//static int	color;

//#include <arpa/telnet.h>
//#define TELOPT_COMPRESS2       86


//*********************************************************************
//						cmdNoExist
//*********************************************************************
// This function tells the player the command does not exist.

int cmdNoExist(Player* player, cmd* cmnd) {
	player->print("The command \"%s\" does not exist.\n", cmnd->str[0]);
	return(0);
}

//*********************************************************************
//						cmdNoAuth
//*********************************************************************
// This function tells the player they're not allowed to use that command

int cmdNoAuth(Player* player) {
	player->print("You do not have the proper authorization to use that command.\n");
	return(0);
}

//*********************************************************************
//						getFailFd
//*********************************************************************
// This function grabs the fd to print error messages to.
// A check of "fd != ffd" means messages are going to different places,
// hence fd is a pet and ffd is a player. "fd == ffd" means a
// player is running the command.

int getFailFd(Creature *user) {
	if(!user)
		return(-1);
	return(user->isPet() ? user->getMaster()->fd : user->fd);
}


//*********************************************************************
//						command
//*********************************************************************
// This function handles the main prompt commands, and calls the
// appropriate function, depending on what service is requested by the
// player.

void command(Socket* sock, bstring str) {
	cmd cmnd;
	int	n;
	Player* ply = sock->getPlayer();

	ASSERTLOG( ply );

	/*
	this logn command will print out all the commands entered by players.
	It should be used in extreme cases when trying to isolate a players
	input which may be causing a crash.
	*/
	//zero(&cmnd, sizeof(cmd));


	if(ply->getClass() == CARETAKER && !dmIson() )
		log_immort(false, ply, "%s-%d (%s): %s\n", ply->getCName(), sock->getFd(),
			ply->getRoomParent()->fullName().c_str(), str.c_str());

	if(str == "!")
		str = ply->getLastCommand();

	if(str[0])
		ply->setLastCommand(str);


	cmnd.fullstr = str;

	stripBadChars(str); // removes '.' and '/'
	lowercize(str, 0);
	parse(str, &cmnd);


	n = 0;
	if(cmnd.num)
		n = cmdProcess(ply, &cmnd);
	else
		n = PROMPT;

	if(n == DISCONNECT) {
		sock->write("Goodbye!\n\r\n\r");
		sock->disconnect();
	} else if(n == PROMPT) {
		ply->sendPrompt();
	}

}

//#ifdef NEW_PARSER

//*********************************************************************
//						parse
//*********************************************************************
// This function takes the string in the first parameter and breaks it
// up into its component words, stripping out useless words. The
// resulting words are stored in a command structure pointed to by the
// second argument.

void parse(const bstring str, cmd *cmnd) {
	int		i=0, j=0, l=0, n=0;
	//char	token[MAX_TOKEN_SIZE];
	bstring token;
	int		isquote=0;

	j = str.length();

	for(i=0; i<=j; i++) {

		// look for first non space or comment
		if(str[i] == ' ')
			continue;

		// ok we at first non space
		if(str[i] == '\"') {
			isquote = 1;
			// skip quote char
			i++;
		}
		// save this position as the begining of a token
		l = i;

		// now find the end of the token
		if(isquote) {
			while(str[i] != '\0' && str[i] != '\"')
				i++;

//			// terminate the token
//			if(str[i] == '\"')
//				str[i] = '\0';
		} else {
			while(str[i] != '\0' && str[i] != ' ')
				i++;

			// terminate the token
//			str[i] = '\0';
		}

		// don't overflow the buffers
		token = str.substr(l, i-l);

		/* was there any thing here? */
		if(token.empty()) {
			isquote = 0;
			continue;
		}

		if(isquote) {
			strncpy(cmnd->str[n], token.c_str(), MAX_TOKEN_SIZE);
			cmnd->str[n][MAX_TOKEN_SIZE - 1] = '\0';
			cmnd->val[n] = 1L;
			isquote = 0;
		} else {
			// Copy into command structure
			if(n == 0) {
				strncpy(cmnd->str[n], token.c_str(), MAX_TOKEN_SIZE);
				cmnd->str[n][MAX_TOKEN_SIZE - 1] = '\0';
				// set the value to 1 in case there is non following
				cmnd->val[n] = 1L;
				n++;
			}
			else if(isdigit((int)token[0]) || (token[0] == '-' &&
					isdigit((int)token[1]))) {
				// this is a value for the previous command
				cmnd->val[MAX(0, n - 1)] = atol(token.c_str());
			} else {
				strncpy(cmnd->str[n], token.c_str(), MAX_TOKEN_SIZE);
				cmnd->str[n][MAX_TOKEN_SIZE - 1] = '\0';
				// set the value to 1 in case there is non following
				cmnd->val[n] = 1L;
				n++;
			}
		}

		if(n >= COMMANDMAX)
			break;
	}

	// set the number of tokens in the command struct
	cmnd->num = n;
}

enum HandleObject {
	Push,
	Pull,
	Press,
};

//*********************************************************************
//						handleObject
//*********************************************************************

void handleObject(Player* player, cmd* cmnd, HandleObject type) {
	Object* object=0;
	long t = time(0);
	MudObject* target=0;

	bstring action;
	bstring action2;
	switch(type) {
		case Push:
			action = "push";
			action2 = "pushes";
			break;
		case Pull:
			action = "pull";
			action2 = "pulls";
			break;
		case Press:
			action = "press";
			action2 = "presses";
			break;
		default:
			return;
	}

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

	if(cmnd->num > 1) {
		target = player->findTarget(FIND_OBJ_INVENTORY | FIND_OBJ_EQUIPMENT | FIND_OBJ_ROOM,
			player->displayFlags(), cmnd->str[1], cmnd->val[1]);

		if(target != NULL)
			object = dynamic_cast<Object*>(target);
	}

	if(!object) {
		player->print("You don't see that here.\n");
		return;
	}

	// If it's press or push, and the object is a combo lock, do that instead
	if((type == Push || type == Press) && object->getSpecial() == SP_COMBO) {
		object->doSpecial(player);
		return;
	}

	// press can only be used for combo locks.
	if((type == Push || type == Pull) && object->flagIsSet(O_PUSH_PULL_SPRINGS_TRAP)) {

		if(player->lasttime[LT_PLAYER_STUNNED].ltime+(player->lasttime[LT_PLAYER_STUNNED].interval) > t) {
			player->pleaseWait((player->lasttime[LT_PLAYER_STUNNED].interval + 1) - t + player->lasttime[LT_PLAYER_STUNNED].ltime - 1);
			return;
		}

		player->printColor("You %s %P^x.\n", action.c_str(), object);
		broadcast(player->getSock(), player->getParent(), "%M %s %P^x.", player, action2.c_str(), object);

		if(player->checkTraps(player->getUniqueRoomParent(), true, false))
			player->stun(3);
		return;
	}

	// other actions failed
	player->print("You can't %s that.\n", action.c_str());
}

//*********************************************************************
//						cmdPush
//*********************************************************************

int cmdPush(Player* player, cmd* cmnd) {
	handleObject(player, cmnd, Push);
	return(0);
}

//*********************************************************************
//						cmdPull
//*********************************************************************

int cmdPull(Player* player, cmd* cmnd) {
	handleObject(player, cmnd, Pull);
	return(0);
}

//*********************************************************************
//						cmdPress
//*********************************************************************

int cmdPress(Player* player, cmd* cmnd) {
	handleObject(player, cmnd, Press);
	return(0);
}


//*********************************************************************
//						doFinger
//*********************************************************************
// sending 0 to cls means we're not a player and we want reduced padding

bstring doFinger(const Player* player, bstring name, unsigned short cls) {
	struct stat f_stat;
	char	tmp[80];
	Player*	target=0;
	std::ostringstream oStr;
	bool online=true;

	// set left aligned
	oStr.setf(std::ios::left, std::ios::adjustfield);
	oStr.imbue(std::locale(""));

	if(name == "")
		return("Finger who?\n");

	name = name.toLower();
	name.setAt(0, up(name.getAt(0)));
	target = gServer->findPlayer(name);

	if(!target) {
		if(!loadPlayer(name.c_str(), &target))
			return("Player does not exist.\n");

		online = false;
	}

	if(target->isStaff() && cls < target->getClass()) {
		if(!online)
			free_crt(target);
		return("You are currently unable to finger that player.\n");
	}

	// cls=0 means we don't want padding
	if(cls) {
		oStr << std::setw(25) << target->getName() << " "
			 << std::setw(15) << gConfig->getRace(target->getDisplayRace())->getName();
		// will they see through the illusion?
		if(player && player->willIgnoreIllusion() && target->getDisplayRace() != target->getRace())
			oStr << " (" << gConfig->getRace(target->getRace())->getName() << ")";
		oStr << " "
			 << target->getTitle() << "\n";
	} else {
		oStr << target->getName() << " the "
			 << gConfig->getRace(target->getDisplayRace())->getAdjective();
		// will they see through the illusion?
		if(player && player->willIgnoreIllusion() && target->getDisplayRace() != target->getRace())
			oStr << " (" << gConfig->getRace(target->getRace())->getAdjective() << ")";
		oStr << " "
			 << target->getTitle() << "\n";
	}

	sprintf(tmp, "%s/%s.txt", Path::Post, name.c_str());
	if(stat(tmp, &f_stat))
		oStr << "No mail.\n";
	else if(f_stat.st_atime > f_stat.st_mtime)
		oStr << "No unread mail since: " << ctime(&f_stat.st_atime);
	else
		oStr << "New mail since: " << ctime(&f_stat.st_mtime);

	if(target->getForum() != "")
		oStr << "Forum account: " << target->getForum() << "\n";

	if(online) {
		oStr << "Currently logged on.\n";
	} else {
		long t = target->getLastLogin();
		free_crt(target, false);
		oStr << "Last login: " << ctime(&t);
	}

	return(oStr.str());
}

//*********************************************************************
//						cmdFinger
//*********************************************************************

int cmdFinger(Player* player, cmd* cmnd) {
	player->clearFlag(P_AFK);

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

	player->print("%s", doFinger(player, cmnd->str[1], player->getClass()).c_str());
	return(0);
}


//*********************************************************************
//						cmdPayToll
//*********************************************************************
// This function will allow players to pay to get through exits. Two
// flags are used for it: X_TOLL_TO_PASS, and X_LEVEL_BASED_TOLL. X_TOLL_TO_PASS sets the exit as
// a toll booth. A toll field will be required in the Exit structure
// defined as a long. X_LEVEL_BASED_TOLL will make the toll vary depending on the
// players' level, making the cost equal exit->toll*player->getLevel().

int cmdPayToll(Player* player, cmd* cmnd) {
	Monster* target=0;
	BaseRoom	*newRoom=0;
	Exit	*exit=0;
	unsigned long tc=0, amt=0;

	if(cmnd->num < 4) {
		player->print("Pay what to whom to get in where?\n");
		player->print("Syntax: pay $(amount) (target) (exit name)\n");
		return(0);
	}

	if(cmnd->num < 3) {
		player->print("Pay to get in where?\n");
		player->print("Syntax: pay $(amount) (target) (exit name)\n");
		return(0);
	}


	if(cmnd->str[1][0] != '$') {
		player->print("Syntax: pay $(amount) (target) (exit name)\n");
		return(0);
	}

	amt = atol(&cmnd->str[1][1]);
	if(!amt || amt < 1) {
		player->print("Please enter the amount to pay the tollkeeper.\n");
		return(0);
	}

	amt = MIN(amt, 30000);

	target = player->getParent()->findMonster(player, cmnd, 2);
	if(!target) {
		player->print("That creature is not here.\n");
		return(0);
	}

	if(!Faction::willDoBusinessWith(player, target->getPrimeFaction())) {
		player->print("%M refuses to do business with you.\n", target);
		return(0);
	}

	if(!target->flagIsSet(M_TOLLKEEPER)) {
		player->print("%M doesn't take toll payments.\n", target);
		return(0);
	}

	exit = findExit(player, cmnd, 3);
	if(!exit) {
		player->print("That exit is not here.\n");
		return(0);
	}
	if(!exit->flagIsSet(X_TOLL_TO_PASS)) {
		player->print("That is not a tolled exit.\n");
		return(0);
	}


	tc = tollcost(player, exit, target);


	if(player->coins[GOLD] < amt) {
		player->print("You do not have that much gold on you.\n");
		return(0);
	}

	if(amt < tc) {
		player->printColor("You must pay at least %ld gold coins to pass through the %s^x.\n", tc, exit->getCName());
		return(0);
	}

	if(amt > tc) {
		player->printColor("That is too much. The cost to pass through %s^x is %ld gold coins.\n", exit->getCName(), tc);
		return(0);
	}

	newRoom = exit->target.loadRoom(player);
	if(!newRoom) {
		player->printColor("The %s^x appears to be jammed shut.\nPlease try again later.\n", exit->getCName());
		return(0);
	}

	UniqueRoom* uRoom = newRoom->getAsUniqueRoom();
	if(uRoom && !player->canEnter(uRoom, true))
		return(0);

	player->coins.sub(amt, GOLD);
	gServer->logGold(GOLD_OUT, player, Money(amt, GOLD), exit, "Toll");

	player->printColor("%M accepts your toll and ushers you through the %s^x.\n", target, exit->getCName());
	broadcast(player->getSock(), player->getParent(), "%M pays %N some coins and goes through the %s^x.", player, target, exit->getCName());

	player->deleteFromRoom();
	player->addToRoom(newRoom);
	player->doPetFollow();
	return(0);
}

//*********************************************************************
//						tollcost
//*********************************************************************

unsigned long tollcost(const Player* player, const Exit* exit, Monster* keeper) {
	unsigned long cost = exit->getToll() ? exit->getToll() : DEFAULT_TOLL;
	if(!player)
		return(cost);
	if(exit->flagIsSet(X_LEVEL_BASED_TOLL))
		cost = cost * player->getLevel() * 2;

	if(!keeper)
		keeper = player->getConstRoomParent()->getTollkeeper();
	if(keeper) {
		Money money;
		money.set(cost, GOLD);
		money = Faction::adjustPrice(player, keeper->getPrimeFaction(), money, true);
		cost = money[GOLD];
	}
	return(cost);
}

//*********************************************************************
//						infoGamestat
//*********************************************************************

int infoGamestat(Player* player, cmd* cmnd) {
	int		players=0, immorts=0;
	int		daytime=0;
	long	t=0, days=0, hours=0, minutes=0;
	char	*str;

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

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

		if(target->isStaff())
			immorts++;
		if(!target->isStaff())
			players++;
	}

	player->print("\nCurrent Realms Gamestat Information\n\n");

	t = time(0);
	daytime = gConfig->currentHour();

	days = (t - StartTime) / (60*60*24);
	hours = (t - StartTime) / (60*60);
	hours %= 24;
	minutes = (t - StartTime) / 60L;
	minutes %= 60;

	player->printColor("^cGame-Time: %d:%02d %s.  %s.\n",
		gConfig->currentHour(true),
		gConfig->currentMinutes(),
		daytime > 11 ? "PM" : "AM",
		isDay() ? "It is day" : "It is night"
	);

	if(player->isCt())
		player->printColor("^gThe mud has been running for %d game days.\n", gConfig->calendar->getTotalDays());

	str = ctime(&t);
	str[strlen(str) - 1] = 0;

	player->printColor("^MReal-Time: %s (%s).\n", str, gServer->getTimeZone().c_str());

	if(!days)
		player->printColor("^RRealms Uptime: %02ld:%02ld:%02ld\n", hours, minutes, (t - StartTime) % 60L);
	else if(days == 1)
		player->printColor("^RRealms Uptime: %ld day %02ld:%02ld:%02ld\n", days, hours, minutes, (t - StartTime) % 60L);
	else
		player->printColor("^RRealms Uptime: %ld days %02ld:%02ld:%02ld\n", days, hours, minutes, (t - StartTime) % 60L);

	player->printColor("^yTotal players currently online: %d\n", players);
	if(player->isDm())
		player->printColor("^yTotal staffs online: %d\n", immorts);

	player->print("\n");

	return(0);
}

//*********************************************************************
//						cmdDescription
//*********************************************************************
// this allows a player to set his/her description that is seen when you
// look at them.

int cmdDescription(Player* player, cmd* cmnd) {
	player->clearFlag(P_AFK);

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

	if(cmnd->num < 2) {
		player->print("Syntax: description [text|-d]\n.");
		return(0);
	}
	if(!strcmp(cmnd->str[1], "-d")) {
		player->print("Description cleared.\n");
		player->setDescription("");
		return(0);
	}

	player->setDescription(getFullstrText(cmnd->fullstr, 1));
	player->escapeText();
	player->print("Description set.\n");
	return(0);
}