Mud20/accounts/
Mud20/accounts/c/
Mud20/accounts/f/
Mud20/accounts/k/
Mud20/accounts/s/
Mud20/accounts/t/
Mud20/area_current/
Mud20/area_current/newareas/
Mud20/bin/
Mud20/clans/
Mud20/gods/
Mud20/old-sources/
Mud20/player/
Mud20/player/a/del/
Mud20/player/b/
Mud20/player/b/bak/
Mud20/player/b/del/
Mud20/player/f/
Mud20/player/f/bak/
Mud20/player/f/del/
Mud20/player/k/
Mud20/player/k/bak/
Mud20/player/k/del/
Mud20/player/k/dmp/
Mud20/player/m/
Mud20/player/m/bak/
Mud20/player/o/
Mud20/player/o/bak/
Mud20/player/p/
Mud20/player/s/
Mud20/player/s/bak/
Mud20/player/s/del/
Mud20/player/t/
Mud20/player/t/del/
Mud20/player/v/
Mud20/public_html/
Mud20/races/
Mud20/skilltables/
__MACOSX/Mud20/accounts/
__MACOSX/Mud20/accounts/c/
__MACOSX/Mud20/accounts/f/
__MACOSX/Mud20/accounts/k/
__MACOSX/Mud20/accounts/s/
__MACOSX/Mud20/area_current/
__MACOSX/Mud20/area_current/core_areas/
__MACOSX/Mud20/area_current/helps/
__MACOSX/Mud20/area_current/newareas/
__MACOSX/Mud20/backups/
__MACOSX/Mud20/bin/
__MACOSX/Mud20/clans/
__MACOSX/Mud20/gods/
__MACOSX/Mud20/log/
__MACOSX/Mud20/old-sources/
__MACOSX/Mud20/player/
__MACOSX/Mud20/player/a/del/
__MACOSX/Mud20/player/b/
__MACOSX/Mud20/player/b/bak/
__MACOSX/Mud20/player/f/
__MACOSX/Mud20/player/f/bak/
__MACOSX/Mud20/player/f/del/
__MACOSX/Mud20/player/k/
__MACOSX/Mud20/player/k/bak/
__MACOSX/Mud20/player/k/del/
__MACOSX/Mud20/player/k/dmp/
__MACOSX/Mud20/player/m/
__MACOSX/Mud20/player/m/bak/
__MACOSX/Mud20/player/o/
__MACOSX/Mud20/player/o/bak/
__MACOSX/Mud20/player/p/
__MACOSX/Mud20/player/s/
__MACOSX/Mud20/player/s/bak/
__MACOSX/Mud20/player/t/del/
__MACOSX/Mud20/player/v/
__MACOSX/Mud20/public_html/
__MACOSX/Mud20/races/
__MACOSX/Mud20/skilltables/
/***************************************************************************
 * Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming   *
 * License by Wizards of the Coast. All comments referring to D20, OGL,    *
 * and SRD refer to the System Reference Document for the Open Gaming      *
 * system. Any inclusion of these derivatives must include credit to the   *
 * Mud20 system, the full and complete Open Gaming LIcense, and credit to  *
 * the respective authors. See ../doc/srd.txt for more information.        *
 *                                                                         *
 * Emud  2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem.   *
 *                                                                         *
 * MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey                *
 *                                                                         *
 * 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 St{rfeld, Tom Madsen, and Katje Nyboe.     *
 ***************************************************************************/

/***************************************************************************
 * comm.c: All server connection and terminal functions									   *
 ***************************************************************************/

#include <sys/time.h>
#include <sys/socket.h>

#include <unistd.h>
#include <stdarg.h>

#include "mud.h"
#include "telnet.h"

/*
	Socket and TCP/IP stuff.
*/

#include <signal.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/wait.h>


void		pipe_handler( int signal );
void		trap_handler( int signal );
void		scan_object_for_dup( CHAR_DATA *ch, OBJ_DATA *obj);
char	*	prompt_return( CHAR_DATA *, char *);
int			pager( DESCRIPTOR_DATA *, const char *, int, char *);
void		scroll(DESCRIPTOR_DATA *, const char *, int);
void		save_auth_list	args( ( void ) );


/*
	OS-dependent declarations.
*/

#if defined(linux)
int	close		args( ( int fd ) );
#ifndef __GLIBC__2__
int	getpeername	args( ( int s, struct sockaddr *name, int *namelen ) );
int	getsockname	args( ( int s, struct sockaddr *name, int *namelen ) );
int	listen		args( ( int s, int backlog ) );
#endif

int	gettimeofday	args( ( struct timeval *tp, struct timezone *tzp ) );
int	select		args( ( int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout ) );
int	socket		args( ( int domain, int type, int protocol ) );
#endif

/*
	Locals.
*/

char				logfilename[25];
OBJ_REF_HASH		obj_ref_list[MAX_OBJ_REF];
lg_int			obj_ref_key;

/*
	OS-dependent local functions.
*/

void	game_loop_unix			args( ( void ) );
int	init_socket			args( ( ) );
void	new_descriptor			args( ( void ) );
bool	read_from_descriptor	args( ( DESCRIPTOR_DATA *d ) );


/*
	Other local functions (OS-independent).
*/

CHAR_DATA *	check_reconnecting	args( ( DESCRIPTOR_DATA *d, ACCOUNT_DATA *acct));
ACCOUNT_DATA *lookup_account		args( (char *name) );
bool		add_account							args( ( DESCRIPTOR_DATA *d, char *name ) );
void		free_account						args( ( ACCOUNT_DATA *acct ) );
void		save_account						args( ( DESCRIPTOR_DATA *d ) );
int			main										args( ( int argc, char **argv ) );
bool		nanny										args( ( DESCRIPTOR_DATA *d, char *argument ) );
bool		process_output					args( ( DESCRIPTOR_DATA *d, bool fPrompt ) );
void		read_from_buffer				args( ( DESCRIPTOR_DATA *d ) );
void		stop_idling							args( ( DESCRIPTOR_DATA *d ) );
void		remove_bad_desc_acct		args( ( char *name ) );

int main (int argc, char **argv)
{
	struct timeval now_time;
	int count;
	int cntr, i;
	FILE *pid_file;

	push_call("main(%p,%p)",argc,argv);

	ALLOCMEM(mud,				MUD_DATA,			1);
	ALLOCMEM(mud->usage,			USAGE_DATA,		1);
	ALLOCMEM(mud->time_info,		TIME_INFO_DATA,	1);
	ALLOCMEM(mud->tactical,		TACTICAL_MAP,		1);
	ALLOCMEM(mud->last_player_cmd,	unsigned char, 	MAX_INPUT_LENGTH);
	ALLOCMEM(mud->mccp_buf,		unsigned char,		COMPRESS_BUF_SIZE);

	mud->str_hash_size = sizeof(STR_HASH_DATA);

	for (count = 0 ; count < MOST_MOST ; count++)
	{
		ALLOCMEM(mud->high_scores[count], HISCORE_LIST, 1);
	}

	str_empty				= STRALLOC("");

	obj_ref_key			= get_game_usec();

	/*
		place the pid of this process in the file mud.pid
	*/

	pid_file = my_fopen("../mud.pid","w",FALSE);

	if (pid_file)
	{
		fprintf(pid_file, "%d\n", getpid());
		fflush(pid_file);
		my_fclose(pid_file);
	}
	else
	{
		log_printf("Could not write the pid number (%d) in ../mud.pid", getpid());
	}

	/*
		Let's log the current system constraints as seen by MrMud - Chaos 5/3/98
	*/

	{
		struct rlimit rlpt;

		getrlimit(RLIMIT_DATA, &rlpt);
		log_printf("System memory usage: %ld max %ld current", (long)rlpt.rlim_max, (long)rlpt.rlim_cur);

		rlpt.rlim_max = RLIM_INFINITY;
		rlpt.rlim_cur = RLIM_INFINITY;
		setrlimit(RLIMIT_DATA, &rlpt);
		getrlimit(RLIMIT_DATA, &rlpt);
		log_printf("Unlimited memory usage: %ld max %ld current", (long)rlpt.rlim_max, (long)rlpt.rlim_cur);
	}

	for (cntr = 0 ; cntr < argc ; cntr++)
	{
		log_printf("argv[%d] = %s",cntr,argv[cntr]);
	}


	/*
		Init time.
	*/

	gettimeofday( &now_time, NULL );

	mud->current_time = now_time.tv_sec;
	mud->boot_time	   = now_time.tv_sec;
	mud->port         = 4000;
	logfilename[0]    = '\0';

	if (argc > 1)
	{
		for (count = 1 ; count < argc ; count++)
		{
			if (!strcmp(argv[count], "-b"))
			{
				mud->boot_time = atol(argv[++count]);
			}

			if (!strcmp(argv[count], "-c"))
			{
				mud->control = atol(argv[++count]);
				log_printf("Set control to %d", mud->control);
				SET_BIT(mud->flags, MUD_EMUD_COPYOVER);
			}

			if (!strcmp(argv[count], "-p"))
			{
				mud->port = atol(argv[++count]);
			}
		}
	}

	{
		FILE *logtst;

		for (i = 1000 ; i < 10000 ; i++)
		{
			sprintf(logfilename, "../log/%d.log", i);

			if ((logtst = my_fopen(logfilename, "r", TRUE)) != NULL)
			{
				my_fclose(logtst);
			}
			else
			{
				break;
			}
		}
		sprintf(logfilename, "../log/%d.log", --i);

		log_printf("Found logfile %s", logfilename);
	}

	if (!IS_SET(mud->flags, MUD_EMUD_COPYOVER))
	{
		nice(5);
	}

	if ((i = check_dirs()) == -1)
	{
		log_printf("Checking of directories failed: %s", strerror(errno));
		abort();
	}
	else
	{
		log_printf("Directories are okay (%d dirs were newly created).", i);
	}

	if (mud->port == 4000)
	{
		SET_BIT(mud->flags, MUD_EMUD_REALGAME);
	}

	/*
		Ignored Signals
	*/

	signal(SIGPIPE,	pipe_handler);

	/*
		Known signals
	*/

	signal(SIGTERM,	trap_handler);
	signal(SIGSEGV,	trap_handler);
	signal(SIGALRM,	trap_handler);

	/*
		Added just in case
	*/

	signal(SIGINT,		trap_handler);
	signal(SIGHUP,		trap_handler);
	signal(SIGFPE,		trap_handler);
	signal(SIGABRT,	trap_handler);

	/*
		Run the game.
	*/

	if (fpReserve)
	{
		log_string ( "fpReserve : already opened");
	}
	else
	{
		fpReserve = my_fopen( NULL_FILE, "r", FALSE );

		if (fpReserve)
		{
			log_string ( "fpReserve : opened readonly");
		}
		else
		{
			log_string ( "fpReserve : failed to open readonly");
		}
	}

	if (fpAppend)
	{
		log_string( "fpAppend  : already opened" );
	}
	else
	{
		fpAppend = my_fopen( NULL_FILE, "r",FALSE );

		if (fpAppend)
		{
			log_string ( "fpAppend  : opened readonly" );
		}
		else
		{
			log_string ( "fpAppend  : failed to open readonly" );
		}
	}

	boot_db(IS_SET(mud->flags, MUD_EMUD_COPYOVER));

	if (!IS_SET(mud->flags, MUD_EMUD_COPYOVER))
	{
		log_printf("Initializing socket for port %d", mud->port);
		mud->control = init_socket( );
	}

	log_printf("starting game_loop_unix: control = %d, port = %d", mud->control, mud->port);

	game_loop_unix();

	close(mud->control);

	log_string("Normal termination of game.");

	unlink("../mud.pid");

	exit(0);
}


void pipe_handler(int signal)
{
	log_printf("broken_pipe: dumping stack");
	dump_stack();
}


void trap_handler(int signal)
{
	DESCRIPTOR_DATA *d;
	PLAYER_GAME	 *gpl;
	FILE_DATA       *fdp;
	char stoptype[80];

	if (IS_SET(mud->flags, MUD_EMUD_CRASH))
	{
		log_string("Emud crashed twice: shutting down");
		dump_stack();
		exit(-1);
	}
	else
	{
		SET_BIT(mud->flags, MUD_EMUD_CRASH);
	}

	if (fflush(NULL) != 0)
	{
		perror("failed to flush");
		dump_stack();
		exit (-1);
	}

	dump_stack();
	dump_desc_characters();

	log_printf("Last player command: %s", mud->last_player_cmd);

	switch (signal)
	{
		case SIGSEGV:
			log_string("*** SEGV SIGNAL CAUGHT *** trying to shutdown gracefully.\n");
			sprintf(stoptype, "*** SIGNAL_SEGV ***");
			break;
		case SIGHUP:
			log_string("*** HUP SIGNAL CAUGHT *** trying to shutdown gracefully.\n");
			sprintf(stoptype, "*** SIGNAL_HUP ***");
			break;
		case SIGTERM:
			log_string("*** TERM SIGNAL CAUGHT *** shutting down gracefully.\n");
			sprintf(stoptype, "*** SIGNAL_TERM ***");
			break;
		case SIGALRM:
			log_string("*** ALARM SIGNAL CAUGHT *** shutting down gracefully.\n");
			sprintf(stoptype, "*** SIGNAL_ALRM ***");
			break;
		default :
			log_printf("*** UNKNOWN SIGNAL PASSED (%d) *** not shutting down gracefully.\n", signal);
			sprintf(stoptype, "*** UNKNOWN SIGNAL (%d) TRAPPED! ***", signal);
			exit (-1);
			break;
	}

	log_printf("Saving players...");

	for (d = mud->f_desc ; d ; d = d->next)
	{
		if (d->original)
		{
			do_return(d->character, NULL);
		}
	}

	for (gpl = mud->f_player ; gpl ; gpl = gpl->next)
	{
		save_char_obj(gpl->ch, NORMAL_SAVE);

		if (gpl->ch->desc)
		{
			write_to_descriptor(gpl->ch->desc, "\n\r{078}The mud has crashed, but will probably soon recover.\n\r\n\r", 0);

			write_to_port(gpl->ch->desc);
		}
	}

	log_printf("Saving Modified Areas...");
	do_savearea(NULL, "forreal");

	log_string("Saving Clans...");
	save_all_clans();

	log_string("Saving Hiscores...");
	save_hiscores();

	log_string("Saving Auth List...");
	save_auth_list();

	log_string("Listing opened files...");

	for (fdp = mud->f_open_file ; fdp ; fdp = fdp->next)
	{
		log_printf("%s [%s]", fdp->filename, fdp->opentype);
	}

	log_string("\nSHUTTING DOWN NOW");

	/*
		the developer (defined in mud.h) will automatically
		receive a mail with the log to trace down the error
		Manwe, 08 Jan 2000

		Disabled, only Michiel (Manwe) used it and he's no
		longer coding for the Emud project.
		Scandum 14-04-2002

		{
			char sysbuf[250];
			sprintf(sysbuf,"mail -s '%s' %s < %s", stoptype, DEVELOPER_MAIL, logfilename);
			log_printf("Mailed log %s to %s.",logfilename,DEVELOPER_MAIL);
			system(sysbuf);
		}
	*/

	exit(-1);
}

int init_socket( void )
{
	static struct sockaddr_in sa_zero;
	struct sockaddr_in sa;
	int x=1;
	int fd;

	push_call("init_socket(void)");

	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("init_socket: socket");
		abort();
	}

	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &x, sizeof(x)) < 0)
	{
		perror("init_socket: SO_REUSEADDR");
		close(fd);
		abort();
	}

	{
		struct linger ld;

		ld.l_onoff  = 0;
		ld.l_linger = 100;

		if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld)) < 0)
		{
			perror("init_socket: SO_LINGER");
			close(fd);
			abort();
		}
	}

/*
	{
		int sockbuf;
		unsigned int socksize;

		socksize = sizeof(sockbuf);

		if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &sockbuf, &socksize) < 0)
		{
			perror("getsockopt: SO_RCVBUF");
			close(fd);
			abort();
		}

		log_printf("Initial SO_RCVBUF size:  %d", sockbuf);


		if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sockbuf, &socksize) < 0)
		{
			perror("getsockopt: SO_SNDBUF");
			close(fd);
			abort();
		}

		log_printf("Initial SO_SNDBUF size: %d", sockbuf);

		sockbuf = MAX_INPUT_LENGTH;

		if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &sockbuf, sizeof(socksize)) < 0)
		{
			perror("getsockopt: SO_RCVBUF");
			close(fd);
			abort();
		}

		if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &sockbuf, &socksize) < 0)
		{
			perror("getsockopt: SO_RCVBUF");
			close(fd);
			abort();
		}

		log_printf("Modified SO_RCVBUF size: %d", sockbuf);


		sockbuf = MAX_STRING_LENGTH;

		if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sockbuf, sizeof(socksize)) < 0)
		{
			perror("getsockopt: SO_SNDBUF");
			close(fd);
			abort();
		}

		if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sockbuf, &socksize) < 0)
		{
			perror( "getsockopt: SO_SNDBUF" );
			close( fd );
			abort( );
		}

		log_printf("Modified SO_SNDBUF size: %d", sockbuf);
	}
*/
	sa			= sa_zero;
	sa.sin_family	= AF_INET;
	sa.sin_port	= htons( mud->port );

	if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
	{
		perror( "init_socket: bind");
		close(fd);
		abort();
	}

	if (listen(fd, 20) < 0)
	{
		perror("init_socket: listen");
		close(fd);
		abort();
	}
	pop_call();
	return fd;
}


void game_loop_unix( void )
{
	static struct timeval null_time;
	struct timeval last_time;
	DESCRIPTOR_DATA *d;

	fd_set in_set;
	fd_set out_set;
	fd_set exc_set;
	int maxdesc;

	struct timeval now_time, stall_time;

	int usec_gameloop, usec_gamewait;

	push_call("game_loop_unix(void)");

	gettimeofday(&last_time, NULL);
	mud->current_time = (time_t) last_time.tv_sec;

	stall_time.tv_sec  = 0;

	/*
		Main loop
	*/

	while (!IS_SET(mud->flags, MUD_EMUD_DOWN))
	{
		gettimeofday(&last_time, NULL);

		mud->current_time = last_time.tv_sec;

		if (mud->f_desc == NULL && IS_SET(mud->flags, MUD_EMUD_REBOOT))
		{
			SET_BIT(mud->flags, MUD_EMUD_DOWN);
		}

		alarm(60);

		mud->time = *localtime(&mud->current_time);

		/*
			Take care of various update routines
		*/

		update_handler();

		start_timer( TIMER_SCAN_DESC );

		/*
			Poll all active descriptors.
		*/

		FD_ZERO( &in_set  );
		FD_ZERO( &out_set );
		FD_ZERO( &exc_set );
		FD_SET( mud->control, &in_set );

		maxdesc = mud->control;

		for (d = mud->f_desc ; d ; d = d->next)
		{
			maxdesc = UMAX(maxdesc, d->descriptor);
			FD_SET( d->descriptor, &in_set  );
			FD_SET( d->descriptor, &out_set );
			FD_SET( d->descriptor, &exc_set );
		}

		if (select(maxdesc+1, &in_set, &out_set, &exc_set, &null_time ) < 0)
		{
			perror( "Game_loop: select: poll" );
			abort( );
		}

		/*
			New connection?
		*/

		if (FD_ISSET(mud->control, &in_set))
		{
			new_descriptor();
		}

		/*
			Kick out the freaky folks.
		*/

		for (d = mud->f_desc ; d ; d = mud->update_gld)
		{
			mud->update_gld = d->next;

			if (FD_ISSET(d->descriptor, &exc_set) || IS_SET(d->comm_flags, COMM_FLAG_DISCONNECT))
			{
				FD_CLR( d->descriptor, &in_set  );
				FD_CLR( d->descriptor, &out_set );
				d->outtop	 = 0;

				if (d->character != NULL)
				{
					log_printf("Kicking off %s for being freaky", d->character->name);
				}
				close_socket(d, TRUE);
			}
		}

		if (mud->mp_group_greeter && mud->mp_group_greeted)
		{
			if (mud->mp_group_greet_obj)
			{
				set_supermob(mud->mp_group_greet_obj);
				mprog_percent_check(mud->mp_group_greeter, mud->mp_group_greeted, NULL, NULL, GROUP_GREET_PROG);
				release_supermob();
				mud->mp_group_greet_obj = NULL;
			}	
			else if (MP_VALID_MOB(mud->mp_group_greeter))
			{
				mprog_percent_check(mud->mp_group_greeter, mud->mp_group_greeted, NULL, NULL, GROUP_GREET_PROG);
			}
			mud->mp_group_greeter = mud->mp_group_greeted = NULL;
		}

		close_timer( TIMER_SCAN_DESC );

		start_timer( TIMER_PROCESS_INPUT );

		/*
			Process input.
		*/

		if (mud->f_desc != NULL && !IS_SET(mud->flags, MUD_EMUD_BOOTING))
		{
			for (d = mud->f_desc ; d ; d = mud->update_gld)
			{
				mud->update_gld = d->next;

				REMOVE_BIT(d->comm_flags, COMM_FLAG_COMMAND);

				if (FD_ISSET(d->descriptor, &in_set))
				{
					if (d->character == NULL || d->character->desc != d || !IS_SET(CH(d)->pcdata->interp, INTERP_ALIAS))
					{
						if (!read_from_descriptor(d))
						{
							FD_CLR( d->descriptor, &out_set );
							d->outtop  = 0;
							SET_BIT(d->comm_flags, COMM_FLAG_DISCONNECT);
							close_socket(d , TRUE);
							continue;
						}
					}
				}

				if (d->connected < CON_PLAYING && d->connecting_time + DESCRIPTOR_TIMEOUT < mud->current_time)
				{
					write_to_descriptor(d, "\n\r\n\rLogin time out reached.\n\r\n\r", 0);
					log_god_printf("Login timeout reached for D%d@%s", d->descriptor, d->host);
					close_socket(d, TRUE);
					continue;
				}

				if (d->character != NULL && d->character->wait > 0)
				{
					if (IS_NPC(d->character))
					{
						d->character->wait--;
						if (d->character->wait == 0)
						{
							vt100prompt( d->character );
						}
						continue;
					}

					--d->character->wait;
					if (d->character->wait < 0)
					{
						d->character->wait = 0;
					}
					if (d->character->wait == 0)
					{
						vt100prompt( d->character );
					}
					continue;
				}

				/*
					Reset characters from aliasses
				*/

				if (*d->incomm == '\0' && d->connected >= CON_PLAYING && d->character && d == d->character->desc && *d->inbuf == '\0' && d->intop == 0)
				{
					REMOVE_BIT(CH(d)->pcdata->interp, INTERP_ALIAS);

					if (d->back_buf != NULL)
					{
						d->intop = str_cpy_max(d->inbuf, d->back_buf, MAX_INPUT_LENGTH-10);
						STRFREE(d->back_buf);
						d->back_buf = NULL;
						if (*d->inbuf == '\r' && d->intop == 1)
						{
							memset(d->inbuf, 0, MAX_INPUT_LENGTH);
							d->intop = 0;
						}
					}
					else
					{
						SET_BIT(d->comm_flags, COMM_FLAG_NOINPUT);
					}
				}
				read_from_buffer( d );

				/*
					Check input
				*/
				if (d->incomm[0] != '\0')
				{
					if (IS_SET(d->comm_flags, COMM_FLAG_NOINPUT))
					{
						REMOVE_BIT(d->comm_flags, COMM_FLAG_NOINPUT);
						SET_BIT(d->comm_flags, COMM_FLAG_COMMAND);
					}
					stop_idling(d);

					if (!IS_SET(d->comm_flags, COMM_FLAG_DISCONNECT))
					{
						if (d->connected == CON_PLAYING)
						{
							interpret( d->character, d->incomm );
						}
						else if (d->connected == CON_EDITING)
						{
							edit_buffer(d->character, d->incomm);
						}
						else
						{
							if (nanny(d, d->incomm))
							{
								continue;
							}
						}
					}

				}
			}
		}

		close_timer( TIMER_PROCESS_INPUT );

		/*
			Prompt and Tactical.
		*/

		start_timer(TIMER_TACTICAL_UPD);

		if (mud->f_desc != NULL && !IS_SET(mud->flags, MUD_EMUD_BOOTING))
		{
			for (d = mud->f_desc ; d ; d = mud->update_gld)
			{
				mud->update_gld = d->next;

				*d->incomm = '\0';

				if ((IS_SET(d->comm_flags, COMM_FLAG_COMMAND) || d->outtop > 0) && FD_ISSET(d->descriptor, &out_set))
				{
					if (!process_output(d, TRUE) || IS_SET(d->comm_flags, COMM_FLAG_DISCONNECT))
					{
						d->outtop = 0;
						close_socket( d , TRUE);
					}
				}
			}
		}


		if (mud->f_desc != NULL && !IS_SET(mud->flags, MUD_EMUD_BOOTING))
		{
			for (d = mud->f_desc ; d ; d = mud->update_gld)
			{
				mud->update_gld = d->next;

				if (!IS_SET(d->comm_flags, COMM_FLAG_DISCONNECT) && d->character && d->connected >= CON_PLAYING && CH(d)->pcdata && IS_SET(CH(d)->pcdata->interp, INTERP_TACT_UPDATE))
				{
					vt100prompter( d->character );
				}
			}
		}

		close_timer( TIMER_TACTICAL_UPD );

		start_timer( TIMER_PROCESS_OUTPUT );

		if (mud->f_desc != NULL && !IS_SET(mud->flags, MUD_EMUD_BOOTING))
		{
			for (d = mud->f_desc ; d ; d = mud->update_gld)
			{
				mud->update_gld = d->next;

				if (IS_SET(d->comm_flags, COMM_FLAG_DISCONNECT))
				{
					continue;
				}
				if (d->outtop)
				{
					if (d->outbuf[d->outtop - 1] != '\r')
					{
						if (IS_SET(d->comm_flags, COMM_FLAG_EOR))
						{
							d->outbuf[d->outtop++] = IAC;
							d->outbuf[d->outtop++] = EOR;
						}
						else
						{
							d->outbuf[d->outtop++] = IAC;
							d->outbuf[d->outtop++] = GA;
						}
					}
					write_to_port(d);
				}
			}
		}

		close_timer(TIMER_PROCESS_OUTPUT);

		/*
			pause the loop
		*/

		gettimeofday(&now_time, NULL);

		if (now_time.tv_sec == last_time.tv_sec)
		{
			usec_gameloop = now_time.tv_usec - last_time.tv_usec;
		}
		else
		{
			usec_gameloop = 1000000 - last_time.tv_usec + now_time.tv_usec;
		}

		usec_gamewait = 1000000 / PULSE_PER_SECOND - usec_gameloop;

		stall_time.tv_usec = usec_gamewait;

		mud->total_io_ticks++;
		mud->total_io_exec  += usec_gameloop;
		mud->total_io_delay += usec_gamewait;

		if (usec_gamewait >= 0)
		{
			if (select(0, NULL, NULL, NULL, &stall_time) < 0)
			{
				perror( "Game_loop: select: stall" );
			}
		}
		else
		{
			/*
			log_printf("game_loop_unix: heartbeat violation of %lld usec", usec_gameloop - 1000000LL / PULSE_PER_SECOND);
			*/
		}

	}
	pop_call();
	return;
}


/*
 * Replace old function with this one - Kregor
 */
void init_descriptor (DESCRIPTOR_DATA *dnew, int desc)
{
	dnew->descriptor			= desc;
	dnew->connected			= CON_GET_ACCOUNT;
	dnew->connecting_time		= mud->current_time;
	dnew->port_size			= 10000;
	dnew->terminal_type			= STRDUPE(str_empty);
	dnew->inlast				= STRDUPE(str_empty);
}


void new_descriptor( void )
{
	DESCRIPTOR_DATA *dnew;
	BAN_DATA *pban;
	struct sockaddr_in sock;
	int desc, sockbuf;
	unsigned int size;
	struct linger ld;

	size = sizeof(sock);

	push_call("new_descriptor(void)");

	getsockname(mud->control, (struct sockaddr *) &sock, &size);

	if ((desc = accept(mud->control, (struct sockaddr *) &sock, &size)) < 0)
	{
		perror( "New_descriptor: accept" );
		pop_call();
		return;
	}

	if (fcntl(desc, F_SETFL, O_NDELAY|O_NONBLOCK) == -1)
	{
		perror("new_descriptor: fcntl: O_NDELAY|O_NONBLOCK");
		pop_call();
		return;
	}

	sockbuf = 2048;

	if (setsockopt(desc, SOL_SOCKET, SO_RCVBUF, (char *) &sockbuf, sizeof(sockbuf)) < 0)
	{
		perror( "new_socket: SO_RCVBUF" );
	}

	ld.l_onoff  = 0;
	ld.l_linger = 100;

	if (setsockopt(desc, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld)) < 0)
	{
		perror("new_socket: SO_LINGER");
	}

	/*
		Cons a new descriptor.
	*/

	ALLOCMEM(dnew, DESCRIPTOR_DATA, 1);
	init_descriptor (dnew, desc);

	size = sizeof(sock);

	if (getpeername(desc, (struct sockaddr *) &sock, &size) < 0)
	{
		perror( "new_descriptor: getpeername" );
		dnew->host   = STRALLOC( "(unknown)" );
		SET_BIT(dnew->comm_flags, COMM_FLAG_DISCONNECT);
	}
	else
	{
		int addr, address[4];
		char buf[MAX_INPUT_LENGTH];

		addr = ntohl( sock.sin_addr.s_addr );

		address[0] = ( addr >> 24 ) & 0xFF ;
		address[1] = ( addr >> 16 ) & 0xFF ;
		address[2] = ( addr >>  8 ) & 0xFF ;
		address[3] = ( addr       ) & 0xFF ;

		sprintf(buf, "%d.%d.%d.%d", address[0], address[1], address[2], address[3]);
		dnew->host = STRALLOC(buf);

		log_god_printf("New connect: %s D%d", dnew->host , dnew->descriptor);
	}

	/*
		Init descriptor data.
	*/

	LINK(dnew, mud->f_desc, mud->l_desc, next, prev);

	mud->total_desc++;

	for (pban = mud->f_ban ; pban ; pban = pban->next)
	{
		if (!str_prefix(pban->name, dnew->host))
		{
			write_to_descriptor(dnew, "\n\rYour site has been banned from this Mud.\n\r\n\r", 0 );
			log_god_printf("Ban prefix '%s' for '%s', denying access.", pban->name, dnew->host);
			close_socket(dnew, TRUE);

			pop_call();
			return;
		}
	}

	if (mud->total_desc > MAX_LINKPERPORT)  /* Limit descriptors  */
	{
		char buf[MAX_STRING_LENGTH];

		sprintf(buf,"\n\rWe have a limit of %d players.\n\rPlease try back later.\n\r\n\r", MAX_LINKPERPORT);
		write_to_descriptor( dnew, buf, 0);
		close_socket( dnew, TRUE);
		pop_call();
		return;
	}

	/*
		Inform the client about mud's telnet support
	*/

	send_compress_on(dnew);
	send_ttype_on(dnew);
	send_eor_on(dnew);

	send_environ_on(dnew);

	/*
		Send the greeting.
	*/
	{
		char buf[40];

		sprintf(buf, "greeting%d", number_range(1, 6));

		force_help(dnew, buf);
	}
	pop_call();
	return;
}


