SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |   (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 * ------------------------------------------------------------------------ *
 *			 Command interpretation module								    *
 ****************************************************************************/

#include	"stdafx.h"
#include	"smaug.h"
#include	"social.h"
#include	"commands.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"Exits.h"
#include	"SmaugWizDoc.h"
#include	"descriptor.h"
#include	"character.h"
#include	"smaugx.h"

// Externals
void	refresh_page (CCharacter *ch);
void	subtract_times (struct timeval *etime, struct timeval *stime);
void	DoLogging (CCharacter& Ch, const char* logline, CCmdType& Cmd,
			BOOL bFound);

// Log-all switch.
BOOL	fLogAll = FALSE;


// Character not in position for command?
BOOL check_pos (CCharacter *ch, short position)
{
	if (ch->IsNpc () && ch->GetPosition () > POS_STUNNED)
		return TRUE;						// Band-aid alert?  -- Blod

	if (ch->GetPosition () < position) {
		switch (ch->GetPosition ()) {
		  case POS_DEAD:
			ch->SendText ("A little difficult to do when you are DEAD...\n\r");
			break;

		  case POS_MORTAL:
		  case POS_INCAP:
			ch->SendText ("You are hurt far too bad for that.\n\r");
			break;

		  case POS_STUNNED:
			ch->SendText ("You are too stunned to do that.\n\r");
			break;

		  case POS_SLEEPING:
			ch->SendText ("In your dreams, or what?\n\r");
			break;

		  case POS_RESTING:
			ch->SendText ("Nah... You feel too relaxed...\n\r");
			break;

		  case POS_SITTING:
			ch->SendText ("You can't do that sitting down.\n\r");
			break;

		  case POS_FIGHTING:
		  case POS_DEFENSIVE:
		  case POS_AGGRESSIVE:
		  case POS_BERSERK:
			if (position <= POS_EVASIVE)
				ch->SendText ("This fighting style is too demanding for that!\n\r");
			else
				ch->SendText ("No way!  You are still fighting!\n\r");
			break;

		  case POS_EVASIVE:
			ch->SendText ("No way!  You are still fighting!\n\r");
			break;
		}
		return FALSE;
	}
	return TRUE;
}


extern char lastplayercmd [MAX_INPUT_LENGTH*2];


// The main entry point for executing commands.
// Can be recursively called from 'at', 'order', 'force'.
void interpret (CCharacter *ch, char *argument)
{
	char		command [MAX_INPUT_LENGTH];
	char		logline [MAX_INPUT_LENGTH];
	char		logname [MAX_INPUT_LENGTH];
	CTimerData	*timer = NULL;
	CCmdType	*cmd = NULL;
	int			trust;
	BOOL		found;
	struct timeval time_used;
	long		tmptime;

	ASSERT (ch);

	try {
		found = FALSE;
		logline [0] = 0;
		if (ch->GetSubstate () == SUB_REPEATCMD) {
			DO_FUN *fun;

			if ((fun = ch->last_cmd) == NULL) {
				ch->SetSubstate (SUB_NONE);
				bug ("interpret: SUB_REPEATCMD with NULL last_cmd", 0);
				return;
			} else {
				int x;

				// yes... we lose out on the hashing speediness here...
				// but the only REPEATCMDS are wizcommands (currently)
				for (x = 0; x < MAX_COMMANDS; ++x) {
					for (cmd = CommandTable.GetCommand (x); cmd;
					  cmd = cmd->GetNext ())
						if (cmd->do_fun == fun) {
							found = TRUE;
							break;
						}
					if (found)
						break;
				}
				if (!found) {
					cmd = NULL;
					bug ("interpret: SUB_REPEATCMD: last_cmd invalid");
					return;
				}
				sprintf (logline, "(%s) %s", cmd->GetName (), argument);
			}
		}

		if (! cmd) {
			if (! argument || *argument == 0) {
				bug ("interpret: null argument!");
				return;
			}

			// Strip leading spaces.
			while (isspace (*argument))
				argument++;
			if (argument [0] == 0)
				return;

			timer = get_timerptr (ch, TIMER_DO_FUN);

			// REMOVE_BIT (ch->affected_by, AFF_HIDE);

			// Implement freeze command.
			if (! ch->IsNpc () && ch->IsFrozen ()) {
				ch->SendText ("You're totally frozen!\n\r");
				return;
			}

			// Grab the command word.
			// Special parsing so ' can be a command,
			// also no spaces needed after punctuation.
			strcpy (logline, argument);
			if (! isalpha (argument [0]) && ! isdigit (argument [0])) {
				command [0] = argument [0];
				command [1] = '\0';
				argument++;
				while (isspace (*argument))
					++argument;
			}
			else argument = one_argument (argument, command);

			// Look for command in command table.
			// Check for council powers and/or bestowments
			trust = ch->GetTrustLevel ();
			int		hash = CommandTable.GetHash (command [0]);
			for (cmd = CommandTable.GetCommand (hash); cmd; cmd = cmd->GetNext ())
				if (! str_prefix (command, cmd->GetName ())
				  && (cmd->level <= trust
				  || (!ch->IsNpc () && ch->GetPcData ()->council
				  && is_name (cmd->GetName (), ch->GetPcData ()->council->powers)
				  && cmd->level <= (trust+MAX_CPD))
				  || (!ch->IsNpc () && ch->GetPcData ()->HasBestowments ()
				  && is_name (cmd->GetName (), ch->GetPcData ()->GetBestowments ())
				  && cmd->level <= (trust+5)))) {
					found = TRUE;
					break;
				}

			// Turn off afk bit when any command performed.
			if (ch->IsAction (PLR_AFK) && (str_cmp (command, "AFK"))) {
				ch->ClrActBit (PLR_AFK);
				act (AT_GREY, "$n is no longer afk.", ch, NULL, NULL, TO_ROOM);
			}
		}
		sprintf (lastplayercmd, "** %s: %s", ch->GetName (), logline);


		// Log and snoop.
		DoLogging (*ch, logline, *cmd, found);

		if (ch->GetDesc () && ch->GetDesc ()->m_pSnoopBy) {
			sprintf (logname, "%s", ch->GetName ());
			ch->GetDesc ()->m_pSnoopBy->WriteToBuffer (logname);
			ch->GetDesc ()->m_pSnoopBy->WriteToBuffer ("% ", 2);
			ch->GetDesc ()->m_pSnoopBy->WriteToBuffer (logline);
			ch->GetDesc ()->m_pSnoopBy->WriteToBuffer ("\n\r", 2);
		}

		// Handle the build interface stuff
		if (ch->IsMenuActive ())
			if (ch->EditMenu (argument, command))
				return;

		if (timer) {
			int		tempsub;

			tempsub = ch->GetSubstate ();
			ch->SetSubstate (SUB_TIMER_DO_ABORT);

			timer->DoFun (ch, "");

			if (char_died (ch))
				return;

			if (ch->GetSubstate () != SUB_TIMER_CANT_ABORT) {
				ch->SetSubstate (tempsub);
				extract_timer (ch, timer);
			} else {
				ch->SetSubstate (tempsub);
				return;
			}
		}

		// Look for command in skill and socials table.
		if (!found) {
			if (!check_skill (ch, command, argument)
			  && !check_social (ch, command, argument)) {
				CExitData	*pexit;

				// check for an automatic exit command
				if ((pexit = find_door (ch, command, TRUE)) != NULL
				  && pexit->CanAuto ()) {
					if (pexit->IsClosed ()
					  && (! ch->CanPass ()
					  || pexit->IsNoPass ())) {
						if (! pexit->IsSecret ())
						act (AT_PLAIN, "The $d is closed.", ch, NULL,
							pexit->keyword, TO_CHAR);
						else
							ch->SendText ("You cannot do that here.\n\r");
						return;
					}
					move_char (ch, pexit, 0);
					return;
				}
				ch->SendText ("Huh?\n\r");
			}
			return;
		}

		// Character not in position for command?
		if (!check_pos (ch, cmd->position))
			return;

		// Berserk check for flee.. maybe add drunk to this?.. but too much
		// hardcoding is annoying.. -- Altrag
		if (!str_cmp (cmd->GetName (), "flee") && ch->IsBeserk ()) {
			ch->SendText ("You aren't thinking very clearly..\n\r");
			return;
		}

		// Dispatch the command.
		ch->prev_cmd = ch->last_cmd;    // haus, for automapping
		ch->last_cmd = cmd->do_fun;
		start_timer (&time_used);

		(*cmd->do_fun) (ch, argument);

		end_timer (&time_used);

		// Update the record of how many times this command has been used (haus)
		update_userec (&time_used, &cmd->userec);
		tmptime = UMIN (time_used.tv_sec,19) * 1000000 + time_used.tv_usec;

		// laggy command notice: command took longer than 1.5 seconds
		if (tmptime > 1500000) {
			sprintf (log_buf, "[*****] LAG: %s: %s %s (R:%d S:%ld.%06ld)",
				ch->GetName (), cmd->GetName (),
				(cmd->log == LOG_NEVER ? "XXX" : argument),
				ch->GetInRoom () ? ch->GetInRoom ()->vnum : 0,
				time_used.tv_sec, time_used.tv_usec);
			gpDoc->LogString (log_buf, LOG_BUG, ch->GetTrustLevel ());
		}
	}
	catch (CException* ex) {
		CSwException	sx;
		char			buf [128];
		UINT			id;

		if (ex->GetErrorMessage (buf, sizeof (buf), &id))
			sx.Printf ("CException %u:%s in Interp (%s:%s).", id, buf,
				ch->GetName (), logline [0] ? logline : argument);
		else sx.Printf ("Unknown CException in Interp (%s:%s).",
			ch->GetName (), logline [0] ? logline : argument);
		ex->Delete ();
		throw sx;
	}
}


void DoLogging (CCharacter& Ch, const char* logline, CCmdType& Cmd, BOOL bFound)
{
	if (bFound && Cmd.log == LOG_NEVER) then return;

	LogTypes	LogType = bFound ? (LogTypes) Cmd.log : LOG_NORMAL;

	if ((! Ch.IsNpc () && Ch.IsLogged ()) || fLogAll || LogType == LOG_BUILD
	  || LogType == LOG_HIGH || LogType == LOG_ALWAYS) {
		// Added by Narn to show who is switched into a mob that executes
		// a logged command.  Check for descriptor in case force is used.
		if (Ch.GetDesc () && Ch.GetDesc ()->m_pOriginal) 
			sprintf (log_buf, "Log %s (%s): %s", Ch.GetName (),
				Ch.GetDesc ()->m_pOriginal->GetName (), logline);
		else
			sprintf (log_buf, "Log %s: %s", Ch.GetName (), logline);

		// Make it so a 'log all' will send most output to the log
		// file only, and not spam the log channel to death	-Thoric
		if (fLogAll && LogType == LOG_NORMAL
		  && (Ch.IsNpc () || ! Ch.IsLogged ()))
			LogType = LOG_ALL;

		gpDoc->LogString (log_buf, LogType, Ch.GetTrustLevel ());
	}
}


BOOL check_social (CCharacter *ch, char *command, const char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	CSocialType	*social;

	if ((social = SocialTable.Find (command)) == NULL)
		return FALSE;

	if (!ch->IsNpc () && ch->IsNoEmote ()) {
		ch->SendText ("You are anti-social!\n\r");
		return TRUE;
	}

	switch (ch->GetPosition ()) {
	  case POS_DEAD:
		ch->SendText ("Lie still; you are DEAD.\n\r");
		return TRUE;

	  case POS_INCAP:
	  case POS_MORTAL:
		ch->SendText ("You are hurt far too bad for that.\n\r");
		return TRUE;

	  case POS_STUNNED:
		ch->SendText ("You are too stunned to do that.\n\r");
		return TRUE;

	  case POS_SLEEPING:
		// I just know this is the path to a 12" 'if' statement.  : (
		// But two players asked for it already!  -- Furey
		if (!str_cmp (social->GetName (), "snore"))
			break;
		ch->SendText ("In your dreams, or what?\n\r");
		return TRUE;
	}

	// Search room for chars ignoring social sender and
	// remove them from the room until social has been completed
	CPtrList	List;
	CCharacter	*victim = ch->GetInRoom ()->first_person;

	while (victim) {
		CCharacter	*pNext = victim->GetNextInRoom ();
		if (victim->IsIgnoring (ch)) {
			if (ch->IsMortal ()
			  || victim->GetTrustLevel () > ch->GetTrustLevel ()) {
				victim->RemoveFromRoom ();
				List.AddTail (victim);
			} else {
				set_char_color (AT_IGNORE, victim);
				victim->SendTextf (
					"You attempt to ignore %s, but are unable to do so.\n\r",
					ch->GetName ());
			}
		}
		victim = pNext;
	}

	one_argument (argument, arg);
	victim = NULL;
	if (arg[0] == '\0') {
		act (AT_SOCIAL, social->others_no_arg, ch, NULL, victim, TO_ROOM);
		act (AT_SOCIAL, social->char_no_arg,   ch, NULL, victim, TO_CHAR);
	}
	else if ((victim = get_char_room (ch, arg)) == NULL) {
		// If they aren't in the room, they may be in the list of
		// people ignoring...
		BOOL	bFound = FALSE;
		POSITION	pos = List.GetHeadPosition ();
		while (pos) {
			victim = (CCharacter*) List.GetNext (pos);
			if (nifty_is_name (victim->GetName (), arg) ||
			  nifty_is_name_prefix (arg, victim->GetName ())) {
				set_char_color (AT_IGNORE, ch);
				ch->SendTextf ("%s is ignoring you.\n\r", victim->GetName ());
				bFound = TRUE;
				break;
			}
		}

		if (! bFound)
			ch->SendText ("They aren't here.\n\r");
	}
	else if (victim == ch) {
		act (AT_SOCIAL, social->others_auto,   ch, NULL, victim, TO_ROOM);
		act (AT_SOCIAL, social->char_auto,     ch, NULL, victim, TO_CHAR);
	} else {
		act (AT_SOCIAL, social->others_found,  ch, NULL, victim, TO_NOTVICT);
		act (AT_SOCIAL, social->char_found,    ch, NULL, victim, TO_CHAR);
		act (AT_SOCIAL, social->vict_found,    ch, NULL, victim, TO_VICT);

		if (! ch->IsNpc () && victim->IsNpc ()
		  && ! victim->IsCharmed ()
		  && victim->IsAwake () 
		  && ! victim->GetMobIndex ()->m_Progtypes.IsSet (ACT_PROG)) {
			switch (number_bits (4)) {
			  case 0:
				if (! ch->GetInRoom ()->IsSafe () && ch->IsEvil ())
					multi_hit (victim, ch, TYPE_UNDEFINED);
				else if (ch->IsNeutral ()) {
					act (AT_ACTION, "$n slaps $N.", victim, NULL, ch, TO_NOTVICT);
					act (AT_ACTION, "You slap $N.", victim, NULL, ch, TO_CHAR);
					act (AT_ACTION, "$n slaps you.", victim, NULL, ch, TO_VICT);
				} else {
					act (AT_ACTION, "$n acts like $N doesn't even exist.",
						victim, NULL, ch, TO_NOTVICT);
					act (AT_ACTION, "You just ignore $N.", victim, NULL, ch,
						TO_CHAR);
					act (AT_ACTION, "$n appears to be ignoring you.",
						victim, NULL, ch, TO_VICT);
				}
				break;

			  case 1: case 2: case 3: case 4:
			  case 5: case 6: case 7: case 8:
				act (AT_SOCIAL, social->others_found, victim, NULL, ch, TO_NOTVICT);
				act (AT_SOCIAL, social->char_found, victim, NULL, ch, TO_CHAR);
				act (AT_SOCIAL, social->vict_found, victim, NULL, ch, TO_VICT);
				break;

			  case 9: case 10: case 11: case 12:
				act (AT_ACTION, "$n slaps $N.", victim, NULL, ch, TO_NOTVICT);
				act (AT_ACTION, "You slap $N.", victim, NULL, ch, TO_CHAR);
				act (AT_ACTION, "$n slaps you.", victim, NULL, ch, TO_VICT);
				break;
			}
		}
	}

	// Replace the chars in the ignoring list to the room
	// note that the ordering of the players in the room might change
	while (! List.IsEmpty ()) {
		victim = (CCharacter*) List.RemoveHead ();
		victim->SendToRoom (ch->GetInRoom ());
	}

	return TRUE;
}


// Return true if an argument is completely numeric.
BOOL is_number (char *arg)
{
	if (*arg == '\0')
		return FALSE;

	for (; *arg != '\0'; arg++) {
		if (!isdigit (*arg))
			return FALSE;
	}

	return TRUE;
}


// Given a string like 14.foo, return 14 and 'foo'
int number_argument (char *argument, char *arg)
{
	char *pdot;
	int number;

	for (pdot = argument; *pdot != '\0'; pdot++) {
		if (*pdot == '.') {
			*pdot = '\0';
			number = atoi (argument);
			*pdot = '.';
			strcpy (arg, pdot+1);
			return number;
		}
	}

	strcpy (arg, argument);
	return 1;
}


// Pick off one argument from a string and return the rest.
// Understands quotes.
char *one_argument (const char *argument, char *arg_first)
{
	char	cEnd, c;
	short	count;

	count = 0;

	while (isspace (*argument))
		argument++;

	cEnd = ' ';
	if (*argument == '\'' || *argument == '"')
		cEnd = *argument++;

	while ((c = *argument) != 0 || ++count >= 255) {
		if (c == cEnd) {
			++argument;
			break;
		}
		if (c == '&' && IsColor (argument))
				argument += 2;
		else {
			*arg_first = LOWER (c);
			++arg_first;
			++argument;
		}
	}
	*arg_first = 0;

	while (isspace (*argument))
		++argument;

	return NCCP argument;
}


// Pick off one argument from a string and return the rest.
// Understands quotes.  Delimiters = { ' ', '-' }
char *one_argument2 (const char *argument, char *arg_first)
{
	char	cEnd, c;
	short	count;

	count = 0;

	while (isspace (*argument))
		argument++;

	cEnd = ' ';
	if (*argument == '\'' || *argument == '"')
		cEnd = *argument++;

	while ((c = *argument) != 0 || ++count >= 255) {
		if (c == cEnd || c == '-') {
			++argument;
			break;
		}
		if (c == '&' && IsColor (argument))
				argument += 2;
		else {
			*arg_first = LOWER (c);
			++arg_first;
			++argument;
		}
	}
	*arg_first = 0;

	while (isspace (*argument))
		++argument;

	return NCCP argument;
}


void do_timecmd (CCharacter *ch, char *argument)
{
	struct timeval		stime;
	struct timeval		etime;
	static BOOL			timing;
	extern CCharacter	*timechar;
	char				arg [MAX_INPUT_LENGTH];

	ch->SendText ("Timing\n\r");
	if (timing)
		return;

	one_argument (argument, arg);
	if (!*arg) {
		ch->SendText ("No command to time.\n\r");
		return;
	}
	if (!str_cmp (arg, "update")) {
		if (timechar)
			ch->SendText ("Another person is already timing updates.\n\r");
		else {
			timechar = ch;
			ch->SendText ("Setting up to record next update loop.\n\r");
		}
		return;
	}

	set_char_color (AT_PLAIN, ch);
	ch->SendText ("Starting timer.\n\r");
	timing = TRUE;
	gpDoc->gettimeofday (&stime, NULL);

	interpret (ch, argument);

	gpDoc->gettimeofday (&etime, NULL);
	timing = FALSE;
	set_char_color (AT_PLAIN, ch);
	ch->SendText ("Timing complete.\n\r");
	subtract_times (&etime, &stime);
	ch->SendTextf ("Timing took %d.%06d seconds.\n\r",
	etime.tv_sec, etime.tv_usec);
}


void start_timer (struct timeval *stime)
{
	if (!stime) {
		bug ("Start_timer: NULL stime.");
		return;
	}
	gpDoc->gettimeofday (stime, NULL);
}


time_t end_timer (struct timeval *stime)
{
	struct timeval etime;

	// Mark etime before checking stime, so that we get a better reading..
	gpDoc->gettimeofday (&etime, NULL);
	if (!stime || (!stime->tv_sec && !stime->tv_usec)) {
		bug ("End_timer: bad stime.", 0);
		return 0;
	}
	subtract_times (&etime, stime);
	// stime becomes time used
	*stime = etime;
	return (etime.tv_sec*1000000)+etime.tv_usec;
}


void send_timer (struct timerset *vtime, CCharacter *ch)
{
	struct timeval	ntime;
	int				carry;

	if (vtime->num_uses == 0)
		return;

	ntime.tv_sec = vtime->total_time.tv_sec / vtime->num_uses;
	carry = (vtime->total_time.tv_sec % vtime->num_uses) * 1000000;
	ntime.tv_usec = (vtime->total_time.tv_usec + carry) / vtime->num_uses;
	ch->SendTextf ("Has been used %d times this boot.\n\r", vtime->num_uses);
	ch->SendTextf ("Time (in secs): min %d.%0.6d; avg: %d.%0.6d; "
		"max %d.%0.6d\n\r", vtime->min_time.tv_sec, vtime->min_time.tv_usec,
		ntime.tv_sec, ntime.tv_usec, vtime->max_time.tv_sec,
		vtime->max_time.tv_usec);
}


void update_userec (struct timeval *time_used, timerset *userec)
{
	userec->num_uses++;
	if (!timerisset (&userec->min_time)
	  || timercmp (time_used, &userec->min_time, <)) {
		userec->min_time.tv_sec  = time_used->tv_sec;
		userec->min_time.tv_usec = time_used->tv_usec;
	}

	if (!timerisset (&userec->max_time)
	  || timercmp (time_used, &userec->max_time, >)) {
		userec->max_time.tv_sec  = time_used->tv_sec;
		userec->max_time.tv_usec = time_used->tv_usec;
	}

	userec->total_time.tv_sec  += time_used->tv_sec;
	userec->total_time.tv_usec += time_used->tv_usec;

	while (userec->total_time.tv_usec >= 1000000) {
		userec->total_time.tv_sec++;
		userec->total_time.tv_usec -= 1000000;
	}
}