void close_socket( DESCRIPTOR_DATA *dclose , bool Force)
{
	CHAR_DATA *ch;
	DESCRIPTOR_DATA *dt;
	AUTH_LIST *old_auth;

	push_call("close_socket(%p,%p)",dclose,Force);

	for (dt = mud->f_desc ; dt ; dt = dt->next)
	{
		if (dt == dclose)
		{
			break;
		}
	}

	if (dt == NULL)
	{
		log_string( "close_socket: unlinked desc called in close_socket");
		pop_call();
		return;
	}

	if (!IS_SET(dclose->comm_flags, COMM_FLAG_DISCONNECT))
	{
		write_to_port(dclose);
	}

	if (dclose->snoop_by != NULL)
	{
		write_to_buffer(dclose->snoop_by, "Your victim has left the game.\n\r", 0);
		dclose->snoop_by = NULL;
	}

	for (dt = mud->f_desc ; dt ; dt = dt->next)
	{
		if (dt->snoop_by == dclose)
		{
			dt->snoop_by = NULL;
		}
	}

	if (Force && (ch = dclose->character) != NULL)
	{
		if (ch->desc)
		{
			do_return(ch, NULL);

			ch = dclose->character;
		}

		log_god_printf("Closing link to %s@%s D%d Connected %d.",
			ch->name,
			dclose->host,
			dclose->descriptor,
			dclose->connected);

		/* Link dead auth -- Rantic */
		old_auth = get_auth_name( ch->name );
		if( old_auth != NULL && old_auth->state == AUTH_ONLINE )
		{
			old_auth->state = AUTH_LINK_DEAD;
			save_auth_list();
		}

		if (dclose->connected >= CON_PLAYING)
		{
			act( "$n has lost $s link.", ch, NULL, NULL, TO_ROOM );
			ch->desc = NULL;
		}
		else
		{
			dclose->character = NULL;
			if (!dclose->lookup)
			{
				ch->desc = NULL;
				extract_char(ch);
			}
		}
	}
	
	// close out account - Kregor
	if (dclose->account != NULL)
	{
		free_account(dclose->account);
	}

	dclose->outtop = 0;

	if (dclose->mccp)
	{
		end_compress(dclose);
	}

	if (dclose == mud->update_gld)
	{
		mud->update_gld = dclose->next;
	}

	if ((dclose->prev == NULL && dclose != mud->f_desc)
	||  (dclose->next == NULL && dclose != mud->l_desc))
	{
		log_printf("UNLINK ERROR unlinking descriptor %d.", dclose->descriptor);
	}
	else
	{
		UNLINK(dclose, mud->f_desc, mud->l_desc, next, prev);
	}

	mud->total_desc--;

	close(dclose->descriptor);

	STRFREE (dclose->host);
	STRFREE (dclose->back_buf);
	STRFREE (dclose->terminal_type);
	STRFREE (dclose->inlast);
	FREEMEM (dclose);

	pop_call();
	return;
}

bool read_from_descriptor( DESCRIPTOR_DATA *d )
{
	CHAR_DATA *ch;
	bool bufin[MAX_INPUT_LENGTH];
	int nRead, nSkip;

	push_call("read_from_descriptor(%p)",d);

	ch = d->original ? d->original : d->character;

	/*
		Hold horses if pending command already.
	*/

	if (d->incomm[0] != '\0')
	{
		pop_call();
		return TRUE;
	}

	/*
		Was there anything here to begin with ?
	*/

	if (d->back_buf != NULL)
	{
		pop_call();
		return( TRUE );
	}

	/*
		Check for overflow.
	*/

	if (d->intop > MAX_INPUT_LENGTH -100)
	{
		log_god_printf("%s input overflow!", d->host);
		write_to_descriptor( d, "\n\r*** PUT A LID ON IT!!! ***\n\rYou have just overflowed your buffer.  You may get back on the game.\n\r", 0 );

		pop_call();
		return FALSE;
	}

	/*
		Snarf input.
	*/

	while (TRUE)
	{
		memset(bufin, 0, MAX_INPUT_LENGTH);

		nRead = read(d->descriptor, bufin, MAX_INPUT_LENGTH);

		if (nRead > 0)
		{
			if (!IS_SET(d->comm_flags, COMM_FLAG_ECHO))
			{
				for (nSkip = 0 ; nSkip < nRead && d->intop < MAX_INPUT_LENGTH - 10 ; nSkip++, d->intop++)
				{
					while (bufin[nSkip] == IAC)
					{
						nSkip += do_telopts(d, &bufin[nSkip]);
					}

					d->inbuf[d->intop] = bufin[nSkip];

					if (d->inbuf[d->intop] == '\0')
					{
						break;
					}
				}
			}
			else
			{
				for (nSkip = 0 ; nSkip < nRead && d->intop < MAX_INPUT_LENGTH - 10 ; nSkip++, d->intop++)
				{
					while (bufin[nSkip] == IAC)
					{
						nSkip += do_telopts(d, &bufin[nSkip]);
					}

					d->inbuf[d->intop] = bufin[nSkip];

					switch (d->inbuf[d->intop])
					{
						case   0:
							pop_call();
							return TRUE;
						case   8:
						case 127:
							d->inbuf[d->intop] = '\b';
							write(d->descriptor, "\b \b", 3);
							break;
						case  13:
							write(d->descriptor, "\n\r", 2);
							break;
						default:
							if (IS_SET(d->comm_flags, COMM_FLAG_PASSWORD))
							{
								write(d->descriptor, "*", 1);
							}
							else
							{
								write(d->descriptor, &bufin[nSkip], 1);
							}
							break;
					}
				}
			}
		}
		else if (nRead == 0)
		{
			pop_call();
			return FALSE;
		}
		else if (errno == EWOULDBLOCK)
		{
			break;
		}
		else
		{
			log_god_printf("Read_from_descriptor D%d@%s errno %d", d->descriptor, d->host, errno);
			pop_call();
			return FALSE;
		}
	}
	pop_call();
	return TRUE;
}

/*
	Transfer one line from input buffer to input line.
*/

void read_from_buffer( DESCRIPTOR_DATA *d )
{
	int i, j, k;
	CHAR_DATA *ch,*sh;

	/*
		Hold horses if pending command already.
	*/

	push_call("read_from_buffer(%p)",d);

	if (d->incomm[0] != '\0')
	{
		pop_call();
		return;
	}

	if (d->inbuf[0] == '\0')
	{
		if (d->intop > 0)
		{
			fprintf(stderr, "%12s: ITE: %3d %3d %3d %3d %3d %3d %3d %3d %3d\n",
				CH(d) ? CH(d)->name : "Nanny",
				d->inbuf[0], d->inbuf[1], d->inbuf[2],
				d->inbuf[3], d->inbuf[4], d->inbuf[5],
				d->inbuf[6], d->inbuf[7], d->inbuf[8]);

			memmove(&d->inbuf[0], &d->inbuf[1], MAX_INPUT_LENGTH - 1);

			d->intop--;
		}
		pop_call();
		return;
	}

	ch = d->original ? d->original : d->character;

	/*
		Look for at least one new line.
	*/

	if (d->intop < MAX_INPUT_LENGTH -10)
	{
		for (i = 0 ; d->inbuf[i] != '\n' && d->inbuf[i] != '\r' ; i++)
		{
			if (d->inbuf[i] == '\0')
			{
				pop_call();
				return;
			}
		}
	}

	for (i = k = 0 ; d->inbuf[i] != '\n' && d->inbuf[i] != '\r' ; i++)
	{
		if (i >= MAX_INPUT_LENGTH - 20)
		{
			write_to_buffer(d, "Line too long.\n\r", 0);

			d->inbuf[i]	= '\n';
			d->inbuf[i+1]	= '\0';
			d->intop		= i+1;
			break;
		}

		if (d->inbuf[i] == '\b' && k > 0)
		{
			--k;
		}
		else if (isascii(d->inbuf[i]) && isprint(d->inbuf[i]))
		{
			d->incomm[k++] = d->inbuf[i];
		}
	}
	/*
		Finish off the line.
	*/

	if (k == 0)
	{
		d->incomm[k++] = ' ';
	}
	d->incomm[k] = '\0';

	/*
		Do '!' substitution.
	*/

	if ((d->incomm[0] == '.' || d->incomm[0] == '!')
	&&   d->connected >= CON_PLAYING
	&&   !IS_SET(ch->pcdata->interp, INTERP_ALIAS))
	{
		if (tolower(d->incomm[1]) >= 'a' && tolower(d->incomm[1]) <= 'z')
		{
			str_cpy_max(d->incomm, ch->pcdata->back_buf[tolower(d->incomm[1]) - 'a'], MAX_INPUT_LENGTH);
		}
		else
		{
			strcpy( d->incomm, d->inlast );
		}
	}
	else
	{
		if (d->connected >= CON_PLAYING
		&&  d->incomm[0] != '\0'
		&& !IS_SET(ch->pcdata->interp, INTERP_ALIAS))
		{
			RESTRING(d->inlast, d->incomm);

			if (tolower(d->incomm[0]) >= 'a' && tolower(d->incomm[0]) <= 'z')
          	{
				STRFREE(ch->pcdata->back_buf[tolower(d->incomm[0]) - 'a']);
				ch->pcdata->back_buf[tolower(d->incomm[0]) - 'a'] = STRDUPE(d->inlast);
			}
		}
	}

	if (d->snoop_by && d->character && d->incomm[0] != '\n')
	{
		sh = d->snoop_by->original ? d->snoop_by->original : d->snoop_by->character;
		if (sh != NULL && sh->desc != NULL && sh->desc->character == sh)
		{
			ch_printf_color(sh, "{168}%s {078}[{178}%s{078}]\n\r", ch->name, d->incomm);
		}
	}

	/*
		Shift the input buffer.
	*/

	while (d->inbuf[i] == '\n' || d->inbuf[i] == '\r')
	{
		i++;
	}

	for (j = 0 ; j + i < d->intop ; j++)
	{
		d->inbuf[j] = d->inbuf[i+j];
	}
	d->intop -= i;

	memset(&d->inbuf[d->intop], 0, MAX_INPUT_LENGTH - d->intop);

	pop_call();
	return;
}

void do_copyover (CHAR_DATA *ch, char * argument)
{
	FILE 		*fp;
	PLAYER_GAME	*gpl;
	DESCRIPTOR_DATA *d, *d_next;
	char buf[100], cport[10], ccontrol[10], cboot[20];

	push_call("do_copyover(%p,%p)",ch,argument);

	log_printf("COPYOVER by %s: port = %d, control = %d", ch->name, mud->port, mud->control);

	fp = my_fopen (COPYOVER_FILE, "w", FALSE);

	if (!fp)
	{
		perror(COPYOVER_FILE);
		ch_printf_color(ch, "Copyover file not writeable, aborted.\n\r");

		pop_call();
		return;
	}

	sprintf(buf, "%s", ansi_translate("\n\r {138}OOC: *** COPYOVER in progress - please remain seated! ***\n\r"));

	log_printf("COPYOVER: saving all clans.");
	save_all_clans();

	log_printf("COPYOVER: saving modified areas.");
	do_savearea(NULL, "forreal");

	log_printf("COPYOVER: saving hiscores.");
	save_hiscores();

	log_printf("COPYOVER: saving timeinfo.");
	save_timeinfo();

	log_printf("COPYOVER: saving storerooms.");
	save_lockers();

	log_printf("COPYOVER: saving skill tables.");
	save_classes();
// 	save_races();
	save_domains();
	save_styles();
	fwrite_components();

	log_printf("COPYOVER: saving players.");

	for (gpl = mud->f_player ; gpl ; gpl = gpl->next)
	{
		save_char_obj(gpl->ch, NORMAL_SAVE);
	}

	log_printf("COPYOVER: saving descriptors.");

	for (d = mud->f_desc ; d ; d = d_next)
	{
		d_next = d->next;

		if (!d->character || d->connected < 0)
		{
			write_to_descriptor (d, "\n\rSorry, we are rebooting. Come back in one minute.\n\r", 0);
			close_socket(d, TRUE);
		}
		else
		{
			fprintf (fp, "D %d %d %s~ %s~ %s~ %s~\n",
				d->descriptor, d->comm_flags, CH(d)->name, d->account->player_name, d->host, d->terminal_type);

			end_compress(d);

			write_to_descriptor(d, buf, 0);
		}
	}
	fprintf(fp, "$\n");
	my_fclose (fp);

	my_fclose(fpReserve);
	my_fclose(fpAppend);

	/*
		exec - descriptors are inherited
	*/

	sprintf(cport,		"%d", mud->port);
	sprintf(ccontrol,	"%d", mud->control);
	sprintf(cboot,		"%ld", mud->boot_time);

	execl(REAL_FILE, "mud", "-p", cport, "-c", ccontrol, "-b", cboot, (char *) NULL);

	perror ("do_copyover: execl");
	send_to_char ("Copyover FAILED!\n\r",ch);

	/*
		Here you might want to reopen fpReserve
	*/

	if ((fpReserve = my_fopen(NULL_FILE, "r", FALSE)) == NULL)
	{
		log_printf("do_copyover(): fpReserve -> Could not open %s to read.",NULL_FILE);
		perror( NULL_FILE );
		abort( );
	}

	if ((fpAppend = my_fopen(NULL_FILE, "r", FALSE)) == NULL)
	{
		log_printf("do_copyover(): fpAppend -> Could not open %s to read.",NULL_FILE);
		perror( NULL_FILE );
		abort( );
	}
	pop_call();
	return;
}

/* Recover from a copyover - load players */

void copyover_recover ()
{
	DESCRIPTOR_DATA *d;
	FILE *fp;
	char *name, *account, letter;
	int desc;
	bool fOld, fAcct;

	log_printf ("Copyover recovery initiated");

	fp = my_fopen (COPYOVER_FILE, "r", FALSE);

	if (!fp)
	{
		perror ("copyover_recover:my_fopen");
		log_printf ("Copyover file not found. Exiting.\n\r");
		exit (1);
	}

	while (TRUE)
	{
		letter = fread_letter(fp);

		if (letter != 'D')
		{
			if (letter != '$')
			{
				log_printf("***WARNING*** copyover_recovery: bad format");
			}
			fread_to_eol(fp);
			break;
		}

		desc				= fread_number(fp);

		ALLOCMEM(d, DESCRIPTOR_DATA, 1);

		init_descriptor(d, desc);

		d->comm_flags		= fread_number(fp);
		name				= fread_string(fp);
		account			= fread_string(fp);
		d->host			= fread_string(fp);
		d->terminal_type	= fread_string(fp);
		d->connected		= CON_COPYOVER_RECOVER;

		LINK(d, mud->f_desc, mud->l_desc, next, prev);

		mud->total_desc++;

		fAcct = add_account(d, account);
		fOld = load_char_obj(d, name);

		STRFREE(name);

		if (!fOld) /* Player file not found?! */
		{
			write_to_descriptor (d, "\n\rSomehow, your character was lost in the copyover. Please reconnect.\n\r", 0);
			close_socket (d, TRUE);
		}
		else if (!fAcct)
		{
			write_to_descriptor (d, "\n\rError reinitializing account. Please reconnect.\n\r", 0);
			close_socket (d, TRUE);
		}
		else
		{
			if (!d->character->in_room)
			{
				d->character->in_room = get_room_index(ROOM_VNUM_TEMPLE);
			}

			d->connected = CON_PLAYING;

			add_char (d->character);
			add_player( d->character );

			if (IS_SET(d->comm_flags, COMM_FLAG_MCCP))
			{
				start_compress(d);
			}

			char_to_room(d->character, d->character->pcdata->was_in_room, TRUE);

			vt100prompt( d->character);

			enter_game(d->character);
			
			act( "{138}OOC: Copyover complete. Have a nice day!", d->character, NULL, NULL, TO_CHAR);
		}
	}
	my_fclose(fp);

	unlink(COPYOVER_FILE);

	log_printf("Copyover recovery complete");
}

/*
	Low level output function.
*/

bool process_output( DESCRIPTOR_DATA *d, bool fPrompt )
{
	char buf[MAX_STRING_LENGTH];

	push_call("process_output(%p,%p)",d,fPrompt);

	if (d == NULL)
	{
		pop_call();
		return FALSE;
	}

	/*
		Bust a prompt.
	*/

	if (fPrompt && !IS_SET(mud->flags, MUD_EMUD_DOWN) && d->connected == CON_PLAYING)
	{
		CHAR_DATA *ch, *sh;

		ch = CH(d);
		sh = d->character;

		if (!is_desc_valid(sh))
		{
	  		pop_call();
			return FALSE;
		}

		if (ch->pcdata->vt100 == 0)
		{
			if (IS_SET(ch->act, PLR_BLANK))
			{
				sprintf(buf, "%s\n\r", get_color_string(ch, COLOR_PROMPT, VT102_DIM));
			}
			else
			{
				strcpy(buf, "");
			}

			strcat( buf, prompt_return(sh, ch->pcdata->prompt_layout));

			SET_BIT(ch->pcdata->interp, INTERP_SCROLL);

			write_to_buffer( d, buf, 1000000 );

			REMOVE_BIT(ch->pcdata->interp, INTERP_SCROLL);
		}
		else
		{
			vt100prompt(ch);
		}
	}
	pop_call();
	return TRUE;
}

/*
	The buffer that works with the page pauser
*/

int pager( DESCRIPTOR_DATA *d, const char *istr, int lng, char *ostr)
{
	int lines, pt, breakpt, lengt, cnt;
	char pag_buf[MAX_STRING_LENGTH], buf[MAX_INPUT_LENGTH], lcc[10];
	register char *ptt, *pto, *pti;

	push_call("pager(%p,%p,%p,%p)",d,istr,lng,ostr);

	if (d->connected < CON_PLAYING
	||  CH(d)->pcdata->last_command
	||  IS_SET(CH(d)->act, PLR_PAGER)
	||  IS_SET(CH(d)->pcdata->interp, INTERP_PAGE)
	||  IS_SET(CH(d)->pcdata->interp, INTERP_SCROLL))
	{
		REMOVE_BIT(CH(d)->pcdata->interp, INTERP_PAUSE);

		pop_call();
		return (str_cpy_max(ostr, istr, MAX_STRING_LENGTH));
	}

	for (ptt = (char *)istr, pt = 0, lines = 0 ; pt < lng ; pt++, ptt++)
	{
		if (*ptt == '\n')
		{
			lines++;
		}
	}

	breakpt = get_pager_breakpt(CH(d));

	if (lines <= breakpt+1)
	{
		REMOVE_BIT(CH(d)->pcdata->interp, INTERP_PAUSE);

		pop_call();
		return (str_cpy_max(ostr, istr, MAX_STRING_LENGTH));
	}

	SET_BIT(CH(d)->pcdata->interp, INTERP_PAUSE);

	pti = (char *)istr;
	pto = ostr;

	lcc[0] = '\0';

	for (pt = 0, lines = 0 ; lines < breakpt ; pt++, pti++, pto++)
	{
		*pto = *pti;

		if (*pti == '\033' && *(pti+1) == '[')
		{
			for (cnt = 0 ; *(pti+cnt) != 'm' && *(pti+cnt) != '\0' ; cnt++)
			{
				lcc[cnt] = *(pti+cnt);
			}
			lcc[cnt] = 'm';
			cnt++;
			lcc[cnt] = '\0';
		}
		if (*pti == '\n')
		{
			lines++;
		}
	}

	if (*pti == '\r')
	{
		*pto = *pti;
		pto++;
		pti++;
		pt++;
	}

	lengt = pt;

	*pto = '\0';

	for (pto = pag_buf, cnt = 0 ; lcc[cnt] != '\0' ; cnt++, pto++)
	{
		*pto = lcc[cnt];
	}

	for ( ; pt < lng ; pto++, pti++, pt++)
	{
		*pto = *pti;
	}
	*pto = '\0';

	if (CH(d)->pcdata->vt100 != 1)
	{
		sprintf(buf, "%s --------------------------[Press Return to Continue]--------------------------%s\n\r", ansi_translate_text(CH(d), "{128}"), ansi_translate_text(CH(d), "{300}"));
		str_apd_max(ostr, buf, lengt, MAX_STRING_LENGTH);
	}
	STRFREE(CH(d)->pcdata->page_buf);
	CH(d)->pcdata->page_buf = STRALLOC(pag_buf);

	SET_BIT(CH(d)->pcdata->interp, INTERP_PAGE);

	pop_call();
	return( lengt );
}

/*
	The buffers that store in the pcdata for the BUFFER and GREP commands
*/

void scroll( DESCRIPTOR_DATA *d, const char *txi, int lng)
{
	register int cnt;
	register char *pti, *pto;
	register int scr_end;

	push_call("scroll(%p,%p,%p)",d,txi,lng);

	if (d->connected != CON_PLAYING)
	{
		pop_call();
		return;
	}

	if (IS_SET(CH(d)->pcdata->interp, INTERP_SCROLL))
	{
		pop_call();
		return;
	}

	pto  = CH(d)->pcdata->scroll_buf;
	pto += CH(d)->pcdata->scroll_end;
	pti  = (char *)txi;
	scr_end = CH(d)->pcdata->scroll_end;

	for (cnt = 0 ; cnt < lng ; cnt++, pti++)
	{
		*pto = *pti;
		pto++;
		scr_end++;

		if (scr_end == MAX_BUFFER_LENGTH)
		{
			CH(d)->pcdata->scroll_start=1;
			CH(d)->pcdata->scroll_end = 0;
			scr_end = 0;
			pto = CH(d)->pcdata->scroll_buf;
		}
	}
	CH(d)->pcdata->scroll_end = scr_end;

	pop_call();
	return;
}

/*
	Append onto an output buffer.
*/

void write_to_buffer( DESCRIPTOR_DATA *d, char *txt, int length )
{
	char buf[MAX_STRING_LENGTH];
	char txo[MAX_STRING_LENGTH];
	CHAR_DATA *ch, *sh;
	int size;

	push_call("write_to_buffer(%p,%p,%p)",d,txt,length);

	if (d == NULL || txt == NULL)
	{
		pop_call();
		return;
	}

	ch = CH(d);

	if (IS_SET(mud->flags, MUD_SKIPOUTPUT) && length != 1000000)
	{
		if (ch == NULL || IS_NPC(ch) || !IS_PLR(ch, PLR_HOLYLIGHT))
		{
			pop_call();
			return;
		}
	}

	if (ch != NULL)
	{
		if (ch->pcdata->vt100 == 1 && length != 1000000)
		{
			if (txt[0] == '\0')
			{
				pop_call();
				return;
			}
			size = UMIN(strlen(txt), MAX_STRING_LENGTH - 200);

			size = pager(d, txt, size, txo);

			if (length != 1000000)
			{
				scroll(d, txo, size);
			}

			if (IS_SET(ch->pcdata->vt100_flags, VT102_FAST))
			{
				if (d->outtop == 0)
				{
					sprintf(buf, "\033[K\0337\033[%d;1H%s%s\0338", ch->pcdata->vt100_type % 100 - 2, IS_SET(ch->act, PLR_BLANK) ? "\n\r" : "", txo);
				}
				else
				{
					sprintf(buf, "\033[%d;1H%s\0338", ch->pcdata->vt100_type % 100 - 2, txo);
				}
			}
			else
			{
				if (d->outtop == 0)
				{
					sprintf(buf, "\033[K%s\0337\033[%d;1H%s%s\0338", get_color_string(ch, COLOR_PROMPT, VT102_DIM), ch->pcdata->vt100_type % 100 - 2, IS_SET(ch->act, PLR_BLANK) ? "\n\r" : "", txo);
				}
				else
				{
					sprintf(buf, "\033[K%s\0337\033[%d;1H%s\0338", get_color_string(ch, COLOR_PROMPT, VT102_DIM), ch->pcdata->vt100_type % 100 - 2, txo);
				}
			}

			if (IS_SET(ch->pcdata->interp, INTERP_PAUSE))
			{
				cat_sprintf(buf, "%s --------------------------[Press Return to Continue]--------------------------%s", ansi_translate_text(ch, "{128}"), get_color_string(ch, COLOR_PROMPT, VT102_DIM));
			}
		}
		else
		{
			if (ch->pcdata->vt100 == 0 && d->connected >= CON_PLAYING && d->outtop == 0 && !IS_SET(d->comm_flags, COMM_FLAG_COMMAND))
			{
				d->outbuf[0] = '\n';
				d->outbuf[1] = '\r';
				d->outbuf[2] = '\0';
				d->outtop = 2;
			}

			size = UMIN(strlen(txt), MAX_STRING_LENGTH - 100);

			size = pager(d, txt, size, txo);

			if (length != 1000000)
			{
				scroll(d, txo, size);
			}
			strcpy(buf, txo);
		}

		if (d->snoop_by && length != 1000000)
		{
			sh = CH(d->snoop_by);

			if (sh && sh->desc && sh->desc->character == sh)
			{
				if (IS_SET(CH(d)->pcdata->interp, INTERP_PAUSE))
				{
					send_to_char(txo, sh);
				}
				else
				{
					send_to_char(txt, sh);
				}
			}
		}
	}
	else
	{
		if (d->outtop == 0 && d->connected >= CON_PLAYING && !IS_SET(d->comm_flags, COMM_FLAG_COMMAND))
		{
			d->outbuf[0] = '\n';
			d->outbuf[1] = '\r';
			d->outbuf[2] = '\0';
			d->outtop    = 2;
		}
		strcpy(buf, txt);
	}

	d->outtop = str_apd_max(d->outbuf, buf, d->outtop, MAX_STRING_LENGTH);

	pop_call();
	return;
}


bool write_to_descriptor(DESCRIPTOR_DATA *d, char *txt, int length)
{
	push_call("write_to_descriptor(%p,%p,%p)",d,txt,length);

	if (length == 0)
	{
		length = UMIN(strlen(txt), MAX_STRING_LENGTH - d->outtop - 10);
	}
	else
	{
		length = UMIN(length, MAX_STRING_LENGTH - d->outtop - 10);
	}
	memcpy(&d->outbuf[d->outtop], txt, length);

	d->outtop += length;

	write_to_port(d);

	pop_call();
	return TRUE;
}


/*
	Write text to the file descriptor utilizing the port size feature.
	Used to flush the outbuf buffer to the actual socket.
	Chaos 5/17/95
*/

void write_to_port( DESCRIPTOR_DATA *d )
{
	int nWrite, tWrite, nBlock, failure = FALSE;

	push_call("write_to_port(%p)",d);

	if (IS_SET(d->comm_flags, COMM_FLAG_DISCONNECT))
	{
		pop_call();
		return;
	}

	if (d->mccp)
	{
		write_compressed(d);
		pop_call();
		return;
	}

	for (tWrite = 0 ; tWrite < d->outtop ; tWrite += nWrite)
	{
		nBlock = UMIN(d->outtop - tWrite, d->port_size);

		if ((nWrite = write(d->descriptor, d->outbuf + tWrite, nBlock)) < 1)
		{
			if (errno != EAGAIN && errno != ENOSR)
			{
				log_god_printf("write_to_port D%d@%s", d->descriptor, d->host);
				dump_stack();
				SET_BIT(d->comm_flags , COMM_FLAG_DISCONNECT);
			}
			else
			{
				failure = TRUE;
			}
			break;
		}
	}

	if (failure)
	{
		if (++d->timeout < 60)
		{
			log_god_printf("write_to_port: timeout D%d@%s", d->descriptor, d->host);
			SET_BIT(d->comm_flags , COMM_FLAG_DISCONNECT);
		}
		else
		{
			pop_call();
			return;
		}
	}

	d->timeout = 0;

	mud->total_io_bytes += tWrite;

	d->outtop	 = 0;
	pop_call();
	return;
}

/*
	Scandum - 03-05-2002
*/

void display_empty_screen( DESCRIPTOR_DATA *d)
{
	push_call("display_empty_screen(%p)",d);

	if (CH(d) && CH(d)->pcdata->vt100 == 0)
	{
		write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
		write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
		write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
		write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
	}
	else
	{
		write_to_buffer( d, "\033[2J", 1000000);
	}
	pop_call();
	return;
}

void display_class_selections( DESCRIPTOR_DATA *d)
{
	char buf[MAX_INPUT_LENGTH];
	CHAR_DATA *ch;
	int iClass, col;

	push_call("display_class_selection(%p)",d);

	ch = d->character;

	strcpy(buf, "{078}You may choose from the following classes, or type help [class] to learn more:\n\r\n\r");
	write_to_buffer( d, (char *)ansi_translate_text(ch, buf), 1000000);

	for (col = iClass = 0, buf[0] = '\0' ; iClass < MAX_CLASS ; iClass++)
	{
		if (class_table[iClass].pc_class)
		{
			cat_sprintf(buf, "{068} %-16s", class_table[iClass].who_name_long);
		}
		else
		{
			continue;
		}
		if (++col % 4 != 1)
		{
			strcat(buf, "  ");
		}
		if (col % 4 == 0)
		{
			cat_sprintf(buf, "\n\r", buf);
		}
	}
	if (col % 4 != 0)
	{
		cat_sprintf(buf, "\n\r");
	}
	cat_sprintf(buf, "\n\r{078}Please choose a class: {178}");
	write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
	
	pop_call();
	return;
}

/*
 * added to CON_REROLL for stat swapping - Kregor
 */
void display_stats( DESCRIPTOR_DATA *d)
{
	char buf[MAX_INPUT_LENGTH];
	CHAR_DATA *ch;
	int cnt;

	push_call("display_class_selection(%p)",d);

	ch = d->character;

	sprintf( buf, "{078}Stats for %s the %s %s:{068}\n\r\n\r", ch->name,
	race_table[ch->race].race_name,
	class_table[ch->class].who_name_long );
	write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
	sprintf( buf, "{068}Str: {178}%2d{078}(%2d) {068}Dex: {178}%2d{078}(%2d) {068}Con: {178}%2d{078}(%2d) {068}Int: {178}%2d{078}(%2d) {068}Wis: {178}%2d{078}(%2d) {068}Cha: {178}%2d{078}(%2d)\n\r\n\r",
		ch->perm_str, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_STR) ? ch->perm_str + 2 : ch->perm_str + race_table[ch->race].race_mod[0],
		ch->perm_dex, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_DEX) ? ch->perm_dex + 2 : ch->perm_dex + race_table[ch->race].race_mod[1],
		ch->perm_con, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_CON) ? ch->perm_con + 2 : ch->perm_con + race_table[ch->race].race_mod[2],
		ch->perm_int, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_INT) ? ch->perm_int + 2 : ch->perm_int + race_table[ch->race].race_mod[3],
		ch->perm_wis, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_WIS) ? ch->perm_wis + 2 : ch->perm_wis + race_table[ch->race].race_mod[4],
		ch->perm_cha, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_CHA) ? ch->perm_cha + 2 : ch->perm_cha + race_table[ch->race].race_mod[5]);
	write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
	
	strcpy(buf, "{068}Languages Known: {178}");
	for (cnt = 0 ; cnt < MAX_LANG ; cnt++)
	{
		if (IS_SHIFT(ch->language, cnt))
		{
			cat_sprintf(buf, "%s ", lang_names[cnt]);
		}
	}
	strcat(buf, "\n\r");
	
	write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
	send_to_char("Stats in () are after racial mods.\n\r", ch);
	strcpy(buf, "\n\r{078}You may now swap your stats around to suit yourself.");
	cat_sprintf(buf, "\n\r{078}Syntax: Swap <str|dex|con|int|wis|cha> <str|dex|con|int|wis|cha>\n\r'Swap' stats, 'Keep' your PC, or 'Cancel' to start over?\n\r");
	write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);

	pop_call();
	return;
}

/*
	Chaos - 3/21/99
*/
void display_race_selections( DESCRIPTOR_DATA *d )
{
	char buf[MAX_INPUT_LENGTH];
	int iRace, col;
	CHAR_DATA *ch;

	push_call("display_race_selections(%p)", d);

	ch    = d->character;

	strcpy( buf, "{078}You may choose from the following races, or type help [race] to learn more:\n\r\n\r" );
	write_to_buffer( d, (char *)ansi_translate_text(ch, buf), 1000000);
	for (col = iRace = 0, buf[0] = '\0' ; iRace < MAX_RACE ; iRace++)
	{
		if (race_table[iRace].pcrace == 1)
		{
			cat_sprintf(buf, "{068} %-12s", race_table[iRace].race_name);
		}
		else
		{
			continue;
		}
		if (++col % 5 != 1)
		{
			strcat(buf, "  ");
		}
		if (col % 5 == 0)
		{
			cat_sprintf(buf, "\n\r");
		}
	}
	if (col % 5 != 0)
	{
		cat_sprintf(buf, "\n\r");
	}
	cat_sprintf(buf, "\n\r\n\r{078}Please choose a race: {178}" );
	write_to_buffer( d, (char *)ansi_translate_text(ch, buf), 1000000);

	pop_call();
	return;
}

/*
 * Account system nanny stuff - Kregor
 */
void display_account_menu( DESCRIPTOR_DATA *d )
{
	char buf[MAX_INPUT_LENGTH];
	char colw[20], colW[20], colg[20], colY[20];

	push_call("display_account_menu(%p)", d);
	
	strcpy(colw, ansi_translate("{078}"));
	strcpy(colW, ansi_translate("{178}"));
	strcpy(colg, ansi_translate("{068}"));
	strcpy(colY, ansi_translate("{138}"));

	sprintf(buf, "    %sChoose from the following menu options:\n\r\n\r", colw );
	cat_sprintf(buf, "            %s PLAY: %sPlay one of your current PCs.\n\r", colg, colw );
	cat_sprintf(buf, "            %s  NEW: %sCreate a new PC.\n\r", colg, colw );
	cat_sprintf(buf, "            %s  WHO: %sShow who's playing the game.\n\r", colg, colw );
	if (d->account->vt100 != 2)
		cat_sprintf(buf, "            %sVT100: %sTurn VT100 interface ON.\n\r", colg, colw );
	else
		cat_sprintf(buf, "            %sVT100: %sTurn VT100 interface OFF.\n\r", colg, colw );
	if (d->account->ansi)
		cat_sprintf(buf, "            %s ANSI: %sTurn ANSI color OFF.\n\r", colg, colw );
	else
		cat_sprintf(buf, "            %s ANSI: %sTurn ANSI color ON.\n\r", colg, colw );
	cat_sprintf(buf, "            %s QUIT: %sQuit the game.\n\r", colg, colw );
	cat_sprintf( buf, "%s   ========================================================================\n\r", colY );
	cat_sprintf( buf, "\n\r   %sWhat is your choice? ", colw );
	write_to_buffer( d, buf, 1000000);

	pop_call();
	return;
}

void display_get_acct( DESCRIPTOR_DATA *d )
{
	char buf[MAX_INPUT_LENGTH];

	push_call("display_get_acct(%p)", d);

	strcpy( buf, "\n\r\n\rYou must choose a user name for your player account. This is not\n\r" );
	cat_sprintf( buf, "your character name, rather the account you will store your characters\n\r" );
	cat_sprintf( buf, "under. Thus, you may name yourself as you wish, so long as it is not\n\r" );
	cat_sprintf( buf, "vulgar or in some way offensive to other players. See HELP ACCOUNT.\n\r" );
	cat_sprintf( buf, "What is your account name? " );
	write_to_buffer( d, buf, 1000000);

	pop_call();
	return;
}

void display_get_name( DESCRIPTOR_DATA *d )
{
	char buf[MAX_INPUT_LENGTH];

	push_call("display_get_name(%p)", d);

	strcpy( buf, "\n\r\n\r  Now you must choose a name. You must use a name appropriate to a fantasy\n\r" );
	cat_sprintf( buf, "  fiction setting. Well-known names from existing fantasy literature are\n\r" );
	cat_sprintf( buf, "  NOT allowed. No names composed of multiple words (eg, AssKicker, etc)\n\r" );
	cat_sprintf( buf, "  unless it is a race-appropriate fantasy name, no profanity, no HaK3r lingo.\n\r" );
	cat_sprintf( buf, "  In short, take your character name seriously; we will, and the imm staff\n\r" );
	cat_sprintf( buf, "  reserves the right to reject your name.\n\r\n\r" );
	cat_sprintf( buf, "  Type 'cancel' to return to the main menu.\n\r\n\r" );
	cat_sprintf( buf, "  What is your name, young wanderer? " );
	write_to_buffer( d, buf, 1000000);

	pop_call();
	return;
}

/*
 * Adding age disclaimer for muds that need them - Kregor
 */
void display_age_disclaimer( DESCRIPTOR_DATA *d )
{
	char buf[MAX_INPUT_LENGTH];

	push_call("display_age_disclaimer(%p)", d);

	strcpy( buf, "\n\r\n\rThe admins of this server have given it a rating of ADULTS ONLY as defined\n\r" );
	cat_sprintf( buf, "by the ESRB standards. This means during the course of gameplay and roleplay,\n\r" );
	cat_sprintf( buf, "you could encounter graphically depicted violence, strong language, as well as\n\r" );
	cat_sprintf( buf, "sexual content. As such, you must be at least 18 years of age to play on this\n\r" );
	cat_sprintf( buf, "server. By saying 'yes' you are saying you are an adult. If we later find you\n\r" );
	cat_sprintf( buf, "to NOT be an adult, expect to have your account banned.\n\r\n\r" );
	cat_sprintf( buf, "For further information see HELP POLICY.\n\r\n\r" );
	cat_sprintf( buf, "I attest I am 18 years of age or older? (Y)es or (N)o: " );
	write_to_buffer( d, buf, 1000000);

	pop_call();
	return;
}

/*
 * More account system stuff - Kregor
 */
void display_menu_top( DESCRIPTOR_DATA *d )
{
	char buf[MAX_INPUT_LENGTH];

	push_call("display_menu_top(%p)", d);

	strcpy( buf, "{138}                                      !\n\r" );
	cat_sprintf( buf, "{138}                                    _/^\\_\n\r" );
	cat_sprintf( buf, "{138}                                   //////\\\n\r" );
	cat_sprintf( buf, "{138}        |                       | (=======) |                       |\n\r" );
	cat_sprintf( buf, "{138}       /^\\  |                  /^\\ \\////// /^\\                  |  /^\\\n\r" );
	cat_sprintf( buf, "{038}       |O| {138}/^\\                (===){038}|{068}-----{038}|{138}(===)                /^\\ {038}|O|\n\r" );
	cat_sprintf( buf, "{038}       |_| |-|            |^-^|{068}---{038}||{068}-----{038}||{068}---{038}|^-^|            |-| |_|\n\r" );
	cat_sprintf( buf, "{038}       |O| |O|            |{138}/^\\{038}|{138}/^\\{038}||{138}  |  {038}||{138}/^\\{038}|{138}/^\\{038}|            |O| |O|\n\r" );
	cat_sprintf( buf, "{038}       |-| |-|            |{138}|_|{038}|{138}|_|{038}||{138} /^\\ {038}||{138}|_|{038}|{138}|_|{038}|            |-| |-|\n\r" );
	cat_sprintf( buf, "{038}       |O| |O|            |{138}/^\\{038}|{138}/^\\{038}||{138}(   ){038}||{138}/^\\{038}|{138}/^\\{038}|            |O| |O|\n\r" );
	cat_sprintf( buf, "{138}   /______________________=========================________________________\\\n\r" );
	cat_sprintf( buf, "{138}   |___|_________________________[ {068}WELCOME{138} ]___________________________|___|\n\r" );
	write_to_buffer( d, ansi_translate(buf), 1000000);

	pop_call();
	return;
}


void display_char_list( DESCRIPTOR_DATA *d )
{
	char buf[MAX_INPUT_LENGTH];
	char colw[20], colW[20], colg[20];
	int cnt, col;
	ACCOUNT_DATA *acct;

	push_call("display_char_list(%p)", d);
	
	strcpy(colw, ansi_translate("{078}"));
	strcpy(colW, ansi_translate("{178}"));
	strcpy(colg, ansi_translate("{068}"));

	if ((acct = d->account) == NULL)
	{
		write_to_buffer( d, "Error: Could not read account.\n\r", 1000000);
		pop_call();
		return;
	}

	sprintf( buf, "    %sTo play one of your characters, type the name from the list below.\n\r", colw );
	cat_sprintf( buf, "    %sOr, type 'cancel' to return to the main menu.\n\r\n\r", colw );

	for (col = cnt = 0; cnt < MAX_PC ; cnt++)
	{
		if (acct->character[cnt])
		{
			// add this little check to remove deleted PCs from account list - Kregor
			if (pvnum_index[acct->character[cnt]] == NULL || IS_SET(pvnum_index[acct->character[cnt]]->flags, PVNUM_DELETED))
			{
				acct->character[cnt] = 0;
				continue;
			}
			if (!is_string(pvnum_index[acct->character[cnt]]->name))
				cat_sprintf(buf, "        %s%d: %s%-12s\n\r", colg, col+1, colW, "ERROR: Null PC name!");
			else
				cat_sprintf(buf, "        %s%d: %s%-12s\n\r", colg, col+1, colW, pvnum_index[acct->character[cnt]]->name);
		}
		else
		{
			continue;
		}
		++col;
	}
	if (!col)
	{
		sprintf(buf, "%s    You currently have no characters. \n\r   Type 'new' to create one, or 'cancel' to return to the menu.\n\r", colw);
	}
	else 
	{
		cat_sprintf(buf, "\n\r   %sWho shall you be playing today: ", colw);
	}
	write_to_buffer( d, buf, 1000000);
	pop_call();
	return;
}

/*
 * Like WHO for the account screen - Kregor
 */
void display_player_list( DESCRIPTOR_DATA *d )
{
	CHAR_DATA			*wch		= NULL;
	PLAYER_GAME		*fpl		= NULL;
	char buf[MAX_STRING_LENGTH];
	char tmp[MAX_INPUT_LENGTH];
	int nTotal;
	char colw[10], colg[10], colW[10], col2[10];

	push_call("display_player_list(%p)",d);

	strcpy(colg, "{068}");
	strcpy(colw, "{078}");
	strcpy(colW, "{178}");
	strcpy(col2, "{134}");

	nTotal = 0;
	sprintf(buf, "%sPlayers currently online:\n\r\n\r", colg);

	for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
	{
		wch = fpl->ch;

		if (IS_IMMORTAL(wch))
		{
			continue;
		}
		else
		{
			nTotal++;
		}

		sprintf(tmp, "  %s%s%s%-60s                                     ",
			colW, wch->name, colw, ansi_strip(ansi_translate(wch->pcdata->title)));

		tmp[66+strlen(colw)] = '\0';

		cat_sprintf(buf, "%s%s", tmp, col2);

		if (IS_SET(wch->act, PLR_AFK))
			cat_snprintf(buf, 13, "     AFK     ");
		else if (wch->pcdata->switched)
			cat_snprintf(buf, 13, "   Switched  ");
		else if (wch->desc == NULL)
			cat_snprintf(buf, 13, "  Link Lost  ");
		else
			cat_snprintf(buf, 13, "   Unknown   ");
		cat_sprintf(buf, "%s\n\r", colw);
	}
	cat_sprintf(buf, "\n\r%d players.\n\r{078}", nTotal);

	write_to_buffer(d, ansi_translate(buf), 1000000);
	pop_call();
	return;
}

/*
 * Checks if a certain alignment is proper for the chosen class - Kregor
 */
bool class_align(int class, int align, int ethos)
{
	switch (class)
	{
		case CLASS_PALADIN:
			if (align == 1000 && ethos == 1000)
				return TRUE;
			else
				return FALSE;
			break;

		case CLASS_BARBARIAN:
			if (ethos == 1000)
				return FALSE;
			else
				return TRUE;
			break;

		case CLASS_MONK:
			if (ethos != 1000)
				return FALSE;
			else
				return TRUE;
			break;

		case CLASS_DRUID:
		case CLASS_BARD:
			if (ethos != 0 && align != 0)
				return FALSE;
			else
				return TRUE;
			break;
	}
	return TRUE;
}

bool language_fixer(CHAR_DATA *ch)
{
	bool fixed = FALSE;
	
	push_call("language_fixer(%p)",ch);
	
	if (!IS_SET(ch->language, race_table[ch->race].understands))
	{
		SET_BIT(ch->language, race_table[ch->race].understands);
		fixed = TRUE;
	}

	if (class_level(ch, CLASS_DRUID) && !IS_SET(ch->language, LANG_DRUIDIC))
	{
		SET_BIT(ch->language, LANG_DRUIDIC);
		fixed = TRUE;
	}
	if (!class_level(ch, CLASS_DRUID) && IS_SET(ch->language, LANG_DRUIDIC))
	{
		REMOVE_BIT(ch->language, LANG_DRUIDIC);
		fixed = TRUE;
	}
	if (class_level(ch, CLASS_ROGUE) && !IS_SET(ch->language, LANG_THIEVESCANT))
	{
		SET_BIT(ch->language, LANG_THIEVESCANT);
		fixed = TRUE;
	}
	if (!class_level(ch, CLASS_ROGUE) && IS_SET(ch->language, LANG_THIEVESCANT))
	{
		REMOVE_BIT(ch->language, LANG_THIEVESCANT);
		fixed = TRUE;
	}
	if (ch->speak == 0)
	{
		ch->speak = race_table[ch->race].speaks;
		fixed = TRUE;
	}
	if (!IS_SET(ch->language, ch->speak))
	{
		ch->speak = race_table[ch->race].speaks;
		fixed = TRUE;
	}
	pop_call();
	return fixed;
}

/*
 * Create starting armor for char at creation based on class.
 * Also create deity favored weapon for cleric,
 * and spellbook for wizard - Kregor 7/20/12
 */
void starting_equip( CHAR_DATA *ch )
{
	OBJ_DATA *obj;
	int type, vnum, material;
	char *desc;

	push_call("starting_equip(%p)", ch);

	if (ch->class == CLASS_CLERIC && ch->god)
	{
		if ((obj = create_object(get_obj_index(OBJ_VNUM_CLERIC_WEAPON), 0)) != NULL)
		{
			obj->value[0] = god_table[ch->god].favored_weapon;
			switch (weapon_table[obj->value[0]].material)
			{
				case MATERIAL_TYPE_HIDE:
					material = MATERIAL_LEATHER;
					break;
				case MATERIAL_TYPE_METAL:
					material = MATERIAL_STEEL;
					break;
				case MATERIAL_TYPE_WOOD:
					material = MATERIAL_HARDWOOD;
					break;
			}
			obj->material = material;
			desc = replace_str(weapon_table[obj->value[0]].name, "XxX", obj->name);
			RESTRING(obj->name, desc);
			desc = replace_str(weapon_table[obj->value[0]].name, "XxX", obj->short_descr);
			RESTRING(obj->short_descr, desc);
			desc = replace_str(weapon_table[obj->value[0]].name, "XxX", obj->long_descr);
			RESTRING(obj->long_descr, desc);
			obj_to_char(obj, ch);
		}
	}
	
	if (ch->class == CLASS_WIZARD)
	{
		if ((obj = create_object(get_obj_index(OBJ_VNUM_SPELLBOOK), 0)) != NULL)
		{
			obj_to_char(obj, ch);
		}
	}

	// set the armor type based on starting class
	switch (ch->class)
	{
		case CLASS_DRUID:
		case CLASS_BARBARIAN:
		case CLASS_RANGER:
			type = ARMOR_TYPE_HIDE;
			material = MATERIAL_HIDE;
			break;
		case CLASS_ROGUE:
		case CLASS_BARD:
			type = ARMOR_TYPE_LEATHER;
			material = MATERIAL_LEATHER;
			break;
		case CLASS_FIGHTER:
		case CLASS_PALADIN:
		case CLASS_CLERIC:
			type = ARMOR_TYPE_SCALE_MAIL;
			material = MATERIAL_STEEL;
			break;
		default:
			pop_call();
			return;
	}

	for (vnum = OBJ_VNUM_STARTING_ARMOR ; vnum <= OBJ_VNUM_STARTING_HELM ; vnum++)
	{
		if ((obj = create_object(get_obj_index(vnum), 0)) == NULL)
			continue;

		obj->value[0] = type;
		obj->material = material;
		desc = replace_str(armor_table[type].name, "XxX", obj->name);
		RESTRING(obj->name, desc);
		desc = replace_str(armor_table[type].name, "XxX", obj->short_descr);
		RESTRING(obj->short_descr, desc);
		desc = replace_str(armor_table[type].name, "XxX", obj->long_descr);
		RESTRING(obj->long_descr, desc);
		obj_to_char(obj, ch);
		wear_obj(ch, obj, TRUE, -1, FALSE);
	}
	pop_call();
	return;
}


bool nanny(DESCRIPTOR_DATA *d, char *argument )
{
	char buf[MAX_STRING_LENGTH],buf2[MAX_STRING_LENGTH];
	BAN_DATA *pban;
	CHAR_DATA *ch;
	ACCOUNT_DATA *acct;
	int iClass,iRace,iGod,iLang,cnt,col,align = 0,ethos = 0;
	bool found = FALSE;
	bool fOld;
	bool outp;

	push_call_format("nanny(%d,%s)",d->connected,argument);

	outp = FALSE;
	while ( isspace((int)*argument) )
	{
		argument++;
	}

	ch = d->character;

	{
		switch ( d->connected )
		{
			default:
				bug( "Nanny: bad d->connected %d.", d->connected );
				close_socket( d , TRUE);
				pop_call();
				return(outp);

			case CON_GET_ACCOUNT:
				if ( argument[0] == '\0' )
				{
					close_socket( d , TRUE);
					pop_call();
					return(outp);
				}

				argument[0] = UPPER(argument[0]);

				if (!check_parse_name(argument, FALSE))
				{
					write_to_buffer(d, "\n\rIf you wish to create a new account, enter 'NEW' as your name.\n\r\n\rWho art thou: ", 1000000);
					pop_call();
					return(outp);
				}

				acct = lookup_account( argument );

				d->account   = NULL;
				fOld         = FALSE;

				/*
					kick out halfway connected connections with same name
				*/

				remove_bad_desc_acct( argument );

				if (acct == NULL)
				{
					fOld = add_account( d, argument );

					if (d->account == NULL)
					{
						write_to_buffer( d, "Your account is faulty. Contact the admins.\n\r", 0 );
						close_socket( d , TRUE);
						pop_call();
						return(outp);
					}
					acct						= d->account;
					d->lookup_acct	= FALSE;

					if (IS_SET(mud->flags, MUD_WIZLOCK) && !IS_SET(d->account->functions, FUNCTION_GOD))
					{
						write_to_buffer(d, "The game is wizlocked.\n\r", 0 );
						close_socket( d , TRUE);
						pop_call();
						return(outp);
					}

					if (acct->player_name == NULL || strcasecmp(acct->player_name, argument))
					{
						write_to_buffer( d, "Your account is faulty. Contact the admins.\n\r", 0 );
						close_socket( d , TRUE);
						pop_call();
						return(outp);
					}
				}
				else
				{
					write_to_buffer( d, "This account is already being played.\n\r", 0 );
					close_socket( d , TRUE);
					pop_call();
					return(outp);
				}

				if (acct->player_name == NULL)
				{
					log_string( "Found nullified account" );
					close_socket(d, TRUE);
					pop_call();
					return(outp);
				}

				if (!strcasecmp(argument, "new"))
				{
					for (pban = mud->f_nban ; pban ; pban = pban->next)
					{
						if (!str_prefix(pban->name, d->host))
						{
							write_to_descriptor(d, "\n\rYou may not create new accounts from this site.\n\r\n\rWho art thou: ", 0 );
							pop_call();
							return (outp);
						}
					}
					free_account( d->account );
					d->character = NULL;
					display_get_acct( d );
					d->connected = CON_GET_NEW_ACCOUNT;
					pop_call();
					return( outp );
				}

				if (fOld)
				{
					/*
						Old player
					*/

					if (IS_SET(d->account->flags, ACCT_DENIED) || d->account->strikes >= 3)
					{
						log_god_printf("Denying access to %s@%s.", argument, d->host );

						write_to_buffer( d, "You are denied access.\n\r", 0 );
						if (!IS_SET(d->account->flags, ACCT_DENIED))
							SET_BIT(d->account->flags, ACCT_DENIED);
						close_socket( d , TRUE);
						pop_call();
						return(outp);
					}

					if (d->lookup_acct == FALSE)
					{
						send_naws(d);
					}
					send_echo_off(d);

					write_to_buffer( d, "Password: ", 1000000 );
					d->connected = CON_GET_OLD_PASSWORD;

					pop_call();
					return(outp);
				}
				else
				{
					write_to_buffer(d, "\n\rIf you wish to create a new account, enter 'NEW' as your name.\n\r\n\rWho art thou: ", 1000000);
					free_account( d->account );
					d->account = NULL;
					pop_call();
					return(outp);
				}
				break;

			case CON_GET_NEW_ACCOUNT:
				if (!strcasecmp(argument, "new"))
				{
					write_to_buffer( d, "\n\rIllegal name, try another.\n\r\n\rName: ", 1000000 );
					pop_call();
					return(outp);
				}

				if (!check_parse_name(argument, TRUE))
				{
					write_to_buffer( d, "\n\rYou have chosen an illegal name.  Please try another.\n\r\n\rNew name: ", 1000000 );
					pop_call();
					return(outp);
				}

				acct = lookup_account( argument );

				/*
					see if this char is on game now playing
					We don't care if they are not quite connected
				*/
				d->account = NULL;
				remove_bad_desc_acct( argument );

				fOld = FALSE;

				if (acct != NULL)
				{
					fOld = TRUE;
				}
				else
				{
					fOld = add_account(d, argument);
					acct = d->account;
				}

				if (IS_SET(mud->flags, MUD_WIZLOCK))
				{
					write_to_buffer( d, "The game is wizlocked.\n\r", 0 );
					close_socket( d , TRUE);
					pop_call();
					return(outp);
				}

				if (fOld)
				{
					if (d->account != NULL)
					{
						free_account(d->account);
					}
					d->account = NULL;
					acct = NULL;
					write_to_buffer( d, "\n\rYou have chosen a name that already exists. Please try another.\n\r\n\rNew name: ", 1000000 );
					pop_call();
					return( outp );
				}

				/*
					New player
				*/
				send_echo_off(d);
				sprintf(buf, "\n\rYou must now choose a password.  This password must contain at least five\n\rcharacters, with at least one of them being a number.\n\r\n\rGive me a good password for %s: ", acct->player_name);
				write_to_buffer(d, buf, 0);

				d->connected = CON_GET_NEW_PASSWORD;
				break;

			case CON_GET_OLD_PASSWORD:
				if (acct->player_name == NULL)
				{
					log_string( "Found nullified account" );
					close_socket( d , TRUE);
					pop_call();
					return(outp);
				}

				if (encrypt64(argument) != d->account->password)
				{
					write_to_buffer( d, "\n\rWrong password.\n\r", 1000000 );

					if (d->lookup_acct)
					{
						close_socket( d , FALSE);
					}
					else
					{
						close_socket( d , TRUE);
					}
					pop_call();
					return(outp);
				}

				/*
					Determine if it's a new guy over limit
				*/
				if (mud->total_plr >= MAX_LINKPERPORT && !d->lookup_acct && !IS_SET(d->account->functions, FUNCTION_GOD))
				{
					write_to_buffer( d, "\n\rThe game currently has the maximum amount of players online\n\rTry back in a few minutes.\n\r", 1000000 );
					close_socket( d , TRUE);
					pop_call();
					return(outp);
				}
				send_echo_on(d);

				write_to_buffer( d, "\n\r", 0 );

				if ((ch = check_reconnecting(d, acct)) != NULL)
				{
					log_god_printf("%s@%s has reconnected.  D%d", ch->name, d->host, d->descriptor );
					ch->desc = d;

					d->connected		= CON_PLAYING;
					d->original			= NULL;
					d->character		= ch;
					d->incomm[0]		= '\0';
					ch->pcdata->interp	= 0;
					outp				= TRUE;

					if (acct->vt100 != 0)
					{
						acct->vt100 = ch->pcdata->vt100 = 2;
						vt100prompt(ch);
					}
					send_to_char( "Reconnecting.\n\r",  ch);

					RESTRING(ch->pcdata->host, d->host);

					check_auth_state( ch );

					send_naws(d);
				}
				else
				{
					display_empty_screen(d);
					display_menu_top(d);
					display_account_menu(d);
					d->connected = CON_ACCOUNT_MENU;
				}
				d->lookup_acct = FALSE;
				break;

			case CON_GET_NEW_PASSWORD:
				write_to_buffer( d, "\n\r", 2 );

				if ( strlen(argument) < 5 )
				{
					write_to_buffer( d,	"\n\rPassword must be at least five characters long.\n\r\n\rPassword: ", 1000000 );
					pop_call();
					return(outp);
				}

				if( !is_valid_password( argument ) )
				{
					write_to_buffer( d,"\n\rThat password is not acceptable, try again.\n\r\n\rPasswords may only contain letters (case sensitive), or numbers.\n\rAt least one number is required in the password.\n\r\n\rPassword: ", 1000000 );
					pop_call();
					return(outp);
				}

				d->account->password = encrypt64(argument);

				write_to_buffer( d, "\n\rPlease retype the password to confirm: ", 1000000 );
				d->connected = CON_CONFIRM_NEW_PASSWORD;
				break;

			case CON_CONFIRM_NEW_PASSWORD:
				write_to_buffer( d, "\n\r", 2 );

				if (encrypt64(argument) != d->account->password)
				{
					write_to_buffer( d, "\n\rPasswords don't match.\n\r\n\rRetype password: ", 1000000 );
					d->connected = CON_GET_NEW_PASSWORD;
					pop_call();
					return(outp);
				}

				write_to_buffer( d, "\n\r", 1000000);
				send_echo_on(d);

				display_empty_screen(d);
				display_age_disclaimer(d);
				d->connected = CON_GET_NEW_AGE;
				break;

			case CON_GET_NEW_AGE:
				switch (tolower(*argument))
				{
					case 'y':
						acct->consent	= TRUE;
						break;

					case 'n':
						close_socket( d , TRUE);
						pop_call();
						return(outp);
						break;

					default:
						write_to_buffer( d, "\n\rPlease type Yes or No? ", 1000000 );
						pop_call();
						return(outp);
						break;
				}

				write_to_buffer( d, "\n\rDo you wish to use ANSI color? (Y)es or (N)o ", 0);
				d->connected = CON_ANSI;
				break;

			case CON_ANSI:
				switch (tolower(*argument))
				{
					case 'y':
						acct->ansi	= TRUE;
						d->connected		= CON_VT100;
						break;

					case 'n':
						acct->ansi	= FALSE;
						d->connected		= CON_VT100;
						break;

					default:
						write_to_buffer( d, "\n\rPlease type Yes or No? ", 1000000 );
						break;
				}

				if (d->connected == CON_VT100)
				{
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "Does your client Support the VT102 protocol?  Clients known to support VT102\n\r",	1000000 );
					write_to_buffer( d, "are: Zmud, Putty, Windows Telnet, Unix Telnet, SoTerm and WinTin++.\n\r",			1000000 );
					write_to_buffer( d, "You can download WinTin++ for free at: http://tintin.sourceforge.net\n\r\n\r",		1000000 );
					write_to_buffer( d, "Do you wish to use the VT102 protocol? (Y)es or (N)o ", 								1000000 );
				}
				break;

			case CON_VT100:
				switch (tolower(*argument))
				{
					case 'y':
						acct->vt100	= 2;
						d->connected		= CON_ACCOUNT_MENU;
						break;

					case 'n':
						acct->vt100	= 0;
						d->connected		= CON_ACCOUNT_MENU;
						break;

					default:
						write_to_buffer( d, "\n\rPlease type Yes or No? ", 1000000 );
						break;
				}
				if (d->connected == CON_ACCOUNT_MENU)
				{
					display_empty_screen(d);
					display_menu_top(d);
					display_account_menu(d);
				}
				break;

			case CON_ACCOUNT_MENU:
				one_argument(argument, buf2);
				
				if (!strcasecmp(buf2, "new"))
				{
					bool fAvail = FALSE;
					bool fBanned = FALSE;
					
					for (pban = mud->f_nban ; pban ; pban = pban->next)
					{
						if (!str_prefix(pban->name, d->host))
						{
							write_to_descriptor(d, "\n\rYou may not create new characters from this site.\n\r\n\r   Please choose another option: ", 0 );
							pop_call();
							return (outp);
						}
					}
					for (cnt = 0 ; cnt < 5 ; cnt++)
					{
						if (acct->character[cnt]
						&& pvnum_index[acct->character[cnt]] != NULL
						&& !IS_SET(pvnum_index[acct->character[cnt]]->flags, PVNUM_DELETED))
							continue;
						if (acct->character[cnt]
						&& pvnum_index[acct->character[cnt]] != NULL
						&& IS_SET(pvnum_index[acct->character[cnt]]->flags, PVNUM_FROZEN|PVNUM_DENIED|PVNUM_ARRESTED))
							fBanned = TRUE;

						fAvail = TRUE;
					}
					if (fBanned)
					{
						write_to_buffer(d, "\n\r   You cannot create another alt while one of your PCs is being punished.\n\r   Please choose another option: ", 1000000);
						pop_call();
						return(outp);
					}
					if (!fAvail)
					{
						write_to_buffer(d, "\n\r   You cannot create anymore alts without deleting one of your others.\n\r   Please choose another option: ", 1000000);
						pop_call();
						return(outp);
					}
					display_empty_screen(d);
					display_get_name( d );
					d->connected = CON_GET_NEW_NAME;
					break;
				}
				
				if (!strcasecmp(buf2, "play"))
				{
					for (cnt = 0 ; cnt < 5 ; cnt++)
					{
						if (!acct->character[cnt])
							continue;
						// if pvnum index is null or deleted, clear it out - Kregor
						if (pvnum_index[acct->character[cnt]] == NULL || IS_SET(pvnum_index[acct->character[cnt]]->flags, PVNUM_DELETED))
						{
							acct->character[cnt] = 0;
							continue;
						}
					}
					display_empty_screen(d);
					display_menu_top(d);
					display_char_list( d );
					d->connected = CON_GET_NAME;
					break;
				}
				
				if (!strcasecmp(buf2, "quit"))
				{
					save_account(d);
					send_goodbye(d);
					write_to_port(d);
					*d->inbuf = '\0';
					close_socket (d, TRUE);

					pop_call();
					return(outp);
				}

				if (!strcasecmp(buf2, "who"))
				{
					display_empty_screen(d);
					display_player_list(d);

					pop_call();
					return(outp);
				}

				if (!strcasecmp(buf2, "") || !strcasecmp(buf2, " "))
				{
					display_empty_screen(d);
					display_menu_top(d);
					display_account_menu(d);

					pop_call();
					return(outp);
				}

				if (d->connected == CON_GET_NAME)
				{
					display_empty_screen(d);
					display_char_list(d);
					pop_call();
					return(outp);
				}
				
				if (d->connected == CON_ACCOUNT_MENU)
				{
					write_to_buffer(d, "\n\r   That is not a valid option.\n\r   Please choose an option: ", 1000000);
					pop_call();
					return(outp);
				}
				break;

			case CON_GET_NAME:
				argument = one_argument(argument, buf2);
				
				if (buf2[0] == '\0' || !strcasecmp(buf2, "cancel"))
				{
					d->connected = CON_ACCOUNT_MENU;
					display_empty_screen(d);
					display_menu_top(d);
					display_account_menu(d);

					pop_call();
					return(outp);
				}
				
				if (!strcasecmp(buf2, "new"))
				{
					bool fAvail = FALSE;
					
					for (pban = mud->f_nban ; pban ; pban = pban->next)
					{
						if (!str_prefix(pban->name, d->host))
						{
							write_to_descriptor(d, "\n\rYou may not create new characters from this site.\n\r\n\r   Please choose another option: ", 0 );
							pop_call();
							return (outp);
						}
					}
					for (cnt = 0 ; cnt < 5 ; cnt++)
					{
						if (acct->character[cnt]
						&& pvnum_index[acct->character[cnt]] != NULL
						&& !IS_SET(pvnum_index[acct->character[cnt]]->flags, PVNUM_DELETED))
							continue;

						fAvail = TRUE;
					}
					if (!fAvail)
					{
						write_to_buffer(d, "\n\r   You cannot create anymore alts without deleting one of your others.\n\r   Please choose another option: ", 1000000);
						pop_call();
						return(outp);
					}
					display_empty_screen(d);
					display_get_name( d );
					d->connected = CON_GET_NEW_NAME;
					break;
				}
				
				buf2[0] = UPPER(buf2[0]);

				int value = -1;

				if (is_number(buf2))
					value = atoi(buf2);				
				else if (!check_parse_name(buf2, TRUE))
				{
					write_to_buffer( d, "\n\r   That is not one of your characters.\n\r   If you wish to create a new character, enter 'NEW' at the main menu.\n\r   To load an old PC into this account, add the PCs password.\n\r   (Syntax: <name> <password>)\n\r   Choose a character: ", 1000000 );
					pop_call();
					return(outp);
				}
				
				for (cnt = col = 0 ; cnt < MAX_PC ; cnt++)
				{
					if (!acct->character[cnt])
						continue;

					// if pvnum index is null or deleted, clear it out - Kregor
					if (pvnum_index[acct->character[cnt]] == NULL || IS_SET(pvnum_index[acct->character[cnt]]->flags, PVNUM_DELETED))
					{
						acct->character[cnt] = 0;
						continue;
					}

					col++;

					if (acct->character[cnt] && value == col)
					{
						found = TRUE;
						break;
					}

					if (!strcasecmp(buf2, pvnum_index[acct->character[cnt]]->name))
					{
						found = TRUE;
						break;
					}
				}
				if (!found && *argument == '\0')
				{
					write_to_buffer( d, "\n\r   That is not one of your characters.\n\r   If you wish to create a new character, enter 'NEW' at the main menu.\n\r   To load an old PC into this account, add the PCs password.\n\r   (Syntax: <name> <password>)\n\r   Choose a character: ", 1000000 );
					pop_call();
					return(outp);
				}

				if (value != -1)
				{
					ch = get_char_pvnum(acct->character[cnt]);
					strcpy(buf2, pvnum_index[acct->character[cnt]]->name);
				}
				else
					ch = lookup_char( buf2 );

				d->character = NULL;
				fOld         = FALSE;

				/*
					kick out halfway connected connections with same name
				*/

				remove_bad_desc_name( buf2 );

				if (ch == NULL || ch->pcdata == NULL)
				{
					fOld = load_char_obj( d, buf2 );

					if (d->character == NULL)
					{
						write_to_buffer( d, "   Your character is faulty.  Contact the Gods.\n\r", 0 );
						close_socket( d , TRUE);
						pop_call();
						return(outp);
					}
					ch					= d->character;
					d->lookup		= FALSE;

					if (IS_SET(mud->flags, MUD_WIZLOCK) && !IS_IMMORTAL(ch))
					{
						write_to_buffer(d, "The game is wizlocked.\n\r", 0 );
						close_socket( d , TRUE);
						pop_call();
						return(outp);
					}

					if (ch->name == NULL || strcasecmp(ch->name, buf2))
					{
						write_to_buffer( d, "   Your character is faulty.  Contact the Gods.\n\r", 0 );
						close_socket( d , TRUE);
						pop_call();
						return(outp);
					}
				}
				else
				{
					fOld         = TRUE;
					d->character = ch;
					d->lookup    = TRUE;
				}

				if (ch->name == NULL || ch->pcdata == NULL)
				{
					log_string( "Found nullified character" );
					close_socket(d, TRUE);
					pop_call();
					return(outp);
				}
				
				if (is_string(ch->pcdata->account) && is_string(acct->player_name) && strcasecmp(ch->pcdata->account, acct->player_name))
				{
					write_to_buffer( d, "   That character does not belong to you!", 0 );
					free_char( d->character );
					d->character = NULL;
					pop_call();
					return(outp);
				}
				
				if (!found)
				{
					if (encrypt64(argument) != d->account->password)
					{
						write_to_buffer( d, "\n\r   Passwords don't match.\n\r\n\r   Choose a character: ", 1000000 );
						free_char( d->character );
						d->character = NULL;
						pop_call();
						return(outp);
					}
					for (cnt = 0 ; cnt < MAX_PC ; cnt++)
					{
						if (!acct->character[cnt]
						|| pvnum_index[acct->character[cnt]] == NULL
						|| IS_SET(pvnum_index[acct->character[cnt]]->flags, PVNUM_DELETED))
						{
							acct->character[cnt] = ch->pcdata->pvnum;
							save_account(d);
							found = TRUE;
							break;
						}
					}
				}
				if (!found)
				{
					write_to_buffer( d, "   Could not find an empty slot for this PC.", 0 );
					free_char( d->character );
					d->character = NULL;
					pop_call();
					return(outp);
				}
							
				if (!fOld)
				{
					write_to_buffer(d, "\n\r   If you wish to create a new character, enter 'NEW' at the main menu.\n\r\n\r   Choose a character: ", 1000000);
					free_char( d->character );
					d->character = NULL;
					pop_call();
					return(outp);
				}
				else
				{
					/*
						Old player
					*/

					if (IS_SET(pvnum_index[ch->pcdata->pvnum]->flags, PVNUM_DENIED))
					{
						log_god_printf("Denying access to %s@%s.", buf2, d->host );
						SET_BIT(acct->flags, ACCT_DENIED);
						write_to_buffer( d, "You are denied access.\n\r", 0 );
						close_socket( d , TRUE);
						pop_call();
						return(outp);
					}

					if (d->lookup == FALSE)
					{
						send_naws(d);
					}
					display_empty_screen(d);
					do_help( ch, "motd");
					d->connected = CON_READ_MOTD;
					break;
				}
				break;

			case CON_GET_NEW_NAME:
				if (*argument == '\0')
				{
					write_to_buffer( d, "\n\rPlease type a name, or type 'cancel' to return to the main menu.\n\r\n\rName: ", 1000000 );
					pop_call();
					return(outp);
				}

				if (!strcasecmp(argument, "cancel"))
				{
					d->connected = CON_ACCOUNT_MENU;
					display_empty_screen(d);
					display_menu_top(d);
					display_account_menu(d);

					pop_call();
					return(outp);
				}

				if (!strcasecmp(argument, "new"))
				{
					write_to_buffer( d, "\n\rIllegal name, try another.\n\r\n\rName: ", 1000000 );
					pop_call();
					return(outp);
				}

				if (!check_parse_name(argument, TRUE))
				{
					write_to_buffer( d, "\n\rYou have chosen an illegal name.  Please try another.\n\r\n\rNew name: ", 1000000 );
					pop_call();
					return(outp);
				}

				ch = lookup_char( argument );

				/*
					see if this char is on game now playing
					We don't care if they are not quite connected
				*/
				d->character = NULL;
				remove_bad_desc_name( argument );

				fOld = FALSE;

				if (ch != NULL)
				{
					fOld = TRUE;
				}
				else
				{
					fOld = load_char_obj(d, argument);
					ch = d->character;
				}

				if (IS_SET(mud->flags, MUD_WIZLOCK))
				{
					write_to_buffer( d, "The game is wizlocked.\n\r", 0 );
					close_socket( d , TRUE);
					pop_call();
					return(outp);
				}

				if (fOld)
				{
					if (d->character != NULL)
					{
						free_char(d->character);
					}
					d->character = NULL;
					ch = NULL;
					write_to_buffer( d, "\n\rYou have chosen a name that already exists.  Please try another.\n\r\n\rNew name: ", 1000000 );
					pop_call();
					return( outp );
				}
				ch->pcdata->ansi = acct->ansi;
				ch->pcdata->vt100 = acct->vt100;

				reset_color( ch );
				display_empty_screen(d);

				sprintf( buf, "\n\r{078}You may choose from the following genders:\n\r\n\r{178}    M{068} - Male\n\r{178}    F{068} - Female\n\r\n\r{078}Select your sex: {178}" );
				write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000 );

				d->connected = CON_GET_NEW_SEX;
				break;

			case CON_GET_NEW_SEX:
				switch ( argument[0] )
				{
					case 'm':
					case 'M':
						ch->sex = SEX_MALE;
						break;
					case 'f':
					case 'F':
						ch->sex = SEX_FEMALE;
						break;
					default:
						sprintf( buf, "\n\r{138}That's not a sex.\n\r\n\r{078}Select your sex: {178}" );
						write_to_buffer( d, (char *)ansi_translate_text(ch, buf), 1000000 );
						pop_call();
						return(outp);
				}
				send_naws(d);

				display_empty_screen(d);
				display_race_selections(d);

				d->connected = CON_GET_NEW_RACE;
				break;

			case CON_GET_NEW_RACE:
				argument = one_argument(argument, buf2);

				if (!strcasecmp( buf2, "help") )
				{
					for ( iRace = 0; iRace < MAX_RACE; iRace++ )
					{
						if ( toupper(argument[0]) == toupper(race_table[iRace].race_name[0]) && !str_prefix( argument, race_table[iRace].race_name) )
						{
							ch->race = iRace;
							display_empty_screen(d);
							strcpy( buf, race_table[iRace].race_name );
							do_help(ch, buf);
							write_to_buffer( d, "\n\r", 1000000);
							display_race_selections ( d );
							pop_call();
							return(outp);
						}
					}
					strcpy( buf, "\n\r{138}No help on that topic.\n\r\n\r{078}Please choose a race: {178}" );
					write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
					pop_call();
					return (outp);
				}

				for (iRace = 0 ; iRace < MAX_RACE ; iRace++)
				{
					if ( toupper(buf2[0]) == toupper(race_table[iRace].race_name[0]) && !str_prefix( buf2, race_table[iRace].race_name ) )
					{
						if(race_table[iRace].pcrace == 1)
						{
							ch->race = iRace;
							height_weight(ch);
							break;
						}
						else
						{
							strcpy(buf, "\n\r{138}That is not a PC race\n\r\n\r{078}Please choose a race : {178}" );
							write_to_buffer(d, (char *)ansi_translate_text(ch,buf), 1000000);
							pop_call();
							return (outp);
						}
					}
				}
				if (iRace == MAX_RACE)
				{
					strcpy( buf, "\n\r{138}That's not a race.\n\r\n\r{078}Please choose a race: {178}" );
					write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
					pop_call();
					return(outp);
				}

				display_empty_screen(d);
				display_class_selections(d);
				d->connected = CON_GET_NEW_CLASS;
				break;

			case CON_GET_NEW_CLASS:
					argument = one_argument(argument, buf2);
					if (!strcasecmp( buf2, "help") )
					{
						for ( iClass = 0; iClass < MAX_CLASS; iClass++ )
						{
							if (toupper(argument[0]) == toupper(class_table[iClass].who_name_long[0]) && !str_prefix(argument, class_table[iClass].who_name_long))
							{
								display_empty_screen(d);

								strcpy( buf, "{068}");
								write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
								strcpy( buf, class_table[iClass].who_name_long );
								do_help(ch, buf);
								write_to_buffer( d, "\n\r", 1000000);
								display_class_selections( d );
								pop_call();
								return(outp);
							}
						}
						strcpy( buf, "\n\r{138}No help on that topic.\n\r\n\r{078}Please choose a class: {178}");
						write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
						pop_call();
						return (outp);
					}

					for (iClass = 0 ; iClass < MAX_CLASS ; iClass++)
					{
						if (toupper(buf2[0]) == toupper(class_table[iClass].who_name_long[0])
							&&   !str_prefix( buf2, class_table[iClass].who_name_long ))
						{
							ch->class = iClass;
							break;
						}
					}

					if ( iClass == MAX_CLASS )
					{
						strcpy(buf, "\n\r{138}That's not a class.\n\r\n\r{078}Please choose a class: {178}");
						write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
						pop_call();
						return(outp);
					}

					if( !class_table[iClass].pc_class )
					{
						strcpy( buf, "\n\r\n\r{068}That class is not allowed for starting PCs.\n\r{078}Please choose another class: {178}");
						write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
						pop_call();
						return(outp);
					}
					roll_race(ch);
					display_empty_screen(d);
					display_stats(d);
					d->connected = CON_REROLL;
					break;

			case CON_REROLL:
				switch ( *argument )
				{
					case 'c':
					case 'C':
						ch->perm_str = ch->perm_dex = ch->perm_con = ch->perm_int = ch->perm_wis = ch->perm_cha = 0;
						display_empty_screen(d);
						display_race_selections( d );
						d->connected = CON_GET_NEW_RACE;
						break;

					case 'k':
					case 'K':
						if (ch->class == CLASS_PALADIN)
						{
							ch->alignment = 1000;
							ch->ethos = 1000;
							display_empty_screen(d);
							d->connected = CON_GET_LANGS;
							break;
						}
						display_empty_screen(d);
						d->connected = CON_GET_ALIGNMENT;
						break;

					default:
						argument = one_argument(argument, buf);
						swap(ch, argument);
						d->connected = CON_REROLL;
						break;
				}

				if (d->connected == CON_GET_LANGS)
				{
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "You can now choose to add to your starting language, if you can. Every PC\n\r", 1000000 );
					write_to_buffer( d, "starts with one or two languages. You may choose to start with additional\n\r", 1000000 );
					write_to_buffer( d, "languages from your choice of bonus languages, up to a maximum of your INT\n\r", 1000000 );
					write_to_buffer( d, "bonus. Don't worry, if you don't have enough intelligence to learn another\n\r", 1000000 );
					write_to_buffer( d, "language, the code will tell you.\n\r\n\r", 1000000 );
					strcpy(buf, "\n\r{068}You know the following languages:\n\r{078}");
					strcat(buf, flag_string(ch->language, lang_names));
					strcat(buf, "\n\r{068}Your possible bonus languages are: {078}");
					strcat(buf, flag_string(race_table[ch->race].bonus_langs, lang_names));
					strcat(buf, "\n\r\n\r{078}Choose another language, or type 'done': {178}");
					write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
				}
				if (d->connected == CON_GET_ALIGNMENT)
				{
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "Choose an alignment from the list below. Make your selection by number.\n\r",	1000000 );
					write_to_buffer( d, "You can also type 'help' followed by the number of the selection for\n\r",			1000000 );
					write_to_buffer( d, "a description of that alignment.\n\r\n\r",		1000000 );
					write_to_buffer( d, ansi_translate("1{068}] {078}Lawful Good          2{068}] {078}Neutral Good         3{068}] {078}Chaotic Good\n\r"), 1000000 );
					write_to_buffer( d, ansi_translate("4{068}] {078}Lawful Neutral       5{068}] {078}True Neutral         6{068}] {078}Chaotic Neutral\n\r"), 1000000 );
					write_to_buffer( d, ansi_translate("7{068}] {078}Lawful Evil          8{068}] {078}Neutral Evil         9{068}] {078}Chaotic Evil\n\r\n\r"), 1000000 );
					write_to_buffer( d, "Make your selection (1-9):  ", 								1000000 );
				}
				if (d->connected == CON_REROLL)
				{
					if (ch->perm_str+ch->perm_dex+ch->perm_con+ch->perm_int+ch->perm_wis+ch->perm_cha == 0)
						roll_race(ch);
					display_empty_screen(d);

					sprintf( buf, "{078}Stats for %s the %s %s:{068}\n\r\n\r", ch->name,
					race_table[ch->race].race_name,
					class_table[ch->class].who_name_long );
					write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
					sprintf( buf, "{068}Str: {178}%2d{078}(%2d) {068}Dex: {178}%2d{078}(%2d) {068}Con: {178}%2d{078}(%2d) {068}Int: {178}%2d{078}(%2d) {068}Wis: {178}%2d{078}(%2d) {068}Cha: {178}%2d{078}(%2d)\n\r\n\r",
						ch->perm_str, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_STR) ? ch->perm_str + 2 : ch->perm_str + race_table[ch->race].race_mod[0],
						ch->perm_dex, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_DEX) ? ch->perm_dex + 2 : ch->perm_dex + race_table[ch->race].race_mod[1],
						ch->perm_con, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_CON) ? ch->perm_con + 2 : ch->perm_con + race_table[ch->race].race_mod[2],
						ch->perm_int, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_INT) ? ch->perm_int + 2 : ch->perm_int + race_table[ch->race].race_mod[3],
						ch->perm_wis, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_WIS) ? ch->perm_wis + 2 : ch->perm_wis + race_table[ch->race].race_mod[4],
						ch->perm_cha, (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_CHA) ? ch->perm_cha + 2 : ch->perm_cha + race_table[ch->race].race_mod[5]);
					write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);

					strcpy(buf, "{068}Languages Known: {178}");
					for (cnt = 0 ; cnt < MAX_LANG ; cnt++)
					{
						if (IS_SHIFT(ch->language, cnt))
						{
							cat_sprintf(buf, "%s ", lang_names[cnt]);
						}
					}
					strcat(buf, "\n\r");

					write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
					send_to_char("Stats in () are after racial mods.\n\r", ch);
					strcpy(buf, "\n\r{078}You may now swap your stats around to suit yourself.");
					cat_sprintf(buf, "\n\r{078}Syntax: Swap <str|dex|con|int|wis|cha> <str|dex|con|int|wis|cha>\n\r'Swap' stats, 'Keep' your PC, or 'Cancel' to start over?\n\r");
					write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
				}
				break;

			case CON_GET_DEITY:
					argument = one_argument(argument, buf2);
					if (!strcasecmp( buf2, "help") )
					{
						for ( iGod = 0; iGod < MAX_GOD; iGod++ )
						{
							if (toupper(argument[0]) == toupper(god_table[iGod].god_name[0]) && !str_prefix(argument, god_table[iGod].god_name))
							{
								display_empty_screen(d);

								strcpy( buf, "{068}");
								write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
								strcpy( buf, god_table[iGod].god_name );
								do_help(ch, buf);
								write_to_buffer( d, "\n\r", 1000000);
								break;
							}
						}
						strcpy( buf, "\n\r{138}No help on that topic.\n\r\n\r{078}Please choose a deity or 'none': {178}");
						write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
						break;
					}

					for (iGod = 0 ; iGod < MAX_GOD ; iGod++)
					{
						if (toupper(buf2[0]) == toupper(god_table[iGod].god_name[0])
							&&   !str_prefix( buf2, god_table[iGod].god_name ))
						{
							ch->god = iGod;
							break;
						}
					}

					if( !strcasecmp( buf2, "none"))
					{
						ch->god = 0;
					}
					else
					{
						if ( iGod == MAX_GOD )
						{
							strcpy(buf, "\n\r{138}That's not a valid deity.\n\r\n\r{078}Please choose a deity, or 'none': {178}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							break;
						}
	
						if (ch->class == CLASS_CLERIC)
						{
							if (ch->alignment != god_table[iGod].align
							&& ch->ethos != god_table[iGod].ethos)
							{
								strcpy(buf, "\n\r{138}That's not a valid choice for your alignment.\n\r\n\r{078}Please choose a deity, or 'none': {178}");
								write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
								break;
							}
							else
							{
								if ((ch->alignment != god_table[iGod].align
								&& ch->alignment + god_table[iGod].align == 0)
								|| (ch->ethos != god_table[iGod].ethos
								&& ch->ethos + god_table[iGod].ethos == 0))
								{
									strcpy(buf, "\n\r{138}That's not a valid choice for your alignment.\n\r\n\r{078}Please choose a deity, or 'none': {178}");
									write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
									break;
								}
							}
						}
						else if (ch->class == CLASS_DRUID)
						{
							if (!god_table[iGod].domain[DOMAIN_AIR]
							&& !god_table[iGod].domain[DOMAIN_ANIMAL]
							&& !god_table[iGod].domain[DOMAIN_EARTH]
							&& !god_table[iGod].domain[DOMAIN_FIRE]
							&& !god_table[iGod].domain[DOMAIN_MOON]
							&& !god_table[iGod].domain[DOMAIN_PLANT]
							&& !god_table[iGod].domain[DOMAIN_SCALYKIND]
							&& !god_table[iGod].domain[DOMAIN_WATER]
							&& !god_table[iGod].domain[DOMAIN_WEATHER])
							{
								strcpy(buf, "\n\r{138}You must choose a deity with a nature aspect.\n\r\n\r{078}Please choose a deity, or 'none': {178}");
								write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
								break;
							}
						}
						else
						{
							if (ch->alignment > god_table[iGod].align_hi
							||	ch->alignment < god_table[iGod].align_lo
							||	ch->ethos > god_table[iGod].ethos_hi
							||	ch->ethos < god_table[iGod].ethos_lo )
							{
								strcpy(buf, "\n\r{138}That's not a valid choice for your alignment.\n\r\n\r{078}Please choose a deity, or 'none': {178}");
								write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
								break;
							}
						}

						if (!IS_SET(race_table[ch->race].flags, god_table[iGod].race))
						{
							strcpy(buf, "\n\r{138}That's not a valid choice for your race.\n\r\n\r{078}Please choose a deity, or 'none': {178}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							break;
						}
					}

					display_empty_screen(d);
					do_help( ch, "motd");
					d->connected = CON_READ_MOTD;
					break;

			case CON_GET_ALIGNMENT:
				
				argument = one_argument(argument, buf2);
				
				if (!strcasecmp( buf2, "help") )
				{
					switch (atoi(argument))
					{
						default:
							write_to_buffer( d, "\n\rThat's not a valid choice.", 1000000 );
							break;
	
						case 1:
							display_empty_screen(d);
							strcpy( buf, "{068}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							do_help(ch, "lawful good");
							write_to_buffer( d, "\n\r", 1000000);
							break;
	
						case 2:
							display_empty_screen(d);
							strcpy( buf, "{068}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							do_help(ch, "neutral good");
							write_to_buffer( d, "\n\r", 1000000);
							break;
	
						case 3:
							display_empty_screen(d);
							strcpy( buf, "{068}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							do_help(ch, "chaotic good");
							write_to_buffer( d, "\n\r", 1000000);
							break;
	
						case 4:
							display_empty_screen(d);
							strcpy( buf, "{068}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							do_help(ch, "lawful neutral");
							write_to_buffer( d, "\n\r", 1000000);
							break;
	
						case 5:
							display_empty_screen(d);
							strcpy( buf, "{068}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							do_help(ch, "true neutral");
							write_to_buffer( d, "\n\r", 1000000);
							break;
	
						case 6:
							display_empty_screen(d);
							strcpy( buf, "{068}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							do_help(ch, "chaotic neutral");
							write_to_buffer( d, "\n\r", 1000000);
							break;
	
						case 7:
							display_empty_screen(d);
							strcpy( buf, "{068}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							do_help(ch, "lawful evil");
							write_to_buffer( d, "\n\r", 1000000);
							break;
	
						case 8:
							display_empty_screen(d);
							strcpy( buf, "{068}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							do_help(ch, "neutral evil");
							write_to_buffer( d, "\n\r", 1000000);
							break;
	
						case 9:
							display_empty_screen(d);
							strcpy( buf, "{068}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							do_help(ch, "chaotic evil");
							write_to_buffer( d, "\n\r", 1000000);
							break;
					}
					write_to_buffer( d, ansi_translate("1{068}] {078}Lawful Good          2{068}] {078}Neutral Good         3{068}] {078}Chaotic Good\n\r"), 1000000 );
					write_to_buffer( d, ansi_translate("4{068}] {078}Lawful Neutral       5{068}] {078}True Neutral         6{068}] {078}Chaotic Neutral\n\r"), 1000000 );
					write_to_buffer( d, ansi_translate("7{068}] {078}Lawful Evil          8{068}] {078}Neutral Evil         9{068}] {078}Chaotic Evil\n\r\n\r"), 1000000 );
					write_to_buffer( d, "Make your selection (1-9):  ", 								1000000 );
					break;
				}
				switch (atoi(buf2))
				{
					case 1:
						ethos	= 1000;
						align	= 1000;
						break;

					case 2:
						ethos	= 0;
						align	= 1000;
						break;

					case 3:
						ethos	= -1000;
						align	= 1000;
						break;

					case 4:
						ethos	= 1000;
						align	= 0;
						break;

					case 5:
						ethos	= 0;
						align	= 0;
						break;

					case 6:
						ethos	= -1000;
						align	= 0;
						break;

					case 7:
						ethos	= 1000;
						align	= -1000;
						break;

					case 8:
						ethos	= 0;
						align	= -1000;
						break;

					case 9:
						ethos	= -1000;
						align	= -1000;
						break;

					default:
						write_to_buffer( d, "\n\rThat's not a valid choice.\n\r Please choose a number (1-9): ", 1000000 );
						break;
				}
				
				if (!class_align(ch->class, align, ethos))
				{
					write_to_buffer( d, "\n\rThat's not a valid alignment for your class choice.\n\r Please choose another: ", 1000000 );
					break;
				}
				else if ((race_table[ch->race].alignment == -1000 && align == 1000) || (race_table[ch->race].alignment == 1000 && align == -1000))
				{
					write_to_buffer( d, "\n\rThat's not a valid alignment for your race.\n\r Please choose another: ", 1000000 );
					break;
				}
				else
				{
					ch->alignment = align;
					ch->ethos = ethos;
					d->connected = CON_GET_LANGS;
				}

				if (d->connected == CON_GET_LANGS)
				{
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
					write_to_buffer( d, "You can now choose to add to your starting language, if you can. Every PC\n\r", 1000000 );
					write_to_buffer( d, "starts with one or two languages. You may choose to start with additional\n\r", 1000000 );
					write_to_buffer( d, "languages from your choice of bonus languages, up to a maximum of your INT\n\r", 1000000 );
					write_to_buffer( d, "bonus. Don't worry, if you don't have enough intelligence to learn another\n\r", 1000000 );
					write_to_buffer( d, "language, the code will tell you.\n\r\n\r", 1000000 );
					strcpy(buf, "\n\r{068}You know the following languages:\n\r{078}");
					strcat(buf, flag_string(ch->language, lang_names));
					strcat(buf, "\n\r{068}Your possible bonus languages are: {078}");
					strcat(buf, flag_string(race_table[ch->race].bonus_langs, lang_names));
					strcat(buf, "\n\r\n\r{078}Choose another language, or type 'done': {178}");
					write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
				}
				break;

			case CON_GET_LANGS:
					argument = one_argument(argument, buf2);

					if( !strcasecmp( buf2, "done"))
					{
						d->connected = CON_GET_DEITY;
					}
					else
					{
						for (iLang = 0 ; iLang < MAX_LANG ; iLang++)
						{
							if (toupper(buf2[0]) == toupper(lang_names[iLang][0])
								&& !str_prefix( buf2, lang_names[iLang] ))
							{
								break;
							}
						}
	
						if ( iLang == MAX_LANG )
						{
							display_empty_screen(d);
							strcpy(buf, "\n\r{138}That is not a known language in the realms.\n\r");
							strcat(buf, "\n\r{068}You know the following languages:\n\r{078}");
							strcat(buf, flag_string(ch->language, lang_names));
							strcat(buf, "\n\r{068}Your possible bonus languages are: {078}");
							strcat(buf, flag_string(race_table[ch->race].bonus_langs, lang_names));
							strcat(buf, "\n\r\n\r{078}Choose another language, or type 'done': {178}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							break;
						}
	
						if (!IS_SET(ch->language, 1 << iLang) && count_tongues(ch) >= max_tongues(ch))
						{
							display_empty_screen(d);
							strcpy(buf, "\n\r{138}You would need more intelligence to learn more languages.\n\r");
							strcat(buf, "\n\r{068}You know the following languages:\n\r{078}");
							strcat(buf, flag_string(ch->language, lang_names));
							strcat(buf, "\n\r{068}Your possible bonus languages are: {078}");
							strcat(buf, flag_string(race_table[ch->race].bonus_langs, lang_names));
							strcat(buf, "\n\r\n\r{078}Choose another language, or type 'done': {178}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							break;
						}
	
						if (IS_SET(race_table[ch->race].understands, 1 << iLang))
						{
							display_empty_screen(d);
							strcpy(buf, "\n\r{138}You cannot select or deselect an automatic language.\n\r");
							strcat(buf, "\n\r{068}You know the following languages:\n\r{078}");
							strcat(buf, flag_string(ch->language, lang_names));
							strcat(buf, "\n\r{068}Your possible bonus languages are: {078}");
							strcat(buf, flag_string(race_table[ch->race].bonus_langs, lang_names));
							strcat(buf, "\n\r\n\r{078}Choose another language, or type 'done': {178}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							break;
						}
	
						if (!IS_SET(race_table[ch->race].bonus_langs, 1 << iLang))
						{
							display_empty_screen(d);
							strcpy(buf, "\n\r{138}That is not one of your bonus languages.\n\r");
							strcat(buf, "\n\r{068}You know the following languages:\n\r{078}");
							strcat(buf, flag_string(ch->language, lang_names));
							strcat(buf, "\n\r{068}Your possible bonus languages are: {078}");
							strcat(buf, flag_string(race_table[ch->race].bonus_langs, lang_names));
							strcat(buf, "\n\r\n\r{078}Choose another language, or type 'done': {178}");
							write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
							break;
						}

						TOGGLE_BIT(ch->language, 1 << iLang);
						display_empty_screen(d);
						strcpy(buf, "\n\r{068}You know the following languages:\n\r{078}");
						strcat(buf, flag_string(ch->language, lang_names));
						strcat(buf, "\n\r{068}Your possible bonus languages are: {078}");
						strcat(buf, flag_string(race_table[ch->race].bonus_langs, lang_names));
						strcat(buf, "\n\r\n\r{078}Choose another language, or type 'done': {178}");
						write_to_buffer( d, (char *)ansi_translate_text(ch,buf), 1000000);
						break;
					}

					if (d->connected == CON_GET_DEITY)
					{
						write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
						write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
						write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
						write_to_buffer( d, "\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r", 1000000);
						write_to_buffer( d, "You can now choose a deity for your character. A deity is required for\n\r",	1000000 );
						write_to_buffer( d, "certain classes, like paladins and clerics, but optional on others. You\n\r", 1000000 );
						write_to_buffer( d, "have to choose a deity that is applicable to the campaign setting of the\n\r", 1000000 );
						write_to_buffer( d, "MUD. Type 'help <deity name>' to see if the deity is a valid choice.\n\r", 1000000 );
						write_to_buffer( d, "You can also convert later, with immortal and/or priestly intervention.\n\r\n\r", 1000000 );
						write_to_buffer( d, "Note that your alignment choice will affect what deity you can select.\n\r", 1000000 );
						write_to_buffer( d, "Clerics also have to be within one alignment step of their deity. For\n\r", 1000000 );
						write_to_buffer( d, "example, a cleric of a lawful good deity could also be neutral good, or\n\r", 1000000 );
						write_to_buffer( d, "lawful neutral, so long as one axis matches, and the other is only one\n\r", 1000000 );
						write_to_buffer( d, "step away.\n\r\n\r", 1000000 );
						write_to_buffer( d, "Choose a deity, or type 'none': ", 1000000 );
					}
					break;

			case CON_READ_MOTD:
				if (new_notes(ch))
				{
					write_to_buffer( d, ansi_translate_text(ch, "{506}There are new notes.{088}\n\r\n\r"), 0);
				}

				if (ch->pcdata->created <= 0)
				{
					ch->pcdata->created = mud->current_time;
				}
				
				if (language_fixer(ch))
					send_to_char ("Languages messed up, fixing...\n\r", ch);

				if (ch->size == 0)
				{
					ch->size = race_table[ch->race].size;
				}
				
				if (ch->height == 0 || ch->weight == 0)
				{
					height_weight(ch);
				}
				
				if (ch->pcdata->pvnum == 0)
				{
					ch->gold = starting_gold(ch);
					starting_equip(ch);
					ch->carry_weight = get_carry_w(ch);

					ch->pcdata->pvnum = find_free_pvnum();
					add_pvnum(ch);
					add_to_auth(ch);

					for (cnt = 0 ; cnt < 5 ; cnt++)
					{
						if (!acct->character[cnt]
						|| pvnum_index[acct->character[cnt]] == NULL
						|| IS_SET(pvnum_index[acct->character[cnt]]->flags, PVNUM_DELETED))
						{
							acct->character[cnt] = ch->pcdata->pvnum;
							break;
						}
					}
					save_players();
				}

				add_char( ch );
				d->connected	= CON_PLAYING;
				add_player( ch );
				check_auth_state( ch );

				if (ch->pcdata->recall < 1 || room_index[ch->pcdata->recall] == NULL)
				{
					ch->pcdata->recall = ROOM_VNUM_TEMPLE;
				}

				if (ch->pcdata->death_room < 1 || room_index[ch->pcdata->death_room] == NULL)
				{
					ch->pcdata->death_room = ROOM_VNUM_TEMPLE;
				}

				if ( ch->level == 0 )
				{
					log_god_printf("New player %s@%s has entered the game.", ch->name, d->host );

					ch->perm_str += (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_STR) ? 2 : race_table[ch->race].race_mod[0];
					ch->perm_dex += (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_DEX) ? 2 : race_table[ch->race].race_mod[1];
					ch->perm_con += (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_CON) ? 2 : race_table[ch->race].race_mod[2];
					ch->perm_int += (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_INT) ? 2 : race_table[ch->race].race_mod[3];
					ch->perm_wis += (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_WIS) ? 2 : race_table[ch->race].race_mod[4];
					ch->perm_cha += (ch->race == RACE_HUMAN && class_table[ch->class].attr_prime == APPLY_CHA) ? 2 : race_table[ch->race].race_mod[5];

					if (race_table[ch->race].hit_dice > 1)
					{
						int new_class = ch->class;
						
						ch->class = CLASS_MONSTER;
						monster_levels(ch, race_table[ch->race].hit_dice);
						ch->class = new_class;
					}

					ch->level	= 1;
					ch->mclass[ch->class] = 1;
					ch->pcdata->class_level[ch->level] = ch->class;

					/* 1st level x 4 skill points */
					ch->pcdata->practice = ch->pcdata->pract_level[ch->level] = (class_table[ch->class].skill_pts + stat_bonus(FALSE, ch, APPLY_INT)) * 4;
					ch->pcdata->feat_pts = 1; 
					
					if (ch->race == RACE_HUMAN)
					{
						ch->pcdata->practice += 4;
						ch->pcdata->pract_level[ch->level] += 4;
						ch->pcdata->feat_pts += 1;
					}
					/* variant rule, half-humans get half benefits for skill points */
					if (ch->race == RACE_HALFELF || ch->race == RACE_HALFORC)
					{
						ch->pcdata->practice += 2;
						ch->pcdata->pract_level[ch->level] +=2;
					}
					
					ch->max_hit = ch->pcdata->hit_level[ch->level] = class_table[ch->class].hp_max;
					ch->mana[ch->class] = ch->max_mana[ch->class] = get_max_mana(ch,ch->class);
					ch->max_move = ch->pcdata->move_level[ch->level] = ch->move = class_table[ch->class].hp_max / 2 + 200;

					if (ch->class == CLASS_DRUID && !IS_SET(ch->language, LANG_DRUIDIC))
						SET_BIT(ch->language, LANG_DRUIDIC);

					if (ch->class == CLASS_ROGUE && !IS_SET(ch->language, LANG_THIEVESCANT))
						SET_BIT(ch->language, LANG_THIEVESCANT);

					ch->hit = get_max_hit(ch);
					ch->move = get_max_move(ch);

					set_title( ch, ", the Young Adventurer" );

					char_to_room(ch, ROOM_VNUM_SKILLS, TRUE);
					
					ch->gold = starting_gold(ch);
					ch->carry_weight = get_carry_w(ch);
					
					if (acct->vt100 != 0)
					{
						vt100prompt(ch);
					}
					ch->speed = 1;
				}
				else
				{
					log_god_printf("%s@%s has entered the game.", ch->name, d->host );

					if (ch->level < LEVEL_IMMORTAL && room_is_private(room_index[ch->pcdata->was_in_room]))
					{
						char_to_room(ch, ROOM_VNUM_TEMPLE, TRUE);
						send_to_char("The room you quit in is now private.\n\r", ch);
					}
					else
					{
						char_to_room(ch, ch->pcdata->was_in_room, TRUE);
					}
					if (acct->vt100 != 0)
					{
						vt100prompt( ch);
					}
				}
				enter_game(ch);
				break;
		}
	}
	pop_call();
	return(outp);
}


/*
 * The ubiquitious 4d6, drop the lowest die
 */
int best_three_of_four(void)
{
	int die1, die2, die3, die4, total;
	
	push_call("best_three_of_four()");
	
	die1 = dice(1, 6);
	die2 = dice(1, 6);
	die3 = dice(1, 6);
	die4 = dice(1, 6);
	
	total = die1 + die2 + die3 + die4 -
      UMIN(die1, UMIN(die2, UMIN(die3, die4)));	
  
//   if (die1 <= die2 && die1 <= die3 && die1 <= die4)
// 	{
// 		total = die2 + die3 + die4;
// 	}
// 	else if (die2 <= die1  && die2 <= die3 && die2 <= die4)
// 	{
// 		total = die1 + die3 + die4;
// 	}
// 	else if (die3 <= die1  && die3 <= die2 && die3 <= die4)
// 	{
// 		total = die1 + die2 + die4;
// 	}
// 	else if (die4 <= die1  && die4 <= die2 && die4 <= die3)
// 	{
// 		total = die1 + die2 + die3;
// 	}
	
	pop_call();
	return total;
}


void roll_race( CHAR_DATA *ch )
{
	int race;
	race = ch->race;

	push_call("roll_race(%p)",ch);

	ch->perm_str = ch->perm_dex = ch->perm_con = ch->perm_int = ch->perm_wis = ch->perm_cha = 0;

	ch->perm_str = best_three_of_four();
	ch->perm_dex = best_three_of_four();
	ch->perm_con = best_three_of_four();
	ch->perm_int = best_three_of_four();
	ch->perm_wis = best_three_of_four();
	ch->perm_cha = best_three_of_four();
	
	ch->mod_str = 0;
	ch->mod_dex = 0;
	ch->mod_con = 0;
	ch->mod_int = 0;
	ch->mod_wis = 0;
	ch->mod_cha = 0;

	ch->language = race_table[race].understands;
	ch->speak = race_table[race].speaks;
	pop_call();
	return;
}


void swap(CHAR_DATA *ch, char *argument)
{	
	char arg1[MAX_INPUT_LENGTH];
	char arg2[MAX_INPUT_LENGTH];
	int score1, score2, stat1, stat2;
	
	push_call("swap(%p,%p)",ch,argument);
	
	argument = one_argument(argument, arg1);
	argument = one_argument(argument, arg2);	
	
	if (!strcasecmp(arg1, "str"))
	{
		stat1 = APPLY_STR;
		score1 = ch->perm_str;
	}
	else if (!strcasecmp(arg1, "dex"))
	{
		stat1 = APPLY_DEX;
		score1 = ch->perm_dex;
	}
	else if (!strcasecmp(arg1, "con"))
	{
		stat1 = APPLY_CON;
		score1 = ch->perm_con;
	}
	else if (!strcasecmp(arg1, "int"))
	{
		stat1 = APPLY_INT;
		score1 = ch->perm_int;
	}
	else if (!strcasecmp(arg1, "wis"))
	{
		stat1 = APPLY_WIS;
		score1 = ch->perm_wis;
	}
	else if (!strcasecmp(arg1, "cha"))
	{
		stat1 = APPLY_CHA;
		score1 = ch->perm_cha;
	}
	else
	{
		pop_call();
		return;
	}
	
	if (!strcasecmp(arg2, "str"))
	{
		stat2 = APPLY_STR;
		score2 = ch->perm_str;
	}
	else if (!strcasecmp(arg2, "dex"))
	{
		stat2 = APPLY_DEX;
		score2 = ch->perm_dex;
	}
	else if (!strcasecmp(arg2, "con"))
	{
		stat2 = APPLY_CON;
		score2 = ch->perm_con;
	}
	else if (!strcasecmp(arg2, "int"))
	{
		stat2 = APPLY_INT;
		score2 = ch->perm_int;
	}
	else if (!strcasecmp(arg2, "wis"))
	{
		stat2 = APPLY_WIS;
		score2 = ch->perm_wis;
	}
	else if (!strcasecmp(arg2, "cha"))
	{
		stat2 = APPLY_CHA;
		score2 = ch->perm_cha;
	}
	else
	{
		pop_call();
		return;
	}
	
	if (stat1 == stat2)
	{
		pop_call();
		return;
	}
	
	switch (stat1)
	{
		case APPLY_STR:
			ch->perm_str = score2;
			break;
		case APPLY_DEX:
			ch->perm_dex = score2;
			break;
		case APPLY_CON:
			ch->perm_con = score2;
			break;
		case APPLY_INT:
			ch->perm_int = score2;
			break;
		case APPLY_WIS:
			ch->perm_wis = score2;
			break;
		case APPLY_CHA:
			ch->perm_cha = score2;
			break;
	}
	
	switch (stat2)
	{
		case APPLY_STR:
			ch->perm_str = score1;
			break;
		case APPLY_DEX:
			ch->perm_dex = score1;
			break;
		case APPLY_CON:
			ch->perm_con = score1;
			break;
		case APPLY_INT:
			ch->perm_int = score1;
			break;
		case APPLY_WIS:
			ch->perm_wis = score1;
			break;
		case APPLY_CHA:
			ch->perm_cha = score1;
			break;
	}	
	pop_call();
	return;
}

/*
	Parse a name for acceptability.
*/

bool check_parse_name( char *name , bool mobcheck)
{
	push_call("check_parse_name(%p,%p)",name,mobcheck);

	if (is_name(name, "new"))
	{
		pop_call();
		return TRUE;
	}

	/*	Reserved Words	*/

	if (is_name(name, "all auto immortal self god enemy clan target race class someone north east south west down order chaos open close hours hit mana armor damage castle save bak del dmp hours"))
	{
		pop_call();
		return FALSE;
	}

	/*	Length restrictions.*/

	if (strlen(name) <  3)
	{
		pop_call();
		return FALSE;
	}

	if (strlen(name) > 12)
	{
		pop_call();
		return FALSE;
	}

	/*
		Alphanumerics only.
	*/

	{
		char *pc;

		for (pc = name ; *pc != '\0' ; pc++)
		{
			if ((*pc < 'a' || *pc > 'z') && (*pc < 'A' || *pc > 'Z'))
			{
				pop_call();
				return FALSE;
			}
		}

		/*
			No more names containing 'you' - Scandum 16-05-2002
		*/

		for (pc = name ; *(pc+2) != '\0' ; pc++)
		{
			if ((*(pc+0) == 'Y' || *(pc+0) == 'y')
			&&  (*(pc+1) == 'O' || *(pc+1) == 'o')
			&&  (*(pc+2) == 'U' || *(pc+2) == 'u'))
			{
				pop_call();
				return FALSE;
			}
		}
	}

	/*
		Don't allow players to name themself after mobs unless you
		make changes to mob_prog.c - Scandum
	*/

	if (mobcheck)
	{
		int vnum;

		for (vnum = 0 ; vnum < MAX_VNUM ; vnum++)
		{
			if (mob_index[vnum] && is_name(name, mob_index[vnum]->player_name))
			{
				pop_call();
				return FALSE;
			}
		}
	}
	pop_call();
	return TRUE;
}

/*
	Look for link-dead player to reconnect.
	Reworked replacement for original function - Kregor
*/
CHAR_DATA * check_reconnecting( DESCRIPTOR_DATA *d, ACCOUNT_DATA *acct )
{
	DESCRIPTOR_DATA *desc;
	PLAYER_GAME *fpl;
	int cnt;

	push_call("check_reconnecting(%p,%p)",d,acct);

// 	if (d->lookup == FALSE)
// 	{
// 		pop_call();
// 		return NULL;
// 	}
// 	
	for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
	{
		for (cnt = 0 ; cnt < MAX_PC ; cnt++)
		{
			if (fpl->ch->pcdata->pvnum == acct->character[cnt])
			{
				if ((desc = fpl->ch->desc) != NULL)
				{
					desc->original = desc->character = NULL;
					SET_BIT(desc->comm_flags, COMM_FLAG_DISCONNECT);
				}
				pop_call();
				return fpl->ch;
			}
		}
	}

	pop_call();
	return NULL;
}

/*
 * Check for staying at an Inn or camping while offline,
 * called in enter_game - Kregor 
 */
void offline_recover( CHAR_DATA *ch )
{
	AFFECT_DATA *paf, *paf_next;					
 	int hours, hpgain, days, cnt;
  	
	push_call("offline_recover(%p)",ch);

	if((ch->in_room != NULL && IS_SET(ch->in_room->room_flags, ROOM_INN))
	|| IS_AFFECTED(ch, AFF2_CAMPING))
  {
		// treat 1 RL hour offline rest = 1 hour IG rest
		hours = (mud->current_time - ch->pcdata->last_time) / 3600;
		// every 8 RL hours = 8 IG hours of hitpoint regen
		days = (mud->current_time - ch->pcdata->last_time) / 28800;
		hpgain = ch->level * days;

		if (hours >= 1)
		{
			send_to_char ("You rested while you were offline.\n\r", ch);
			
			mana_gain(ch, hours);
			
			if (hours >= 8)
			{
				ch->move = get_max_move(ch);
			}
			else if (hours >= 2)
			{
				if (ch->move < get_max_move(ch) * 2 / 3)
					ch->move += get_max_move(ch) * 2 / 3;
			}
			else if (hours == 1)
			{
				if (ch->move < get_max_move(ch) / 3)
					ch->move = get_max_move(ch) / 3;
			}
		}
		if (hpgain > 0)
		{
			for (paf = ch->first_affect ; paf ; paf = paf_next)
			{
				paf_next = paf->next;

				if (paf->duration >= 0)
					affect_strip(ch, paf->type);
			}
			
			if (!IS_AFFECTED(ch, AFF2_LONGTERM_CARE))
			{
				if( ch->hit + hpgain > get_max_hit(ch) )
					ch->hit = get_max_hit(ch);
				else
					ch->hit = ch->hit + hpgain;
					
				if( ch->nonlethal - (2 * hpgain) < 0 )
					ch->nonlethal = 0;
				else
					ch->nonlethal -= (2 * hpgain);
			}
			else
			{
				ch->hit = get_max_hit(ch);
				ch->nonlethal = 0;
				REMOVE_AFFECT(ch, AFF2_LONGTERM_CARE);
			}
			if (hours >= 8)
			{
				for (cnt = 0 ; *skill_table[cnt].name != '\0' ; cnt++)
					ch->uses[cnt] = 0;
	
				ch->rest = 0;
			}
			else
			{
				ch->rest = hours * 10;
			}
		}
	}
	pop_call();
	return;
}

void enter_game( CHAR_DATA *ch )
{
	push_call("enter_game(%p)",ch);

	if (!IS_IMMORTAL(ch))
		broadcast_channel(ch, format("%s has entered the game.", get_name(ch)), CHANNEL_PLAYER);

	if (ch->pcdata->corpse != NULL)
	{
		obj_to_room(ch->pcdata->corpse, ch->pcdata->corpse_room);
		if (ch->pcdata->corpse->in_room->first_person)
			act( "Glancing around, you notice $p lying here.", ch->pcdata->corpse->in_room->first_person, ch->pcdata->corpse, NULL, TO_ALL);
	}

	if (ch->pcdata->cart != NULL)
	{
		obj_to_room(ch->pcdata->cart, ch->pcdata->cart_room);
	}

	if (ch->desc)
	{
		RESTRING(ch->pcdata->host, ch->desc->host);

		if (!ch->desc->account)
		{
			if ((ch->desc->account = lookup_account(ch->pcdata->account)) == NULL)
			{
				log_printf("enter_game: Character %s entering game without account data!", ch->name);
				send_to_char( "There is an issue with your account. Please contact the server admins.\n\r" , ch);
				close_socket(ch->desc, TRUE);
				pop_call();
				return;
			}
		}	

		if (IS_AFFECTED( ch, AFF_SLEEP))
		{
			ch->position = POS_SLEEPING;
			send_to_char( "You are sleeping.\n\r" , ch);
		}
		else
		{
			ch->position = POS_STANDING;
			do_look( ch, "auto" );
		}
		offline_recover(ch);

		ch->pcdata->last_connect = mud->current_time;
		ch->desc->account->last_connect = ch->pcdata->last_connect;
	}

	scan_object_for_dup(ch, ch->first_carrying);

	pop_call();
	return;
}

void do_lookup( CHAR_DATA *ch, char *arg )
{
	PET_DATA			*pet;
	ROOM_TIMER_DATA	*rtimer;
	ACCOUNT_DATA  *acct;
	char buf[MAX_STRING_LENGTH];
	int cnt;

	push_call("do_lookup(%p,%p)",ch,arg);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}

	buf[0] = '\0';

	if (!strcasecmp(arg, "accounts"))
	{
		sprintf(buf, "{200}%-20s %-20s\n\r", "Account", "Character");
		for (cnt = 0, acct = mud->f_acct ; acct ; acct = acct->next, cnt++)
		{
			cat_sprintf(buf, "{300}%-20s %-20s\n\r", acct->player_name, acct->ch ? acct->ch->name : "None");
		}
	
		cat_sprintf(buf, "{200}   Accounts online: {300}%d\n\r", cnt);
	}
	
	else if (!strcasecmp(arg, "pets"))
	{
		for (cnt = 0, pet = mud->f_pet ; pet ; pet = pet->next, cnt++)
		{
			cat_sprintf(buf, "{300}%s\n\r", pet->ch->name);
		}
		cat_sprintf(buf, "{200}       Pet list: {300}%d\n\r", cnt);
	}

	else if (!strcasecmp(arg, "timers"))
	{
		for (cnt = 0, rtimer = mud->f_room_timer ; rtimer ; rtimer = rtimer->next, cnt++)
		{
			cat_sprintf(buf, "{300}(%7d) {178}%s - %s\n\r", rtimer->vnum, skill_table[rtimer->type].name, rtimer->caster);
		}
		cat_sprintf(buf, "{200}Room Timer list: {300}%d\n\r", cnt);
	}
	
	else
		strcpy(buf, "Syntax: show list <accounts|pets|timers>\n\r");
	
	send_to_char_color(buf, ch);

	pop_call();
	return;
}


/*
 * Replace old function with this one - Kregor
 */
void remove_bad_desc_name( char *name )
{
	DESCRIPTOR_DATA *d;

	push_call("remove_bad_desc_name(%p)",name);

	for (d = mud->f_desc ; d ; d = d->next)
	{
		if (d->connected < CON_PLAYING)
		{
			if (d->character && !strcasecmp(d->character->name, name))
			{
				log_god_printf("failed connect: %s@%s", d->character->name, d->host);
				d->character->desc = NULL;
				extract_char(d->character);
				d->character = NULL;
				SET_BIT(d->comm_flags, COMM_FLAG_DISCONNECT);
			}
		}
	}
	pop_call();
	return;
}

/*
 * Check and clear out link dead account - Kregor
 */
void remove_bad_desc_acct( char *name )
{
	ACCOUNT_DATA *acct;

	push_call("remove_bad_desc_acct(%p)",name);

	for (acct = mud->f_acct ; acct ; acct = acct->next)
	{
		if (!strcasecmp(acct->player_name, name))
		{
			if (!acct->desc)
			{
				free_account(acct);
			}
		}
	}
	pop_call();
	return;
}

/*
 * Check for bad connection, kick out old desc for new login - Kregor
 */
void check_bad_desc( CHAR_DATA *ch )
{
	DESCRIPTOR_DATA *d;

	push_call("check_bad_desc(%p)",ch);

	for (d = mud->f_desc ; d ; d = d->next)
	{
		if (d->connected < CON_PLAYING)
		{
			if (d->character && d->character == ch)
			{
				log_god_printf("failed connect: %s@%s", d->character->name, d->host);
				d->character = NULL;
				SET_BIT(d->comm_flags, COMM_FLAG_DISCONNECT);
			}
		}
	}
	pop_call();
	return;
}

void stop_idling( DESCRIPTOR_DATA *d )
{
	push_call("stop_idling(%p)",d);

	if (d->character == NULL || d->connected < CON_PLAYING)
	{
		pop_call();
		return;
	}

	if (IS_SET(CH(d)->pcdata->interp, INTERP_AUTO))
	{
		pop_call();
		return;
	}

	CH(d)->timer = 0;

	if (CH(d)->in_room != room_index[ROOM_VNUM_LIMBO])
	{
		pop_call();
		return;
	}

	if (CH(d)->level < LEVEL_IMMORTAL && room_is_private(room_index[CH(d)->pcdata->was_in_room]))
	{
		char_to_room( CH(d), ROOM_VNUM_TEMPLE, TRUE );
		send_to_char("The room you were idling in is now private.\n\r", CH(d));
	}
	else if (CH(d)->level < LEVEL_IMMORTAL)
	{
		char_to_room( CH(d), CH(d)->pcdata->was_in_room, TRUE );
	}

	if (CH(d)->level < LEVEL_IMMORTAL)
		act( "$n has returned to this reality.", CH(d), NULL, NULL, TO_CAN_SEE);

	pop_call();
	return;
}

/*
	Write to one room - Scandum
*/

void send_to_room( char *txt, ROOM_INDEX_DATA *room )
{
	CHAR_DATA *ch;

	for (ch = room->first_person ; ch ; ch = ch->next_in_room)
	{
		send_to_char_color(ansi_justify(txt, get_page_width(ch)), ch);
	}
}

/*
	Write to one char.
*/

void send_to_char( const char *txt, CHAR_DATA *ch )
{
	char buf[MAX_STRING_LENGTH];

	push_call("send_to_char(%p,%p)",txt,ch);

	if (txt == NULL || ch == NULL || ch->desc == NULL)
	{
		pop_call();
		return;
	}

	if (IS_SET(mud->flags, MUD_SKIPOUTPUT))
	{
		if (IS_NPC(ch) || !IS_PLR(ch, PLR_HOLYLIGHT))
		{
			pop_call();
			return;
		}
	}

	if (txt[0] != '\033')
	{
		snprintf(buf, MAX_STRING_LENGTH-1, "%s%s", get_color_string(ch, COLOR_TEXT, VT102_DIM),  txt);
		write_to_buffer(ch->desc, (char *)buf, 0);
	}
	else
	{
		write_to_buffer(ch->desc, (char *)txt, 0);
	}
	pop_call();
	return;
}

void send_to_char_color( char *txt, CHAR_DATA *ch )
{
	char buf[MAX_STRING_LENGTH];

	push_call("send_to_char_color(%p,%p)",txt,ch);

	snprintf(buf, MAX_STRING_LENGTH-1, "%s%s", txt, get_color_string(ch, COLOR_TEXT, VT102_DIM));
	send_to_char(ansi_compress(ch, txt, COLOR_TEXT, VT102_DIM), ch);

	pop_call();
	return;
}


/*
	The primary output interface for formatted output.
*/
void act( const char *format, CHAR_DATA *ch, const void *arg1, const void *arg2, int type )
{
	char buf[MAX_STRING_LENGTH];
	CHAR_DATA *to;
	PLAYER_GAME *fpl;
	CHAR_DATA *vch = (CHAR_DATA *) arg2;
	OBJ_DATA *obj1 = (OBJ_DATA  *) arg1;
	OBJ_DATA *obj2 = (OBJ_DATA  *) arg2;
	const char *str;
	const char *i;
	char *point;

	push_call("act( %p,%p,%p,%p,%p)",format,ch,arg1,arg2,type);

	/*
		Discard null and zero-length messages.
	*/
	if (format == NULL || format[0] == '\0')
	{
		pop_call();
		return;
	}

	if (IS_SET(mud->flags, MUD_SKIPOUTPUT))
	{
		if (IS_NPC(ch) || !IS_PLR(ch, PLR_HOLYLIGHT))
		{
			pop_call();
			return;
		}
	}
	
	if (ch == NULL)
	{
		bug( "Act: null ch.", 0 );
		pop_call();
		return;
	}

	if (IS_ACT(ch, ACT_SECRETIVE))
	{
		pop_call();
		return;
	}

	to = ch->in_room->first_person;

	if (type == TO_VICT)
	{
		if (vch == NULL)
		{
			bug( "Act: null vch with TO_VICT.", 0 );
			pop_call();
			return;
		}
		if (vch->in_room == NULL)
		{
			bug( "Act: null victim room.", 0 );
			pop_call();
			return;
		}
		to = vch->in_room->first_person;
	}

	for ( ; to != NULL ; to = to->next_in_room )
	{
		if (to->desc == NULL || !IS_AWAKE(to))
		{
			continue;
		}
		if ( type == TO_CHAR && to != ch )
		{
			continue;
		}
		if ( type == TO_VICT && ( to != vch || to == ch ) )
		{
			continue;
		}
		if ( type == TO_ROOM && to == ch )
		{
			continue;
		}
		if ( type == TO_CAN_SEE && (to == ch || !can_see(to, ch)))
		{
			continue;
		}
		if ( type == TO_SEEHEAR && (to == ch || (!can_see(to, ch) && !can_hear(to, ch))))
		{
			continue;
		}
		if ( type == TO_NOTVICT && (to == ch || to == vch) )
		{
			continue;
		}

		point = buf;
		str = format;

		while ( *str != '\0' )
		{
			if (*str != '$')
			{
				*point++ = *str++;
				continue;
			}
			++str;

			if (arg1 == NULL && *str == 'p')
			{
				log_printf("Act: missing arg1 for code %d.", *str);
				i = "<ppp>";
			}
			else if ( arg2 == NULL && *str >= 'A' && *str <= 'Z' )
			{
				log_printf("Act: missing arg2 for code %d.", *str);
				i = "<A-Z>";
			}
			else
			{
				switch ( *str )
				{
					default:
						log_printf("Act: act bad code %d.", *str);
						i = "<? ?>";
						break;
					case 't':
						i = (char *) arg1;
						break;
					case 'T':
						i = (char *) arg2;
						break;
					case 'n':
						i = PERS( ch, to );
						break;
					case 'N':
						i = PERS( vch, to );
						break;
					case 'e':
						i = he_she  [URANGE(0, ch->sex,  2)];
						break;
					case 'E':
						i = he_she  [URANGE(0, vch->sex, 2)];
						break;
					case 'm':
						i = him_her [URANGE(0, ch->sex,  2)];
						break;
					case 'M':
						i = him_her [URANGE(0, vch->sex, 2)];
						break;
					case 's':
						i = his_her [URANGE(0, ch->sex,  2)];
						break;
					case 'S':
						i = his_her [URANGE(0, vch->sex, 2)];
						break;
					case 'g':
						i = god_table[ch->god].god_name;
						break;
					case 'G':
						i = god_table[vch->god].god_name;
						break;
					case 'p':
						i = OBJD( obj1, to );
						break;
					case 'P':
						i = OBJD( obj2, to );
						break;
					case 'd':
						if (arg2 == NULL || ((char *) arg2)[0] == '\0')
						{
							i = "door";
						}
						else
						{
							i = arg2;
						}
						break;
					case '/':
						i = "\n\r";
						break;
				}
			}
			if (i == NULL)
			{
				log_string("act: i == NULL");
				dump_stack();
				i = "nothing";
			}
			++str;
			while ((*point = *i) != '\0')
			{
				++point, ++i;
			}
		}
		*point++ = '\n';
		*point++ = '\r';
		*point++ = '\0';

		if (buf[0] == '{')
			buf[5] = UPPER(buf[5]);
		else
			buf[0] = UPPER(buf[0]);

		send_to_char_color(ansi_justify(buf, get_page_width(to)), to);
	}

	// echo the act to audience in arena - Kregor 3/18/12
	if (in_area(ch, ROOM_VNUM_ARENA)
	&& (type == TO_NOTVICT || type == TO_ALL || type == TO_ROOM))
	{
		for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
		{
			if (!IS_IMMORTAL(fpl->ch) && blocking(fpl->ch, ch))
				continue;
			if (fpl->ch == ch)
				continue;
			if (fpl->ch->in_room == NULL || in_same_room(ch, fpl->ch)
			|| !in_area(fpl->ch, ROOM_VNUM_ARENA)
			|| !IS_SET(fpl->ch->in_room->room_flags, ROOM_AUDIENCE))
				continue;

			point = buf;
			str = format;
	
			while ( *str != '\0' )
			{
				if (*str != '$')
				{
					*point++ = *str++;
					continue;
				}
				++str;
	
				if (arg1 == NULL && *str == 'p')
				{
					log_printf("Act: missing arg1 for code %d.", *str);
					i = "<ppp>";
				}
				else if ( arg2 == NULL && *str >= 'A' && *str <= 'Z' )
				{
					log_printf("Act: missing arg2 for code %d.", *str);
					i = "<A-Z>";
				}
				switch ( *str )
				{
					default:
						log_printf("Act: act bad code %d.", *str);
						i = "<? ?>";
						break;
					case 't':
						i = (char *) arg1;
						break;
					case 'T':
						i = (char *) arg2;
						break;
					case 'n':
						i = PERS( ch, fpl->ch );
						break;
					case 'N':
						i = PERS( vch, fpl->ch );
						break;
					case 'e':
						i = he_she  [URANGE(0, ch->sex,  2)];
						break;
					case 'E':
						i = he_she  [URANGE(0, vch->sex, 2)];
						break;
					case 'm':
						i = him_her [URANGE(0, ch->sex,  2)];
						break;
					case 'M':
						i = him_her [URANGE(0, vch->sex, 2)];
						break;
					case 's':
						i = his_her [URANGE(0, ch->sex,  2)];
						break;
					case 'S':
						i = his_her [URANGE(0, vch->sex, 2)];
						break;
					case 'g':
						i = god_table[ch->god].god_name;
						break;
					case 'G':
						i = god_table[vch->god].god_name;
						break;
					case 'p':
						i = OBJD( obj1, fpl->ch );
						break;
					case 'P':
						i = OBJD( obj2, fpl->ch );
						break;
					case 'd':
						if (arg2 == NULL || ((char *) arg2)[0] == '\0')
						{
							i = "door";
						}
						else
						{
							i = arg2;
						}
						break;
					case '/':
						i = "\n\r";
						break;
				}
				if (i == NULL)
				{
					log_string("act: i == NULL");
					dump_stack();
					i = "nothing";
				}
				++str;
				while ((*point = *i) != '\0')
				{
					++point, ++i;
				}
			}
			*point++ = '\n';
			*point++ = '\r';
			*point++ = '\0';
	
			if (buf[0] == '{')
				buf[5] = UPPER(buf[5]);
			else
				buf[0] = UPPER(buf[0]);
	
			send_to_char_color(buf, fpl->ch);
		}
	}

	/*
		Placed act_prog triggering in a seperate loop. This way the act prog
		triggers after the message has been sent. - Scandum
	*/
	if (type == TO_VICT)
	{
		to = vch->in_room->first_person;
	}
	else
	{
		to = ch->in_room->first_person;
	}

	for ( ; to ; to = to->next_in_room )
	{
		if (!MP_VALID_MOB(to) || !IS_SET(to->pIndexData->progtypes, ACT_PROG) || !IS_AWAKE(to))
		{
			continue;
		}
		if ( type == TO_CHAR && to != ch )
		{
			continue;
		}
		if ( type == TO_VICT && ( to != vch || to == ch ) )
		{
			continue;
		}
		if ( type == TO_ROOM && to == ch )
		{
			continue;
		}
		if ( type == TO_NOTVICT && (to == ch || to == vch) )
		{
			continue;
		}

		point = buf;
		str = format;
		while ( *str != '\0' )
		{
			if (*str != '$')
			{
				*point++ = *str++;
				continue;
			}
			++str;

			if (arg1 == NULL && *str == 'p')
			{
				i = "<ppp>";
			}
			else if ( arg2 == NULL && *str >= 'A' && *str <= 'Z' )
			{
				i = "<A-Z>";
			}

			switch ( *str )
			{
				default:
					i = "<? ?>";
					break;
				case 't':
					i = (char *) arg1;
					break;
				case 'T':
					i = (char *) arg2;
					break;
				case 'n':
					i = PERS( ch, to );
					break;
				case 'N':
					i = PERS( vch, to );
					break;
				case 'e':
					i = he_she  [URANGE(0, ch->sex,  2)];
					break;
				case 'E':
					i = he_she  [URANGE(0, vch->sex, 2)];
					break;
				case 'm':
					i = him_her [URANGE(0, ch->sex,  2)];
					break;
				case 'M':
					i = him_her [URANGE(0, vch->sex, 2)];
					break;
				case 's':
					i = his_her [URANGE(0, ch->sex,  2)];
					break;
				case 'S':
					i = his_her [URANGE(0, vch->sex, 2)];
					break;
				case 'p':
					i = OBJD( obj1, to );
					break;
				case 'P':
					i = OBJD( obj2, to );
					break;
				case 'd':
					if (arg2 == NULL || ((char *) arg2)[0] == '\0')
					{
						i = "door";
					}
					else
					{
						i = arg2;
					}
					break;
				case '/':
					i = "\n\r";
					break;
			}

			if (i == NULL)
			{
				log_string("act: i == NULL");
				i = "nothing";
			}
			++str;
			while ((*point = *i) != '\0')
			{
				++point, ++i;
			}
		}
		*point++ = '\n';
		*point++ = '\r';
		*point++ = '\0';

		buf[0]   = UPPER(buf[0]);

		mprog_act_trigger(buf, to, ch, NULL, vch);
		oprog_act_trigger(buf, to, ch, NULL, vch);
		rprog_act_trigger(buf, to, ch, NULL, vch);
	}
	pop_call();
	return;
}

void ch_printf(CHAR_DATA *ch, const char *fmt, ...)
{
	char buf[MAX_STRING_LENGTH];
	va_list args;

	va_start(args, fmt);
	vsprintf(buf, fmt, args);
	va_end(args);

	send_to_char(buf, ch);
}

void ch_printf_color(CHAR_DATA *ch, const char *fmt, ...)
{
	char buf[MAX_STRING_LENGTH];
	va_list args;

	va_start(args, fmt);
	vsprintf(buf, fmt, args);
	va_end(args);

	send_to_char_color(buf, ch);
}

void force_help( DESCRIPTOR_DATA *d, char *argument)
{
	AREA_DATA *pArea;
	HELP_DATA *pHelp;

	push_call("force_help(%p,%p)",d,argument);

	for (pArea = mud->f_area ; pArea ; pArea = pArea->next)
	{
		for (pHelp = pArea->first_help ; pHelp ; pHelp = pHelp->next)
		{
			if (is_name(argument, pHelp->keyword))
			{
				write_to_buffer(d, ansi_translate(pHelp->text), 1000000);
				pop_call();
				return;
			}
		}
	}
	pop_call();
	return;
}

bool is_desc_valid( CHAR_DATA *ch)
{
	push_call("is_desc_valid(%p)",ch);

	if (ch == NULL)
	{
		pop_call();
		return( FALSE );
	}

	if (ch->desc != NULL && ch->desc->character == ch )
	{
		pop_call();
		return( TRUE );
	}
	else
	{
		pop_call();
		return( FALSE );
	}
}


void add_player( CHAR_DATA *ch )
{
	PLAYER_GAME *gpl, *fpl;

	push_call("add_player(%p)",ch);

	if (get_char_pvnum(ch->pcdata->pvnum) != NULL)
	{
		log_printf("add_player: already listed as playing");
		dump_stack();
	}
	else
	{
		pvnum_index[ch->pcdata->pvnum]->ch = ch;
		REMOVE_BIT(pvnum_index[ch->pcdata->pvnum]->flags, PVNUM_DELETED);
	}

	ALLOCMEM(gpl, PLAYER_GAME, 1);
	gpl->ch = ch;

	mud->total_plr++;

	for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
	{
		if (ch->level > fpl->ch->level)
		{
			continue;
		}
		if (ch->level < fpl->ch->level || ch->pcdata->played < fpl->ch->pcdata->played)
		{
			INSERT_LEFT(gpl, fpl, mud->f_player, next, prev);
			break;
		}
	}
	if (fpl == NULL)
	{
		LINK(gpl, mud->f_player, mud->l_player, next, prev);
	}

	pop_call();
	return;
}


void sub_player( CHAR_DATA *ch )
{
	PLAYER_GAME *fpl;

	push_call("sub_player(%p)",ch);

	for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
	{
		if (fpl->ch == ch)
		{
			UNLINK(fpl, mud->f_player, mud->l_player, next, prev);
			FREEMEM(fpl);
			mud->total_plr--;
			break;
		}
	}

	if (get_char_pvnum(ch->pcdata->pvnum) != ch)
	{
		pop_call();
		return;
	}

	pvnum_index[ch->pcdata->pvnum]->ch = NULL;

	pop_call();
	return;
}


CHAR_DATA * start_partial_load( CHAR_DATA *ch, char *argument)
{
	CHAR_DATA *fch = NULL;
	int exists;
	DESCRIPTOR_DATA *d;

	push_call("start_partial_load(%p,%p)",ch,argument);

	exists = check_parse_name(argument, FALSE);

	if (exists)
	{
		ALLOCMEM(d, DESCRIPTOR_DATA, 1);
		d->original	= NULL;
		d->descriptor	= -999;  /* Special case for partial loads */
		exists		= load_char_obj(d, argument);
		fch			= d->character;
		fch->desc		= d;
	}

	if (!exists)
	{
		if (ch)
		{
			ch_printf_color(ch, "The character named '%s' cannot be found.\n\r", argument);
		}
		if (fch)
		{
			clear_partial_load(fch);
		}
		pop_call();
		return NULL;
	}
	pop_call();
	return fch;
}

void clear_partial_load( CHAR_DATA *ch )
{
	DESCRIPTOR_DATA * d = ch->desc;

	push_call("clear_partial_load(%p)",ch);

	ch->desc = NULL;
	extract_char(ch);
	d->character = NULL;
	d->original  = NULL;
	FREEMEM( d );

	pop_call();
	return;
}

void quit_to_menu( DESCRIPTOR_DATA *d )
{
	CHAR_DATA *ch;
	DESCRIPTOR_DATA *dt;

	push_call("quit_to_menu(%p,%p)",d);

	for (dt = mud->f_desc ; dt ; dt = dt->next)
	{
		if (dt == d)
		{
			break;
		}
	}

	if (dt == NULL)
	{
		log_string( "close_socket: unlinked desc called in close_to_menu");
		pop_call();
		return;
	}

	if (!IS_SET(d->comm_flags, COMM_FLAG_DISCONNECT))
	{
		write_to_port(d);
	}

	if (d->snoop_by != NULL)
	{
		write_to_buffer(d->snoop_by, "Your victim has left the game.\n\r", 0);
		d->snoop_by = NULL;
	}

	for (dt = mud->f_desc ; dt ; dt = dt->next)
	{
		if (dt->snoop_by == d)
		{
			dt->snoop_by = NULL;
		}
	}

	if ((ch = d->character) != NULL)
	{
		d->character = NULL;
		ch->desc = NULL;
		d->account->ch = NULL;
		extract_char(ch);
	}
	
	display_empty_screen(d);
	display_menu_top(d);
	display_account_menu(d);
	d->connected = CON_ACCOUNT_MENU;
	
	pop_call();
	return;
}

void delete_player(CHAR_DATA *ch)
{
	char buf1[100], buf2[100], name_buf[100];
	PLAYER_GAME *gpl;
	DESCRIPTOR_DATA *d;

	push_call("delete_player(%p)",ch);

	one_argument(ch->name, name_buf);
	strcpy( name_buf, capitalize(name_buf) );

	if (ch->pcdata->clan != NULL)
	{
		if (!strcasecmp(ch->name, ch->pcdata->clan->leader[0]))
		{
			destroy_clan(ch->pcdata->clan);
		}
		else
		{
			--ch->pcdata->clan->members;

			if (is_clan_leader(ch))
			{
				RESTRING(ch->pcdata->clan->leader[ch->pcdata->clan_position], "");
			}
			RESTRING(ch->pcdata->clan_name, "");
			ch->pcdata->clan = NULL;
		}
	}

	del_data(ch);

	sprintf(buf1, "%s/%c/%s", PLAYER_DIR, tolower(ch->name[0]), name_buf);
	sprintf(buf2, "%s/%c/del/%s", PLAYER_DIR, tolower(ch->name[0]), name_buf);

	remove(buf2);
	rename(buf1, buf2);

	if (ch->desc)
	{
		if (ch->desc->descriptor == -999)
		{
			clear_partial_load(ch);
			pop_call();
			return;
		}

		if (ch->desc->descriptor == -998)
		{
			extract_char(ch);
			pop_call();
			return;
		}

		if (ch->desc->connected < CON_PLAYING)
		{
			extract_char(ch);
			pop_call();
			return;
		}
	}

	if (ch->master != NULL)
	{
		stop_follower (ch);
	}

	do_return(ch, NULL);
	char_from_combat(ch);

	for (gpl = mud->f_player ; gpl ; gpl = gpl->next)
	{
		if (gpl->ch->pcdata->reply == ch)
		{
			gpl->ch->pcdata->reply = NULL;
		}
	}

	for (d = mud->f_desc ; d ; d = d->next)
	{
		if (d != ch->desc && d->character == ch)
		{
			close_socket(d, FALSE);
		}
	}

	if (ch->desc && ch->desc->snoop_by != NULL)
	{
		write_to_buffer(ch->desc->snoop_by, "Your victim has deleted.\n\r", 0);
		ch->desc->snoop_by = NULL;
	}

	remove_from_auth( name_buf );

	if (ch->pcdata->vt100 != 0 && is_desc_valid(ch))
	{
		vt100off (ch);
	}

	d = ch->desc;

	if (d)
	{
		force_help (d, "goodbye");
		write_to_port (d);
		*d->inbuf = '\0';
	}
	extract_char(ch);

	if (d != NULL)
	{
		d->character = NULL;
		close_socket(d, TRUE);
	}
	pop_call();
	return;
}

void do_delete( CHAR_DATA *ch, char *arg )
{
	bool PassRqd;
	DESCRIPTOR_DATA *d;

	push_call("do_delete(%p,%p)",ch,arg);

	if (IS_NPC(ch) || in_combat(ch))
	{
		send_to_char( "You may not do that now.\n\r", ch);
		pop_call();
		return;
	}
	
	if ((d = ch->desc) == NULL || d->original != NULL)
	{
		send_to_char( "You can't delete when you're not yourself.\n\r", ch);
		pop_call();
		return;
	}

	if (ch->level >= starting_level(ch) + 3)
	{
		send_to_char_color( center("You can no longer delete your character after advancing past level 4.\n\r", get_page_width(ch)), ch);
		send_to_char_color( center("Please contact the administration to request deletion.\n\r", get_page_width(ch)), ch);
		send_to_char_color( center("See HELP DELETE.\n\r", get_page_width(ch)), ch);
		pop_call();
		return;
	}

	if (ch->pcdata->just_died_ctr > 0)
	{
		ch_printf_color(ch, "Just cool down a bit before you rage quit!\n\r");
		pop_call();
		return;
	}

	if (arg == NULL)
	{
		PassRqd = FALSE;
	}
	else
	{
		PassRqd = TRUE;
	}

	if (PassRqd && arg[0] == '\0')
	{
		send_to_char_color(center("{138}You must use a password with this command.\n\r", get_page_width(ch)), ch);
		send_to_char_color(center("{118}************** WARNING!!**************\n\r", get_page_width(ch)), ch);
		send_to_char_color(center("{138}This command permanently deletes your character!\n\r", get_page_width(ch)), ch);
		pop_call();
		return;
	}

	if (!PassRqd || encrypt64(arg) == d->account->password)
	{
		if (PassRqd)
		{
			send_to_char( center("You have permanently deleted your character!\n\r", get_page_width(ch)), ch);
		}
		else
		{
			send_to_char( center("Your character has been permanently deleted!\n\r", get_page_width(ch)), ch);
			send_to_char( center("You have a serious bug that could not be corrected.\n\r", get_page_width(ch)),ch);
		}
		save_char_obj(ch, NORMAL_SAVE);

		broadcast_channel(ch, format("%s has left the game.", get_name(ch)), CHANNEL_PLAYER);

		log_printf ("%s has deleted.", get_name(ch));

		delete_player(ch);

		pop_call();
		return;
	}
	else
	{
		send_to_char_color( center("{138}That was not the correct password, try again.\n\r", get_page_width(ch)), ch);
		send_to_char_color( center("{118}************** WARNING!!**************\n\r", get_page_width(ch)), ch);
		send_to_char_color( center("{138}This command permanently deletes your character!\n\r", get_page_width(ch)), ch);
	}
	pop_call();
	return;
}


void do_qui (CHAR_DATA * ch, char *argument)
{
	push_call("do_qui(%p,%p)",ch,argument);

	send_to_char ("If you want to QUIT, you have to spell it out.\n\r", ch);
	pop_call();
	return;
}


void do_quit (CHAR_DATA * ch, char *argument)
{
	DESCRIPTOR_DATA *d;
	PLAYER_GAME *gpl;
	ACCOUNT_DATA *acct;
	AUTH_LIST *old_auth;

	push_call("do_quit(%p,%p)",ch,argument);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}

	if (argument && ch->in_room && ch->in_room->vnum == 12001)
	{
		send_to_char ("You have been summoned here by the GMs. Please do not quit until you\n\r", ch);
		send_to_char ("have spoken with one and have been dismissed from this room.\n\r", ch);
		pop_call();
		return;
	}
	if (argument && in_combat(ch))
	{
		send_to_char ("No way! You are fighting.\n\r", ch);
		pop_call();
		return;
	}

	if (argument && ch->position < POS_STUNNED)
	{
		send_to_char ("You're not DEAD yet.\n\r", ch);
		pop_call();
		return;
	}

	if (argument && IS_SET(ch->in_room->room_flags, ROOM_NO_SAVE))
	{
		send_to_char("You can't save here, so you had better not quit.\n\r", ch);
		pop_call();
		return;
	}

	if (!IS_IMMORTAL(ch) && argument && ch->pcdata->last_combat + 60 > mud->current_time)
	{
		ch_printf_color(ch, "{018}Your adrenaline is pumping too hard to quit!\n\r");
		pop_call();
		return;
	}

	if (ch->fear_level)
	{
		ch_printf_color(ch, "{018}Your mind is racing too fast with fear to quit!\n\r");
		pop_call();
		return;
	}

	if (ch->pcdata->just_died_ctr > 0)
	{
		ch_printf_color(ch, "You cannot quit for 10 minutes after dying.\n\r");
		pop_call();
		return;
	}

	if (argument && ch->pcdata->pnote != NULL && strcasecmp(argument, "now"))
	{
		send_to_char ("You must specify 'quit now'.\n\rYou have a note in progress.\n\r", ch);
		pop_call();
		return;
	}

	for (d = mud->f_desc ; d ; d = d->next)
	{
		if (d->original == ch)
		{
			do_return(d->character, NULL);
		}
	}

	if (ch->in_room->vnum == ROOM_VNUM_LIMBO)
	{
		char_from_room(ch);
		char_to_room(ch, ch->pcdata->was_in_room, TRUE);
	}

	if (IS_SET(ch->in_room->room_flags, ROOM_NO_SAVE))
	{
		char_from_room (ch);
		char_to_room(ch, ROOM_VNUM_TEMPLE, TRUE);
	}

	if (ch->master != NULL)
	{
		stop_follower(ch);
	}

	if (ch->pcdata->editor)
	{
		stop_editing(ch);
	}

	save_char_obj(ch, NORMAL_SAVE);

	// clear CH entry from account - Kregor
	for (acct = mud->f_acct ; acct ; acct = acct->next)
	{
		if (acct->ch && acct->ch == ch)
		{
			acct->ch = NULL;
			break;
		}
	}

	for (gpl = mud->f_player ; gpl ; gpl = gpl->next)
	{
		if (gpl->ch->pcdata->reply == ch)
		{
			gpl->ch->pcdata->reply = NULL;
		}
	}

	/* new auth */
	{
		old_auth = get_auth_name( ch->name );
		if( old_auth != NULL 
		&& (old_auth->state == AUTH_ONLINE || old_auth->state == AUTH_LINK_DEAD) )
			old_auth->state = AUTH_OFFLINE; /* Logging off */
	}

	if (in_combat(ch))
		withdraw_combat(ch);

	if (ch->mounting)
		do_dismount(ch, "");

	if (!IS_IMMORTAL(ch))
		broadcast_channel(ch, format("%s has left the game.", get_name(ch)), CHANNEL_PLAYER);

	log_god_printf("%s has quit!", get_name (ch));

	/*
		After extract_char the ch is no longer valid!
	*/

	if (ch->desc && ch->desc->snoop_by != NULL)
	{
		write_to_buffer(ch->desc->snoop_by, "Your victim has quit.\n\r", 0);
		ch->desc->snoop_by = NULL;
	}

	vt100off(ch);

	d = NULL;

	if (ch->desc != NULL)
	{
		d = ch->desc;
		write_to_port(d);
		*d->inbuf = '\0';
	}

	extract_char(ch);

	if (d != NULL)
	{
		d->character = NULL;
	}
	if (d != NULL)
	{
// 		quit_to_menu(d); // need to fix crash after copyover & quit - Kregor
		close_socket(d, TRUE);
	}
	pop_call();
	return;
}

void do_finger( CHAR_DATA *ch, char *argument)
{
	CHAR_DATA *fch;
	char buf[MAX_STRING_LENGTH];
	char outbuf[MAX_STRING_LENGTH];
	char textBld[10], textDim[10], promBld[10], promDim[10];
	char *pt;
	int time_left;
	CRIME_DATA *pcd;
	sh_int rel_days, crcnt;
	sh_int rel_hrs;
	sh_int rel_mins;

	push_call("do_finger(%p,%p)",ch,argument);

	if (!IS_GOD(ch)
	&& !PLR_FUNCTION(ch, FUNCTION_ENFORCER|FUNCTION_GOD|FUNCTION_SECRET_AGENT))
	{
		send_to_char("Sorry, that information is only available to admins.\n\r", ch);
		pop_call();
		return;
	}

	if ((fch = start_partial_load(ch, argument)) == NULL)
	{
		pop_call();
		return;
	}
	
	strcpy(textDim, get_color_string(ch, COLOR_TEXT,   VT102_DIM));
	strcpy(textBld, get_color_string(ch, COLOR_TEXT,   VT102_BOLD));
	strcpy(promDim, get_color_string(ch, COLOR_ACCENT, VT102_DIM));
	strcpy(promBld, get_color_string(ch, COLOR_ACCENT, VT102_BOLD));

	sprintf(outbuf, "%sInformation on %s%s%s ", promDim, textBld, get_name(fch), fch->pcdata->title);

	cat_sprintf(outbuf, "%s[%s%2d ", promDim, textBld, fch->level);

	if (fch->level < LEVEL_IMMORTAL)
	{
		cat_sprintf(outbuf, "%s", class_table[fch->class].who_name);
	}
	else
	{
		strcat(outbuf, "DM");
	}

	if (fch->level >= LEVEL_IMMORTAL)
	{
		strcat(outbuf, " ---");
	}
	else
	{
		snprintf(buf, 3, " %s", race_table[fch->race].race_name);
		strcat(outbuf, buf);
	}
	cat_sprintf(outbuf, "%s]\n\r", promDim);

	cat_sprintf(outbuf, "%sPlayed: %s%d %shours\n\r", promDim, textBld, fch->pcdata->played/3600+fch->pcdata->previous_hours, textDim);

	if (IS_SET(fch->act, PLR_OUTCAST))
	{
		cat_sprintf(outbuf, "%s is an Outcast!\n\r", get_name(fch));
	}
	else if (fch->pcdata->clan)
	{
		if (!strcasecmp(fch->name, fch->pcdata->clan->leader[0]))
		{
			cat_sprintf(outbuf, "%s is the founder of %s.\n\r", get_name(fch), fch->pcdata->clan_name);
		}
		else if (is_clan_leader(fch))
		{
			cat_sprintf(outbuf, "%s is a leader of %s.\n\r", get_name(fch), fch->pcdata->clan_name);
		}
		else
		{
			cat_sprintf(outbuf, "%s is a member of %s.\n\r", get_name(fch), fch->pcdata->clan_name);
		}
	}

	if (IS_SET(pvnum_index[fch->pcdata->pvnum]->flags, PVNUM_ARRESTED))
	{
		time_left = fch->pcdata->jailtime - (mud->current_time - fch->pcdata->jaildate);
		if (time_left > 0)
		{
			rel_days = time_left / 86400;
			rel_hrs  = time_left % 86400 / 3600;
			rel_mins = time_left % 86400 % 3600 / 60;

			cat_sprintf(outbuf, "Imprisoned in the dungeons for %d days, %d hours and %d minutes.\n\r", rel_days, rel_hrs, rel_mins);
		}
	}

	if (IS_GOD(ch)
	|| PLR_FUNCTION(ch, FUNCTION_ENFORCER|FUNCTION_GOD|FUNCTION_SECRET_AGENT))
	{
		if (fch->pcdata->first_record != NULL)
		{
			crcnt = 0;
			for (pcd = fch->pcdata->first_record ; pcd ; pcd = pcd->next)
			{
				if (pcd)
				{
					crcnt++;
				}
			}
			cat_sprintf(outbuf, "Has %d criminal records.\n\r",crcnt);
		}
		cat_sprintf(outbuf,"%sFunctions: %s%s\n\r", promDim, textBld, !fch->pcdata->functions ? "None" : flag_string(fch->pcdata->functions, function_flags));		
	}
	if (*fch->pcdata->mail_address != '\0')
	{
		cat_sprintf(outbuf, "\n\r%sEmail: %s%s\n\r", promDim, textDim, fch->pcdata->mail_address);
	}

	if (*fch->pcdata->html_address != '\0')
	{
		strcpy( buf, fch->pcdata->html_address );
		for (pt = buf ; *pt != '\0' ; pt++)
		{
			if (*pt == '*')
			{
				*pt = '~';
			}
		}
		cat_sprintf(outbuf, "%sHome Page: %s%s\n\r", promDim, textDim, buf);
	}

	if (IS_GOD(ch))
	{
		if (!lookup_char(argument))
		{
			if (fch->pcdata->last_time > 0)
			{
				cat_sprintf(outbuf, "%sLast logged: %s%s\n\r", promDim, textBld, get_time_string(fch->pcdata->last_time));
			}
			cat_sprintf(outbuf, "%sLast IP: %s%s\n\r", promDim, textBld, fch->pcdata->host);
		}
		else if (is_desc_valid(fch))
		{
			cat_sprintf(outbuf, "%sCurrent IP: %s%s\n\r", promDim, textBld, fch->pcdata->host);
		}
	}
	send_to_char(outbuf, ch);

	clear_partial_load(fch);

	pop_call();
	return;
}

bool is_valid_password( char *pass )
{
	char *pt;
	bool found_number;
	bool good_char;

	push_call("is_valid_password(%p)",pass);

	for (found_number = FALSE, pt = pass ; *pt != '\0' ; pt++)
	{
		good_char = FALSE;
		if (*pt >= 'a' && *pt <= 'z' )
			good_char = TRUE;
		if (*pt >= 'A' && *pt <= 'Z' )
			good_char = TRUE;
		if (*pt >= '0' && *pt <= '9' )
		{
			good_char = TRUE;
			found_number = TRUE;
		}
		if (!good_char)
		{
			pop_call();
			return( FALSE );
		}
	}
	if (!found_number && strlen(pass) < 8)
	{
		pop_call();
		return( FALSE );
	}
	else
	{
		pop_call();
		return( TRUE );
	}
}

void do_mpquiet( CHAR_DATA *ch, char *arg )
{
	push_call("do_mpquiet(%p,%p)", ch, arg);

	if (!IS_NPC(ch) || IS_AFFECTED( ch, AFF_DOMINATE))
	{
		pop_call();
		return;
	}

	if (!strcasecmp(arg, "on"))
	{
		SET_BIT(mud->flags, MUD_SKIPOUTPUT);
	}

	if (!strcasecmp( arg, "off" ))
	{
		REMOVE_BIT(mud->flags, MUD_SKIPOUTPUT);
	}
	pop_call();
	return;
}

void do_prompt( CHAR_DATA *ch, char *argument )
{
	char buf[MAX_INPUT_LENGTH];

	push_call("do_prompt(%p,%p)",ch,argument);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}

	if (ch->pcdata->vt100 != 0)
	{
		vt100off(ch);
		send_to_char("Prompt mode enabled.\n\r", ch);

		pop_call();
		return;
	}

	str_cpy_max(buf, argument, 160);

	smash_tilde(buf);

	if (buf[0] == '\0')
	{
		send_to_char("Set your prompt to what?\n\r", ch);

		pop_call();
		return;
	}

	switch (buf[0])
	{
		case '.':
			strcpy(buf, "");
			break;
		case '1':
			sprintf(buf, "{078}<$hhp $mm $vmv $Xxp> {300}");
			break;
		case '2':
			sprintf(buf, "{078}<$c$h{078}hp $c$m{078}m $c$v{078}mv $c$X{078}xp> {300}");
			break;
		case '3':
			sprintf(buf, "{078}<$hhp $mm $vmv $Xxp> <$e> ($l) ($L) {300}");
			break;
		case '4':
			sprintf(buf, "{078}<$c$h{078}hp $c$m{078}m $c$v{078}mv $c$X{078}xp> <{178}$e{078}> ($c$l{078}) ($c$L{078}) ");
			break;
	}

	RESTRING(ch->pcdata->prompt_layout, buf);
	ch_printf_color(ch, "Prompt set to: %s\n\r", buf);

	pop_call();
	return;
}

char *get_gradient( bool gradient, int current, int max)
{
	int percent;

	push_call("get_gradient(%p,%p,%p)",gradient,current,max);

	if (gradient == FALSE)
	{
		pop_call();
		return "";
	}

	percent = 100 * UMAX(0, current) / UMAX(1, max);

	switch (percent / 25)
	{
		case 0:
			pop_call();
			return "{118}";
		case 1:
		case 2:
			pop_call();
			return "{138}";
	}
	pop_call();
	return "{128}";
}

char *prompt_return( CHAR_DATA *ch, char *layout )
{
	AFFECT_DATA *paf;
	EXIT_DATA *pExit;
	static char prompt_buffer[MAX_INPUT_LENGTH];
	char tbuf[MAX_INPUT_LENGTH];
	char *pti, *pto;
	bool last_was_str, gradient;
	int door;
	struct tm clk;

	push_call("prompt_return(%p,%p)",ch,layout);

	if (!IS_NPC(ch) && ch->level >= LEVEL_IMMORTAL && ch->pcdata->editmode != MODE_NONE && ch->pcdata->subprompt && ch->pcdata->subprompt[0] != '\0' )
	{
		sprintf (prompt_buffer, "{138}<{158}%s{138}> {078}", ch->pcdata->subprompt);
	}
	else if (layout == NULL || *layout == '\0' )
	{
		sprintf(prompt_buffer,  "%s<%dhp %dm %dmv %dxp> ", get_color_string(ch, COLOR_PROMPT, VT102_DIM), ch->hit, ch->mana[ch->class], ch->move, exp_level(ch, ch->level) - (IS_NPC(ch) ? 0 : ch->pcdata->exp));
	}
	else
	{
		last_was_str = FALSE;
		gradient     = FALSE;
		pti  = layout;
		strcpy(prompt_buffer, get_color_string(ch, COLOR_PROMPT, VT102_DIM));
		pto  = prompt_buffer;
		pto += strlen(prompt_buffer);

		while( *pti != '\0' )
		{
			if (last_was_str)
			{
				last_was_str = FALSE;

				switch (*pti++)
				{
					case 'h':
						sprintf( tbuf, "%s%d", get_gradient(gradient, ch->hit, get_max_hit(ch)), ch->hit);
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'H':
						sprintf( tbuf, "%s%d", get_gradient(gradient, ch->hit, get_max_hit(ch)), get_max_hit(ch));
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;

					case 'v':
						sprintf( tbuf, "%s%d", get_gradient(gradient, ch->move, get_max_move(ch)), ch->move);
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'V':
						sprintf( tbuf, "%s%d", get_gradient(gradient, ch->move, get_max_move(ch)), get_max_move(ch));
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'm':
						sprintf( tbuf, "%s%d", get_gradient(gradient, ch->mana[ch->class], get_max_mana(ch, ch->class)), ch->mana[ch->class]);
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'M':
						sprintf( tbuf, "%s%d", get_gradient(gradient, ch->mana[ch->class], get_max_mana(ch, ch->class)), get_max_mana(ch, ch->class));
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'x':
						sprintf( tbuf, "%s%d", get_gradient(gradient, exp_level(ch, ch->level) - (IS_NPC(ch) ? 0 : ch->pcdata->exp), exp_level(ch, ch->level) - exp_level(ch, ch->level-1)), IS_NPC(ch) ? 0 : ch->pcdata->exp);
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'X':
						sprintf( tbuf, "%s%d", get_gradient(gradient, exp_level(ch, ch->level) - (IS_NPC(ch) ? 0 : ch->pcdata->exp), exp_level(ch, ch->level) - exp_level(ch, ch->level-1)), exp_level(ch, ch->level) - (IS_NPC(ch) ? 0 : ch->pcdata->exp));
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'l':
						sprintf( tbuf, "%s%d", get_gradient(gradient, ch->hit, get_max_hit(ch)), 100 * ch->hit / UMAX(1, get_max_hit(ch)) );
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'L':
						if (who_fighting(ch))
						{
							sprintf( tbuf, "%s%d", get_gradient(gradient, who_fighting(ch)->hit, who_fighting(ch)->max_hit), 100 * who_fighting(ch)->hit / UMAX(1, who_fighting(ch)->max_hit) );
						}
						else
						{
							sprintf( tbuf, "--");
						}
						strcpy( pto, tbuf );
						pto += strlen (tbuf);
						break;
					case 'g':
						sprintf( tbuf, "%s%d", get_gradient(gradient, ch->gold, ch->level * 1000000), ch->gold);
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'a':
						sprintf( tbuf, "%s%d", get_gradient(gradient, ch->alignment + 1000, 2000), ch->alignment);
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 's':
						*pto = move_speeds[ch->speed][0];
						pto++;
						break;
					case 'S':
						strcpy(tbuf, lang_names[UNSHIFT(CH(ch->desc)->speak)]);
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
						
					case 'f':
						for (paf = ch->first_affect ; paf ; paf = paf->next)
						{
							*pto = skill_table[paf->type].name[0] - 32;	pto++;
							*pto = skill_table[paf->type].name[1];		pto++;
						}
						break;
					case 'e':
						if (can_see_in_room(ch, ch->in_room))
						{
							for (door = 0 ; door < 6 ; door++)
							{
								if ((pExit = get_exit(ch->in_room->vnum, door)) != NULL
								&&   !IS_SET(pExit->exit_info, EX_CLOSED)
								&&  (!IS_SET(ch->in_room->room_flags, ROOM_FOG)	|| can_see_smoke(ch))
								&&  (!IS_SET(pExit->exit_info, EX_HIDDEN) || can_see_hidden(ch, door))
								&&   can_use_exit(ch, pExit))
								{
									switch (door)
									{
										case 0: *pto = 'N'; pto++; break;
										case 1: *pto = 'E'; pto++; break;
										case 2: *pto = 'S'; pto++; break;
										case 3: *pto = 'W'; pto++; break;
										case 4: *pto = 'U'; pto++; break;
										case 5: *pto = 'D'; pto++; break;
									}
								}
							}
						}
						break;
					case 'w':
						ch->wait ? (*pto = '*') : (*pto = ' ');
						pto++;
						break;
					case 't':
						sprintf( tbuf, "%s", tocivilian(mud->time_info->hour));
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case 'T':
						clk = *localtime (&mud->current_time);
						if (CH(ch->desc)->pcdata->clock > 99)
						{
							sprintf( tbuf, "%2d:%02d", (clk.tm_hour + (CH(ch->desc)->pcdata->clock % 100)) % 24, clk.tm_min);
						}
						else
						{
							sprintf( tbuf, "%2d:%02d %s", clk.tm_hour % 12 != 0 ? clk.tm_hour % 12 : 12, clk.tm_min, clk.tm_hour >= 12 ? "pm" : "am");
						}
						if (IS_SET(CH(ch->desc)->act, PLR_WIZTIME))
						{
							sprintf( tbuf, "%02d:%02d", clk.tm_sec, (int) (get_game_usec() % 1000000 / 10000));
						}
						strcpy( pto, tbuf );
						pto += strlen( tbuf );
						break;
					case '$':
						*pto = '$';
						pto++;
						break;
					case '/':
						strcpy( pto, "\n\r" );
						pto += 2;
						break;
					case 'c':
						gradient = TRUE;
						continue;
						break;
					default:
						*pto = '$';
						pto ++;
						*pto = *pti;
						pto ++;
						break;
				}
				gradient = FALSE;
			}
			else
			{
				if (*pti != '$')
				{
					*pto = *pti;
					pto++;
				}
				else
				{
					last_was_str = TRUE;
				}
				pti++;
			}
		}
		*pto = '\0';
	}

	pop_call();
	return (ansi_translate_text(ch, prompt_buffer));
}

/*
 * Sends to all wiztime players in room for bugging.
 */
void wiz_printf_room(CHAR_DATA *ch, const char *fmt, ...)
{
	CHAR_DATA *rch, *rch_next;
	char buf[MAX_STRING_LENGTH];
	va_list args;

	va_start(args, fmt);
	vsprintf(buf, fmt, args);
	va_end(args);

	for (rch = ch->in_room->first_person ; rch != NULL ; rch = rch_next)
	{
		rch_next = rch->next_in_room;

		if (IS_NPC(rch))
			continue;
		if (!IS_SET(rch->act, PLR_WIZTIME))
			continue;
		ch_printf_color(rch, "{058}DEBUG: %s", buf);
	}
}

void log_printf (char * fmt, ...)
{
	char buf [MAX_STRING_LENGTH];
	va_list args;

	va_start (args, fmt);
	vsprintf (buf, fmt, args);
	va_end (args);

	log_string (buf);
}

void log_build_printf (int vnum, char * fmt, ...)
{
	char buf [MAX_STRING_LENGTH];
	va_list args;

	va_start (args, fmt);
	vsprintf (buf, fmt, args);
	va_end (args);

	log_build_string (vnum, buf);
}

void log_god_printf (char * fmt, ...)
{
	char buf [MAX_STRING_LENGTH];
	va_list args;

	va_start (args, fmt);
	vsprintf (buf, fmt, args);
	va_end (args);

	log_god_string (buf);
}

/*
 * Logs and sends string to all wiztime players - Kregor
 */
void wiz_printf (char * fmt, ...)
{
	char buf [MAX_STRING_LENGTH];
	va_list args;

	va_start (args, fmt);
	vsprintf (buf, fmt, args);
	va_end (args);

	log_wiz_string (buf);
}

void scan_object_for_dup( CHAR_DATA *ch , OBJ_DATA *obj)
{
	OBJ_DATA *ref;
	int ref_ind;

	push_call("scan_object_for_dup(%p,%p)",ch,obj);

	while (obj)
	{
		ref_ind = obj->obj_ref_key % MAX_OBJ_REF;

		for (ref = obj_ref_list[ref_ind].first ; ref ; ref = ref->next_ref)
		{
			if (ref != obj && ref->obj_ref_key == obj->obj_ref_key)
			{
				if (IS_SET(ref->extra_flags, ITEM_NOT_VALID))
				{
					continue;
				}
				log_printf( "OBJ DUPE %p [%d] carried by %s",  obj, obj->pIndexData->vnum, ch->name);

				if (ref->carried_by)
				{
					log_printf( "OBJ DUPE %p [%d] carried by %s", ref, ref->pIndexData->vnum, ref->carried_by->name);
				}
				else if (ref->in_obj && ref->in_obj->carried_by)
				{
					log_printf( "OBJ DUPE %p [%d] carried by %s inside [%d]", ref, ref->pIndexData->vnum, ref->in_obj->carried_by->name, ref->in_obj->pIndexData->vnum);
				}
				else
				{
					log_printf( "OBJ DUPE %p [%d] carried by UNKNOWN", ref, ref->pIndexData->vnum);
				}
				SET_BIT(obj->extra_flags, ITEM_NOT_VALID);
				break;
			}
		}

		if (obj->first_content != NULL)
		{
			scan_object_for_dup( ch, obj->first_content );
		}
		obj = obj->next_content;
	}
	pop_call();
	return;
}

void add_obj_ref_hash( OBJ_DATA *obj )
{
	int ref_ind;

	push_call("add_obj_ref_hash(%p)",obj);

	if (obj->obj_ref_key == 0)
	{
		obj->obj_ref_key = obj_ref_key++;
	}

	ref_ind = obj->obj_ref_key % MAX_OBJ_REF;

	LINK(obj, obj_ref_list[ref_ind].first, obj_ref_list[ref_ind].last, next_ref, prev_ref);

	pop_call();
	return;
}


void rem_obj_ref_hash( OBJ_DATA *obj)
{
	int ref_ind;

	push_call("rem_obj_ref_hash(%p)",obj);

	ref_ind = obj->obj_ref_key % MAX_OBJ_REF;

	UNLINK(obj, obj_ref_list[ref_ind].first, obj_ref_list[ref_ind].last, next_ref, prev_ref);

	pop_call();
	return;
}


void wait_state(CHAR_DATA *ch, int npulse)
{
	push_call("wait_state(%p,%p)",ch,npulse);

	if (ch->desc)
	{
		if (IS_GOD(ch))
		{
			ch->wait = UMIN(0, UMAX(ch->wait, npulse));
		}
		else
		{
			ch->wait = UMAX(ch->wait, npulse);
		}
		vt100prompt(ch);
	}
	pop_call();
	return;
}


void do_maillog(CHAR_DATA *ch, char *argument)
{
	FILE *logfp;
	char pathname[MAX_INPUT_LENGTH], sysbuf[MAX_INPUT_LENGTH];

	push_call("do_maillog(%p,%p)",ch,argument);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}

	if (ch->level < MAX_LEVEL)
	{
		send_to_char("You're not allowed to mail the logs.\n\r",ch);
		pop_call();
		return;
	}

	if (strstr(ch->pcdata->mail_address, "@") == NULL)
	{
		send_to_char("You don't have a valid email address set-up.\n\r",ch);
		pop_call();
		return;
	}

	/*
		actual mail sending should happen here...
	*/

	if (argument[0] == '\0')
	{
		sprintf(sysbuf, "mail -s 'Your request for log %s' %s < %s", logfilename, ch->pcdata->mail_address, logfilename);
		system(sysbuf);

		ch_printf_color(ch, "log %s sent to %s\n\r", logfilename, ch->pcdata->mail_address);
		pop_call();
		return;
	}
	else
	{
		sprintf(pathname, "../log/%d.log", atoi(argument));

		logfp = my_fopen(pathname, "r", FALSE);

		if (logfp == NULL)
		{
			send_to_char("There is no logfile with that number, sorry\n\r", ch);
			pop_call();
			return;
		}

		sprintf(sysbuf, "mail -s 'Your request for log %d.log'  %s < ../log/%d.log", atoi(argument), ch->pcdata->mail_address, atoi(argument));
		system(sysbuf);

		ch_printf_color(ch, "log %d.log sent to mail-address %s\n\r", atoi(argument), ch->pcdata->mail_address);
		pop_call();
		return;
	}
}


void do_llog( CHAR_DATA *ch, char *argument )
{
	int lines;
	char syscmd[100];
	FILE *fp;
	char buf[ MAX_STRING_LENGTH ];
	char letter;
	char *pt;

	push_call("do_llog(%p,%p)",ch,argument);

	lines = URANGE(20, atoi(argument), 200);

	close_reserve();

	ch_printf_color(ch, "{128}Log file used is %s\n\r", logfilename);

	sprintf(syscmd, "/usr/bin/tail -%d %s > data/llog.out", lines, logfilename);
	system( syscmd );

	fp = my_fopen( "data/llog.out", "r",FALSE);

	if (fp == NULL)
	{
		send_to_char( "No llog generated.\n\r", ch );
		open_reserve();
		pop_call();
		return;
	}

	*buf		= '\0';
	pt		= buf;
	letter	= getc( fp );

	while (letter != EOF)
	{
		if (letter != '\r')
		{
			*pt = letter;
			pt++;
		}

		if (letter == '\n')
		{
			*pt = '\r';	pt++;
			*pt = '{'; 	pt++;
			*pt = '1'; 	pt++;
			*pt = '7'; 	pt++;
			*pt = '8'; 	pt++;
			*pt = '}'; 	pt++;
		}
		letter = getc( fp );
	}

	*pt = '\0';

	ch_printf_color(ch, "{178}%s", buf);

	my_fclose( fp );
	open_reserve();

	unlink("data/llog.out");

	pop_call();
	return;
}

/*
 * Lookup account data for a character desc
 * (account system coded by Kregor for mud20
 */
ACCOUNT_DATA *lookup_account( char *name )
{
	ACCOUNT_DATA *acct;

	push_call("lookup_account(%p)",name);

	for (acct = mud->f_acct ; acct ; acct = acct->next)
	{
		if (!strcasecmp(acct->player_name, name))
		{
			pop_call();
			return( acct );
		}
	}
	pop_call();
	return( NULL);
}



/*
 * The awesome Mortal Realms VT tactical interface!
 */
void vt100on (CHAR_DATA * ch)
{
	DESCRIPTOR_DATA *d;
	char buf[MAX_STRING_LENGTH];
	int n1, n2, n3;

	push_call("vt100on(%p)",ch);

	if (!is_desc_valid(ch))
	{
		pop_call();
		return;
	}

	d = ch->desc;

	write_to_port(d);

	if (CH(d)->pcdata->tactical == NULL)
	{
		ALLOCMEM(CH(d)->pcdata->tactical, TACTICAL_MAP, 1);
	}

	write_to_port(d);

	n1 = 1 + CH(d)->pcdata->tactical_mode % 100;

	n2 = CH(d)->pcdata->vt100_type % 100 - 2;

	n3 = CH(d)->pcdata->vt100_type % 100 - 1;

	sprintf(buf, "\033[2J\033[%d;%dr\033[%d;1H", n1, n2, n3);

	write_to_descriptor (d, buf, 0);

	sprintf(buf, "\033[%d;1H\033[2K\033[0m", CH(d)->pcdata->vt100_type % 100);
	write_to_descriptor(d, buf, 0);

	CH(d)->pcdata->vt100 = 1;

	clear_tactical_map(CH(d)->pcdata->tactical);

	vt100prompt(ch);

	write_to_port (d);

	pop_call();
	return;
}

void vt100off (CHAR_DATA * ch)
{
	DESCRIPTOR_DATA *d;
	char buf[MAX_STRING_LENGTH];

	push_call("vt100off(%p)",ch);

	if (!is_desc_valid (ch))
	{
		pop_call();
		return;
	}

	if (CH(ch->desc)->pcdata->vt100 == 0)
	{
		pop_call();
		return;
	}

	d = ch->desc;

	write_to_port (d);

	sprintf(buf, "\033[r\033[0m\033[2J");
	write_to_descriptor (d, buf, 0);
	d->outtop = 0;
	CH(d)->pcdata->vt100 = 0;

	REMOVE_BIT(CH(d)->pcdata->interp, INTERP_TACT_UPDATE);

	if (CH(d)->pcdata->tactical != NULL)
	{
		FREEMEM(CH(d)->pcdata->tactical);
		CH(d)->pcdata->tactical = NULL;
	}
	pop_call();
	return;
}

void vt100prompt(CHAR_DATA * ch)
{
	push_call("vt100prompt(%p)",ch);

	if (is_desc_valid(ch))
	{
		if (CH(ch->desc)->pcdata->vt100 == 3)
		{
			do_refresh(ch, "");
		}
		else if (CH(ch->desc)->pcdata->vt100 == 2)
		{
			vt100prompter(ch);
		}
		else if (CH(ch->desc)->pcdata->vt100 == 1)
		{
			SET_BIT(CH(ch->desc)->pcdata->interp, INTERP_TACT_UPDATE);
		}
	}
	pop_call();
	return;
}

void vt100prompter (CHAR_DATA * ch)
{
	DESCRIPTOR_DATA *d;
	char buf[MAX_STRING_LENGTH];
	TACTICAL_MAP *tact;

	push_call("vt100prompter(%p)",ch);

	tact = NULL;

	if (!is_desc_valid(ch))
	{
		pop_call();
		return;
	}

	d = ch->desc;

	if (CH(d)->pcdata->vt100 == 2)
	{
		vt100on(ch);
	}

	if (CH(d)->pcdata->vt100 == 3)
	{
		do_refresh(ch, "");
		pop_call();
		return;
	}

	if (d->connected != CON_EDITING)
	{
		write_to_buffer (d, "\033[0K", 1000000);
	}

	tact = get_diff_tactical(ch);

	REMOVE_BIT(CH(d)->pcdata->interp, INTERP_TACT_UPDATE);

	if (tact == NULL)
	{
		pop_call();
		return;
	}

	sprintf(buf, "\0337%s\0338%s\033[0K", get_tactical_string(ch, tact), get_color_string(ch, COLOR_PROMPT, VT102_DIM));

	write_to_buffer(d, buf, 1000000);

	pop_call();
	return;
}

void do_port (CHAR_DATA * ch, char *argument)
{
	int size;

	push_call("do_port(%p,%p)",ch,argument);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}

	if (ch->desc->original != NULL)
	{
		pop_call();
		return;
	}

	size = atol (argument);

	if (size == 0)
	{
		ch_printf_color(ch, "Current port settings:\n\rBlock size: %d\n\r", ch->desc->port_size);
		pop_call();
		return;
	}

	if (size < 100 || size > 10000)
	{
		send_to_char ("The range of sizes are 100 to 10000\n\r", ch);
	}

	size = URANGE(100, size, 10000);

	ch->desc->port_size		= size;
	ch->pcdata->port_size	= size;

	ch_printf_color(ch, "Port size set to %d.\n\r", size);
	pop_call();
	return;
}


/*
 * Quick command for setting and changing interface colors
 */
void do_color (CHAR_DATA * ch, char *argument)
{
	char buf[MAX_STRING_LENGTH];
	char buf2[MAX_INPUT_LENGTH];
	int col1, col2, col3, cnt;

	push_call("do_color(%p,%p)",ch,argument);

	if (IS_NPC (ch) || !is_desc_valid (ch))
	{
		pop_call();
		return;
	}

	if (!strcasecmp ("on", argument))
	{
		if (ch->pcdata->ansi == TRUE)
		{
			send_to_char ("You already have color on.\n\r", ch);
			pop_call();
			return;
		}
		ch->pcdata->ansi = TRUE;
		do_refresh(ch, "");
		ch_printf_color(ch, "{300}Ansi color is ON!\n\r");
		pop_call();
		return;
	}

	if (!strcasecmp ("reset", argument))
	{
		/*
			Scandum's favorite colors
		*/
		reset_color (ch);
		do_refresh(ch, "");
		send_to_char ("Colors reset to defaults.\n\r", ch);
		pop_call();
		return;
	}

	if (!strcasecmp ("off", argument))
	{
		ch->pcdata->ansi = FALSE;
		do_refresh(ch, "");
		send_to_char ("Ansi color is OFF.\n\r", ch);
		pop_call();
		return;
	}

	if (!strcasecmp ("", argument))
	{
		char bufx[40];
		char text[20];
		sprintf(text, "%s%s", ansi_translate_text(ch, "{088}"), get_color_string (ch, COLOR_TEXT, VT102_DIM));

		for (buf[0] = '\0', cnt = 0 ; cnt < COLOR_MAX ; cnt++)
		{
			switch (cnt)
			{
				case 0:  strcpy (bufx, "Standard Text             "); break;
				case 1:  strcpy (bufx, "Status Bars               "); break;
				case 2:  strcpy (bufx, "Accent color              "); break;
				case 3:  strcpy (bufx, "Game Alerts               "); break;
				case 4:  strcpy (bufx, "You are hit               "); break;
				case 5:  strcpy (bufx, "You hit others            "); break;
				case 6:  strcpy (bufx, "Prompt line               "); break;
				case 7:  strcpy (bufx, "Exits                     "); break;
				case 8:  strcpy (bufx, "Party member is hit       "); break;
				case 9:  strcpy (bufx, "Speech dialogue           "); break;
				case 10: strcpy (bufx, "Objects                   "); break;
				case 11: strcpy (bufx, "Living things             "); break;
				case 12: strcpy (bufx, "General Tactical Map      "); break;
				case 13: strcpy (bufx, "Party members in Tactical "); break;
				case 14: strcpy (bufx, "Enemies in Tactical       "); break;
				case 15: strcpy (bufx, "Neutrals in Tactical      "); break;
				case 16: strcpy (bufx, "Chat Channel              "); break;
				case 17: strcpy (bufx, "Immtalk Channel           "); break;
				case 18: strcpy (bufx, "Question Channel          "); break;
			}
			cat_sprintf(buf, "%s%2d - %s%s%s  foreground %2d, background %2d\n\r", text, cnt, get_color_string(ch, cnt, VT102_DIM), bufx, text, ch->pcdata->color[cnt] % 10, ch->pcdata->color[cnt] / 10);
		}
		cat_sprintf(buf, "%s\n\r%sSyntax:  COLOR <field number> <forground number> <background number>\n\r", text, text);
		send_to_char(buf, ch);
		pop_call();
		return;
	}

	argument = one_argument (argument, buf);
	argument = one_argument (argument, buf2);
	if (buf[0] < '0' || buf[0] > '9')
	{
		send_to_char ("Try using HELP COLOR.\n\r", ch);
		pop_call();
		return;
	}
	col1 = atol (buf) % COLOR_MAX;
	col2 = atol (buf2) % 10;
	col3 = atol (argument) % 10;
	ch->pcdata->color[col1] = col2 + col3 * 10;
	do_refresh(ch, "");
	ch_printf_color(ch, "{1%d%d}Color field #%d set to color forground %d, background %d.{088}\n\r", ch->pcdata->color[col1] % 10, ch->pcdata->color[col1] / 10, col1, col2, col3);

	pop_call();
	return;
}

void reset_color (CHAR_DATA * ch)
{
	push_call("reset_color(%p)",ch);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}

	ch->pcdata->color[ 0] = 7 + 8 * 10;
	ch->pcdata->color[ 1] = 7 + 4 * 10;
	ch->pcdata->color[ 2] = 6 + 8 * 10;
	ch->pcdata->color[ 3] = 3 + 8 * 10;
	ch->pcdata->color[ 4] = 1 + 8 * 10;
	ch->pcdata->color[ 5] = 2 + 8 * 10;
	ch->pcdata->color[ 6] = 7 + 8 * 10;
	ch->pcdata->color[ 7] = 2 + 8 * 10;
	ch->pcdata->color[ 8] = 4 + 8 * 10;
	ch->pcdata->color[ 9] = 2 + 8 * 10;
	ch->pcdata->color[10] = 7 + 8 * 10;
	ch->pcdata->color[11] = 7 + 8 * 10;
	ch->pcdata->color[12] = 7 + 0 * 10;
	ch->pcdata->color[13] = 2 + 8 * 10;
	ch->pcdata->color[14] = 1 + 8 * 10;
	ch->pcdata->color[15] = 3 + 8 * 10;
	ch->pcdata->color[16] = 3 + 8 * 10;
	ch->pcdata->color[17] = 5 + 8 * 10;
	ch->pcdata->color[18] = 7 + 8 * 10;

	pop_call();
	return;
}


void clear_tactical_map (TACTICAL_MAP *tact)
{
	static const TACTICAL_MAP clear_tact;

	*tact = clear_tact;
/*
	memset(tact->map,   0, MAX_TACTICAL_ROW * MAX_TACTICAL_COL);
	memset(tact->color, 0, MAX_TACTICAL_ROW * MAX_TACTICAL_COL);
*/
	return;
}

/*
	Chaos - 3/1/95
*/

char *get_tactical_string (CHAR_DATA * ch, TACTICAL_MAP * tact)
{
	DESCRIPTOR_DATA *d;

	static char buf[MAX_STRING_LENGTH];

	int cnt, lcnt;
	int new_bold, old_bold;
	int old_for,  old_bak;		/* last color selected */
	int new_for,  new_bak;
	int prev_h,   prev_v;		/* last known change in text */
	int size_h,   size_v;

	int ccnt, stbar;
	unsigned char *tm, *tc, *cm, *cc;	/* Pointers to tacticals */

	push_call("get_tactical_string(%p,%p)",ch,tact);

	d = ch->desc;

	if (!is_desc_valid(ch))
	{
		pop_call();
		return NULL;
	}

	prev_h   = -10;
	prev_v   = -10;
	old_bold = -1;
	old_for  = -1;
	old_bak  = -1;

	buf[0]   = '\0';

	stbar  = CH(d)->pcdata->vt100_type % 100 - 1;

	size_v = CH(d)->pcdata->tactical_mode % 100 + 1;
	size_h = CH(d)->pcdata->vt100_type    / 100;

	tm = (unsigned char *) tact->map;
	tc = (unsigned char *) tact->color;

	cm = (unsigned char *) CH(d)->pcdata->tactical->map;
	cc = (unsigned char *) CH(d)->pcdata->tactical->color;

	for (lcnt = 0 ; lcnt < size_v ; lcnt++)
	{
		for (cnt = 0 ; cnt < size_h ; cnt++, tm++, tc++, cm++, cc++)
		{
			if (*tm != *cm || *tc != *cc)
			{
				if (IS_SET(CH(d)->pcdata->vt100_flags, VT100_BOLD))
				{
					if (*tc >= 128 || *tm >= 128)
					{
						new_bold = 1;
					}
					else if (IS_SET(CH(d)->pcdata->vt100_flags, VT100_ECMA48))
					{
						new_bold = 22;
					}
					else
					{
						new_bold = 0;
					}
				}
				else
				{
					new_bold = 0;
				}

				if (CH(d)->pcdata->ansi == FALSE)
				{
					new_for = 0;
					new_bak = 1;
				}
				else
				{
					new_for = *tc % 8;
					new_bak = (*tc % 128) / 8;
				}
				/*
					Get color shifting
				*/

				strcat(buf, get_color_diff(ch, old_for, old_bak, old_bold, new_for, new_bak, new_bold));

				if (lcnt == CH(d)->pcdata->tactical_mode % 100)
				{
					ccnt = stbar - 1;
				}
				else
				{
					ccnt = lcnt;
				}

				if (prev_v == ccnt + 1 && prev_h == cnt)
				{
					cat_sprintf(buf, "%c", *tm % 128);
				}
				else
				{
					/*
						Check for slow VT102 processing
					*/

					if (!IS_SET(CH(d)->pcdata->vt100_flags, VT102_FAST))
					{
						cat_sprintf(buf, "\033[%d;%dH%c", ccnt + 1, cnt + 1, *tm % 128);
					}
					else
					{
						if (ccnt + 1 == prev_v)	/* Same row optimize */
						{
							cat_sprintf(buf, "\033[%dC%c", cnt - prev_h, *tm % 128);
						}
						else	/* Direct cursor positioning */
						{
							cat_sprintf(buf, "\033[%d;%dH%c", ccnt + 1, cnt + 1, *tm % 128);
						}
					}
				}
				prev_v = ccnt + 1;
				prev_h = cnt + 1;
				old_for = new_for;
				old_bak = new_bak;
				old_bold = new_bold;
			}
		}
	}

	/* Update the new map for player */

	memcpy(CH(d)->pcdata->tactical->map,	tact->map,	lcnt * size_h);
	memcpy(CH(d)->pcdata->tactical->color,	tact->color,	lcnt * size_h);

	pop_call();
	return(buf);
}

TACTICAL_MAP *get_tactical_map (CHAR_DATA * ch)
{
	register int val, cnt, lcnt, size_v, size_h, cuc, door;
	bool *pti, *pto;
	bool *ptoc;
	AFFECT_DATA *paf;
	PC_DATA *pcd;			/* Shortcutt one reference */
	char buf[MAX_STRING_LENGTH], buf2[MAX_INPUT_LENGTH], buf3[MAX_INPUT_LENGTH];
	char lang[MAX_STRING_LENGTH];
	bool *ptb;
	bool color1, color2, color3;
	int col, row, bld, colors, max_width, offset, vo, hour, cw;
	struct tm clk;
	bool *tm, *tc;
	EXIT_DATA *pExit;
	bool found;
	CHAR_DATA *fch;
	bool cval;

	push_call("get_tactical_map(%p)",ch);

	pcd  = CH(ch->desc)->pcdata;

	clk = *localtime (&mud->current_time);

	clear_tactical_map(mud->tactical);

	tm = (unsigned char *) mud->tactical->map;
	tc = (unsigned char *) mud->tactical->color;

	color1 = pcd->color[COLOR_STAT_BAR] % 10 + pcd->color[COLOR_STAT_BAR] / 10 * 8;
	color2 = pcd->color[COLOR_TACTICAL] % 10 + pcd->color[COLOR_TACTICAL] / 10 * 8;
	color3 = pcd->color[COLOR_STAT_BAR] % 10 + pcd->color[COLOR_STAT_BAR] / 10 * 8;

	val = pcd->tactical_mode;

	size_v = val % 100 + 1;
	size_h = pcd->vt100_type / 100;

	for (lcnt = 0 ; lcnt < size_v ; lcnt++)
	{
		for (cnt = 0 ; cnt < size_h ; cnt++, tm++, tc++)
		{
			*tm = 128 + ' ';

			if ((val / 1000 % 10 == 0 && lcnt == 0)
			||  (val / 1000 % 10 != 0 && lcnt == val-1))
			{
				*tc = color1;
			}
			else if (lcnt < val % 100)
			{
				*tc = color2;
			}
			else
			{
				*tc = color3;
			}
		}
	}

	/* Let's use these as a index pointer for speed */

	tm = (unsigned char *) mud->tactical->map;
	tc = (unsigned char *) mud->tactical->color;

	if ((val / 1000) % 10 == 0)
	{
		pto = tm;
	}
	else
	{
		pto = tm + size_h * (size_v - 2);
	}

	if (!IS_NPC(ch))
	{
		for (cnt = 0, pti = (bool *) ch->name ; *pti != '\0' ; pti++, pto++, cnt++)
		{
			*pto = *pti;
		}
	}
	else
	{
		for (cnt = 0, pti = (bool *) ch->short_descr ; *pti != '\0' && cnt < 73 ; pti++, pto++, cnt++)
		{
			*pto = *pti;
		}
	}

	for ( ; cnt < size_h ; cnt++, pto++)
	{
		*pto = ' ';
	}

	{
		hour = mud->time_info->hour % 12;

		pto--;

		*pto = ' ' + 128;
		*pto = 'm' + 128;
		if (mud->time_info->hour >= 12)
		{
			pto--;	*pto = 'p' + 128;
		}
		else
		{
			pto--;	*pto = 'a' + 128;
		}

		if (hour == 0)
		{
			hour = 12;
		}
		pto--;	*pto = '0' + (hour % 10) + 128;
		if (hour >= 10)
		{
			pto--;	*pto = '1' + 128;
		}
		if (ch->wait != 0)
		{
			pto--;	*pto = '*';
		}
		else
		{
			pto--;	*pto = ' ';
		}
		pto--;	*pto = ' ';
		pto--;	*pto = ' ';
	}

	switch (ch->speed)
	{
		case 0:
			pto--;	*pto = 128 + 'f';
			pto--;	*pto = 128 + 'l';
			pto--;	*pto = 128 + 'a';
			pto--;	*pto = 128 + 'H';
			break;
		case 1:
			pto--;	*pto = 128 + 'm';
			pto--;	*pto = 128 + 'r';
			pto--;	*pto = 128 + 'o';
			pto--;	*pto = 128 + 'N';
			break;
		case 2:
			pto--;	*pto = 128 + 'e';
			pto--;	*pto = 128 + 'l';
			pto--;	*pto = 128 + 't';
			pto--;	*pto = 128 + 's';
			pto--;	*pto = 128 + 'u';
			pto--;	*pto = 128 + 'H';
			break;
		case 3:
			pto--;	*pto = 128 + 'n';
			pto--;	*pto = 128 + 'u';
			pto--;	*pto = 128 + 'R';
			break;
		case 4:
			pto--;	*pto = 128 + 'e';
			pto--;	*pto = 128 + 't';
			pto--;	*pto = 128 + 's';
			pto--;	*pto = 128 + 'a';
			pto--;	*pto = 128 + 'H';
			break;
		case 5:
			pto--;	*pto = 128 + 'e';
			pto--;	*pto = 128 + 'z';
			pto--;	*pto = 128 + 'a';
			pto--;	*pto = 128 + 'l';
			pto--;	*pto = 128 + 'B';
			break;
	}

	pto--;	*pto = ':';
	pto--;	*pto = 'd';
	pto--;	*pto = 'e';
	pto--;	*pto = 'e';
	pto--;	*pto = 'p';
	pto--;	*pto = 'S';
	pto--;	*pto = ' ';
	pto--;	*pto = ' ';
	pto--;	*pto = ' ';

	col = 0;

	if (IS_AFFECTED (ch, AFF_HOODED))
	{
		pto--;	*pto = 'd' + 128;
		pto--;	*pto = 'o' + 128;
		pto--;	*pto = 'o' + 128;
		pto--;	*pto = 'H' + 128;
	}

	if (!IS_NPC(ch))
	{
		if (IS_SET (ch->act, PLR_HOLYLIGHT))
		{
			pto--;	*pto = 'l' + 128;
			pto--;	*pto = 'o' + 128;
			pto--;	*pto = 'H' + 128;
		}
		if (IS_SET (ch->act, PLR_WIZINVIS))
		{
			pto--;	*pto = 'z' + 128;
			pto--;	*pto = 'i' + 128;
			pto--;	*pto = 'W' + 128;
		}
		if (IS_SET (ch->act, PLR_WIZCLOAK))
		{
			pto--;	*pto = 'C' + 128;
			pto--;	*pto = 'z' + 128;
			pto--;	*pto = 'W' + 128;
		}
	}

	if (ch->first_affect != NULL)
	{
		int type;
		for (type = cnt = 0, paf = ch->first_affect ; paf && cnt < (size_h - 40) / 2 ; paf = paf->next, cnt++)
		{
			if (paf->type == type)
				continue;
			pto--;	*pto = skill_table[paf->type].name[2] + 128;
			pto--;	*pto = skill_table[paf->type].name[1] + 128;
			pto--;	*pto = skill_table[paf->type].name[0] + 128 - 32;
			type = paf->type;
		}
	}

	if (ch->first_affect == NULL
	&& !IS_SET(CH(ch->desc)->act, PLR_WIZINVIS)
	&& !IS_SET(CH(ch->desc)->act, PLR_HOLYLIGHT)
	&& !IS_SET(CH(ch->desc)->act, PLR_WIZCLOAK)
	&& !IS_AFFECTED(ch, AFF_HOODED))
	{
		pto--;	*pto = 128 + 'e';
		pto--;	*pto = 128 + 'n';
		pto--;	*pto = 128 + 'o';
		pto--;	*pto = 128 + 'N';
	}
	pto--;	*pto = ':';
	pto--;	*pto = 't';
	pto--;	*pto = 'c';
	pto--;	*pto = 'e';
	pto--;	*pto = 'f';
	pto--;	*pto = 'f';
	pto--;	*pto = 'A';
	pto--;	*pto = ' ';
	pto--;	*pto = ' ';
	pto--;	*pto = ' ';

	sprintf (lang, "%s", lang_names[UNSHIFT(CH(ch->desc)->speak)]);

	for (cnt = strlen(lang) - 1 ; cnt >= 0 ; cnt--)
	{
		pto--; *pto = 128 + lang_names[UNSHIFT(CH(ch->desc)->speak)][cnt];
	}
	pto--;

	*pto = ':';		pto--;
	*pto = 'k';		pto--;
	*pto = 'a';		pto--;
	*pto = 'e';		pto--;
	*pto = 'p';		pto--;
	*pto = 'S';		pto--;
	*pto = ' ';		pto--;

	/*
		Add the bottom stat bar
	*/

	pto	= tm + (size_h * (size_v - 1));
	ptoc = tc + (size_h * (size_v - 1));

	cuc = color3;

	if (!IS_NPC(ch) && IS_IMMORTAL(ch) && ch->pcdata->editmode != MODE_NONE && pcd->subprompt && pcd->subprompt[0] != '\0')
	{
		sprintf(buf, "%-61s", ch->pcdata->subprompt);
		for (ptb = (bool *) buf ; *ptb != '\0' ; ptb++)
		{
			*pto = *ptb + 128;
			pto++;
			*ptoc = cuc;
			ptoc++;
		}
		for (cnt = 61 ; cnt < size_h - 19 ; cnt++)
		{
			*pto = 128 + ' ';	pto++;
		}
	}
	else
	{
		*pto		= 128 + ' ';	pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'H';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'i';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 't';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'P';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 't';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 's';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= ':';		pto++;
		*ptoc	= cuc;		ptoc++;

		val = UMAX(0, ch->hit);

		switch (val * 10 / UMAX(1, get_max_hit(ch)) / 4)
		{
			case 0:	cuc = 1 + pcd->color[COLOR_STAT_BAR] / 10 * 8;	break;
			case 1:	cuc = 3 + pcd->color[COLOR_STAT_BAR] / 10 * 8;	break;
			case 2:	cuc = 2 + pcd->color[COLOR_STAT_BAR] / 10 * 8;	break;
		}

		sprintf(buf, "%3d%%", (int) what_percent(ch->hit, UMAX(1, get_max_hit(ch))));

		*pto		= 128 + buf[0];	pto++;
		*ptoc	= cuc;			ptoc++;
		*pto		= 128 + buf[1];	pto++;
		*ptoc	= cuc;			ptoc++;
		*pto		= 128 + buf[2];	pto++;
		*ptoc	= cuc;			ptoc++;
		*pto		= 128 + buf[3];	pto++;
		*ptoc	= cuc;			ptoc++;

		cuc = color3;

		*pto		= 128 + ' ';	pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 128 + ' ';	pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 128 + ' ';	pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'N';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'o';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'n';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'L';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'e';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 't';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'h';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'a';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'l';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= ':';		pto++;
		*ptoc	= cuc;		ptoc++;

		val = (int) what_percent(URANGE(0, ch->nonlethal, ch->hit), UMAX(1,ch->hit));

		if (val > 66)
			cuc = 1 + pcd->color[COLOR_STAT_BAR] / 10 * 8;
		else if (val > 33)
			cuc = 3 + pcd->color[COLOR_STAT_BAR] / 10 * 8;
		else
			cuc = 2 + pcd->color[COLOR_STAT_BAR] / 10 * 8;

		sprintf(buf, "%3d%%", val);

		*pto		= 128 + buf[0];	pto++;
		*ptoc	= cuc;			ptoc++;
		*pto		= 128 + buf[1];	pto++;
		*ptoc	= cuc;			ptoc++;
		*pto		= 128 + buf[2];	pto++;
		*ptoc	= cuc;			ptoc++;
		*pto		= 128 + buf[3];	pto++;
		*ptoc	= cuc;			ptoc++;

		if (get_max_mana(ch, ch->class) > 0)
		{
			cuc = color3;
	
			*pto		= 128 + ' ';		pto++;
			*ptoc	= cuc;			ptoc++;
			*pto		= 128 + ' ';		pto++;
			*ptoc	= cuc;			ptoc++;
			*pto		= 128 + ' ';	pto++;
			*ptoc	= cuc;		ptoc++;
	
			*pto		= 'M';			pto++;
			*ptoc	= cuc;			ptoc++;
			*pto		= 'a';			pto++;
			*ptoc	= cuc;			ptoc++;
			*pto		= 'n';			pto++;
			*ptoc	= cuc;			ptoc++;
			*pto		= 'a';			pto++;
			*ptoc	= cuc;			ptoc++;
			*pto		= ':';			pto++;
			*ptoc	= cuc;			ptoc++;
	
			val = UMAX(0, ch->mana[ch->class]);
	
			switch (val * 10 / UMAX(1, get_max_mana(ch, ch->class)) / 4)
			{
				case 0:	cuc = 1 + pcd->color[COLOR_STAT_BAR] / 10 * 8;	break;
				case 1:	cuc = 3 + pcd->color[COLOR_STAT_BAR] / 10 * 8;	break;
				case 2:	cuc = 2 + pcd->color[COLOR_STAT_BAR] / 10 * 8;	break;
			}
	
			sprintf(buf, "%3d%%", (int) what_percent(ch->mana[ch->class], UMAX(1, get_max_mana(ch, ch->class))));
	
			*pto		= 128 + buf[0];	pto++;
			*ptoc	= cuc;			ptoc++;
			*pto		= 128 + buf[1];	pto++;
			*ptoc	= cuc;			ptoc++;
			*pto		= 128 + buf[2];	pto++;
			*ptoc	= cuc;			ptoc++;
			*pto		= 128 + buf[3];	pto++;
			*ptoc	= cuc;			ptoc++;
		}

		cuc		= color3;

		*pto		= 128 + ' ';	pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 128 + ' ';	pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 128 + ' ';	pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'M';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'o';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'v';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= 'e';		pto++;
		*ptoc	= cuc;		ptoc++;
		*pto		= ':';		pto++;
		*ptoc	= cuc;		ptoc++;

		val = UMAX(0, ch->move);

		switch (val * 10 / UMAX(1, get_max_move(ch)) / 2)
		{
			case 0:	case 1:	cuc = 1 + pcd->color[COLOR_STAT_BAR] / 10 * 8;	break;
			case 2:	case 3:	cuc = 3 + pcd->color[COLOR_STAT_BAR] / 10 * 8;	break;
			case 4:	case 5:	cuc = 2 + pcd->color[COLOR_STAT_BAR] / 10 * 8;	break;
		}

		sprintf(buf, "%3d%%", (int) what_percent(ch->move, UMAX(1, get_max_move(ch))));

		*pto		= 128 + buf[0];	pto++;
		*ptoc	= cuc;			ptoc++;
		*pto		= 128 + buf[1];	pto++;
		*ptoc	= cuc;			ptoc++;
		*pto		= 128 + buf[2];	pto++;
		*ptoc	= cuc;			ptoc++;
		*pto		= 128 + buf[3];	pto++;
		*ptoc	= cuc;			ptoc++;

//  		for (cnt = 32 ; cnt < size_h - 48 ; cnt++)
// 		{
// 			*pto	= 128 + ' ';	pto++;	ptoc++;
// 		}
// 
// 		*pto		= 128 + ' ';	pto++;
// 		*pto		= 128 + ' ';	pto++;
// 
// 		cuc = color3;
// 
// 		if (IS_SET(CH(ch->desc)->act, PLR_EXP_TO_LEVEL))
// 		{
// 			*pto		= 'X';	pto++;
// 			*pto		= 'P';	pto++;
// 			*pto		= 't';	pto++;
// 			*pto		= 'o';	pto++;
// 			*pto		= 'L';	pto++;
// 			*pto		= 'v';	pto++;
// 			*pto		= 'l';	pto++;
// 		}
// 		else
// 		{
// 			*pto		= 'E';	pto++;
// 			*pto		= 'x';	pto++;
// 			*pto		= 'p';	pto++;
// 			*pto		= 'T';	pto++;
// 			*pto		= 't';	pto++;
// 			*pto		= 'l';	pto++;
// 		}
// 		*pto		= ':';	pto++;
// 
// 		if (ch->pcdata->exp >= exp_level(ch, ch->level))
// 		{
// 			if (ch->level >= LEVEL_HERO)
// 			{
// 				strcpy (buf, "  ***MAX***   ");
// 			}
// 			else
// 			{
// 				strcpy (buf, " **LEVEL UP** ");
// 			}
// 		}
// 		else if (IS_SET(CH(ch->desc)->act, PLR_EXP_TO_LEVEL))
// 		{
// 			if (ch->level >= LEVEL_HERO)
// 			{
// 				strcpy (buf, "  ***MAX***   ");
// 			}
// 			else
// 			{
// 				sprintf (buf, "%-14d", exp_level(ch, ch->level) - (IS_NPC(ch) ? 0 : ch->pcdata->exp));
// 			}
// 		}
// 		else
// 		{
// 			sprintf (buf, "%-14d", IS_NPC(ch) ? 0 : ch->pcdata->exp);
// 		}
// 
// 		for (ptb = (bool *) buf ; *ptb != '\0' ; ptb++)
// 		{
// 			*pto		= *ptb + 128;	pto++;
// 		}
	}

// 	*pto = 'E';	pto++;
// 	*pto = 'x';	pto++;
// 	*pto = 'i';	pto++;
// 	*pto = 't';	pto++;
// 	*pto = 's';	pto++;
// 	*pto = ':';	pto++;
// 	*pto = ' ';	pto++;
// 
// 	cnt = 0;
// 
// 	if (can_see_in_room(ch, ch->in_room))
// 	{
// 		for (door = 0; door < 6; door++)
// 		{
// 			if ((pExit = get_exit(ch->in_room->vnum, door)) != NULL
// 			&&   !IS_SET(pExit->exit_info, EX_CLOSED)
// 			&&  (!IS_SET(ch->in_room->room_flags, ROOM_FOG)	|| can_see_smoke(ch))
// 			&&  (!IS_SET(pExit->exit_info, EX_HIDDEN) || can_see_hidden(ch, door))
// 			&&   can_use_exit(ch, pExit))
// 			{
// 				cnt++;
// 				switch (door)
// 				{
// 					case 0:	*pto = 'N' + 128;	pto++;	break;
// 					case 1:	*pto = 'E' + 128;	pto++;	break;
// 					case 2:	*pto = 'S' + 128;	pto++;	break;
// 					case 3:	*pto = 'W' + 128;	pto++;	break;
// 					case 4:	*pto = 'U' + 128;	pto++;	break;
// 					case 5:	*pto = 'D' + 128;	pto++;	break;
// 				}
// 			}
// 		}
// 	}
// 	for ( ; cnt < 7 ; cnt++)
// 	{
// 		*pto = 128 + ' ';	pto++;
// 	}

	/* Do the room mobiles  */

	if (size_v - 1 > 1)
	{
		colors = 7;
		cw = pcd->compass_width;

		if (cw == 0)
			max_width = size_h - 17;
		else
			max_width = size_h - 17 - 3 - 3 * cw;

		col = 2;
		row = 1;

		if (can_see_in_room(ch, ch->in_room) && ch->position > POS_SLEEPING)
		{
			for (fch = ch->in_room->first_person ; fch ; fch = fch->next_in_room)
			{
				if (col < max_width && (!IS_HIDDEN(fch) || ch == fch) && can_see(ch, fch) && !IS_SET(fch->act, ACT_MOBINVIS))
				{
					pti = (bool *) PERS(fch, ch);

					if (fch->last_attacked && (fch->last_attacked == NULL || fch->last_attacked->in_room != fch->in_room))
					{
						stop_fighting (fch, FALSE);
					}

					if (fch->last_attacked != NULL)	/* Bold for those fighting */
						bld = 128;
					else
						bld = 0;

					if (fch->hit >= get_max_hit(fch))
						cval = 'F';
					else if ((cval = '0' + (what_percent(fch->hit, UMAX(1, get_max_hit(fch))) / 10)) < '0')
						cval = '-';

					if ((pcd->tactical_mode / 1000) % 10 == 0)
					{
						pto = tm + size_h * row + col;
						ptoc = tc + size_h * row + col;
					}
					else
					{
						pto = tm + size_h * (row - 1) + col;
						ptoc = tc + size_h * (row - 1) + col;
					}
					cnt = 0;

					*pto = cval;
					pto++;
					if (what_percent(fch->hit, UMAX(1, get_max_hit(fch))) > 69)
					{
						*ptoc = 2 + pcd->color[COLOR_TACTICAL] / 10 * 8;
					}
					else if (what_percent(fch->hit, UMAX(1, get_max_hit(fch))) > 30)
					{
						*ptoc = 3 + pcd->color[COLOR_TACTICAL] / 10 * 8;
					}
					else
					{
						*ptoc = 1 + pcd->color[COLOR_TACTICAL] / 10 * 8;
					}
					ptoc++;
					{
						*pto = '-';
						pto++;
						ptoc++;
					}

					if (fch->last_attacked && is_same_group(fch->last_attacked, ch))
					{
						colors = pcd->color[COLOR_TACT_ENEMY] % 10   + pcd->color[COLOR_TACT_ENEMY] / 10 * 8;
					}
					else if (is_same_group (fch, ch))
					{
						colors = pcd->color[COLOR_TACT_PARTY] % 10   + pcd->color[COLOR_TACT_PARTY] / 10 * 8;
					}
					else
					{
						colors = pcd->color[COLOR_TACT_NEUTRAL] % 10 + pcd->color[COLOR_TACT_NEUTRAL] / 10 * 8;
					}

					for (; cnt < 12 && *pti != '\0'; cnt++, pti++, pto++, ptoc++)
					{
						*pto = *pti + bld;
						*ptoc = colors;
					}
					row++;
					if (row > size_v - 2)
					{
						row = 1;
						col += 16;
					}
				}
			}
		}
		/* Compass code  */
		if (can_see_in_room(ch, ch->in_room))
		{
			if (size_v - 1 > 3 && cw > 0)
			{
				ROOM_INDEX_DATA *room = ch->in_room;
				for (door = -1 ; door <= 5 ; door++)
				{
					if (door == -1)
					{
						if (!can_see_in_room(ch, room))
						{
							strcpy(buf3, "Too dark to tell");
						}
						else
						{
							strcpy(buf3, get_dynamic_room_title(room));
						}
						colors = sector_table[room->sector_type].color;
					}
					else
					{
						if ((pExit = get_exit(room->vnum, door)) == NULL)
						{
							continue;
						}
						if (IS_SET(ch->in_room->room_flags, ROOM_FOG) && !can_see_smoke(ch))
						{
							continue;
						}
						if (IS_SET(pExit->exit_info, EX_HIDDEN) && !can_see_hidden(ch, door))
						{
							continue;
						}
						if (!can_use_exit(ch, pExit))
						{
							continue;
						}
						colors = sector_table[room_index[pExit->to_room]->sector_type].color;
						if (!IS_SET(pExit->exit_info, EX_CLOSED))
						{
							if (!can_see_in_room(ch, room_index[pExit->to_room]))
							{
								strcpy (buf3, "Too dark to tell");
							}
							else
							{
								strcpy (buf3, get_dynamic_room_title(room_index[pExit->to_room]));
							}
						}
						else
						{
							if (pExit->keyword[0] != '\0')
							{
								strcpy(buf3, capitalize(pExit->keyword));
							}
							else
							{
								strcpy(buf3, "Door");
							}
						}
					}

					if ((pcd->tactical_mode / 1000) % 10 == 0)
					{
						vo = 0;
					}
					else
					{
						vo = -1;
					}
					offset = 0;

					if (buf3[0] == 't' || buf3[0] == 'T')
					{
						if (buf3[1] == 'h' || buf3[1] == 'H')
						{
							if (buf3[2] == 'e' || buf3[2] == 'E')
							{
								if (buf3[3] == ' ')
								{
									offset = 4;
								}
							}
						}
					}
					if (buf3[0] == 'a' || buf3[0] == 'A')
					{
						if (buf3[1] == ' ')
						{
							offset = 2;
						}
					}
					str_cpy_max (buf2, capitalize (buf3 + offset), cw + 1);
					if (size_v - 1 > 5)
					{
						switch (door)
						{
							case -1:
								pto  = tm + size_h * (3 + vo) + size_h - 1 - 2 * cw;
								ptoc = tc + size_h * (3 + vo) + size_h - 1 - 2 * cw;
								break;
							case 0:
								pto  = tm + size_h * (2 + vo) + size_h - 2 - 3 * cw / 2;
								*pto = '|';
								pto  = tm + size_h * (1 + vo) + size_h - 1 - 2 * cw;
								ptoc = tc + size_h * (1 + vo) + size_h - 1 - 2 * cw;
								break;
							case 1:
								pto  = tm + size_h * (3 + vo) + size_h - 1 - cw;
								*pto = '-';
								pto  = tm + size_h * (3 + vo) + size_h - cw;
								ptoc = tc + size_h * (3 + vo) + size_h - cw;
								break;
							case 2:
								pto  = tm + size_h * (4 + vo) + size_h - 2 - 3 * cw / 2;
								*pto = '|';
								pto  = tm + size_h * (5 + vo) + size_h - 1 - 2 * cw;
								ptoc = tc + size_h * (5 + vo) + size_h - 1 - 2 * cw;
								break;
							case 3:
								pto  = tm + size_h * (3 + vo) + size_h - 2 - 2 * cw;
								*pto = '-';
								pto  = tm + size_h * (3 + vo) + size_h - 2 - 3 * cw;
								ptoc = tc + size_h * (3 + vo) + size_h - 2 - 3 * cw;
								break;
							case 4:
								pto  = tm + size_h * (2 + vo) + size_h - 1 - cw;
								*pto = '/';
								pto  = tm + size_h * (1 + vo) + size_h - cw;
								ptoc = tc + size_h * (1 + vo) + size_h - cw;
								break;
							case 5:
								pto  = tm + size_h * (4 + vo) + size_h - 2 - 2 * cw;
								*pto = '/';
								pto  = tm + size_h * (5 + vo) + size_h - 2 - 3 * cw;
								ptoc = tc + size_h * (5 + vo) + size_h - 2 - 3 * cw;
								break;
						}
						for (cnt = 0, found = FALSE, pti = (bool *)buf2 ; cnt < cw ; ptoc++, pto++, cnt++)
						{
							if (found)
							{
								*pto  = ' ';
								*ptoc = (bool) colors;
							}
							else if (*pti == '\n' || *pti == '\r' || *pti == '\0')
							{
								found = TRUE;
								*pto  = ' ';
								*ptoc = (bool) colors;
							}
							else
							{
								*pto  = *pti;
								*ptoc = (bool) colors;
								pti++;
							}
						}
					}
					else
					{
						switch (door)
						{
							case -1:
								pto  = tm + size_h * (2 + vo) + size_h - 1 - 2 * cw;
								ptoc = tc + size_h * (2 + vo) + size_h - 1 - 2 * cw;
								break;
							case 0:
								pto  = tm + size_h * (1 + vo) + size_h - 1 - 2 * cw;
								ptoc = tc + size_h * (1 + vo) + size_h - 1 - 2 * cw;
								break;
							case 1:
								pto  = tm + size_h * (2 + vo) + size_h - 1 - cw;
								*pto = '-';
								pto  = tm + size_h * (2 + vo) + size_h - cw;
								ptoc = tc + size_h * (2 + vo) + size_h - cw;
								break;
							case 2:
								pto  = tm + size_h * (3 + vo) + size_h - 1 - 2 * cw;
								ptoc = tc + size_h * (3 + vo) + size_h - 1 - 2 * cw;
								break;
							case 3:
								pto  = tm + size_h * (2 + vo) + size_h - 2 - 2 * cw;
								*pto = '-';
								pto  = tm + size_h * (2 + vo) + size_h - 2 - 3 * cw;
								ptoc = tc + size_h * (2 + vo) + size_h - 2 - 3 * cw;
								break;
							case 4:
								pto  = tm + size_h * (1 + vo) + size_h - 1 - cw;
								*pto = '/';
								pto  = tm + size_h * (1 + vo) + size_h - cw;
								ptoc = tc + size_h * (1 + vo) + size_h - cw;
								break;
							case 5:
								pto  = tm + size_h * (3 + vo) + size_h - 2 - 2 * cw;
								*pto = '/';
								pto  = tm + size_h * (3 + vo) + size_h - 2 - 3 * cw;
								ptoc = tc + size_h * (3 + vo) + size_h - 2 - 3 * cw;
								break;
						}
						for (cnt = 0, found = FALSE, pti = (bool *) buf2 ; cnt < cw; ptoc++, pto++, cnt++)
						{
							if (found)
							{
								*pto  = ' ';
								*ptoc = (bool) colors;
							}
							else if (*pti == '\n' || *pti == '\r' || *pti == '\0')
							{
								found = TRUE;
								*pto  = ' ';
								*ptoc = (bool) colors;
							}
							else
							{
								*pto  = *pti;
								*ptoc = (bool) colors;
								pti++;
							}
						}
					}
				}
			}
		}
	}
	pop_call();
	return (mud->tactical);
}

TACTICAL_MAP *get_diff_tactical (CHAR_DATA * ch)
{
	int size_v, size_h;

	push_call("get_diff_tactical(%p)",ch);

	size_v = CH(ch->desc)->pcdata->tactical_mode % 100 + 1;
	size_h = CH(ch->desc)->pcdata->vt100_type    / 100;

	get_tactical_map(ch);

	if (CH(ch->desc)->pcdata->tactical == NULL)
	{
		pop_call();
		return (mud->tactical);
	}

	if (memcmp(mud->tactical->map, CH(ch->desc)->pcdata->tactical->map, size_v * size_h))
	{
		pop_call();
		return mud->tactical;
	}

	if (memcmp(mud->tactical->color, CH(ch->desc)->pcdata->tactical->color, size_v * size_h))
	{
		pop_call();
		return mud->tactical;
	}
	pop_call();
	return NULL;
}


char *get_color_diff (CHAR_DATA * ch, bool old_for, bool old_bak, bool old_bold, bool new_for, bool new_bak, bool new_bold)
{
	static char buf[20];

	push_call("get_color_diff(%p,%p,%p,%p,%p,%p,%p)",ch,old_for,old_bak,old_bold,new_for,new_bak,new_bold);

	buf[0] = '\0';

	if (ch->desc == NULL)
	{
		pop_call();
		return buf;
	}

	if (CH(ch->desc)->pcdata->ansi == FALSE && !IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_BOLD) && !IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_REVERSE))
	{
		pop_call();
		return buf;
	}

	/* supporting bold and background color */

	if (CH(ch->desc)->pcdata->ansi == FALSE)
	{
		if (new_bak == old_bak && new_bold == old_bold)
		{
			pop_call();
			return buf;
		}

		if (new_bak > 0 && new_bak != 8 && IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_REVERSE))
		{
			if (new_bak != old_bak)
			{
				strcpy(buf, "\033[7m");
			}
		}
		else
		{
			switch (new_bold)
			{
				case 0:
					if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_ECMA48))
					{
						strcpy(buf, "\033[m");
					}
					else
					{
						strcpy(buf, "\033[0m");
					}
					break;
				case 1:
					if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_BOLD))
					{
						strcpy(buf, "\033[1m");
					}
					break;
				case 4:
					if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_UNDERLINE))
					{
						strcpy(buf, "\033[4m");
					}
					break;
				case 5:
					if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_FLASH))
					{
						strcpy(buf, "\033[5m");
					}
					break;
				case 7:
					if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_REVERSE))
					{
						strcpy(buf, "\033[7m");
					}
					break;
				case 22:
					if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_ECMA48))
					{
						strcpy(buf, "\033[22m");
					}
					break;
				default:
					pop_call();
					return buf;
			}
		}
		pop_call();
		return buf;
	}

	if (old_for == new_for && old_bak == new_bak && old_bold == new_bold)
	{
		pop_call();
		return buf;
	}

	/*
		VT100 and minor ECMA-48 support - Scandum 27-02-2002
	*/

	strcpy(buf, "\033[");

	if (new_bold != old_bold)
	{
		switch (new_bold)
		{
			case 0:
				strcat(buf, "0");
				break;
			case 1:
				if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_BOLD))
				{
					strcat(buf, "1");
				}
				break;
			case 4:
				if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_UNDERLINE))
				{
					strcat(buf, "4");
				}
				break;
			case 5:
				if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_FLASH))
				{
					strcat(buf, "5");
				}
				break;
			case 7:
				if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_REVERSE))
				{
					strcat(buf, "7");
				}
				break;
			case 22:
				if (IS_SET(CH(ch->desc)->pcdata->vt100_flags, VT100_ECMA48))
				{
					strcat(buf, "22");
				}
				break;
		}
	}
	if (new_for != 8 && ((new_bold != old_bold && new_bold == 0) || new_for != old_for))
	{
		cat_sprintf(buf, "%s%d", (buf[2] != '\0') ? ";" : "", 30 + new_for);
	}

	if (new_bak != 8 && ((new_bold != old_bold && new_bold == 0) || new_bak != old_bak))
	{
		cat_sprintf(buf, "%s%d", (buf[2] != '\0') ? ";" : "", 40 + new_bak);
	}
	strcat(buf, "m");

	pop_call();
	return buf;
}


/*
 *	Retrieve amount of empty space available on character's screen
 */
int get_pager_breakpt( CHAR_DATA *ch )
{
	int breakpt = ch->pcdata->vt100_type % 100;

	push_call("get_pager_breakpt(%p",ch);

	if (ch->pcdata->vt100 != 0)
	{
		breakpt -= 4 + ch->pcdata->tactical_mode % 100;
	}
	else if (IS_SET(ch->act, PLR_BLANK))
	{
		breakpt -= 4;
	}
	else
	{
		breakpt -= 3;
	}
	pop_call();
	return breakpt;
}

int get_page_width( CHAR_DATA *ch )
{
	push_call("get_page_width(%p)",ch);

	if (ch->desc)
	{
		pop_call();
		return (CH(ch->desc)->pcdata->vt100_type / 100);
	}
	pop_call();
	return 80;
}