/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 * ------------------------------------------------------------------------ *
 * 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{rfeldt, Tom Madsen, and Katja Nyboe.     *
 * ------------------------------------------------------------------------ *
 *			 Low-level communication module                                 *
 ****************************************************************************/

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include "mud.h"
#ifdef USE_IMC
#include "imc.h"
#include "icec.h"
#endif


/*
 * Socket and TCP/IP stuff.
 */
#ifdef WIN32
  #include <io.h>
  #undef EINTR
  #undef EMFILE
  #define EINTR WSAEINTR
  #define EMFILE WSAEMFILE
  #define EWOULDBLOCK WSAEWOULDBLOCK
  #define MAXHOSTNAMELEN 32

  #define  TELOPT_ECHO        '\x01'
  #define  GA                 '\xF9'
  #define  SB                 '\xFA'
  #define  WILL               '\xFB'
  #define  WONT               '\xFC'
  #define  DO                 '\xFD'
  #define  DONT               '\xFE'
  #define  IAC                '\xFF'
  void bailout(void);
  void shutdown_checkpoint (void);
#else
  #include <sys/socket.h>
  #include <netinet/in.h>
//  #include <netinet/in_systm.h>
  #include <netinet/ip.h>
  #include <arpa/inet.h>
  #include <arpa/telnet.h>
  #include <netdb.h>
  #define closesocket close
#endif

#ifdef sun
int gethostname ( char *name, int namelen );
#endif

const	char	echo_off_str	[] = { IAC, WILL, TELOPT_ECHO, '\0' };
const	char	echo_on_str	[] = { IAC, WONT, TELOPT_ECHO, '\0' };
const	char 	go_ahead_str	[] = { IAC, GA, '\0' };

#ifdef MCCP
#define TELOPT_COMPRESS 85
#define TELOPT_COMPRESS2 86
const   char    eor_on_str      [] = { IAC, WILL, TELOPT_EOR, '\0' };
const   char    compress_on_str [] = { IAC, WILL, TELOPT_COMPRESS2, '\0' };
const   char    compress2_on_str [] = { IAC, WILL, TELOPT_COMPRESS2, '\0' };

bool    compressStart   args( ( DESCRIPTOR_DATA *d, unsigned char telopt ) );
bool    compressEnd     args( ( DESCRIPTOR_DATA *d ) );
#endif

void	auth_maxdesc	args( ( int *md, fd_set *ins, fd_set *outs,
				fd_set *excs ) );
void	auth_check	args( ( fd_set *ins, fd_set *outs, fd_set *excs ) );
void	set_auth	args( ( DESCRIPTOR_DATA *d ) );
void	kill_auth	args( ( DESCRIPTOR_DATA *d ) );


void    save_sysdata args( ( SYSTEM_DATA sys ) );

/*
 * External Functions
 */
void	shutdown_mud	args( ( char *reason ) );


/*
 * Global variables.
 */
IMMORTAL_HOST * immortal_host_start;    /* Start of Immortal legal domains */
IMMORTAL_HOST * immortal_host_end;    /* End of Immortal legal domains */
DESCRIPTOR_DATA *   first_descriptor;	/* First descriptor		*/
DESCRIPTOR_DATA *   last_descriptor;	/* Last descriptor		*/
DESCRIPTOR_DATA *   d_next;		/* Next descriptor in loop	*/
int		    num_descriptors;
FILE *		    fpReserve;		/* Reserved file handle		*/
bool		    mud_down;		/* Shutdown			*/
bool		    service_shut_down;  /* Shutdown by operator closing down service */
bool		    wizlock;		/* Game is wizlocked		*/
time_t              boot_time;
HOUR_MIN_SEC  	    set_boot_time_struct;
HOUR_MIN_SEC *      set_boot_time;
struct tm *         new_boot_time;
struct tm           new_boot_struct;
char		    str_boot_time[MAX_INPUT_LENGTH];
char		    lastplayercmd[MAX_INPUT_LENGTH*2];
time_t		    current_time;	/* Time of this pulse		*/
int		    control;		/* Controlling descriptor	*/
int		    newdesc;		/* New descriptor		*/
fd_set		    in_set;		/* Set of desc's for reading	*/
fd_set		    out_set;		/* Set of desc's for writing	*/
fd_set		    exc_set;		/* Set of desc's with errors	*/
int 		    maxdesc;
char *		    alarm_section = "(unknown)";


/*
 * OS-dependent local functions.
 */
void	game_loop		args( ( ) );
int	init_socket		args( ( int port ) );
void	new_descriptor		args( ( int new_desc ) );
bool	read_from_descriptor	args( ( DESCRIPTOR_DATA *d ) );
bool	write_to_descriptor	args( ( int desc, char *txt, int length ) );


/*
 * Other local functions (OS-independent).
 */
bool	check_parse_name	args( ( char *name, bool newchar ) );
bool	check_reconnect		args( ( DESCRIPTOR_DATA *d, char *name,
				    bool fConn ) );
bool	check_playing		args( ( DESCRIPTOR_DATA *d, char *name, bool kick ) );
int	main			args( ( int argc, char **argv ) );
void	nanny			args( ( DESCRIPTOR_DATA *d, char *argument ) );
bool	flush_buffer		args( ( DESCRIPTOR_DATA *d, bool fPrompt ) );
void	read_from_buffer	args( ( DESCRIPTOR_DATA *d ) );
void	stop_idling		args( ( CHAR_DATA *ch ) );
void	free_desc		args( ( DESCRIPTOR_DATA *d ) );
void	display_prompt		args( ( DESCRIPTOR_DATA *d ) );
int	make_color_sequence	args( ( const char *col, char *buf,
					DESCRIPTOR_DATA *d ) );
void	set_pager_input		args( ( DESCRIPTOR_DATA *d,
					char *argument ) );
bool	pager_output		args( ( DESCRIPTOR_DATA *d ) );

void	mail_count		args( ( CHAR_DATA *ch ) );

void    tax_player		args( ( CHAR_DATA *ch ) );
void    mccp_interest		args( ( CHAR_DATA *ch ) );

bool	check_total_ip	args( ( DESCRIPTOR_DATA *dnew) );


int port;

char capitalizeString(char *text)
{
	int i;

	if (text[0] == '\0')
		return *text;

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

	for(i=1;text[i] != '\0';i++)
	{
		text[i] = LOWER(text[i]);
	}

	return *text;
}

/*
====================
send color to a desc	-Nopey
====================
*/
void send_to_desc_color( const char *txt, DESCRIPTOR_DATA *d )
{
	char *colstr;
	const char *prevstr = txt;
	char colbuf[20];
	int ln;

	if ( !d )
	{
	  bug( "send_to_desc_color: NULL *d" );
	  return;
	}

	if ( !txt || !d->descriptor )
	  return;

	while ( (colstr = strpbrk(prevstr, "&^}")) != NULL )
	{
	  if (colstr > prevstr)
	    write_to_buffer(d, prevstr, (colstr-prevstr));
	  ln = make_color_sequence(colstr, colbuf, d);
	  if ( ln < 0 )
	  {
	    prevstr = colstr+1;
	    break;
	  }
	  else if ( ln > 0 )
	    write_to_buffer(d, colbuf, ln);
	  prevstr = colstr+2;
	}
	if ( *prevstr )
	  write_to_buffer(d, prevstr, 0);
	return;
}

#ifdef WIN32
  int mainthread( int argc, char **argv )
#else
  int main( int argc, char **argv )
#endif
{
    struct timeval now_time;
    char hostn[128];

   bool fCopyOver = !TRUE;
    /*
     * Memory debugging if needed.
     */
#if defined(MALLOC_DEBUG)
    malloc_debug( 2 );
#endif

    DONT_UPPER			= FALSE;
    num_descriptors		= 0;
    first_descriptor		= NULL;
    last_descriptor		= NULL;
    sysdata.NO_NAME_RESOLVING	= TRUE;
    sysdata.WAIT_FOR_AUTH	= TRUE;

    /*
     * Init time.
     */
    gettimeofday( &now_time, NULL );
    current_time = (time_t) now_time.tv_sec;
/*  gettimeofday( &boot_time, NULL);   okay, so it's kludgy, sue me :) */
    boot_time = time(0);         /*  <-- I think this is what you wanted */
    strcpy( str_boot_time, ctime( &current_time ) );

    /*
     * Init boot time.
     */
    set_boot_time = &set_boot_time_struct;
    set_boot_time->manual = 0;

    new_boot_time = update_time(localtime(&current_time));
    /* Copies *new_boot_time to new_boot_struct, and then points
       new_boot_time to new_boot_struct again. -- Alty */
    new_boot_struct = *new_boot_time;
    new_boot_time = &new_boot_struct;
    if (new_boot_time->tm_hour > 3)
    	new_boot_time->tm_mday += 1;
    new_boot_time->tm_sec = 0;
    new_boot_time->tm_min = 0;
    new_boot_time->tm_hour = 4;

    /* Update new_boot_time (due to day increment) */
    new_boot_time = update_time(new_boot_time);
    new_boot_struct = *new_boot_time;
    new_boot_time = &new_boot_struct;
    /* Bug fix submitted by Gabe Yoder */
    new_boot_time_t = mktime(new_boot_time);
    reboot_check(mktime(new_boot_time));
    /* Set reboot time string for do_time */
    get_reboot_string();
    init_pfile_scan_time(); /* Pfile autocleanup initializer - Samson 5-8-99 */

    /*
     * Reserve two channels for our use.
     */
    if ( ( fpReserve = fopen( NULL_FILE, "r" ) ) == NULL )
    {
	perror( NULL_FILE );
	exit( 1 );
    }
    if ( ( fpLOG = fopen( NULL_FILE, "r" ) ) == NULL )
    {
	perror( NULL_FILE );
	exit( 1 );
    }

   /*
    * Get the port number.
    */
   port = 4000;
   if ( argc > 1 )
   {
	if ( !is_number( argv[1] ) )
	{
	    fprintf( stderr, "Usage: %s [port #]\n", argv[0] );
	    exit( 1 );
	}
	else if ( ( port = atoi( argv[1] ) ) <= 1024 )
	{
	    fprintf( stderr, "Port number must be above 1024.\n" );
	    exit( 1 );
	}

      if (argv[2] && argv[2][0])
      {
         fCopyOver = TRUE;
         control = atoi( argv[3] );
      }
      else
         fCopyOver = FALSE;
   }


    /*
     * Run the game.
     */
#ifdef WIN32
    {
	/* Initialise Windows sockets library */

	unsigned short wVersionRequested = MAKEWORD(1, 1);
	WSADATA wsadata;
	int err;

	/* Need to include library: wsock32.lib for Windows Sockets */
	err = WSAStartup(wVersionRequested, &wsadata);
	if (err)
	{
	    fprintf(stderr, "Error %i on WSAStartup\n", err);
	    exit(1);
	}

	/* standard termination signals */
	signal(SIGINT, (void *) bailout);
	signal(SIGTERM, (void *) bailout);
  }
#endif /* WIN32 */

/*	Disabled -Goku 10.14.03
    log_string("Booting Chat Robot");
    startchat(CHAT_FILE);
*/
    log_string("Booting Database");
    boot_db( fCopyOver );
    log_string("Initializing socket");
    if (!fCopyOver) /* We have already the port if copyover'ed */
    {
	control  = init_socket( port   );
    }

#ifdef OLD_IMC
    /* Be sure to change RoD to your mud's name! */
    if(port == 4000)
	imc_startup ("RoD", port+5, "imc/");
    else
	imc_startup("RoDBLD", port+5, "imc/");
#else
#ifdef USE_IMC
    imc_startup ("imc/");
    icec_init();
#endif
#endif

    /* I don't know how well this will work on an unnamed machine as I don't
       have one handy, and the man pages are ever-so-helpful.. -- Alty */
    if (gethostname(hostn, sizeof(hostn)) < 0)
    {
      perror("main: gethostname");
      strcpy(hostn, "unresolved");
    }
    sprintf( log_buf, "%s ready at address %s on port %d.",
		sysdata.mud_name, hostn, port );
/*
    sprintf( log_buf, "Realms of Despair ready at address %s on port %d.",
	hostn, port );
*/
    log_string( log_buf );

    game_loop( );

#ifdef USE_IMC
    imc_shutdown(); /* shut down IMC */
#endif

    closesocket( control  );

#ifdef WIN32
    if (service_shut_down)
    {
	CHAR_DATA *vch;

	/* Save all characters before booting. */
	for ( vch = first_char; vch; vch = vch->next )
	    if ( !IS_NPC( vch ) )
	    {
		shutdown_checkpoint ();
		save_char_obj( vch );
	    }
	}

	/* Save MUD time */
	save_timedata();

    /* Shut down Windows sockets */

    WSACleanup();                 /* clean up */
    kill_timer();                 /* stop timer thread */
#endif


    /*
     * That's all, folks.
     */
    log_string( "Normal termination of game." );
    exit( 0 );
    return 0;
}


int init_socket( int port )
{
    char hostname[64];
    struct sockaddr_in	 sa;
    int x = 1;
    int fd;

    gethostname(hostname, sizeof(hostname));


    if ( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
    {
	perror( "Init_socket: socket" );
	exit( 1 );
    }

    if ( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
		    (void *) &x, sizeof(x) ) < 0 )
    {
	perror( "Init_socket: SO_REUSEADDR" );
	closesocket( fd );
	exit( 1 );
    }

#if defined(SO_DONTLINGER) && !defined(SYSV)
    {
	struct	linger	ld;

	ld.l_onoff  = 1;
	ld.l_linger = 1000;

	if ( setsockopt( fd, SOL_SOCKET, SO_DONTLINGER,
			(void *) &ld, sizeof(ld) ) < 0 )
	{
	    perror( "Init_socket: SO_DONTLINGER" );
	    closesocket( fd );
	    exit( 1 );
	}
    }
#endif

    memset(&sa, '\0', sizeof(sa));
    sa.sin_family   = AF_INET;
    sa.sin_port	    = htons( port );

    if ( bind( fd, (struct sockaddr *) &sa, sizeof(sa) ) == -1 )
    {
	perror( "Init_socket: bind" );
	closesocket( fd );
	exit( 1 );
    }

    if ( listen( fd, 50 ) < 0 )
    {
	perror( "Init_socket: listen" );
	closesocket( fd );
	exit( 1 );
    }

    return fd;
}

static void SegVio()
{
  CHAR_DATA *ch;

  for( ch = first_char; ch; ch = ch->next )
  {
    if( !IS_NPC( ch ) )
	save_char_obj( ch );
  }
  if( fork() == 0 )
    abort();
  exit(1);
/*  char buf[MAX_STRING_LENGTH];

  log_string( "SEGMENTATION VIOLATION" );
  log_string( lastplayercmd );
  for ( ch = first_char; ch; ch = ch->next )
  {
    sprintf( buf, "%cPC: %-20s room: %d", IS_NPC(ch) ? 'N' : ' ',
    		ch->name, ch->in_room->vnum );
    log_string( buf );
  }
  exit(0);
*/}

static void SigTerm( int signum )
{
	CHAR_DATA *vch;
	char buf[MAX_STRING_LENGTH];

	sprintf( log_buf, "&RATTENTION!! Message from game server: &YEmergency shutdown called.\a" );
	echo_to_all( AT_RED, log_buf, ECHOTAR_ALL );
	sprintf( log_buf, "Executing emergency shutdown proceedure." );
	echo_to_all( AT_YELLOW, log_buf, ECHOTAR_ALL );
	log_string( "Message from server: Executing emergency shutdown proceedure." );
	shutdown_mud( log_buf );	strcat( buf, "\n\r" );

	if ( auction->item )
		do_auction( supermob, "stop" );

	log_string( "Saving players...." );
	for ( vch = first_char; vch; vch = vch->next )
	{
	 	if ( !IS_NPC( vch ) ) 
		{ 
			save_char_obj( vch );
			sprintf( log_buf, "%s saved.", vch->name );
			log_string( log_buf );
			if( vch->desc )
			{
				write_to_descriptor( vch->desc->descriptor, buf, 0 );
				write_to_descriptor( vch->desc->descriptor, "You have been saved to disk.\n\r", 0 );
			}
		}
	}

	/*  Save morphs so can sort later. --Shaddai */
	if ( sysdata.morph_opt )
	save_morphs( );

	fflush( stderr );	/* make sure strerr is flushed */

	close( control );

	/* Extra ports disabled - Samson 12-1-98 : uncomment
	   this section if you still use them
	close( control2 );
	close( conclient);
	close( conjava  ); */

#ifdef IMC
	imc_shutdown(); /* shut down IMC */
#endif
#ifdef I3
	I3_shutdown( 0 );
#endif 

	log_string( "Emergency shutdown complete." );

	/* Using exit here instead of mud_down because the
	   thing sometimes failed to kill when asked!! */
	exit( 0 );
}

 /*
 * LAG alarm!							-Thoric
 */
void caught_alarm()
{
    char buf[MAX_STRING_LENGTH];

    sprintf(buf, "ALARM CLOCK! In section %s", alarm_section );
    bug( buf );
    strcpy( buf, "Alas, the hideous malevalent entity known only as 'Lag' rises once more!\n\r" );
    echo_to_all( AT_IMMORT, buf, ECHOTAR_ALL );
    if ( newdesc )
    {
	FD_CLR( newdesc, &in_set );
	FD_CLR( newdesc, &out_set );
	FD_CLR( newdesc, &exc_set );
	log_string( "clearing newdesc" );
    }
}

bool check_bad_desc( int desc )
{
    if ( FD_ISSET( desc, &exc_set ) )
    {
	FD_CLR( desc, &in_set );
	FD_CLR( desc, &out_set );
	log_string( "Bad FD caught and disposed." );
	return TRUE;
    }
    return FALSE;
}

/*
 * Determine whether this player is to be watched  --Gorog
 */
bool chk_watch(sh_int player_level, char *player_name, char *player_site)
{
    WATCH_DATA *pw;
/*
    char buf[MAX_INPUT_LENGTH];
    sprintf( buf, "che_watch entry: plev=%d pname=%s psite=%s",
                  player_level, player_name, player_site);
    log_string(buf);
*/
    if ( !first_watch ) return FALSE;

    for ( pw = first_watch; pw; pw = pw->next )
    {
        if ( pw->target_name )
        {
           if ( !str_cmp(pw->target_name, player_name)
           &&   player_level < pw->imm_level )
                 return TRUE;
        }
        else
        if ( pw->player_site )
        {
           if ( !str_prefix(pw->player_site, player_site)
           &&   player_level < pw->imm_level )
                 return TRUE;
        }
    }
    return FALSE;
}


void accept_new( int ctrl )
{
	static struct timeval null_time;
	DESCRIPTOR_DATA *d;
	/* int maxdesc; Moved up for use with id.c as extern */

#if defined(MALLOC_DEBUG)
	if ( malloc_verify( ) != 1 )
	    abort( );
#endif

	/*
	 * Poll all active descriptors.
	 */
	FD_ZERO( &in_set  );
	FD_ZERO( &out_set );
	FD_ZERO( &exc_set );
	FD_SET( ctrl, &in_set );

	maxdesc = ctrl;
	newdesc = 0;
	for ( d = first_descriptor; 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( d->ifd != -1 && d->ipid != -1 )
	    {
	    	maxdesc = UMAX( maxdesc, d->ifd );
	    	FD_SET( d->ifd, &in_set );
	    }
	    if ( d == last_descriptor )
	      break;
	}
	auth_maxdesc(&maxdesc, &in_set, &out_set, &exc_set);

#ifdef USE_IMC
	maxdesc=imc_fill_fdsets(maxdesc, &in_set, &out_set, &exc_set);
#endif

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


	if ( FD_ISSET( ctrl, &exc_set ) )
	{
	    bug( "Exception raise on controlling descriptor %d", ctrl );
	    FD_CLR( ctrl, &in_set );
	    FD_CLR( ctrl, &out_set );
	}
	else
	if ( FD_ISSET( ctrl, &in_set ) )
	{
	    newdesc = ctrl;
	    new_descriptor( newdesc );
	}
}

void game_loop( )
{
    struct timeval	  last_time;
    char cmdline[MAX_INPUT_LENGTH];
    DESCRIPTOR_DATA *d;
/*  time_t	last_check = 0;  */

#ifndef WIN32
    signal( SIGPIPE, SIG_IGN );
    signal( SIGALRM, caught_alarm );
#endif

    signal( SIGSEGV, SegVio );

    signal( SIGTERM, SigTerm ); /* Catch kill signals */

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

    /* Main loop */
    while ( !mud_down )
    {
	accept_new( control  );

	auth_check(&in_set, &out_set, &exc_set);

	/*
	 * Kick out descriptors with raised exceptions
	 * or have been idle, then check for input.
	 */
	for ( d = first_descriptor; d; d = d_next )
	{
	    if ( d == d->next )
	    {
	      bug( "descriptor_loop: loop found & fixed" );
	      d->next = NULL;
	    }
 	    d_next = d->next;

	    d->idle++;	/* make it so a descriptor can idle out */
	    if (d->idle > 2)
	    	d->prev_idle = d->idle;

	    if ( FD_ISSET( d->descriptor, &exc_set ) )
	    {
		FD_CLR( d->descriptor, &in_set  );
		FD_CLR( d->descriptor, &out_set );
		if ( d->character
		&& ( d->connected == CON_PLAYING
		||   d->connected == CON_EDITING ) )
		    save_char_obj( d->character );
		d->outtop	= 0;
		close_socket( d, TRUE );
		continue;
	    }
	    else
	    if ( (!d->character && d->idle > 480)		  /* 2 mins */
            ||   ( d->connected != CON_PLAYING && d->idle > 1200) /* 5 mins */
	    ||     d->idle > 28800 )				  /* 2 hrs  */
	    {
		write_to_descriptor( d->descriptor,
		 "Idle timeout... disconnecting.\n\r", 0 );
		d->outtop	= 0;
		close_socket( d, TRUE );
		continue;
	    }
	    else
	    {
		d->fcommand	= FALSE;

		if ( FD_ISSET( d->descriptor, &in_set ) )
		{
			if ( d->character )
			{
			  d->character->timer = 0;
			  if (d->character->pcdata && d->character->level < 51)
			  {
			  	if (d->idle >= 180)
			  	{
			  	if (d->character->pcdata->iIdle < 0)
			  		d->character->pcdata->iIdle = 0;
			  	if (d->character->pcdata->iIdle > 4)
			  		d->character->pcdata->iIdle = 0;

			  	d->character->pcdata->pIdle[d->character->pcdata->iIdle] = ( d->idle / 4 );
			  	d->character->pcdata->bot_warn[0]++;
			  	d->character->pcdata->iIdle++;
			    }
			  }
			}
			if (d->prev_idle < 2)
				d->psuppress_cmdspam = TRUE;
			else
				d->psuppress_cmdspam = FALSE;

			d->psuppress_channel = 0;
			d->prev_idle = d->idle;
			d->idle = 0;

			if ( !read_from_descriptor( d ) )
			{
			    FD_CLR( d->descriptor, &out_set );
			    if ( d->character
			    && ( d->connected == CON_PLAYING
			    ||   d->connected == CON_EDITING ) )
				save_char_obj( d->character );
			    d->outtop	= 0;
			    close_socket( d, FALSE );
			    continue;
			}
		}

	      /* check for input from the dns */
	      if( ( d->connected == CON_PLAYING || d->character != NULL ) && d->ifd != -1 && FD_ISSET( d->ifd, &in_set ) )
	         process_dns( d );

		if ( d->character && d->character->wait > 0 )
		{
			--d->character->wait;
			continue;
		}

		read_from_buffer( d );
		if ( d->incomm[0] != '\0' )
		{
			d->fcommand	= TRUE;
			stop_idling( d->character );

			strcpy( cmdline, d->incomm );
			d->incomm[0] = '\0';

			if ( d->character )
			  set_cur_char( d->character );

			if ( d->pagepoint )
			  set_pager_input(d, cmdline);
			else
			  switch( d->connected )
			  {
			   default:
 				nanny( d, cmdline );
				break;
			   case CON_PLAYING:
				interpret( d->character, cmdline );
				break;
			   case CON_EDITING:
				edit_buffer( d->character, cmdline );
				break;
/*
    case CON_NOTE_TEXT:
//        handle_con_note_text (d, argument);
			edit_buffer( d->character, cmdline );
        break;
*/
			  }
		}
	    }
	    if ( d == last_descriptor )
	      break;
	}

#ifdef USE_IMC
	/* kick IMC */
	imc_idle_select(&in_set, &out_set, &exc_set, current_time);
#endif

	/*
	 * Autonomous game motion.
	 */
	update_handler( );

	/*
	 * Check REQUESTS pipe
	 */
        check_requests( );

	/*
	 * Output.
	 */
	for ( d = first_descriptor; d; d = d_next )
	{
	    d_next = d->next;

	    if ( ( d->fcommand || d->outtop > 0 )
	    &&   FD_ISSET(d->descriptor, &out_set) )
	    {
	        if ( d->pagepoint )
	        {
	          if ( !pager_output(d) )
	          {
	            if ( d->character
	            && ( d->connected == CON_PLAYING
	            ||   d->connected == CON_EDITING ) )
	                save_char_obj( d->character );
	            d->outtop = 0;
	            close_socket(d, FALSE);
	          }
	        }
		else if ( !flush_buffer( d, TRUE ) )
		{
		    if ( d->character
		    && ( d->connected == CON_PLAYING
		    ||   d->connected == CON_EDITING ) )
			save_char_obj( d->character );
		    d->outtop	= 0;
		    close_socket( d, FALSE );
		}
	    }
	    if ( d == last_descriptor )
	      break;
	}

	/*
	 * Synchronize to a clock.
	 * Sleep( last_time + 1/PULSE_PER_SECOND - now ).
	 * Careful here of signed versus unsigned arithmetic.
	 */
	{
	    struct timeval now_time;
	    long secDelta;
	    long usecDelta;

	    gettimeofday( &now_time, NULL );
	    usecDelta	= ((int) last_time.tv_usec) - ((int) now_time.tv_usec)
			+ 1000000 / PULSE_PER_SECOND;
	    secDelta	= ((int) last_time.tv_sec ) - ((int) now_time.tv_sec );
	    while ( usecDelta < 0 )
	    {
		usecDelta += 1000000;
		secDelta  -= 1;
	    }

	    while ( usecDelta >= 1000000 )
	    {
		usecDelta -= 1000000;
		secDelta  += 1;
	    }

	    if ( secDelta > 0 || ( secDelta == 0 && usecDelta > 0 ) )
	    {
		struct timeval stall_time;

		stall_time.tv_usec = usecDelta;
		stall_time.tv_sec  = secDelta;
#ifdef WIN32
		Sleep( (stall_time.tv_sec * 1000L) + (stall_time.tv_usec / 1000L) );
#else
		if ( select( 0, NULL, NULL, NULL, &stall_time ) < 0 && errno != EINTR )
		{
		    perror( "game_loop: select: stall" );
		    exit( 1 );
		}
#endif
	    }
	}

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

        /* Check every 5 seconds...  (don't need it right now)
	if ( last_check+5 < current_time )
	{
	  CHECK_LINKS(first_descriptor, last_descriptor, next, prev,
	      DESCRIPTOR_DATA);
	  last_check = current_time;
	}
	*/
    }
    /*  Save morphs so can sort later. --Shaddai */
    if ( sysdata.morph_opt )
	save_morphs( );

    fflush(stderr);	/* make sure strerr is flushed */
    return;
}


void new_descriptor( int new_desc )
{
    char buf[MAX_STRING_LENGTH];
    DESCRIPTOR_DATA *dnew;
    struct sockaddr_in sock;
    struct hostent *from;
    int desc;
    int size;
    char bugbuf[MAX_STRING_LENGTH];
#ifdef WIN32
    unsigned long arg = 1;
#endif

    size = sizeof(sock);
    if ( check_bad_desc( new_desc ) )
    {
      set_alarm( 0 );
      return;
    }
    set_alarm( 2 );
    alarm_section = "new_descriptor::accept";
    if ( ( desc = accept( new_desc, (struct sockaddr *) &sock, &size) ) < 0 )
    {
	perror( "New_descriptor: accept" );
	sprintf(bugbuf, "[*****] BUG: New_descriptor: accept");
	log_string_plus( bugbuf, LOG_COMM, sysdata.log_level );
	set_alarm( 0 );
	return;
    }
    if ( check_bad_desc( new_desc ) )
    {
      set_alarm( 0 );
      return;
    }
#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif

    set_alarm( 2 );
    alarm_section = "new_descriptor: after accept";

#ifdef WIN32
    if ( ioctlsocket(desc, FIONBIO, &arg) == -1 )
#else
    if ( fcntl( desc, F_SETFL, FNDELAY ) == -1 )
#endif
    {
	perror( "New_descriptor: fcntl: FNDELAY" );
	set_alarm( 0 );
	return;
    }
    if ( check_bad_desc( new_desc ) )
      return;

    CREATE( dnew, DESCRIPTOR_DATA, 1 );
    dnew->next		= NULL;
    dnew->descriptor	= desc;
    dnew->connected	= CON_GET_NAME;
    dnew->ansi	  = TRUE; /* force ansi */
    dnew->outsize	= 2000;
    dnew->idle		= 0;
    dnew->lines		= 0;
    dnew->scrlen	= 24;
    dnew->port		= ntohs( sock.sin_port );
    dnew->user 		= STRALLOC("(unknown)");
    dnew->newstate	= 0;
    dnew->prevcolor	= 0x07;
    dnew->ifd           = -1;    /* Descriptor pipes, used for DNS resolution and such */
    dnew->ipid          = -1;

    CREATE( dnew->outbuf, char, dnew->outsize );
    strcpy( log_buf, inet_ntoa( sock.sin_addr ) );

    dnew->host = STRALLOC( log_buf );
/*
    if ( !sysdata.NO_NAME_RESOLVING )
    {
	strcpy( buf, in_dns_cache( log_buf ) );

	if( buf[0] == '\0' )
	   resolve_dns( dnew, sock.sin_addr.s_addr );
	else
	{
	   STRFREE( dnew->host2 );
	   dnew->host = STRALLOC( buf );
	}
    }
*/
//		OLD DNS CODE --Saiyr

//    else
//    {
	from = gethostbyaddr( (char *) &sock.sin_addr,
		sizeof( sock.sin_addr), AF_INET );
	dnew->host2 = STRALLOC( (char *)( from ? from->h_name : buf ) );
//    }
    if ( check_total_bans( dnew ) )
    {
          write_to_descriptor (desc,
                         "Your site has been banned from this Mud.\n\r", 0);
          free_desc (dnew);
          set_alarm (0);
          return;
     }
    /*
     * Init descriptor data.
     */

    if ( !last_descriptor && first_descriptor )
    {
	DESCRIPTOR_DATA *d;

	bug( "New_descriptor: last_desc is NULL, but first_desc is not! ...fixing" );
	for ( d = first_descriptor; d; d = d->next )
	   if ( !d->next )
		last_descriptor = d;
    }

    LINK( dnew, first_descriptor, last_descriptor, next, prev );

#ifdef MCCP
    write_to_buffer(dnew, eor_on_str, 0);
    write_to_buffer(dnew, compress2_on_str, 0);
    write_to_buffer(dnew, compress_on_str, 0);
#endif


    /*
     * Send the greeting.
     */
    {
	     extern char * help_greeting;
	if ( help_greeting[0] == '.' )
	    send_to_desc_color( help_greeting+1, dnew );
       else
	    send_to_desc_color( help_greeting , dnew );
      }
	 send_to_desc_color( "&wEnter your character's name, or type &Wnew&w: &D", dnew );

    alarm_section = "new_descriptor: set_auth";
    set_auth(dnew);
    alarm_section = "new_descriptor: after set_auth";

    if ( ++num_descriptors > sysdata.maxplayers )
	sysdata.maxplayers = num_descriptors;
    if ( sysdata.maxplayers > sysdata.alltimemax )
    {
	if ( sysdata.time_of_max )
	  DISPOSE(sysdata.time_of_max);
	sprintf(buf, "%24.24s", ctime(&current_time));
	sysdata.time_of_max = str_dup(buf);
	sysdata.alltimemax = sysdata.maxplayers;
	sprintf( log_buf, "Broke all-time maximum player record: %d", sysdata.alltimemax );
	log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
	to_channel( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
	save_sysdata( sysdata );
    }
    set_alarm(0);
    return;
}

void free_desc( DESCRIPTOR_DATA *d )
{
    kill_auth(d);
    closesocket( d->descriptor );
    DISPOSE( d->outbuf );
    STRFREE( d->user );    /* identd */
    if ( d->pagebuf )
	DISPOSE( d->pagebuf );

#ifdef MCCP
    compressEnd(d);
#endif

   DISPOSE( d );
/*    --num_descriptors;  This is called from more than close_socket -- Alty */
    return;
}

void close_socket( DESCRIPTOR_DATA *dclose, bool force )
{
    CHAR_DATA *ch;
    DESCRIPTOR_DATA *d;
    bool DoNotUnlink = FALSE;

    if( dclose->ipid != -1 )
    {
	int status;

	kill( dclose->ipid, SIGKILL );
	waitpid( dclose->ipid, &status, 0 );
    }
    if( dclose->ifd != -1 )
    	close( dclose->ifd );

    /* flush outbuf */
    if ( !force && dclose->outtop > 0 )
	flush_buffer( dclose, FALSE );

    /* say bye to whoever's snooping this descriptor */
    if ( dclose->snoop_by )
	write_to_buffer( dclose->snoop_by,
	    "Your victim has left the game.\n\r", 0 );

    /* stop snooping everyone else */
    for ( d = first_descriptor; d; d = d->next )
	if ( d->snoop_by == dclose )
	  d->snoop_by = NULL;

    /* Check for switched people who go link-dead. -- Altrag */
    if ( dclose->original )
    {
	if ( ( ch = dclose->character ) != NULL )
	  do_return(ch, "");
	else
	{
	  bug( "Close_socket: dclose->original without character %s",
		(dclose->original->name ? dclose->original->name : "unknown") );
	  dclose->character = dclose->original;
	  dclose->original = NULL;
	}
    }

    ch = dclose->character;

    /* sanity check :( */
    if ( !dclose->prev && dclose != first_descriptor )
    {
	DESCRIPTOR_DATA *dp, *dn;
	bug( "Close_socket: %s desc:%p != first_desc:%p and desc->prev = NULL!",
		ch ? ch->name : d->host, dclose, first_descriptor );
	dp = NULL;
	for ( d = first_descriptor; d; d = dn )
	{
	   dn = d->next;
	   if ( d == dclose )
	   {
		bug( "Close_socket: %s desc:%p found, prev should be:%p, fixing.",
		    ch ? ch->name : d->host, dclose, dp );
		dclose->prev = dp;
		break;
	   }
	   dp = d;
	}
	if ( !dclose->prev )
	{
	    bug( "Close_socket: %s desc:%p could not be found!.",
		    ch ? ch->name : dclose->host, dclose );
	    DoNotUnlink = TRUE;
	}
    }
    if ( !dclose->next && dclose != last_descriptor )
    {
	DESCRIPTOR_DATA *dp, *dn;
	bug( "Close_socket: %s desc:%p != last_desc:%p and desc->next = NULL!",
		ch ? ch->name : d->host, dclose, last_descriptor );
	dn = NULL;
	for ( d = last_descriptor; d; d = dp )
	{
	   dp = d->prev;
	   if ( d == dclose )
	   {
		bug( "Close_socket: %s desc:%p found, next should be:%p, fixing.",
		    ch ? ch->name : d->host, dclose, dn );
		dclose->next = dn;
		break;
	   }
	   dn = d;
	}
	if ( !dclose->next )
	{
	    bug( "Close_socket: %s desc:%p could not be found!.",
		    ch ? ch->name : dclose->host, dclose );
	    DoNotUnlink = TRUE;
	}
    }

    if ( dclose->character )
    {
	sprintf( log_buf, "Closing link to %s.", ch->pcdata->filename );
	log_string_plus( log_buf, LOG_COMM, UMAX( sysdata.log_level, ch->level ) );
/*
	if ( ch->level < LEVEL_DEMI )
	  to_channel( log_buf, CHANNEL_MONITOR, "Monitor", ch->level );
*/

        if ( (dclose->connected == CON_PLAYING
             || dclose->connected == CON_EDITING)
             ||(dclose->connected >= CON_NOTE_TO
             && dclose->connected <= CON_NOTE_FINISH))
	{
            char ldbuf[MAX_STRING_LENGTH];
	    act( AT_ACTION, "$n has lost $s link.", ch, NULL, NULL, TO_CANSEE );

            sprintf( ldbuf, "%s has gone linkdead", ch->name);
            if( !IS_IMMORTAL( ch ) )
              do_info( ch, ldbuf );
            else
              do_ainfo(ch, ldbuf);

	    ch->desc = NULL;
	}
	else
	{
	    /* clear descriptor pointer to get rid of bug message in log */
	    dclose->character->desc = NULL;
	    free_char( dclose->character );
	}
    }


    if ( !DoNotUnlink )
    {
	/* make sure loop doesn't get messed up */
	if ( d_next == dclose )
	  d_next = d_next->next;
	UNLINK( dclose, first_descriptor, last_descriptor, next, prev );
    }

#ifdef MCCP
    compressEnd(dclose);
#endif


    if ( dclose->descriptor == maxdesc )
      --maxdesc;

    free_desc( dclose );
    --num_descriptors;
    return;
}


bool read_from_descriptor( DESCRIPTOR_DATA *d )
{
    int iStart, iErr;

    /* Hold horses if pending command already. */
    if ( d->incomm[0] != '\0' )
	return TRUE;

    /* Check for overflow. */
    iStart = strlen(d->inbuf);
    if ( iStart >= sizeof(d->inbuf) - 10 )
    {
	sprintf( log_buf, "%s input overflow!", d->host );
	log_string( log_buf );
	write_to_descriptor( d->descriptor,
	    "\n\r*** PUT A LID ON IT!!! ***\n\rYou cannot enter the same command more than 20 consecutive times!\n\r", 0 );
	return FALSE;
    }

    for ( ; ; )
    {
	int nRead;

	nRead = recv( d->descriptor, d->inbuf + iStart,
	    sizeof(d->inbuf) - 10 - iStart, 0 );
#ifdef WIN32
	iErr = WSAGetLastError ();
#else
	iErr = errno;
#endif
	if ( nRead > 0 )
	{
	    iStart += nRead;
	    if ( d->inbuf[iStart-1] == '\n' || d->inbuf[iStart-1] == '\r' )
		break;
	}
	else if ( nRead == 0 )
	{
	    log_string_plus( "EOF encountered on read.", LOG_COMM, sysdata.log_level );
	    return FALSE;
	}
	else if ( iErr == EWOULDBLOCK )
	    break;
	else
	{
	    perror( "Read_from_descriptor" );
	    return FALSE;
	}
    }

    d->inbuf[iStart] = '\0';
    return TRUE;
}



/*
 * Transfer one line from input buffer to input line.
 */
void read_from_buffer( DESCRIPTOR_DATA *d )
{
    int i, j, k;

#ifdef MCCP
    int iac = 0;
#endif

    /*
     * Hold horses if pending command already.
     */
    if ( d->incomm[0] != '\0' )
	return;

    /*
     * Look for at least one new line.
     */
    for ( i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r' && i<MAX_INBUF_SIZE;
	  i++ )
    {
	if ( d->inbuf[i] == '\0' )
	    return;
    }

    /*
     * Canonical input processing.
     */
    for ( i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ )
    {
		int z = 0;
		if( d->connected == CON_EDITING )
			z = 254;
		else
			z = 762;
//	if ( k >= 254 ) -- This was the old buffer size, new is above.
	if ( k >= z )
	{
	    write_to_descriptor( d->descriptor, "Line too long.\n\r", 0 );

	    /* skip the rest of the line */
	    /*
	    for ( ; d->inbuf[i] != '\0' || i>= MAX_INBUF_SIZE ; i++ )
	    {
		if ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' )
		    break;
	    }
	    */
	    d->inbuf[i]   = '\n';
	    d->inbuf[i+1] = '\0';
	    break; 	}

#ifdef MCCP
        if ( d->inbuf[i] == (signed char)IAC )
            iac=1;
        else if ( iac==1 && (d->inbuf[i] == (signed char)DO || d->inbuf[i] == (signed char)DONT) )
            iac=2;
        else if ( iac==2 )
        {
            iac = 0;
            if ( d->inbuf[i] == (signed char)TELOPT_COMPRESS )
            {
                if ( d->inbuf[i-1] == (signed char)DO )
                    compressStart(d, TELOPT_COMPRESS);
                else if ( d->inbuf[i-1] == (signed char)DONT )
                    compressEnd(d);
            }
            else if ( d->inbuf[i] == (signed char)TELOPT_COMPRESS2 )
            {
                if ( d->inbuf[i-1] == (signed char)DO )
                    compressStart(d, TELOPT_COMPRESS2);
                else if ( d->inbuf[i-1] == (signed char)DONT )
                    compressEnd(d);
            }
        }
        else
#endif

	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';

    /*
     * Deal with bozos with #repeat 1000 ...
     */
    if ( k > 1 || d->incomm[0] == '!' )
    {
	if ( d->incomm[0] != '!' && strcmp( d->incomm, d->inlast ) )
	{
	    d->repeat = 0;
	}
	else
	{
	    if ( ++d->repeat >= 20 )
	    {
/*		sprintf( log_buf, "%s input spamming!", d->host );
		log_string( log_buf );
*/
		write_to_descriptor( d->descriptor,
		    "\n\r*** PUT A LID ON IT!!! ***\n\rYou cannot enter the same command more than 20 consecutive times!\n\r", 0 );
		strcpy( d->incomm, "quit" );
	    }
	}
    }

    /*
     * Do '!' substitution.
     */
    if ( d->incomm[0] == '!' )
	strcpy( d->incomm, d->inlast );
    else
	strcpy( d->inlast, d->incomm );

    /*
     * Shift the input buffer.
     */
    while ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' )
	i++;
    for ( j = 0; ( d->inbuf[j] = d->inbuf[i+j] ) != '\0'; j++ )
	;
    return;
}



/*
 * Low level output function.
 */
bool flush_buffer( DESCRIPTOR_DATA *d, bool fPrompt )
{
    char buf[MAX_INPUT_LENGTH];
    char promptbuf[MAX_STRING_LENGTH];
    extern bool mud_down;

    /*
     * If buffer has more than 4K inside, spit out .5K at a time   -Thoric
     */
    if ( !mud_down && d->outtop > 4096 )
    {
	memcpy( buf, d->outbuf, 512 );
	d->outtop -= 512;
	memmove( d->outbuf, d->outbuf + 512, d->outtop );
	if ( d->snoop_by )
	{
	    char snoopbuf[MAX_INPUT_LENGTH];

	    buf[512] = '\0';
	    if ( d->character && d->character->name )
	    {
		if (d->original && d->original->name)
		    sprintf( snoopbuf, "%s (%s)", d->character->name, d->original->name );
		else
		    sprintf( snoopbuf, "%s", d->character->name);
		write_to_buffer( d->snoop_by, snoopbuf, 0);
	    }
	    write_to_buffer( d->snoop_by, "% ", 2 );
	    write_to_buffer( d->snoop_by, buf, 0 );
	}
        if ( !write_to_descriptor( d->descriptor, buf, 512 ) )
        {
	    d->outtop = 0;
	    return FALSE;
        }
        return TRUE;
    }


    /*
     * Bust a prompt.
     */
    if ( fPrompt && !mud_down && d->connected == CON_PLAYING )
    {
	CHAR_DATA *ch;

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

	if ( xIS_SET(ch->act, PLR_BLANK))
	    write_to_buffer( d, "\n\r", 2 );

	if (ch->fighting)
	{
		d->psuppress_cmdspam = FALSE;
		d->psuppress_channel = 0;
	}

	if (d->psuppress_channel >= 5)
		d->psuppress_channel = 0;

	if ( xIS_SET(ch->act, PLR_PROMPT) )
	{
		sysdata.outBytesFlag = LOGBOUTPROMPT;
	    if (!d->psuppress_cmdspam && !d->psuppress_channel)
	    {
			display_prompt(d);
			d->psuppress_cmdspam = FALSE;
	    }
	    else if (d->psuppress_channel && ch->pcdata)
	    {
			switch (ch->pcdata->normalPromptConfig)
			{
				default:
					if (ch->race == race_lookup("android"))
						sprintf(promptbuf, "&D<D:%d E:%s T:%s> ", ch->hit, num_punct(ch->mana), abbNumLD(ch->pl));
					else
						sprintf(promptbuf, "&D<L:%d K:%s P:%s> ", ch->hit, num_punct(ch->mana), abbNumLD(ch->pl));
					break;
				case 1:
					if (ch->race == race_lookup("android"))
						sprintf(promptbuf, "&D<D:%d E:%s T:%s> ", ch->hit, num_punct(ch->mana), abbNumLD(ch->pl));
					else
						sprintf(promptbuf, "&D<L:%d K:%s P:%s> ", ch->hit, num_punct(ch->mana), abbNumLD(ch->pl));
					break;
			}
			send_to_char( promptbuf, ch );
			if( get_true_rank( ch ) >= 11 )
			    pager_printf( ch, "(KRT:%d) ", sysdata.kaiRestoreTimer );
		}
	    else
	    	send_to_char( "&D> ", ch );
		sysdata.outBytesFlag = LOGBOUTNORM;
	}

	if (!IS_NPC(ch))

	if ( xIS_SET(ch->act, PLR_TELNET_GA) )
	    write_to_buffer( d, go_ahead_str, 0 );
    }

    /*
     * Short-circuit if nothing to write.
     */
    if ( d->outtop == 0 )
	return TRUE;

    /*
     * Snoop-o-rama.
     */
    if ( d->snoop_by )
    {
        /* without check, 'force mortal quit' while snooped caused crash, -h */
	if ( d->character && d->character->name )
	{
	    /* Show original snooped names. -- Altrag */
	    if ( d->original && d->original->name )
		sprintf( buf, "%s (%s)", d->character->name, d->original->name );
	    else
		sprintf( buf, "%s", d->character->name);
	    write_to_buffer( d->snoop_by, buf, 0);
	}
	write_to_buffer( d->snoop_by, "% ", 2 );
	write_to_buffer( d->snoop_by, d->outbuf, d->outtop );
    }

    /*
     * OS-dependent output.
     */
    if ( !write_to_descriptor( d->descriptor, d->outbuf, d->outtop ) )
    {
	d->outtop = 0;
	return FALSE;
    }
    else
    {
	d->outtop = 0;
	return TRUE;
    }
}



/*
 * Append onto an output buffer.
 */
void write_to_buffer( DESCRIPTOR_DATA *d, const char *txt, int length )
{
    if ( !d )
    {
	bug( "Write_to_buffer: NULL descriptor" );
	return;
    }

    /*
     * Normally a bug... but can happen if loadup is used.
     */
    if ( !d->outbuf )
    	return;

    /*
     * Find length in case caller didn't.
     */
    if ( length <= 0 )
	length = strlen(txt);

/* Uncomment if debugging or something
    if ( length != strlen(txt) )
    {
	bug( "Write_to_buffer: length(%d) != strlen(txt)!", length );
	length = strlen(txt);
    }
*/

    /*
     * Initial \n\r if needed.
     */
    if ( d->outtop == 0 && !d->fcommand )
    {
	d->outbuf[0]	= '\n';
	d->outbuf[1]	= '\r';
	d->outtop	= 2;
    }

    /*
     * Expand the buffer as needed.
     */
    while ( d->outtop + length >= d->outsize )
    {
        if (d->outsize > 32000)
	{
	    /* empty buffer */
	    d->outtop = 0;
	    bug("Buffer overflow. Closing (%s).", d->character ? d->character->name : "???" );
	    close_socket(d, TRUE);
	    return;
 	}
	d->outsize *= 2;
	RECREATE( d->outbuf, char, d->outsize );
    }

    /*
     * Copy.
     */
    strncpy( d->outbuf + d->outtop, txt, length );
    d->outtop += length;
    d->outbuf[d->outtop] = '\0';
    return;
}

#ifdef MCCP
#define COMPRESS_BUF_SIZE 1024

bool write_to_descriptor( int desc, char *txt, int length )
{
    DESCRIPTOR_DATA *d;
    int     iStart = 0;
    int     nWrite = 0;
    int     nBlock;
    int     len;

    if (length <= 0)
        length = strlen(txt);

    for (d = first_descriptor; d; d = d->next)
        if (d->descriptor == desc)
            break;

    if (d && d->descriptor != desc)
        d = NULL;


    if (d && d->out_compress)
    {
        d->out_compress->next_in = (unsigned char *)txt;
        d->out_compress->avail_in = length;

        while (d->out_compress->avail_in)
        {
            d->out_compress->avail_out = COMPRESS_BUF_SIZE - (d->out_compress->next_out - d->out_compress_buf);

            if (d->out_compress->avail_out)
            {
                int status = deflate(d->out_compress, Z_SYNC_FLUSH);

                if (status != Z_OK)
                    return FALSE;
            }

            len = d->out_compress->next_out - d->out_compress_buf;
            if (len > 0)
            {
                for (iStart = 0; iStart < len; iStart += nWrite)
                {
                    nBlock = UMIN (len - iStart, 4096);
                    if ((nWrite = write(d->descriptor, d->out_compress_buf + iStart, nBlock)) < 0)
                    {
                        perror( "Write_to_descriptor: compressed" );
                        return FALSE;
                    }

                    if (!nWrite)
                        break;
                }

                if (!iStart)
                    break;

                if (iStart < len)
                    memmove(d->out_compress_buf, d->out_compress_buf+iStart, len - iStart);

                d->out_compress->next_out = d->out_compress_buf + len - iStart;
            }
        }
        return TRUE;
    }

    for (iStart = 0; iStart < length; iStart += nWrite)
    {
        nBlock = UMIN (length - iStart, 4096);
        if ((nWrite = write(desc, txt + iStart, nBlock)) < 0)
        {
            perror( "Write_to_descriptor" );
            return FALSE;
        }
    }

    return TRUE;
}
#else

/*
 * Lowest level output function.
 * Write a block of text to the file descriptor.
 * If this gives errors on very long blocks (like 'ofind all'),
 *   try lowering the max block size.
 */
bool write_to_descriptor( int desc, char *txt, int length )
{
    int iStart;
    int nWrite;
    int nBlock;

    if ( length <= 0 )
	length = strlen(txt);

    for ( iStart = 0; iStart < length; iStart += nWrite )
    {
	nBlock = UMIN( length - iStart, 4096 );
	if ( ( nWrite = send( desc, txt + iStart, nBlock, 0 ) ) < 0 )
	    { perror( "Write_to_descriptor" ); return FALSE; }
    }

    return TRUE;
}

#endif

/* Added to try to fix hotboot issue */
bool write_to_descriptor_old( int desc, char *txt, int length )
{
    int iStart;
    int nWrite;
    int nBlock;

    if ( length <= 0 )
	length = strlen(txt);

    for ( iStart = 0; iStart < length; iStart += nWrite )
    {
	nBlock = UMIN( length - iStart, 4096 );
	if ( ( nWrite = send( desc, txt + iStart, nBlock, 0 ) ) < 0 )
	    { perror( "Write_to_descriptor" ); return FALSE; }
    }

    return TRUE;
}
/* End added */

void show_title( DESCRIPTOR_DATA *d )
{
    CHAR_DATA *ch;

    ch = d->character;

    if ( !IS_SET( ch->pcdata->flags, PCFLAG_NOINTRO ) )
    {
	if (xIS_SET(ch->act, PLR_RIP))
	  send_rip_title(ch);
	else
	if (xIS_SET(ch->act, PLR_ANSI))
	  send_ansi_title(ch);
	else
	  send_ascii_title(ch);
    }
    else
    {
      write_to_buffer( d, "Press enter...\n\r", 0 );
    }
    d->connected = CON_PRESS_ENTER;
}

/*
 * Deal with sockets that haven't logged in yet.
 */
void nanny( DESCRIPTOR_DATA *d, char *argument )
{
/*	extern int lang_array[];
	extern char *lang_names[];*/
    char buf[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    char buf3[MAX_STRING_LENGTH];
    char buf4[MAX_STRING_LENGTH];
    char arg[MAX_STRING_LENGTH];
    CHAR_DATA *ch;
    char *pwdnew;
    char *p;
	int b = 0;
    int iClass;
//    int iRace;
    bool fOld, chk;
    NOTE_DATA *catchup_notes;
    int i = 0;
// these were making compile warnings making it look ugly :)  -Goku
//    FILE *ipFp;
//    char ipBuf[MAX_STRING_LENGTH];

    if( d->connected != CON_NOTE_TEXT )
    {
        while ( isspace(*argument) )
            argument++;
    }

    ch = d->character;

    switch ( d->connected )
    {

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

    case CON_GET_NAME:
	if ( argument[0] == '\0' )
	{
	    close_socket( d, FALSE );
	    return;
	}

//	argument[0] = UPPER(argument[0]);
	*argument = capitalizeString(argument);

	/* Old players can keep their characters. -- Alty */
	if ( !check_parse_name( argument, (d->newstate != 0) ) )
	{
	    send_to_desc_color( "&wIllegal name, try another.\n\rName: &D", d );
	    return;
	}

        if ( !str_cmp( argument, "New" ) )
	{
	    if (d->newstate == 0) 	    {
              /* New player */
              /* Don't allow new players if DENY_NEW_PLAYERS is true */
      	      if (sysdata.DENY_NEW_PLAYERS == TRUE)
      	      {
       		sprintf( buf, "The mud is currently preparing for a reboot.\n\r" );
      		send_to_desc_color( buf, d );
			sprintf( buf, "New players are not accepted during this time.\n\r" );
      		send_to_desc_color( buf, d );
      		sprintf( buf, "Please try again in a minute.\n\r" );
      		send_to_desc_color( buf, d );
			close_socket( d, FALSE );
              }
              sprintf( buf, "\n\r&gChoosing a name is one of the most important parts of this game...\n\r"
              			"Make sure to pick a name appropriate to the character you are going\n\r"
               			"to role play, and be sure that it fits into the DragonBall Z world.\n\r"
               			"Please type '&WHELP&g' to read what restirictions we have for naming your\n\r"
               			"character.\n\r\n\r&wPlease choose a name for your character: &D");
              send_to_desc_color( buf, d );
	      d->newstate++;
	      d->connected = CON_GET_NAME;
	      return;
	    }
	    else
   	    {
	      send_to_desc_color("&wIllegal name, try another.\n\rName: &D", d);
	      return;
	    }
	}

    if ( !str_cmp( argument, "help" ) )
    {
		HELP_DATA *pHelp;

	    for ( pHelp = first_help; pHelp; pHelp = pHelp->next )
	    {
	    	if (!str_cmp( pHelp->keyword, "dbznames" ))
	    		break;
		}
		if (!pHelp)
		{
			send_to_desc_color( "No help on that word.\n\rName: ", d);
			return;
		}
		send_to_desc_color("\n\r", d);
		send_to_desc_color(pHelp->text, d);
		send_to_desc_color("\n\r\n\r&wName: ", d);
		return;
	}

	if ( check_playing( d, argument, FALSE ) == BERR )
	{
	    write_to_buffer( d, "Name: ", 0 );
	    return;
	}

	fOld = load_char_obj( d, argument, TRUE );
	if ( !d->character )
	{
	    sprintf( log_buf, "Bad player file %s@%s.", argument, d->host );
	    log_string( log_buf );
	    send_to_desc_color( "Your playerfile is corrupt...Please notify GokuDBS@hotmail.com.\n\r", d );
	    close_socket( d, FALSE );
	    return;
	}
	ch   = d->character;
      if ( check_bans( ch, BAN_SITE ) )
      {
              send_to_desc_color ("Your site has been banned from this Mud.\n\r", d);
              close_socket (d, FALSE);
              return;
      }

      if ( fOld ) {
      if ( check_bans( ch, BAN_CLASS ) )
      {
              send_to_desc_color ("Your class has been banned from this Mud.\n\r", d);
              close_socket (d, FALSE);
              return;
      }
      if ( check_bans( ch, BAN_RACE ) )
      {
              send_to_desc_color ("Your race has been banned from this Mud.\n\r", d);
              close_socket (d, FALSE);
              return;
      }
      }

	if ( xIS_SET(ch->act, PLR_DENY) )
	{
	    sprintf( log_buf, "Denying access to %s@%s.", argument, d->host );
	    log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
	    if (d->newstate != 0)
	    {
              send_to_desc_color( "That name is already taken.  Please choose another: ", d );
	      d->connected = CON_GET_NAME;
	      d->character->desc = NULL;
	      free_char( d->character ); /* Big Memory Leak before --Shaddai */
	      d->character = NULL;
	      return;
	    }
	    send_to_desc_color( "You are denied access.\n\r", d );
	    close_socket( d, FALSE );
	    return;
	}
      /*
       *  Make sure the immortal host is from the correct place.
       *  Shaddai
       */

    if ( check_total_ip( d ) )
    {
          send_to_desc_color (
                         "Your maximum amount of players you can have online has been reached.\n\r", d);
          close_socket( d, FALSE );
          return;
     }

      if ( IS_IMMORTAL(ch) && sysdata.check_imm_host
           && !check_immortal_domain( ch , d->host) )
        {
        sprintf (log_buf, "%s's char being hacked from %s.", argument, d->host);
        log_string_plus (log_buf, LOG_COMM, sysdata.log_level);
        sprintf (log_buf, "&R%s's char being hacked from %s.", argument, d->host);
        ch->level = 51;
        do_ainfo(ch, log_buf);
        send_to_desc_color ("This hacking attempt has been logged.\n\r", d);
        close_socket (d, FALSE);
        return;
        }


	chk = check_reconnect( d, argument, FALSE );
	if ( chk == BERR )
	  return;

	if ( chk )
	{
	    fOld = TRUE;
	}
	else
	{
	    if ( wizlock && !IS_IMMORTAL(ch) )
	    {
		send_to_desc_color( "The game is wizlocked.  Only immortals can connect now.\n\r", d );
		send_to_desc_color( "Please try back later.\n\r", d );
		close_socket( d, FALSE );
		return;
	    }
	}

	if ( fOld )
	{
	    if (d->newstate != 0)
	    {
	      send_to_desc_color( "&wThat name is already taken.  Please choose another: &D", d );
	      d->connected = CON_GET_NAME;
	      d->character->desc = NULL;
	      free_char( d->character ); /* Big Memory Leak before --Shaddai */
	      d->character = NULL;
	      return;
	    }
	    /* Old player */
	    send_to_desc_color( "&wPassword: &D", d );
	    write_to_buffer( d, echo_off_str, 0 );
	    d->connected = CON_GET_OLD_PASSWORD;
	    return;
	}
	else
	{
	    /*if ( !check_parse_name( argument ) )
	    {
		write_to_buffer( d, "Illegal name, try another.\n\rName: ", 0 );
		return;
	    }*/

	    if (d->newstate == 0)
	    {
	      /* No such player */
	      send_to_desc_color( "\n\r&wNo such player exists.\n\rPlease check your spelling, or type new to start a new player.\n\r\n\rName: &D", d );
	      d->connected = CON_GET_NAME;
	      d->character->desc = NULL;
	      free_char( d->character ); /* Big Memory Leak before --Shaddai */
	      d->character = NULL;
	      return;
	    }

            sprintf( buf, "&wDid I get that right, %s (&WY&w/&WN&w)? &D", argument );
            send_to_desc_color( buf, d );
            d->connected = CON_CONFIRM_NEW_NAME;
	    return;
	}
	break;

    case CON_GET_OLD_PASSWORD:
	send_to_desc_color( "\n\r", d );

	if ( strcmp( crypt( argument, ch->pcdata->pwd ), ch->pcdata->pwd ) )
	{
	    send_to_desc_color( "&wWrong password.&D\n\r", d );
	    sprintf( log_buf, "WRONG PASSWORD: %s@%s.", ch->name, d->host );
	    log_string( log_buf );
	    /* clear descriptor pointer to get rid of bug message in log */
	    d->character->desc = NULL;
	    close_socket( d, FALSE );
	    return;
	}

	write_to_buffer( d, echo_on_str, 0 );

	if ( check_playing( d, ch->pcdata->filename, TRUE ) )
	    return;

	chk = check_reconnect( d, ch->pcdata->filename, TRUE );
	if ( chk == BERR )
	{
	    if ( d->character && d->character->desc )
	      d->character->desc = NULL;
	    close_socket( d, FALSE );
	    return;
	}
	if ( chk == TRUE )
	  return;

	sprintf( buf, ch->pcdata->filename );
	d->character->desc = NULL;
	free_char( d->character );
	d->character = NULL;
	fOld = load_char_obj( d, buf, FALSE );
	ch = d->character;
        if ( ch->position ==  POS_FIGHTING
          || ch->position ==  POS_EVASIVE           
          || ch->position ==  POS_DEFENSIVE
          || ch->position ==  POS_AGGRESSIVE
          || ch->position ==  POS_BERSERK 
          || ch->position ==  POS_PORTAL )
		ch->position = POS_STANDING;

	sprintf( log_buf, "%s@%s(%s) has connected.", ch->pcdata->filename,
		d->host, d->user );
	if (ch->level == 2)
	{
		xSET_BIT(ch->deaf, CHANNEL_FOS);
		ch->level = 1;
	}
	sprintf( buf3, "%s has logged on", ch->name);
	if (!IS_IMMORTAL(ch))
		do_info(ch, buf3);
	else
		do_ainfo(ch, buf3);

        if ( !IS_IMMORTAL( ch ) && IS_AFFECTED(ch, AFF_DEAD) )
        {
          sprintf( buf4, "%s has a halo", ch->name );
	  log_string_plus( buf4, LOG_HIGH, LEVEL_LESSER );
        }
	/* player data update checks */

	pager_printf(ch, "Checking for player data updates...\n\r");
	if (upgrade_player(ch))
		pager_printf(ch, "Updated player data successfully.\n\r");
	else
		pager_printf(ch, "No updates to make.\n\r");

          if( xIS_SET(ch->act, PLR_2XPL_GAIN ) )
          {
            xREMOVE_BIT( ch->act, PLR_2XPL_GAIN );
            pager_printf( ch, "&RNo more double gains. Sowwy.&D\n\r" );
          }


	adjust_hiscore( "pkill", ch, ch->pcdata->pkills ); /* cronel hiscore */
	adjust_hiscore( "sparwins", ch, ch->pcdata->spar_wins );
	adjust_hiscore( "sparloss", ch, ch->pcdata->spar_loss );
	adjust_hiscore( "mkills", ch, ch->pcdata->mkills );
	adjust_hiscore( "deaths", ch, (ch->pcdata->pdeaths + ch->pcdata->mdeaths) );
	update_plHiscore(ch);
	adjust_hiscore( "played", ch, ((get_age(ch) - 4) * 2) );
	adjust_hiscore( "zeni", ch, ch->gold );
	adjust_hiscore("bounty", ch, ch->pcdata->bkills);
	update_member(ch);

	if ( ch->level < LEVEL_DEMI )
	{
	  /*to_channel( log_buf, CHANNEL_MONITOR, "Monitor", ch->level );*/
	  log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
	}
	else
	  log_string_plus( log_buf, LOG_COMM, ch->level );
	show_title(d);
	break;

    case CON_CONFIRM_NEW_NAME:
	switch ( *argument )
	{
	case 'y': case 'Y':
	    sprintf( buf, "\n\r&wMake sure to use a password that won't be easily guessed by someone else."
	    		  "\n\rPick a good password for %s: %s&D",
		ch->name, echo_off_str );
	    send_to_desc_color( buf, d );
       	xSET_BIT(ch->act, PLR_ANSI);
	    d->connected = CON_GET_NEW_PASSWORD;
	    break;

	case 'n': case 'N':
	    send_to_desc_color( "&wOk, what IS it, then? &D", d );
	    /* clear descriptor pointer to get rid of bug message in log */
	    d->character->desc = NULL;
	    free_char( d->character );
	    d->character = NULL;
	    d->connected = CON_GET_NAME;
	    break;

	default:
	    send_to_desc_color( "&wPlease type &WY&wes or &WN&wo. &D", d );
	    break;
	}
	break;

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

	if ( strlen(argument) < 5 )
	{
	    send_to_desc_color("&wPassword must be at least five characters long.\n\rPassword: &D", d );
	    return;
	}

	pwdnew = crypt( argument, ch->name );
	for ( p = pwdnew; *p != '\0'; p++ )
	{
	    if ( *p == '~' )
	    {
		send_to_desc_color(
		    "&wNew password not acceptable, try again.\n\rPassword: &D",
		    d );
		return;
	    }
	}

	DISPOSE( ch->pcdata->pwd );
	ch->pcdata->pwd	= str_dup( pwdnew );
	send_to_desc_color( "\n\r&wPlease retype the password to confirm: &D", d );
	d->connected = CON_CONFIRM_NEW_PASSWORD;
	break;

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

	if ( strcmp( crypt( argument, ch->pcdata->pwd ), ch->pcdata->pwd ) )
	{
	    send_to_desc_color( "&wPasswords don't match.\n\rRetype password: &D",
		d );
	    d->connected = CON_GET_NEW_PASSWORD;
	    return;
	}

	write_to_buffer( d, echo_on_str, 0 );
	send_to_desc_color( "\n\r&wWhat do you want your last name to be? [press enter for none] &D\n\r", d );
	d->connected = CON_GET_LAST_NAME;
	break;

	case CON_GET_LAST_NAME:
	if ( argument[0] == '\0' )
	{
	write_to_buffer( d, echo_on_str, 0 );
	send_to_desc_color( "\n\rDo you wish to be a &RHARDCORE&w character? (&WY&w/&WN&w)\n\rType &WHELP&w for more information.", d );
	d->connected = CON_GET_HC;
	return;
	}

//	argument[0] = UPPER(argument[0]);
	*argument = capitalizeString(argument);
	/* Old players can keep their characters. -- Alty */
	if ( !check_parse_name( argument, TRUE ) )
	{
	    send_to_desc_color( "&wIllegal name, try another.\n\rLast name: &D", d );
	    return;
	}

    sprintf( buf, "&wDid I get that right, %s (&WY&w/&WN&w)? &D", argument );
    send_to_desc_color( buf, d );
	DISPOSE(ch->pcdata->last_name);
	ch->pcdata->last_name = str_dup( "" );
	buf[0] = ' ';
	strcpy( buf+1, argument );
    ch->pcdata->last_name = strdup(buf);
    d->connected = CON_CONFIRM_LAST_NAME;
	return;
	break;

	case CON_CONFIRM_LAST_NAME:
	switch ( *argument )
	{
	case 'y': case 'Y':
	write_to_buffer( d, echo_on_str, 0 );
	send_to_desc_color( "\n\rDo you wish to be a &RHARDCORE&w character? (&WY&w/&WN&w)\n\rType &WHELP&w for more information.", d );
	d->connected = CON_GET_HC;
	    break;

	case 'n': case 'N':
	    send_to_desc_color( "&wOk, what IS it, then? &D", d );
	    d->connected = CON_GET_LAST_NAME;
	    break;

	default:
	    send_to_desc_color( "&wPlease type &WY&wes or &WN&wo. &D", d );
	    break;
	}
	break;


	case CON_GET_HC:
	if (!str_cmp(argument, "help"))
	{
		HELP_DATA *pHelp;

	    for ( pHelp = first_help; pHelp; pHelp = pHelp->next )
	    {
	    	if (!str_cmp( pHelp->keyword, "HC HARDCORE UNKNOWN" ))
	    		break;
		}
		if (!pHelp)
		{
			send_to_desc_color( "No help on that word.\n\rDo you wish to be a &RHARDCORE&w character? (&WY&w/&WN&w): ", d);
			return;
		}
		send_to_desc_color("\n\r", d);
		send_to_desc_color(pHelp->text, d);
		send_to_desc_color( "&wDo you wish to be a &RHARDCORE&w character? (&WY&w/&WN&w): ", d);
		return;
	}
	switch ( *argument )
	{
	case 'y': case 'Y':
	    sprintf( buf, "\n\r&wOkay, you are now &RHARDCORE&w!&D" );
	    send_to_desc_color( buf, d );
	    xSET_BIT(ch->act, PLR_HC);
	write_to_buffer( d, echo_on_str, 0 );
	break;

	case 'n': case 'N':
	    send_to_desc_color( "\n\r&wOkay, you are a normal character.&D", d );
	write_to_buffer( d, echo_on_str, 0 );
	    break;

	default:
	    send_to_desc_color( "&wPlease type Yes or No. &D", d );
	    return;
	}
	send_to_desc_color( "\n\r&wWhat is your sex (&CM&w/&PF&w/&WN&w)? &D", d );
	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;
	case 'n': case 'N': ch->sex = SEX_NEUTRAL; break;
	default:
	    send_to_desc_color( "&wThat's not a sex.\n\rWhat IS your sex? &D", d );
	    return;
	}

	send_to_desc_color( "\n\r&wThe following Races are Available to You:&D\n\r", d );
	send_to_desc_color( "&c==============================================================================&D",d);
	buf[0] = '\0';

	/*
	 * Take this out SHADDAI
	 */
	i=0;
	for ( iClass = 0; iClass < 11; iClass++ )
	{
		char letters[11] = "abcdefghijk";
	    if ( class_table[iClass]->who_name &&
	    	 class_table[iClass]->who_name[0] != '\0' )
	    {
	    sprintf( buf, "\n\r&w   (&W%d&w)  &c%-12s&w  ('&R%c&w' for help)&D", i,
	    	class_table[iClass]->who_name, letters[i]);
			send_to_desc_color(buf, d);
	    i++;
	    }
	}
	send_to_desc_color( "&c==============================================================================&D",d);
	sprintf( buf, "\n\r&wChoose the number of your race: &D" );
	send_to_desc_color( buf, d );
	d->connected = CON_GET_NEW_CLASS;
	break;

    case CON_GET_NEW_CLASS:
	argument = one_argument(argument, arg);
		if (is_number(arg))
		{
			i = atoi(arg);
			for ( iClass = 0; iClass < 11; iClass++ )
			{
				if ( class_table[iClass]->who_name &&
					class_table[iClass]->who_name[0] != '\0' )
					{
					if ( i == iClass )
					{
						ch->class =  iClass;
						ch->race  =  iClass;
						break;
					}
					}
			}
		}
		else
		{
			char letters[11] = "abcdefghijk";
			for (i=0;i<11;i++)
			{
				if (arg[0] == letters[i])
				{
				sprintf(buf, "%s", class_table[i]->who_name);
				do_help(ch, buf);
				return;
				}
			}
			i=0;
	send_to_desc_color( "\n\r&c==============================================================================&D",d);
			for ( iClass = 0; iClass < 11; iClass++ )
			{
				char letters[11] = "abcdefghijk";
			    if ( class_table[iClass]->who_name &&
			    	 class_table[iClass]->who_name[0] != '\0' )
			    {
			    sprintf( buf, "\n\r&w   (&W%d&w)  &c%-12s&w  ('&R%c&w' for help)&D", i,
			    	class_table[iClass]->who_name, letters[i]);
					send_to_desc_color(buf, d);
			    i++;
			    }
			}
	send_to_desc_color( "\n\r&c==============================================================================&D",d);
			sprintf( buf, "\n\r&wChoose the number of your race: &D" );
			send_to_desc_color( buf, d );
			return;
		}


	if ( iClass >= 11
	||  !class_table[iClass]->who_name
	|| class_table[iClass]->who_name[0] == '\0'
	|| !str_cmp(class_table[iClass]->who_name,"unused"))
	{
	  send_to_desc_color( "&wThat's not a race.\n\rWhat IS your race? &D",d);
	  return;
	}

        if ( check_bans( ch, BAN_CLASS ) )
        {
         send_to_desc_color (
             "&wThat race is not currently avaiable.\n\rWhat IS your race? &D",d);
         return;
        }

if (ch->race == 3 || ch->race == 5 || ch->race == 6 || ch->race == 9 || ch->race == 10)
{
ch->pcdata->haircolor = 24;
ch->pcdata->orignalhaircolor = 24;
}
else
{
send_to_desc_color( "\n\r&wPlease choose a hair color from the following list:&D\n\r", d );
		buf[0] = '\0';

		for ( iClass = 0; iClass < (MAX_HAIR - 2); iClass++ )
		{
			sprintf( buf2, "&w[&W%2d&w] &g%-18.18s  ", iClass, hair_color[iClass]);
			b++;
			strcat(buf,buf2);
			if ( (b % 3) == 0 )
				strcat( buf, "&D\n\r" );
		}
		strcat( buf, "\n\r: " );
		strcat( buf, "\r\r" );
		send_to_desc_color( buf, d );
	d->connected = CON_GET_NEW_HAIR;
	break;
}
	case CON_GET_NEW_HAIR:
		argument = one_argument(argument, arg);
if (ch->race != 3 && ch->race != 5 && ch->race != 6 && ch->race != 9 && ch->race != 10)
{
		if (!str_cmp( arg, "help") )
		{
			do_help(ch, argument);
			send_to_desc_color("&wPlease choose a hair color: &D", d);
			return;
		}
		for ( iClass = 0; iClass < (MAX_HAIR - 2); iClass++ )
		{
			if ( toupper(arg[0]) == toupper(hair_color[iClass][0])
				&&   !str_prefix( arg, hair_color[iClass] ) )
			{
				ch->pcdata->haircolor = iClass;
                                ch->pcdata->orignalhaircolor = iClass;
				break;
			}
			if (is_number(arg) && atoi(arg) == iClass)
			{
				ch->pcdata->haircolor = iClass;
                                ch->pcdata->orignalhaircolor = iClass;
				break;
			}
		}
		if ( iClass == (MAX_HAIR - 2) || !hair_color[iClass] || hair_color[iClass][0] == '\0' )
		{
			send_to_desc_color( "&wThat's not a hair color.\n\rWhat IS it going to be? &D", d );
			return;
		}
}
if (ch->race == 3)
{
		send_to_desc_color("\n\r&wPlease choose a main body color from the following list:&D\n\r", d );
	buf[0] = '\0';
	buf2[0] = '\0';
	b = 0;
	for ( iClass = (MAX_COMPLEXION - 17); iClass < (MAX_COMPLEXION - 14); iClass++ )
	{
		sprintf( buf2, "&w[&W%2d&W] &g%-15s&D", iClass, complexion[iClass]);
		b++;
		strcat(buf,buf2);
		  if ( (b % 4) == 0 )
			  strcat( buf, "\n\r" );
	}
	strcat( buf, "\n\r: " );
	strcat( buf, "\r\r\r\r" );
	send_to_desc_color( buf, d );
	d->connected = CON_GET_NEW_COMPLEXION;
	break;
}
else if (ch->race == 5 || ch->race == 10)
{
		send_to_desc_color( "\n\r&wPlease choose a main body color from the following list:&D\n\r", d );
	buf[0] = '\0';
	buf2[0] = '\0';
	b = 0;
	for ( iClass = (MAX_COMPLEXION - 15); iClass < (MAX_COMPLEXION); iClass++ )
	{
		sprintf( buf2, "&w[&W%2d&w] &g%-15s&D", iClass, complexion[iClass]);
		b++;
		strcat(buf,buf2);
		  if ( (b % 4) == 0 )
			  strcat( buf, "\n\r" );
	}
	strcat( buf, "\n\r: " );
	strcat( buf, "\r\r\r\r" );
	send_to_desc_color( buf, d );
	d->connected = CON_GET_NEW_COMPLEXION;
	break;
}
else if (ch->race == 6)
{
		send_to_desc_color("\n\r&wPlease choose a main body color from the following list:&D\n\r", d );
	buf[0] = '\0';
	buf2[0] = '\0';
	b = 0;
	for ( iClass = (MAX_COMPLEXION - 18); iClass < (MAX_COMPLEXION); iClass++ )
	{
		sprintf( buf2, "&w[&W%2d&W] &g%-15s&D", iClass, complexion[iClass]);
		b++;
		strcat(buf,buf2);
		  if ( (b % 4) == 0 )
			  strcat( buf, "\n\r" );
	}
	strcat( buf, "\n\r: " );
	strcat( buf, "\r\r\r\r" );
	send_to_desc_color( buf, d );
	d->connected = CON_GET_NEW_COMPLEXION;
	break;
}
else if (ch->race == 9)
{
                send_to_desc_color( "\n\r&wPlease choose a main body color from the following list:&D\n\r", d );
        buf[0] = '\0';
        buf2[0] = '\0';
        b = 0;
        for ( iClass = (MAX_COMPLEXION - 20); iClass < (MAX_COMPLEXION); iClass++ )
        {
                sprintf( buf2, "&w[&W%2d&w] &g%-15s&D", iClass, complexion[iClass]);
                b++;
                strcat(buf,buf2);
                  if ( (b % 4) == 0 )
                          strcat( buf, "\n\r" );
        }
        strcat( buf, "\n\r: " );
        strcat( buf, "\r\r\r\r" );
        send_to_desc_color( buf, d );
        d->connected = CON_GET_NEW_COMPLEXION;
        break;
}
else
{
		send_to_desc_color( "\n\r&wPlease choose a complexion from the following list:&D\n\r", d );
	buf[0] = '\0';
	buf2[0] = '\0';
	b = 0;
	for ( iClass = 0; iClass < (MAX_COMPLEXION - 17); iClass++ )
	{
		sprintf( buf2, "&w[&W%2d&w] &g%-15s&D", iClass, complexion[iClass]);
		b++;
		strcat(buf,buf2);
		  if ( (b % 4) == 0 )
			  strcat( buf, "\n\r" );
	}
	strcat( buf, "\n\r: " );
	strcat( buf, "\r\r\r\r" );
	send_to_desc_color( buf, d );
	d->connected = CON_GET_NEW_COMPLEXION;
	break;
}

	case CON_GET_NEW_COMPLEXION:

		argument = one_argument(argument, arg);
if (ch->race == 5 || ch->race == 10)
{
		if (!str_cmp( arg, "help") )
		{
			do_help(ch, argument);
			send_to_desc_color( "&wPlease choose a main body color: &D", d);
			return;
		}
	for ( iClass = (MAX_COMPLEXION - 15); iClass < (MAX_COMPLEXION); iClass++ )
		{
			if ( toupper(arg[0]) == toupper(complexion[iClass][0])
				&&   !str_prefix( arg, complexion[iClass] ) )
			{
				ch->pcdata->complexion = iClass;
				break;
			}
			if (is_number(arg) && atoi(arg) == iClass)
			{
				ch->pcdata->complexion = iClass;
				break;
			}
		}
		if ( iClass == (MAX_COMPLEXION) || !complexion[iClass] || complexion[iClass][0] == '\0' )
		{
			send_to_desc_color("&wThat's not a choice.\n\rWhat IS it going to be? &D", d );
			return;
		}
}
else if (ch->race == 3)
{
		if (!str_cmp( arg, "help") )
		{
			do_help(ch, argument);
			send_to_desc_color( "&wPlease choose a main body color: &D", d);
			return;
		}
	for ( iClass = (MAX_COMPLEXION - 17); iClass < (MAX_COMPLEXION - 14); iClass++ )
		{
			if ( toupper(arg[0]) == toupper(complexion[iClass][0])
				&&   !str_prefix( arg, complexion[iClass] ) )
			{
				ch->pcdata->complexion = iClass;
				break;
			}
			if (is_number(arg) && atoi(arg) == iClass)
			{
				ch->pcdata->complexion = iClass;
				break;
			}
		}
		if ( iClass == (MAX_COMPLEXION - 14) || !complexion[iClass] || complexion[iClass][0] == '\0' )
		{
			send_to_desc_color( "&wThat's not a choice.\n\rWhat IS it going to be? &D", d );
			return;
		}
}
else if (ch->race == 6)
{
		if (!str_cmp( arg, "help") )
		{
			do_help(ch, argument);
			send_to_desc_color( "&wPlease choose a main body color: &D", d);
			return;
		}
	for ( iClass = (MAX_COMPLEXION - 18); iClass < (MAX_COMPLEXION); iClass++ )
		{
			if ( toupper(arg[0]) == toupper(complexion[iClass][0])
				&&   !str_prefix( arg, complexion[iClass] ) )
			{
				ch->pcdata->complexion = iClass;
				break;
			}
			if (is_number(arg) && atoi(arg) == iClass)
			{
				ch->pcdata->complexion = iClass;
				break;
			}
		}
		if ( iClass == (MAX_COMPLEXION - 14) || !complexion[iClass] || complexion[iClass][0] == '\0' )
		{
			send_to_desc_color( "&wThat's not a choice.\n\rWhat IS it going to be? &D", d );
			return;
		}
}
else if (ch->race == 9)
{
                if (!str_cmp( arg, "help") )
                {
                        do_help(ch, argument);
                        send_to_desc_color( "&wPlease choose a main body color: &D", d);
                        return;
                }
        for ( iClass = (MAX_COMPLEXION - 20); iClass < (MAX_COMPLEXION); iClass++ )
                {
                        if ( toupper(arg[0]) == toupper(complexion[iClass][0])
                                &&   !str_prefix( arg, complexion[iClass] ) )
                        {
                                ch->pcdata->complexion = iClass;
                                break;
                        }
                        if (is_number(arg) && atoi(arg) == iClass)
                        {
                                ch->pcdata->complexion = iClass;
                                break;
                        }
                }
                if ( iClass >= (MAX_COMPLEXION) || iClass < (MAX_COMPLEXION - 20) || !complexion[iClass] || complexion[iClass][0] == '\0' )
                {
                        send_to_desc_color( "&wThat's not a choice.\n\rWhat IS it going to be? &D", d );
                        return;
                }
}
else
{
		if (!str_cmp( arg, "help") )
		{
			do_help(ch, argument);
			send_to_desc_color( "&wPlease choose complexion: &D", d);
			return;
		}
	for ( iClass = 0; iClass < (MAX_COMPLEXION - 17); iClass++ )
		{
			if ( toupper(arg[0]) == toupper(complexion[iClass][0])
				&&   !str_prefix( arg, complexion[iClass] ) )
			{
				ch->pcdata->complexion = iClass;
				break;
			}
			if (is_number(arg) && atoi(arg) == iClass)
			{
				ch->pcdata->complexion = iClass;
				break;
			}
		}
		if ( iClass == (MAX_COMPLEXION - 17) || !complexion[iClass] || complexion[iClass][0] == '\0' )
		{
			send_to_desc_color("&wThat's not a complexion.\n\rWhat IS it going to be? &D", d );
			return;
		}
}

/* ------------------------------------------------------------------------------------- */
if (ch->race == 5 || ch->race == 10)
{
		send_to_desc_color("\n\r&wPlease choose a secondary body color from the following list:&D\n\r", d );
	buf[0] = '\0';
	buf2[0] = '\0';
	b = 0;
	for ( iClass = 0; iClass < (MAX_SECONDARYCOLOR - 1); iClass++ )
	{
		sprintf( buf2, "&w[&W%2d&w] &g%-15s&D", iClass, secondary_color[iClass]);
		b++;
		strcat(buf,buf2);
		  if ( (b % 4) == 0 )
			  strcat( buf, "\n\r" );
	}
	strcat( buf, "\n\r: " );
	strcat( buf, "\r\r\r\r" );
	send_to_desc_color( buf, d );
	d->connected = CON_GET_NEW_SECONDARYCOLOR;
	break;

	case CON_GET_NEW_SECONDARYCOLOR:
	/* Black, Brown, Red, Blonde, Strawberry Blonde, Argent, Golden Blonde, Platinum Blonde, Light Brown*/
		argument = one_argument(argument, arg);
		if (!str_cmp( arg, "help") )
		{
			do_help(ch, argument);
			send_to_desc_color( "&wPlease choose a secondary body color: &D", d);
			return;
		}
		for ( iClass = 0; iClass < (MAX_SECONDARYCOLOR - 1); iClass++ )
		{
			if ( toupper(arg[0]) == toupper(secondary_color[iClass][0])
				&&   !str_prefix( arg, secondary_color[iClass] ) )
			{
				ch->pcdata->secondarycolor = iClass;
				break;
			}
			if (is_number(arg) && atoi(arg) == iClass)
			{
				ch->pcdata->secondarycolor = iClass;
				break;
			}
		}
		if ( iClass == (MAX_SECONDARYCOLOR - 1) || !secondary_color[iClass] || secondary_color[iClass][0] == '\0' )
		{
			send_to_desc_color( "&wThat's not a choice.\n\rWhat IS it going to be? &D", d );
			return;
		}
}
/* ------------------------------------------------------------------------------------- */

		send_to_desc_color( "\n\r&wPlease choose a eye color from the following list:&D\n\r", d );
	buf[0] = '\0';
	buf2[0] = '\0';
	b = 0;

	for ( iClass = 0; iClass < (MAX_EYE - 3); iClass++ )
	{
		sprintf( buf2, "&w[&W%2d&w] &g%-15s&D", iClass, eye_color[iClass]);
		b++;
		strcat(buf,buf2);
		  if ( (b % 4) == 0 )
			  strcat( buf, "\n\r" );
	}
	strcat( buf, "\n\r: " );
	strcat( buf, "\r\r\r\r" );
	send_to_desc_color(buf, d );
	d->connected = CON_GET_NEW_EYE;

	break;
	case CON_GET_NEW_EYE:
	/* Black, Brown, Red, Blonde, Strawberry Blonde, Argent, Golden Blonde, Platinum Blonde, Light Brown*/
		argument = one_argument(argument, arg);
		if (!str_cmp( arg, "help") )
		{
			do_help(ch, argument);
			send_to_desc_color( "&wPlease choose a hair color: &D", d);
			return;
		}
		for ( iClass = 0; iClass < (MAX_EYE - 3); iClass++ )
		{
			if ( toupper(arg[0]) == toupper(eye_color[iClass][0])
				&&   !str_prefix( arg, eye_color[iClass] ) )
			{
				ch->pcdata->eyes = iClass;
                                ch->pcdata->orignaleyes = iClass;
				break;
			}
			if (is_number(arg) && atoi(arg) == iClass)
			{
				ch->pcdata->eyes = iClass;
                                ch->pcdata->orignaleyes = iClass;
				break;
			}
		}
		if ( iClass == (MAX_EYE - 3) || !eye_color[iClass] || eye_color[iClass][0] == '\0' )
		{
			send_to_desc_color( "&wThat's not a eye color.\n\rWhat IS it going to be? &D", d );
			return;
		}
/* ------------------------------------------------------------------------------------- */
		send_to_desc_color( "\n\r&wPlease choose a build type from the following list:&D\n\r", d );
	buf[0] = '\0';
	buf2[0] = '\0';
	b = 0;

	for ( iClass = 0; iClass < (MAX_BUILD); iClass++ )
	{
		sprintf( buf2, "&w[&W%2d&w] &g%-15s&D", iClass, build_type[iClass]);
		b++;
		strcat(buf,buf2);
		  if ( (b % 4) == 0 )
			  strcat( buf, "\n\r" );
	}
	strcat( buf, "\n\r: " );
	strcat( buf, "\r\r\r\r" );
	send_to_desc_color( buf, d );
	d->connected = CON_GET_NEW_BUILD;

	break;
	case CON_GET_NEW_BUILD:
	/* Black, Brown, Red, Blonde, Strawberry Blonde, Argent, Golden Blonde, Platinum Blonde, Light Brown*/
		argument = one_argument(argument, arg);
		if (!str_cmp( arg, "help") )
		{
			do_help(ch, argument);
			send_to_desc_color( "&wPlease choose a build type: &D", d);
			return;
		}
		for ( iClass = 0; iClass < (MAX_BUILD); iClass++ )
		{
			if ( toupper(arg[0]) == toupper(build_type[iClass][0])
				&&   !str_prefix( arg, build_type[iClass] ) )
			{
				ch->pcdata->build = iClass;
				break;
			}
			if (is_number(arg) && atoi(arg) == iClass)
			{
				ch->pcdata->build = iClass;
				break;
			}
		}
		if ( iClass == (MAX_BUILD) || !build_type[iClass] || build_type[iClass][0] == '\0' )
		{
			send_to_desc_color( "&wThat's not a build type.\n\rWhat IS it going to be? &D", d );
			return;
		}



/*	send_to_desc_color( "\n\rWould you like RIP, ANSI or no graphic/color support, (R/A/N)? ", d );
	d->connected = CON_GET_WANT_RIPANSI;
        break;

    case CON_GET_WANT_RIPANSI:
	switch ( argument[0] )
	{
	case 'r': case 'R':
	    xSET_BIT(ch->act,PLR_RIP);
	    xSET_BIT(ch->act,PLR_ANSI);
	    break;
	case 'a': case 'A': xSET_BIT(ch->act,PLR_ANSI);  break;
	case 'n': case 'N': break;
	default:
	    write_to_buffer( d, "Invalid selection.\n\rRIP, ANSI or NONE? ", 0 );
	    return;
	}
*/
	sprintf( log_buf, "%s@%s new %s %s.", ch->name, d->host,
		race_table[ch->race]->race_name,
		class_table[ch->class]->who_name );
	log_string_plus( log_buf, LOG_COMM, sysdata.log_level);
	to_channel( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
	send_to_desc_color( "&wPress [&RENTER&w] &D", d );
	show_title(d);
	ch->level = 0;
	ch->position = POS_STANDING;
	d->connected = CON_PRESS_ENTER;
	set_pager_color( AT_PLAIN, ch );
	adjust_hiscore( "pkill", ch, ch->pcdata->pkills ); /* cronel hiscore */
	adjust_hiscore( "sparwins", ch, ch->pcdata->spar_wins );
	adjust_hiscore( "sparloss", ch, ch->pcdata->spar_loss );
	adjust_hiscore( "mkills", ch, ch->pcdata->mkills );
	adjust_hiscore( "deaths", ch, (ch->pcdata->pdeaths + ch->pcdata->mdeaths) );
	update_plHiscore(ch);
	adjust_hiscore( "played", ch, ((get_age(ch) - 4) * 2) );
	adjust_hiscore( "zeni", ch, ch->gold );
	return;
	break;

    case CON_PRESS_ENTER:
        if ( chk_watch(get_trust(ch), ch->name, d->host) ) /*  --Gorog */
           SET_BIT( ch->pcdata->flags, PCFLAG_WATCH );
        else
           REMOVE_BIT( ch->pcdata->flags, PCFLAG_WATCH );

	if ( ch->position == POS_MOUNTED )
		ch->position = POS_STANDING;

	set_pager_color( AT_PLAIN, ch );
	if ( xIS_SET(ch->act, PLR_RIP) )
	  send_rip_screen(ch);
	if ( xIS_SET(ch->act, PLR_ANSI) )
	  send_to_pager( "\033[2J", ch );
	else
	  send_to_pager( "\014", ch );
	if ( IS_IMMORTAL(ch) )
	  do_help( ch, "imotd" );
	if ( ch->level == 50)
	  do_help( ch, "amotd" );
	if ( ch->level < 50 && ch->level > 0 )
	  do_help( ch, "motd" );
	if ( ch->level == 0 )
	  do_help( ch, "nmotd" );
	send_to_pager( "\n\rPress [ENTER] ", ch );
        d->connected = CON_READ_MOTD;
        break;

    case CON_READ_MOTD:
	{
	  char motdbuf[MAX_STRING_LENGTH];

	  sprintf( motdbuf, "\n\rWelcome to %s...\n\r", sysdata.mud_name);
	  send_to_desc_color( motdbuf, d );
	}
	add_char( ch );
	d->connected	= CON_PLAYING;
      /* hopefully clear up some ansi changing issues	  -Nopey */
	      set_char_color( AT_DGREEN, ch );
	if( !xIS_SET(ch->act, PLR_ANSI) && d->ansi == TRUE )
	    d->ansi = FALSE;
	else if( xIS_SET(ch->act, PLR_ANSI) && d->ansi == FALSE)
	    d->ansi = TRUE;

	if ( ch->level == 0 )
	{
	    OBJ_DATA *obj;
	    int iLang;

		ch->pcdata->upgradeL = CURRENT_UPGRADE_LEVEL;

	    ch->pcdata->clan_name = STRALLOC( "" );
	    ch->pcdata->clan	  = NULL;
	    switch ( class_table[ch->class]->attr_prime )
	    {
	    case APPLY_STR: ch->perm_str = 10; break;
	    case APPLY_INT: ch->perm_int = 10; break;
	    case APPLY_DEX: ch->perm_dex = 10; break;
	    case APPLY_CON: ch->perm_con = 10; break;
	    case APPLY_LCK: ch->perm_lck = 10; break;
	    }

	    ch->perm_str	 += race_table[ch->race]->str_plus;
	    ch->perm_int	 += race_table[ch->race]->int_plus;
	    ch->perm_dex	 += race_table[ch->race]->dex_plus;
	    ch->perm_con	 += race_table[ch->race]->con_plus;
	    ch->affected_by	  = race_table[ch->race]->affected;
	    ch->perm_lck	 = number_range(0, 30);

		ch->pcdata->permTstr = ch->perm_str;
		ch->pcdata->permTspd = ch->perm_dex;
		ch->pcdata->permTint = ch->perm_int;
		ch->pcdata->permTcon = ch->perm_con;

        ch->armor		 += race_table[ch->race]->ac_plus;
        ch->alignment	 += race_table[ch->race]->alignment;
        ch->attacks              = race_table[ch->race]->attacks;
        ch->defenses             = race_table[ch->race]->defenses;
	    ch->saving_poison_death  	= race_table[ch->race]->saving_poison_death;
	    ch->saving_wand  		= race_table[ch->race]->saving_wand;
	    ch->saving_para_petri  	= race_table[ch->race]->saving_para_petri;
	    ch->saving_breath  		= race_table[ch->race]->saving_breath;
	    ch->saving_spell_staff	= race_table[ch->race]->saving_spell_staff;

	    ch->height = number_range(race_table[ch->race]->height *.9, race_table[ch->race]->height *1.1);
	    ch->weight = number_range(race_table[ch->race]->weight *.9, race_table[ch->race]->weight *1.1);

	    if ( (iLang = skill_lookup( "common" )) < 0 )
	    	bug( "Nanny: cannot find common language." );
	    else
	    	ch->pcdata->learned[iLang] = 100;

	    for ( iLang = 0; lang_array[iLang] != LANG_UNKNOWN; iLang++ )
	    	if ( lang_array[iLang] == race_table[ch->race]->language )
	    		break;
	    if ( lang_array[iLang] == LANG_UNKNOWN )
	    	bug( "Nanny: invalid racial language." );
	    else
	    {
	    	if ( (iLang = skill_lookup( lang_names[iLang] )) < 0 )
	    		bug( "Nanny: cannot find racial language." );
	    	else
	    		ch->pcdata->learned[iLang] = 100;
	    }

            /* ch->resist           += race_table[ch->race]->resist;    drats */
            /* ch->susceptible     += race_table[ch->race]->suscept;    drats */

	    name_stamp_stats( ch );

	    ch->level	= 1;
	    ch->exp	= 100;
		ch->pl	= 100;
		ch->heart_pl	= 100;
            ch->max_hit    += race_table[ch->race]->hit;
            ch->max_mana   += race_table[ch->race]->mana;
	    	ch->max_move    = 100;
	    ch->hit	= UMAX(1,ch->max_hit);
	    ch->mana	= UMAX(1,ch->max_mana);
	    ch->move	= UMAX(1,ch->max_move);
	    ch->train = 5;
	    ch->max_train = 1;
		ch->pcdata->xTrain = 0;
		ch->pcdata->total_xTrain = 0;
	    ch->practice = 0;
	    ch->max_prac = 0;
	    ch->max_energy = 1;
		ch->pcdata->admintalk = 0;
		ch->pcdata->age = 4;
		ch->pcdata->sparcount = 0;
		if (ch->race == 0 || ch->race == 2 || ch->race == 5 || ch->race == 6)
			ch->pcdata->tail = TRUE;
		else
			ch->pcdata->tail = FALSE;
	    if (ch->race == 4)
	    	ch->pcdata->natural_ac_max = 500;
	    if (ch->race == 6)
	    	ch->pcdata->absorb_pl_mod = 11;
	    if (ch->race == 9)
	    	ch->pcdata->genieform = 11;
	    if (ch->race == 10)
	    	ch->pcdata->dballcount = 0;
	    sprintf( buf, "the %s",
		title_table [ch->class] [ch->level]
		[ch->sex == SEX_FEMALE ? 1 : 0] );
	    set_title( ch, buf );
		ch->pcdata->creation_date = current_time;

            /* Added by Narn.  Start new characters with autoexit and autgold
               already turned on.  Very few people don't use those. */
	    xSET_BIT( ch->act, PLR_AUTOGOLD );
	    xSET_BIT( ch->act, PLR_AUTOEXIT );
		xSET_BIT( ch->act, PLR_AUTO_COMPASS );
	    xSET_BIT( ch->act, PLR_SPAR );
	     SET_BIT( ch->pcdata->flags, PCFLAG_DEADLY);
		xSET_BIT(ch->deaf, CHANNEL_FOS);


    	/* Don't display old notes as 'unread' except for the announcment board */
        for (i = 1; i < MAX_BOARD; i++)
        {
		    for (catchup_notes = ch->pcdata->board->note_first; catchup_notes && catchup_notes->next; catchup_notes = catchup_notes->next);

		    if (!catchup_notes)
    	    ;
		    else
		    {
	        ch->pcdata->last_note[i] = catchup_notes->date_stamp;
		    }
	    }

            /* Added by Brittany, Nov 24/96.  The object is the adventurer's guide
               to the realms of despair, part of Academy.are. */
            {
            OBJ_INDEX_DATA *obj_ind = get_obj_index( 10333 );
            if ( obj_ind != NULL )
            {
              obj = create_object( obj_ind, 0 );
              obj_to_char( obj, ch );
              equip_char( ch, obj, WEAR_HOLD );
            }
            }
	    if (!sysdata.WAIT_FOR_AUTH)
	    {
		if ( ch->race == RACE_SAIYAN )
		    char_to_room( ch, get_room_index( ROOM_START_SAIYAN ) );
                if ( ch->race == RACE_HUMAN )
                    char_to_room( ch, get_room_index( ROOM_START_HUMAN ) );
                if ( ch->race == RACE_HALFBREED )
                    char_to_room( ch, get_room_index( ROOM_START_HALFBREED ) );
                if ( ch->race == RACE_NAMEK )
                    char_to_room( ch, get_room_index( ROOM_START_NAMEK ) );
                if ( ch->race == RACE_ICER )
                    char_to_room( ch, get_room_index( ROOM_START_ICER ) );
                if ( ch->race == RACE_ANDROID )
                    char_to_room( ch, get_room_index( ROOM_START_ANDROID ) );
                if ( ch->race == RACE_BIO_ANDROID )
                    char_to_room( ch, get_room_index( ROOM_START_BIO ) );
                if ( ch->race == RACE_KAIO )
                    char_to_room( ch, get_room_index( ROOM_START_KAIO ) );
                if ( ch->race == RACE_DEMON )
                    char_to_room( ch, get_room_index( ROOM_START_DEMON ) );
                if ( ch->race == RACE_GENIE )
                    char_to_room( ch, get_room_index( ROOM_START_GENIE ) );
                if ( ch->race == RACE_DRAGON )
                    char_to_room( ch, get_room_index( ROOM_START_DRAGON ) );
	    }
	    else
	    {
	      char_to_room( ch, get_room_index( ROOM_AUTH_START ) );
	      ch->pcdata->auth_state = 1;
	      SET_BIT(ch->pcdata->flags, PCFLAG_UNAUTHED);
	    }
	    /* Display_prompt interprets blank as default */
	    ch->pcdata->prompt = STRALLOC("");

            /* Attempt to make "stealing" easier to catch */
            // Put in code to write Created IP to file
//            sprintf( ipBuf, "%s%s", PLAYFROM_DIR, capitalize( ch->name ) );
//            if( !( ipFp = fopen( ipBuf, "a" ) ) )
//            {
//              bug( "Write playfrom: Cannot open file.", 0 );
//              perror( ipBuf );
//            }
//            else
//            {
//              fprintf( ipFp, "CREATED: %s\n\r", ch->desc->host );
//              fclose( ipFp );
//            }
	}
	else
	if ( !IS_IMMORTAL(ch) && ch->pcdata->release_date > 0 &&
		ch->pcdata->release_date > current_time )
	{
	    if ( ch->in_room->vnum == 6
	    ||   ch->in_room->vnum == 8
	    ||   ch->in_room->vnum == 1206 )
		char_to_room( ch, ch->in_room );
	    else
	      char_to_room( ch, get_room_index(8) );
	}
        else
        if( !IS_IMMORTAL( ch ) && ch->in_room
         && ch->in_room->vnum == 2060 )
        {
          act( AT_GREEN, "A Strange Force rips you from the Hyperbolic Time Chamber.", ch, NULL, NULL, TO_CHAR );
          char_to_room( ch, get_room_index( 2059 ) );
        }
	else
	if ( ch->in_room && ( IS_IMMORTAL( ch )
             || !xIS_SET( ch->in_room->room_flags, ROOM_PROTOTYPE ) ) )
	{
	    char_to_room( ch, ch->in_room );
	}
	else
	if ( IS_IMMORTAL(ch) )
	{
	    char_to_room( ch, get_room_index( ROOM_VNUM_CHAT ) );
	}
	else
	{
	    char_to_room( ch, get_room_index( ROOM_VNUM_TEMPLE ) );
	}

        /* Attempt to make "stealing" easier to catch */
        // Put in code to write Connected IP to file
//        sprintf( ipBuf, "%s%s", PLAYFROM_DIR, capitalize( ch->name ) );
//        if( !( ipFp = fopen( ipBuf, "a" ) ) )
//        {
//          bug( "Write playfrom: Cannot open file.", 0 );
//          perror( ipBuf );
//        }
//        else
//        {
//          fprintf( ipFp, "%s\n\r", ch->desc->host );
//          fclose( ipFp );
//        }

/*
 *    if ( get_timer( ch, TIMER_SHOVEDRAG ) > 0 )
 *        remove_timer( ch, TIMER_SHOVEDRAG );
 *
 *    if ( get_timer( ch, TIMER_PKILLED ) > 0 )
 *	remove_timer( ch, TIMER_PKILLED );
 */

    act( AT_ACTION, "$n has entered the game.", ch, NULL, NULL, TO_CANSEE );
    if ( ch->pcdata->pet )
    {
           act( AT_ACTION, "$n returns to $s master from the Void.",
                      ch->pcdata->pet, NULL, ch, TO_NOTVICT );
           act( AT_ACTION, "$N returns with you to the realms.",
                        ch, NULL, ch->pcdata->pet, TO_CHAR );
    }

    /* For the logon pl tracker */
    ch->logon_start = ch->exp;

    do_global_boards( ch, "" );
    do_look( ch, "auto" );
    tax_player(ch);  /* Here we go, let's tax players to lower the gold
			pool -- TRI */
	mccp_interest(ch);
    mail_count(ch);
    check_loginmsg(ch);

    if ( !ch->was_in_room && ch->in_room == get_room_index( ROOM_VNUM_TEMPLE ))
      	ch->was_in_room = get_room_index( ROOM_VNUM_TEMPLE );
    else if ( ch->was_in_room == get_room_index( ROOM_VNUM_TEMPLE ))
        ch->was_in_room = get_room_index( ROOM_VNUM_TEMPLE );
    else if ( !ch->was_in_room )
    	ch->was_in_room = ch->in_room;

    break;

    case CON_NOTE_TO:
        handle_con_note_to (d, argument);
        break;

    case CON_NOTE_SUBJECT:
        handle_con_note_subject (d, argument);
        break; /* subject */

    case CON_NOTE_EXPIRE:
        handle_con_note_expire (d, argument);
        break;

    case CON_NOTE_TEXT:
        handle_con_note_text (d, argument);
        break;
    case CON_NOTE_FINISH:
        handle_con_note_finish (d, argument);
        break;

    }

    return;
}

bool is_reserved_name( char *name )
{
  RESERVE_DATA *res;

  for (res = first_reserved; res; res = res->next)
    if ((*res->name == '*' && !str_infix(res->name+1, name)) ||
        !str_cmp(res->name, name))
      return TRUE;
  return FALSE;
}


/*
 * Parse a name for acceptability.
 */ bool check_parse_name( char *name, bool newchar )
{
 /*
  * Names checking should really only be done on new characters, otherwise
  * we could end up with people who can't access their characters.  Would
  * have also provided for that new area havoc mentioned below, while still
  * disallowing current area mobnames.  I personally think that if we can
  * have more than one mob with the same keyword, then may as well have
  * players too though, so I don't mind that removal.  -- Alty
  */

     if ( is_reserved_name(name) && newchar )
	return FALSE;

     /*
      * Outdated stuff -- Alty
      */
/*     if ( is_name( name, "all auto immortal self someone god supreme demigod dog guard cityguard cat cornholio spock hicaine hithoric death ass fuck shit piss crap quit" ) )
       return FALSE;*/

    /*
     * Length restrictions.
     */
    if ( strlen(name) <  3 )
	return FALSE;

    if ( strlen(name) > 12 )
	return FALSE;

    /*
     * Alphanumerics only.
     * Lock out IllIll twits.
     */
    {
	char *pc;
	bool fIll;

	fIll = TRUE;
	for ( pc = name; *pc != '\0'; pc++ )
	{
	    if ( !isalpha(*pc) )
		return FALSE;
	    if ( LOWER(*pc) != 'i' && LOWER(*pc) != 'l' )
		fIll = FALSE;
	}

	if ( fIll )
	    return FALSE;
    }

    /*
     * Code that followed here used to prevent players from naming
     * themselves after mobs... this caused much havoc when new areas
     * would go in...
     */

    return TRUE;
}



/*
 * Look for link-dead player to reconnect.
 */
bool check_reconnect( DESCRIPTOR_DATA *d, char *name, bool fConn )
{
    CHAR_DATA *ch;

    for ( ch = first_char; ch; ch = ch->next )
    {
	if ( !IS_NPC(ch)
	&& ( !fConn || !ch->desc )
	&&    ch->pcdata->filename
	&&   !str_cmp( name, ch->pcdata->filename ) )
	{
	    if ( fConn && ch->switched )
	    {
	      write_to_buffer( d, "Already playing.\n\rName: ", 0 );
	      d->connected = CON_GET_NAME;
	      if ( d->character )
	      {
		 /* clear descriptor pointer to get rid of bug message in log */
		 d->character->desc = NULL;
		 free_char( d->character );
		 d->character = NULL;
	      }
	      return BERR;
	    }
	    if ( fConn == FALSE )
	    {
		DISPOSE( d->character->pcdata->pwd );
		d->character->pcdata->pwd = str_dup( ch->pcdata->pwd );
	    }
	    else
	    {
		/* clear descriptor pointer to get rid of bug message in log */
		d->character->desc = NULL;
		free_char( d->character );
		d->character = ch;
		ch->desc	 = d;
		ch->timer	 = 0;
		send_to_char( "Reconnecting.\n\r", ch );
		do_look( ch, "auto" );
		act( AT_ACTION, "$n has reconnected.", ch, NULL, NULL, TO_CANSEE );
		sprintf( log_buf, "%s@%s(%s) reconnected.",
				ch->pcdata->filename, d->host, d->user );
		log_string_plus( log_buf, LOG_COMM, UMAX( sysdata.log_level, ch->level ) );
/*
		if ( ch->level < LEVEL_SAVIOR )
		  to_channel( log_buf, CHANNEL_MONITOR, "Monitor", ch->level );
*/
		d->connected = CON_PLAYING;
                /* Inform the character of a note in progress and the possbility of continuation! */
                if (ch->pcdata->in_progress)
                    send_to_char ("You have a note in progress. Type \"note write\" to continue it.\n\r",ch);

            }
	    return TRUE;
	}
    }

    return FALSE;
}



/*
 * Check if already playing.
 */
bool check_playing( DESCRIPTOR_DATA *d, char *name, bool kick )
{
    CHAR_DATA *ch;

    DESCRIPTOR_DATA *dold;
    int	cstate;

    for ( dold = first_descriptor; dold; dold = dold->next )
    {
	if ( dold != d
	&& (  dold->character || dold->original )
	&&   !str_cmp( name, dold->original
		 ? dold->original->pcdata->filename :
		 dold->character->pcdata->filename ) )
	{
	    cstate = dold->connected;
	    ch = dold->original ? dold->original : dold->character;
	    if ( !ch->name
	    || ( cstate != CON_PLAYING && cstate != CON_EDITING ))
	    {
		write_to_buffer( d, "Already connected - try again.\n\r", 0 );
		sprintf( log_buf, "%s already connected.",
				ch->pcdata->filename );
		log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
		return BERR;
	    }
	    if ( !kick )
	      return TRUE;
	    write_to_buffer( d, "Already playing... Kicking off old connection.\n\r", 0 );
	    write_to_buffer( dold, "Kicking off old connection... bye!\n\r", 0 );
	    close_socket( dold, FALSE );
	    /* clear descriptor pointer to get rid of bug message in log */
	    d->character->desc = NULL;
	    free_char( d->character );
	    d->character = ch;
	    ch->desc	 = d;
	    ch->timer	 = 0;
	    if ( ch->switched )
	      do_return( ch->switched, "" );
	    ch->switched = NULL;
	    send_to_char( "Reconnecting.\n\r", ch );
	    do_look( ch, "auto" );
	    act( AT_ACTION, "$n has reconnected, kicking off old link.",
	         ch, NULL, NULL, TO_CANSEE );
	    sprintf( log_buf, "%s@%s reconnected, kicking off old link.",
	             ch->pcdata->filename, d->host );
	    log_string_plus( log_buf, LOG_COMM, UMAX( sysdata.log_level, ch->level ) );
/*
	    if ( ch->level < LEVEL_SAVIOR )
	      to_channel( log_buf, CHANNEL_MONITOR, "Monitor", ch->level );
*/
	    d->connected = cstate;
	    return TRUE;
	}
    }

    return FALSE;
}



void stop_idling( CHAR_DATA *ch )
{
    ROOM_INDEX_DATA *was_in_room;

    /*
    if ( !ch
    ||   !ch->desc
    ||    ch->desc->connected != CON_PLAYING
    ||   !ch->was_in_room
    ||    ch->in_room != get_room_index( ROOM_VNUM_LIMBO ) )
	return;
	*/

    if ( !ch
    ||   !ch->desc
    ||   ch->desc->connected != CON_PLAYING
    ||   !IS_IDLE(ch) )
    	return;

    /*
    if ( IS_IMMORTAL(ch) )
    	return;
    */

    ch->timer = 0;
    was_in_room = ch->was_in_room;
    char_from_room( ch );
    char_to_room( ch, was_in_room );
    ch->was_in_room = ch->in_room;
    /*
    ch->was_in_room	= NULL;
    */
    REMOVE_BIT(ch->pcdata->flags, PCFLAG_IDLE );
    act( AT_ACTION, "$n has returned from the void.", ch, NULL, NULL, TO_ROOM );
    return;
}



/*
 * Write to one char. (update for color -Goku)
 */
void send_to_char( const char *txt, CHAR_DATA *ch )
{
    if ( !ch )
    {
      bug( "Send_to_char: NULL *ch" );
      return;
    }
   send_to_char_color( txt, ch );
   return;
}

/*
 * Same as above, but converts &color codes to ANSI sequences..
 */
void send_to_char_color( const char *txt, CHAR_DATA *ch )
{
  DESCRIPTOR_DATA *d;
  char *colstr;
  const char *prevstr = txt;
  char colbuf[20];
  int ln;

  if ( !ch )
  {
    bug( "Send_to_char_color: NULL *ch" );
    return;
  }
  if ( !txt || !ch->desc )
    return;
  d = ch->desc;

	switch (sysdata.outBytesFlag)
    {
		default:
			sysdata.outBytesOther += strlen(txt);
			break;

		case LOGBOUTCHANNEL:
			sysdata.outBytesChannel += strlen(txt);
			break;

		case LOGBOUTCOMBAT:
			sysdata.outBytesCombat += strlen(txt);
			break;

		case LOGBOUTMOVEMENT:
			sysdata.outBytesMovement += strlen(txt);
			break;

		case LOGBOUTINFORMATION:
			sysdata.outBytesInformation += strlen(txt);
			break;

		case LOGBOUTPROMPT:
			sysdata.outBytesPrompt += strlen(txt);
			break;

		case LOGBOUTINFOCHANNEL:
			sysdata.outBytesInfoChannel += strlen(txt);
			break;
	}
  /* Clear out old color stuff */
/*  make_color_sequence(NULL, NULL, NULL);*/
  while ( (colstr = strpbrk(prevstr, "&^}")) != NULL )
  {
    if (colstr > prevstr)
      write_to_buffer(d, prevstr, (colstr-prevstr));
    ln = make_color_sequence(colstr, colbuf, d);
    if ( ln < 0 )
    {
      prevstr = colstr+1;
      break;
    }
    else if ( ln > 0 )
      write_to_buffer(d, colbuf, ln);
    prevstr = colstr+2;
  }
  if ( *prevstr )
    write_to_buffer(d, prevstr, 0);
  return;
}

void write_to_pager( DESCRIPTOR_DATA *d, const char *txt, int length )
{
  int pageroffset;	/* Pager fix by thoric */

  if ( length <= 0 )
    length = strlen(txt);
  if ( length == 0 )
    return;
  if ( !d->pagebuf )
  {
    d->pagesize = MAX_STRING_LENGTH;
    CREATE( d->pagebuf, char, d->pagesize );
  }
  if ( !d->pagepoint )
  {
    d->pagepoint = d->pagebuf;
    d->pagetop = 0;
    d->pagecmd = '\0';
  }
  if ( d->pagetop == 0 && !d->fcommand )
  {
    d->pagebuf[0] = '\n';
    d->pagebuf[1] = '\r';
    d->pagetop = 2;
  }
  pageroffset = d->pagepoint - d->pagebuf;	/* pager fix (goofup fixed 08/21/97) */
  while ( d->pagetop + length >= d->pagesize )
  {
    if ( d->pagesize > 32000 )
    {
      bug( "Pager overflow.  Ignoring.\n\r" );
      d->pagetop = 0;
      d->pagepoint = NULL;
      DISPOSE(d->pagebuf);
      d->pagesize = MAX_STRING_LENGTH;
      return;
    }
    d->pagesize *= 2;
    RECREATE(d->pagebuf, char, d->pagesize);
  }
  d->pagepoint = d->pagebuf + pageroffset;	/* pager fix (goofup fixed 08/21/97) */

  strncpy(d->pagebuf+d->pagetop, txt, length);
  d->pagetop += length;
  d->pagebuf[d->pagetop] = '\0';
  return;
}

void send_to_pager( const char *txt, CHAR_DATA *ch )
{
  if ( !ch )
  {
    bug( "Send_to_pager: NULL *ch" );
    return;
  }
   send_to_pager_color( txt, ch );
   return;
}

void send_to_pager_color( const char *txt, CHAR_DATA *ch )
{
  DESCRIPTOR_DATA *d;
  char *colstr;
  const char *prevstr = txt;
  char colbuf[20];
  int ln;

  if ( !ch )
  {
    bug( "Send_to_pager_color: NULL *ch" );
    return;
  }
  if ( !txt || !ch->desc )
    return;
  d = ch->desc;
  ch = d->original ? d->original : d->character;
  if ( IS_NPC(ch) || !IS_SET(ch->pcdata->flags, PCFLAG_PAGERON) )
  {
    send_to_char_color(txt, d->character);
    return;
  }
	switch (sysdata.outBytesFlag)
    {
		default:
			sysdata.outBytesOther += strlen(txt);
			break;

		case LOGBOUTCHANNEL:
			sysdata.outBytesChannel += strlen(txt);
			break;

		case LOGBOUTCOMBAT:
			sysdata.outBytesCombat += strlen(txt);
			break;

		case LOGBOUTMOVEMENT:
			sysdata.outBytesMovement += strlen(txt);
			break;

		case LOGBOUTINFORMATION:
			sysdata.outBytesInformation += strlen(txt);
			break;

		case LOGBOUTPROMPT:
			sysdata.outBytesPrompt += strlen(txt);
			break;

		case LOGBOUTINFOCHANNEL:
			sysdata.outBytesInfoChannel += strlen(txt);
			break;
	}

  /* Clear out old color stuff */
/*  make_color_sequence(NULL, NULL, NULL);*/
  while ( (colstr = strpbrk(prevstr, "&^}")) != NULL )
  {
    if ( colstr > prevstr )
      write_to_pager(d, prevstr, (colstr-prevstr));
    ln = make_color_sequence(colstr, colbuf, d);
    if ( ln < 0 )
    {
      prevstr = colstr+1;
      break;
    }
    else if ( ln > 0 )
      write_to_pager(d, colbuf, ln);
    prevstr = colstr+2;
  }
  if ( *prevstr )
    write_to_pager(d, prevstr, 0);
  return;
}

sh_int figure_color( sh_int AType, CHAR_DATA *ch )
{
  int at = AType;
  if (at >= AT_COLORBASE && at < AT_TOPCOLOR)
  {
    at -= AT_COLORBASE;
    if (IS_NPC(ch) || ch->pcdata->colorize[at] == -1)
      at = at_color_table[at].def_color;
    else
      at = ch->pcdata->colorize[at];
  }
  if (at < 0 || at > AT_WHITE+AT_BLINK)
  {
    bug("Figure_color: color %d invalid.", at);
    at = AT_GREY;
  }
  return at;
}

void set_char_color( sh_int AType, CHAR_DATA *ch )
{
    char buf[16];
    CHAR_DATA *och;

    if ( !ch || !ch->desc )
      return;

    och = (ch->desc->original ? ch->desc->original : ch);
    if ( !IS_NPC(och) && xIS_SET(och->act, PLR_ANSI) )
    {
	AType = figure_color(AType, och);
	if ( AType == 7 )
	  strcpy( buf, "\033[0;37m" );
	else
	  sprintf(buf, "\033[0;%d;%s%dm", (AType & 8) == 8,
	        (AType > 15 ? "5;" : ""), (AType & 7)+30);
	write_to_buffer( ch->desc, buf, strlen(buf) );
    }
    return;
}

void set_pager_color( sh_int AType, CHAR_DATA *ch )
{
    char buf[16];
    CHAR_DATA *och;

    if ( !ch || !ch->desc )
      return;

    och = (ch->desc->original ? ch->desc->original : ch);
    if ( !IS_NPC(och) && xIS_SET(och->act, PLR_ANSI) )
    {
	AType = figure_color(AType, och);
	if ( AType == 7 )
	  strcpy( buf, "\033[0;37m" );
	else
	  sprintf(buf, "\033[0;%d;%s%dm", (AType & 8) == 8,
	        (AType > 15 ? "5;" : ""), (AType & 7)+30);
	send_to_pager( buf, ch );
	ch->desc->pagecolor = AType;
    }
    return;
}


/* source: EOD, by John Booth <???> */
void ch_printf(CHAR_DATA *ch, char *fmt, ...)
{
    char buf[MAX_STRING_LENGTH*2];	/* better safe than sorry */
    va_list args;

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

    send_to_char(buf, ch);
}

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

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

    send_to_pager(buf, ch);
}


/*
 * Function to strip off the "a" or "an" or "the" or "some" from an object's
 * short description for the purpose of using it in a sentence sent to
 * the owner of the object.  (Ie: an object with the short description
 * "a long dark blade" would return "long dark blade" for use in a sentence
 * like "Your long dark blade".  The object name isn't always appropriate
 * since it contains keywords that may not look proper.		-Thoric
 */
char *myobj( OBJ_DATA *obj )
{
    if ( !str_prefix("a ", obj->short_descr) )
	return obj->short_descr + 2;
    if ( !str_prefix("an ", obj->short_descr) )
	return obj->short_descr + 3;
    if ( !str_prefix("the ", obj->short_descr) )
	return obj->short_descr + 4;
    if ( !str_prefix("some ", obj->short_descr) )
	return obj->short_descr + 5;
    return obj->short_descr;
}


char *obj_short( OBJ_DATA *obj )
{
    static char buf[MAX_STRING_LENGTH];

    if ( obj->count > 1 )
    {
	sprintf( buf, "%s (%d)", obj->short_descr, obj->count );
	return buf;
    }
    return obj->short_descr;
}

/*
 * The primary output interface for formatted output.
 */
/* Major overhaul. -- Alty */

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

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

     send_to_char_color(buf, ch);
}

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

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

     send_to_pager_color(buf, ch);
}

#define MORPHNAME(ch)   ((ch->morph&&ch->morph->morph)? \
                         ch->morph->morph->short_desc: \
                         IS_NPC(ch) ? ch->short_descr : ch->name)
#define NAME(ch)        (IS_NPC(ch) ? ch->short_descr : ch->name)

char *act_string(const char *format, CHAR_DATA *to, CHAR_DATA *ch,
		 const void *arg1, const void *arg2, int flags, int lower)
{
  static char * const he_she  [] = { "it",  "he",  "she" };
  static char * const him_her [] = { "it",  "him", "her" };
  static char * const his_her [] = { "its", "his", "her" };
  static char buf[MAX_STRING_LENGTH];
  char fname[MAX_INPUT_LENGTH];
  char temp[MAX_STRING_LENGTH];
  char *point = buf;
  const char *str = format;
  const char *i;
  CHAR_DATA *vch = (CHAR_DATA *) arg2;
  OBJ_DATA *obj1 = (OBJ_DATA  *) arg1;
  OBJ_DATA *obj2 = (OBJ_DATA  *) arg2;

  if ( str[0] == '$' )
  	DONT_UPPER = FALSE;
  	
  if (lower > 0)
    DONT_UPPER = TRUE;
  	
  while ( *str != '\0' )
  {
    if ( *str != '$' )
    {
      *point++ = *str++;
      continue;
    }
    ++str;
    if ( !arg2 && *str >= 'A' && *str <= 'Z' )
    {
      bug( "Act: missing arg2 for code %c:", *str );
      bug( format );
      i = " <@@@> ";
    }
    else
    {
      switch ( *str )
      {
      default:  bug( "Act: bad code %c.", *str );
		i = " <@@@> ";						break;
#ifdef I3
	case '$': 
	    i = "$";
	    break;
#endif
      case 't': i = (arg1 ? (char *) arg1 : " ");			break;
      case 'T': i = (arg2 ? (char *) arg2 : " ");			break;
      case 'n':
              if ( ch->morph == NULL )
                  i = (to ? PERS ( ch, to ): NAME ( ch ) );
              else if ( !IS_SET( flags, STRING_IMM ) )
                  i = (to ? MORPHPERS (ch, to) : MORPHNAME (ch));
              else
              {
                sprintf(temp, "(MORPH) %s", (to ? PERS(ch,to):NAME(ch))); 
                i = temp;
              }
            break;
      case 'N':
              if ( vch->morph == NULL )
                   i = (to ? PERS ( vch, to ) : NAME( vch ) );
              else if ( !IS_SET( flags, STRING_IMM ) )
                   i = (to ? MORPHPERS (vch, to) : MORPHNAME (vch));
              else
              {
                sprintf(temp, "(MORPH) %s", (to ? PERS(vch,to):NAME(vch)));
                i = temp;
              }
              break;

      case 'e': if (ch->sex > 2 || ch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", ch->name,
		      ch->sex);
		  i = "it";
		}
		else
		  i = he_she [URANGE(0,  ch->sex, 2)];
		break;
      case 'E': if (vch->sex > 2 || vch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", vch->name,
		      vch->sex);
		  i = "it";
		}
		else
		  i = he_she [URANGE(0, vch->sex, 2)];
		break;
      case 'm': if (ch->sex > 2 || ch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", ch->name,
		      ch->sex);
		  i = "it";
		}
		else
		  i = him_her[URANGE(0,  ch->sex, 2)];
		break;
      case 'M': if (vch->sex > 2 || vch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", vch->name,
		      vch->sex);
		  i = "it";
		}
		else
		  i = him_her[URANGE(0, vch->sex, 2)];
		break;
      case 's': if (ch->sex > 2 || ch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", ch->name,
		      ch->sex);
		  i = "its";
		}
		else
		  i = his_her[URANGE(0,  ch->sex, 2)];
		break;
      case 'S': if (vch->sex > 2 || vch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", vch->name,
		      vch->sex);
		  i = "its";
		}
		else
		  i = his_her[URANGE(0, vch->sex, 2)];
		break;
      case 'q': i = (to == ch) ? "" : "s";				break;
      case 'Q': i = (to == ch) ? "your" :
		    his_her[URANGE(0,  ch->sex, 2)];			break;
      case 'p': i = (!to || can_see_obj(to, obj1)
		  ? obj_short(obj1) : "something");			break;
      case 'P': i = (!to || can_see_obj(to, obj2)
		  ? obj_short(obj2) : "something");			break;
      case 'd':
        if ( !arg2 || ((char *) arg2)[0] == '\0' )
          i = "door";
        else
        {
          one_argument((char *) arg2, fname);
          i = fname;
        }
        break;
      }
    }
    ++str;
    while ( (*point = *i) != '\0' )
      ++point, ++i;
  
   /*  #0  0x80c6c62 in act_string (
    format=0x81db42e "$n has reconnected, kicking off old link.", to=0x0, 
    ch=0x94fcc20, arg1=0x0, arg2=0x0, flags=2) at comm.c:2901 */
  }
  strcpy(point, "\n\r");
  if ( !DONT_UPPER )
     buf[0] = UPPER(buf[0]);
  return buf;
}
#undef NAME
  
void act( sh_int AType, const char *format, CHAR_DATA *ch, const void *arg1, const void *arg2, int type )
{
    char *txt;
    CHAR_DATA *to;
    CHAR_DATA *vch = (CHAR_DATA *)arg2;

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

    if ( !ch )
    {
	bug( "Act: null ch. (%s)", format );
	return;
    }

    if ( !ch->in_room )
      to = NULL;
    else if ( type == TO_CHAR )
      to = ch;
    else
      to = ch->in_room->first_person;

    /*
     * ACT_SECRETIVE handling
     */
    if ( IS_NPC(ch) && xIS_SET(ch->act, ACT_SECRETIVE) && type != TO_CHAR )
	return;

    if ( type == TO_VICT )
    {
	if ( !vch )
	{
	    bug( "Act: null vch with TO_VICT." );
	    bug( "%s (%s)", ch->name, format );
	    return;
	}
	if ( !vch->in_room )
	{
	    bug( "Act: vch in NULL room!" );
	    bug( "%s -> %s (%s)", ch->name, vch->name, format );
	    return;
	}
	to = vch;
/*	to = vch->in_room->first_person;*/
    }

    if ( MOBtrigger && type != TO_CHAR && type != TO_VICT && to )
    {
      OBJ_DATA *to_obj;
      
      txt = act_string(format, NULL, ch, arg1, arg2, STRING_IMM, 0);
      if ( HAS_PROG(to->in_room, ACT_PROG) )
        rprog_act_trigger(txt, to->in_room, ch, (OBJ_DATA *)arg1, (void *)arg2);
      for ( to_obj = to->in_room->first_content; to_obj;
            to_obj = to_obj->next_content )
        if ( HAS_PROG(to_obj->pIndexData, ACT_PROG) )
          oprog_act_trigger(txt, to_obj, ch, (OBJ_DATA *)arg1, (void *)arg2);
    }

    /* Anyone feel like telling me the point of looping through the whole
       room when we're only sending to one char anyways..? -- Alty */
    for ( ; to; to = (type == TO_CHAR || type == TO_VICT)
                     ? NULL : to->next_in_room )
    {
	if ((!to->desc 
	&& (  IS_NPC(to) && !HAS_PROG(to->pIndexData, 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;
	if ( type == TO_CANSEE && ( to == ch || 
	    (!IS_IMMORTAL(to) && !IS_NPC(ch) && (xIS_SET(ch->act, PLR_WIZINVIS) 
	    && (get_trust(to) < (ch->pcdata ? ch->pcdata->wizinvis : 0) ) ) ) ) )
	    continue;

        if ( IS_IMMORTAL(to) )
            txt = act_string (format, to, ch, arg1, arg2, STRING_IMM, 0);
        else
       	    txt = act_string (format, to, ch, arg1, arg2, STRING_NONE, 0);

	if (to->desc)
	{
        set_char_color(AType, to);
        send_to_char( txt, to );
	}
	if (MOBtrigger)
        {
          /* Note: use original string, not string with ANSI. -- Alty */
	  mprog_act_trigger( txt, to, ch, (OBJ_DATA *)arg1, (void *)arg2 );
        }
    }
    MOBtrigger = TRUE;
    return;
}

void actl( sh_int AType, const char *format, CHAR_DATA *ch, const void *arg1, const void *arg2, int type )
{
    char *txt;
    CHAR_DATA *to;
    CHAR_DATA *vch = (CHAR_DATA *)arg2;

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

    if ( !ch )
    {
	bug( "Act: null ch. (%s)", format );
	return;
    }

    if ( !ch->in_room )
      to = NULL;
    else if ( type == TO_CHAR )
      to = ch;
    else
      to = ch->in_room->first_person;

    /*
     * ACT_SECRETIVE handling
     */
    if ( IS_NPC(ch) && xIS_SET(ch->act, ACT_SECRETIVE) && type != TO_CHAR )
	return;

    if ( type == TO_VICT )
    {
	if ( !vch )
	{
	    bug( "Act: null vch with TO_VICT." );
	    bug( "%s (%s)", ch->name, format );
	    return;
	}
	if ( !vch->in_room )
	{
	    bug( "Act: vch in NULL room!" );
	    bug( "%s -> %s (%s)", ch->name, vch->name, format );
	    return;
	}
	to = vch;
/*	to = vch->in_room->first_person;*/
    }

    if ( MOBtrigger && type != TO_CHAR && type != TO_VICT && to )
    {
      OBJ_DATA *to_obj;

      txt = act_string(format, NULL, ch, arg1, arg2, STRING_IMM, 1);
      if ( HAS_PROG(to->in_room, ACT_PROG) )
        rprog_act_trigger(txt, to->in_room, ch, (OBJ_DATA *)arg1, (void *)arg2);
      for ( to_obj = to->in_room->first_content; to_obj;
            to_obj = to_obj->next_content )
        if ( HAS_PROG(to_obj->pIndexData, ACT_PROG) )
          oprog_act_trigger(txt, to_obj, ch, (OBJ_DATA *)arg1, (void *)arg2);
    }

    /* Anyone feel like telling me the point of looping through the whole
       room when we're only sending to one char anyways..? -- Alty */
    for ( ; to; to = (type == TO_CHAR || type == TO_VICT)
                     ? NULL : to->next_in_room )
    {
	if ((!to->desc
	&& (  IS_NPC(to) && !HAS_PROG(to->pIndexData, 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;
	if ( type == TO_CANSEE && ( to == ch ||
	    (!IS_IMMORTAL(to) && !IS_NPC(ch) && (xIS_SET(ch->act, PLR_WIZINVIS)
	    && (get_trust(to) < (ch->pcdata ? ch->pcdata->wizinvis : 0) ) ) ) ) )
	    continue;

        if ( IS_IMMORTAL(to) )
            txt = act_string (format, to, ch, arg1, arg2, STRING_IMM, 1);
        else
       	    txt = act_string (format, to, ch, arg1, arg2, STRING_NONE, 1);

	if (to->desc)
	{
        set_char_color(AType, to);
        send_to_char( txt, to );
	}
	if (MOBtrigger)
        {
          /* Note: use original string, not string with ANSI. -- Alty */
	  mprog_act_trigger( txt, to, ch, (OBJ_DATA *)arg1, (void *)arg2 );
        }
    }
    MOBtrigger = TRUE;
    return;
}

void do_name( CHAR_DATA *ch, char *argument )
{
  char fname[1024];
  struct stat fst;
  CHAR_DATA *tmp;

  if ( !NOT_AUTHED(ch) || ch->pcdata->auth_state != 2)
  {
    send_to_char("Huh?\n\r", ch);
    return;
  }

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

  if (!check_parse_name(argument, TRUE))
  {
    send_to_char("Illegal name, try another.\n\r", ch);
    return;
  }

  if (!str_cmp(ch->name, argument))
  {
    send_to_char("That's already your name!\n\r", ch);
    return;
  }

  for ( tmp = first_char; tmp; tmp = tmp->next )
  {
    if (!str_cmp(argument, tmp->name))
    break;
  }

  if ( tmp )
  {
    send_to_char("That name is already taken.  Please choose another.\n\r", ch);
    return;
  }

  sprintf( fname, "%s%c/%s", PLAYER_DIR, tolower(argument[0]),
                        capitalize( argument ) );
  if ( stat( fname, &fst ) != -1 )
  {
    send_to_char("That name is already taken.  Please choose another.\n\r", ch);
    return;
  }

  STRFREE( ch->name );
  ch->name = STRALLOC( argument );
  STRFREE( ch->pcdata->filename );
  ch->pcdata->filename = STRALLOC( argument );
  send_to_char("Your name has been changed.  Please apply again.\n\r", ch);
  ch->pcdata->auth_state = 0;
  return;
}

/*
char *default_fprompt( CHAR_DATA *ch )
{
  static char buf[60];

  if (ch->race == race_lookup( "android" ))
  {
    strcpy(buf, "&D&z<&wDamage &w(&c%h&w) ");
    strcat(buf, "&wArmor &w(&c%z&w) ");
    strcat(buf, "&wEnergy &w(&c%m&w) ");
    strcat(buf, "&wFocus &w(&c%f&w)&z>");
    if ( IS_NPC(ch) || IS_IMMORTAL(ch) )
      strcat(buf, " %i%R&D\n\r");
    else
      strcat(buf, " &D\n\r");
    strcat(buf, "&D&z<&cTechlevel &c(&w%x&c/&w&W%P&c)&z>&w Enemy: &w(&R%y&w)&D&w ");
  }
  else
  {
    strcpy(buf, "&D&z<&wLifeforce &w(&c%h&w) ");
    strcat(buf, "&wArmor &w(&c%z&w) ");
    strcat(buf, "&wEnergy &w(&c%m&w) ");
    strcat(buf, "&wFocus &w(&c%f&w)&z>");
    if ( IS_NPC(ch) || IS_IMMORTAL(ch) )
      strcat(buf, " %i%R&D\n\r");
    else
      strcat(buf, " &D\n\r");
    strcat(buf, "&D&z<&cPowerlevel &c(&w%x&c/&w&W%P&c)&z>&w Enemy: &w(&R%y&w)&D&w ");
  }
  return buf;
}
*/

char *default_fprompt( CHAR_DATA *ch )
{
	static char buf[60];

	if (!IS_NPC(ch))
	{
		switch (ch->pcdata->battlePromptConfig)
		{
			default:
				if (ch->race == race_lookup( "android" ))
				{
					strcpy(buf, "&D<Damage(&c%h&w) En(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "TL(%x/&Y%P&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "TL(%x/&B%P&w)>");
					else
						strcat(buf, "TL(%x/&W%P&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
				}
				else
				{
					strcpy(buf, "&D<Life(&c%h&w) Ki(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "PL(%x/&Y%P&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "PL(%x/&B%P&w)>");
					else
						strcat(buf, "PL(%x/&W%P&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
				}
				strcat(buf, "\n\r<Enemy(&R%y&w) Focus(&C%f&w) Armor(&c%z&w)> ");
			break;
			case 1:
				if (ch->race == race_lookup( "android" ))
				{
					strcpy(buf, "&D<Damage(&c%h&w) En(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "TL(%x/&Y%P&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "TL(%x/&B%P&w)>");
					else
						strcat(buf, "TL(%x/&W%P&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
				}
				else
				{
					strcpy(buf, "&D<Life(&c%h&w) Ki(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "PL(%x/&Y%P&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "PL(%x/&B%P&w)>");
					else
						strcat(buf, "PL(%x/&W%P&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
				}
				strcat(buf, "\n\r<Enemy(&R%y&w) Focus(&C%f&w)> ");
			break;
			case 2:
				if (ch->race == race_lookup( "android" ))
				{
					strcpy(buf, "&D<Damage(&c%h&w) En(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "TL(&Y%p&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "TL(&B%p&w)>");
					else
						strcat(buf, "TL(&W%p&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
				}
				else
				{
					strcpy(buf, "&D<Life(&c%h&w) Ki(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "PL(&Y%p&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "PL(&B%p&w)>");
					else
						strcat(buf, "PL(&W%p&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
				}
				strcat(buf, "\n\r<Enemy(&R%y&w) Focus(&C%f&w) Armor(&c%z&w)> ");
			break;
			case 3:
				if (ch->race == race_lookup( "android" ))
				{
					strcpy(buf, "&D<Damage(&c%h&w) En(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "TL(&Y%p&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "TL(&B%p&w)>");
					else
						strcat(buf, "TL(&W%p&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
				}
				else
				{
					strcpy(buf, "&D<Life(&c%h&w) Ki(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "PL(&Y%p&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "PL(&B%p&w)>");
					else
						strcat(buf, "PL(&W%p&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
				}
				strcat(buf, "\n\r<Enemy(&R%y&w) Focus(&C%f&w)> ");
			break;
		}
	}
	else
	{
		if (ch->race == race_lookup( "android" ))
		{
			strcpy(buf, "&D<Damage(&c%h&w) En(&c%m&w) ");
			if (ch->exp < ch->pl)
				strcat(buf, "TL(%x/&Y%P&w)>");
			else if (ch->exp > ch->pl)
				strcat(buf, "TL(%x/&B%P&w)>");
			else
				strcat(buf, "TL(%x/&W%P&w)>");

			if (IS_NPC(ch) || IS_IMMORTAL(ch))
				strcat(buf, " %i%R");
		}
		else
		{
			strcpy(buf, "&D<Life(&c%h&w) Ki(&c%m&w) ");
			if (ch->exp < ch->pl)
				strcat(buf, "PL(%x/&Y%P&w)>");
			else if (ch->exp > ch->pl)
				strcat(buf, "PL(%x/&B%P&w)>");
			else
				strcat(buf, "PL(%x/&W%P&w)>");

			if (IS_NPC(ch) || IS_IMMORTAL(ch))
				strcat(buf, " %i%R");
		}
		strcat(buf, "\n\r<Enemy(&R%y&w) Focus(&C%f&w) Armor(&c%z&w)> ");
	}

	return buf;
}



char *default_prompt( CHAR_DATA *ch )
{
	static char buf[60];

/*
	if (ch->desc->psuppress_channel != 0);
	{
		if (ch->race == race_lookup("android"))
			strcpy(buf, "&D<D:%h E:%m> ");
		else
			strcpy(buf, "&D<L:%h K:%m> ");
		
		ch->desc->psuppress_channel = 0;
		return buf;
	}
*/
	if (!IS_NPC(ch))
	{
		switch (ch->pcdata->normalPromptConfig)
		{
			default:
				if (ch->race == race_lookup( "android" ))
				{
					strcpy(buf, "&R<&CDamage&b(&c%h&b) &CEn&b(&c%m&b) ");
					if (ch->exp < ch->pl)
						strcat(buf, "&CTL&b(&w%x&c/&Y%P&b)&R>&D");
					else if (ch->exp > ch->pl)
						strcat(buf, "&CTL&b(&w%x&c/&B%P&b)&R>&D");
					else
						strcat(buf, "&CTL&b(&w%x&c/&W%P&w)>&D");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
					else
						strcat(buf, " ");
				}
				else
				{
					strcpy(buf, "&R<&CLife&b(&c%h&b) &CKi&b(&c%m&b) ");
					if (ch->exp < ch->pl)
						strcat(buf, "&CPL&b(&w%x&c/&Y%P&b)&R>&D");
					else if (ch->exp > ch->pl)
						strcat(buf, "&CPL&b(&w%x&c/&B%P&b)&R>&D");
					else
						strcat(buf, "&CPL&b(&w%x&c/&W%P&b)&R>&D");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
					else
						strcat(buf, " ");
				}
			break;
			case 1:
				if (ch->race == race_lookup( "android" ))
				{
					strcpy(buf, "&D<Damage(&c%h&w) En(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "TL(&Y%p&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "TL(&B%p&w)>");
					else
						strcat(buf, "TL(&W%p&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
					else
						strcat(buf, " ");
				}
				else
				{
					strcpy(buf, "&D<Life(&c%h&w) Ki(&c%m&w) ");
					if (ch->exp < ch->pl)
						strcat(buf, "PL(&Y%p&w)>");
					else if (ch->exp > ch->pl)
						strcat(buf, "PL(&B%p&w)>");
					else
						strcat(buf, "PL(&W%p&w)>");

					if (IS_NPC(ch) || IS_IMMORTAL(ch))
						strcat(buf, " %i%R");
					else
						strcat(buf, " ");
				}
			break;
		}
	}
	else
	{
		if (ch->race == race_lookup( "android" ))
		{
			strcpy(buf, "&D<Damage(&c%h&w) En(&c%m&w) ");
			if (ch->exp < ch->pl)
				strcat(buf, "TL(%x/&Y%P&w)>");
			else if (ch->exp > ch->pl)
				strcat(buf, "TL(%x/&B%P&w)>");
			else
				strcat(buf, "TL(%x/&W%P&w)>");

			if (IS_NPC(ch) || IS_IMMORTAL(ch))
				strcat(buf, " %i%R");
		}
		else
		{
			strcpy(buf, "&D<Life(&c%h&w) Ki(&c%m&w) ");
			if (ch->exp < ch->pl)
				strcat(buf, "PL(%x/&Y%P&w)>");
			else if (ch->exp > ch->pl)
				strcat(buf, "PL(%x/&B%P&w)>");
			else
				strcat(buf, "PL(%x/&W%P&w)>");

			if (IS_NPC(ch) || IS_IMMORTAL(ch))
				strcat(buf, " %i%R");
			else
				strcat(buf, " ");
		}
	}

	return buf;
}

/*
char *default_prompt( CHAR_DATA *ch )
{
  static char buf[60];

  if (ch->race == 4)
  {
  strcpy(buf, "&D&R<&wDamage &b(&c%h&b) ");
  strcat(buf, "&CArmor &b(&c%z&b) ");
  strcat(buf, "&CEnergy &b(&c%m&b)&R>&D");
  if ( IS_NPC(ch) || IS_IMMORTAL(ch) )
    strcat(buf, " %i%R&D\n\r");
  else
    strcat(buf, " &D\n\r");
  strcat(buf, "&D&R<&CTechlevel &b(&w%x&c/&w&W%P&b)&R>&D&w ");
  }
  else
  {
  strcpy(buf, "&R&z<&CLifeforce &b(&c%h&b) ");
  strcat(buf, "&CArmor &w(&c%z&w) ");
  strcat(buf, "&CEnergy &b(&c%m&b)&R>");
  if ( IS_NPC(ch) || IS_IMMORTAL(ch) )
    strcat(buf, " %i%R&D\n\r");
  else
    strcat(buf, " &D\n\r");
  strcat(buf, "&D&z<&CPowerlevel &b(&w%x&c/&w&E%P&b)&R>&D&w ");
  }
  return buf;
}
*/
int getcolor(char clr)
{
  static const char colors[16] = "xrgObpcwzRGYBPCW";
  int r;

  for ( r = 0; r < 16; r++ )
    if ( clr == colors[r] )
      return r;
  return -1;
}

void display_prompt( DESCRIPTOR_DATA *d )
{
  CHAR_DATA *ch = d->character;
  CHAR_DATA *och = (d->original ? d->original : d->character);
  CHAR_DATA *victim;
  bool ansi = (!IS_NPC(och) && xIS_SET(och->act, PLR_ANSI));
  const char *prompt;
  const char *helpstart = "<Type HELP START>";
  char buf[MAX_STRING_LENGTH];
  char *pbuf = buf;
  int stat, percent;
  long double stat_ld = -1;
  long double stat_ldLong = -1;

  if ( !ch )
  {
    bug( "display_prompt: NULL ch" );
    return;
  }

  if ( !IS_NPC(ch) && !IS_SET(ch->pcdata->flags, PCFLAG_HELPSTART ) )
	prompt = helpstart;
  else if ( !IS_NPC(ch) && ch->substate != SUB_NONE && ch->pcdata->subprompt
  &&   ch->pcdata->subprompt[0] != '\0' )
    prompt = ch->pcdata->subprompt;
  else if (IS_NPC (ch) || (!ch->fighting && (!ch->pcdata->prompt
                || !*ch->pcdata->prompt) ) )
    prompt = default_prompt (ch);
  else if ( ch->fighting )
  {
        if ( !ch->pcdata->fprompt || !*ch->pcdata->fprompt )
                prompt = default_fprompt ( ch );
        else
                prompt = ch->pcdata->fprompt;
  }
  else
    prompt = ch->pcdata->prompt;
  if ( ansi )
  {
    strcpy(pbuf, "\033[0;37m");
    d->prevcolor = 0x07;
    pbuf += 7;
  }
  /* Clear out old color stuff */
/*  make_color_sequence(NULL, NULL, NULL);*/
  for ( ; *prompt; prompt++ )
  {
    /*
     * '%' = prompt commands
     * Note: foreground changes will revert background to 0 (black)
     */
    if( *prompt != '%' )
    {
      *(pbuf++) = *prompt;
      continue;
    }
    ++prompt;
    if ( !*prompt )
      break;
    if ( *prompt == *(prompt-1) )
    {
      *(pbuf++) = *prompt;
      continue;
    }
    switch(*(prompt-1))
    {
    default:
      bug( "Display_prompt: bad command char '%c'.", *(prompt-1) );
      break;
    case '%':
      *pbuf = '\0';
      stat = 0x80000000;
      switch(*prompt)
      {
      case '%':
	*pbuf++ = '%';
	*pbuf = '\0';
	break;
      case 'a':
	if ( ch->level >= 10 )
	  stat = ch->alignment;
	else if ( IS_GOOD(ch) )
	  strcpy(pbuf, "good");
	else if ( IS_EVIL(ch) )
	  strcpy(pbuf, "evil");
	else
	  strcpy(pbuf, "neutral");
	break;
      case 'A':
	sprintf( pbuf, "%s%s%s", IS_AFFECTED( ch, AFF_INVISIBLE ) ? "I" : "",
				 IS_AFFECTED( ch, AFF_HIDE )      ? "H" : "",
				 IS_AFFECTED( ch, AFF_SNEAK )     ? "S" : "" );
        break;
        case 'C':  /* Tank */
	  if ( !IS_IMMORTAL( ch ) ) break;
          if ( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
             strcpy( pbuf, "N/A" );
          else if(!victim->fighting||(victim = victim->fighting->who)==NULL)
             strcpy( pbuf, "N/A" );
          else {
              if ( victim->max_hit > 0 )
                    percent = (100 * victim->hit) / victim->max_hit;
              else
                    percent = -1;
                   if (percent >= 100)
                       strcpy (pbuf, "perfect health");
                   else if (percent >= 90)
                       strcpy (pbuf, "slightly scratched");
                   else if (percent >= 80)
                       strcpy (pbuf, "few bruises");
                    else if (percent >= 70)
                       strcpy (pbuf, "some cuts");
                    else if (percent >= 60)
                       strcpy (pbuf, "several wounds");
                    else if (percent >= 50)
                       strcpy (pbuf, "nasty wounds");
                    else if (percent >= 40)
                       strcpy (pbuf, "bleeding freely");
                    else if (percent >= 30)
                       strcpy (pbuf, "covered in blood");
                    else if (percent >= 20)
                       strcpy (pbuf, "leaking guts");
                    else if (percent >= 10)
                       strcpy (pbuf, "almost dead");
                    else
                       strcpy (pbuf, "DYING");
             }
          break;
        case 'c':
	  if ( !IS_IMMORTAL( ch ) ) break;
          if ( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
             strcpy( pbuf, "N/A" );
          else {
              if ( victim->max_hit > 0 )
                    percent = (100 * victim->hit) / victim->max_hit;
              else
                    percent = -1;
                if (percent >= 100)
                       strcpy (pbuf, "perfect health");
                    else if (percent >= 90)
                       strcpy (pbuf, "slightly scratched");
                    else if (percent >= 80)
                       strcpy (pbuf, "few bruises");
                    else if (percent >= 70)
                       strcpy (pbuf, "some cuts");
                    else if (percent >= 60)
                       strcpy (pbuf, "several wounds");
                    else if (percent >= 50)
                       strcpy (pbuf, "nasty wounds");
                    else if (percent >= 40)
                       strcpy (pbuf, "bleeding freely");
                    else if (percent >= 30)
                       strcpy (pbuf, "covered in blood");
                    else if (percent >= 20)
                       strcpy (pbuf, "leaking guts");
                    else if (percent >= 10)
                       strcpy (pbuf, "almost dead");
                    else
                       strcpy (pbuf, "DYING");
           }
          break;
      case 'h':
	stat = ch->hit;
	break;
      case 'H':
	stat = ch->max_hit;
	break;
      case 'm':
	  stat = ch->mana;
	break;
      case 'M':
	  stat = ch->max_mana;
	break;
       case 'P':
//	  sprintf ( pbuf, "%s", num_punct( stat_ldLong = ch->pl ));
	stat_ld = ch->pl;
	break;
       case 'p':
	stat_ldLong = ch->pl;
	break;
        case 'N': /* Tank */
	  if ( !IS_IMMORTAL(ch) ) break;
          if ( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
            strcpy( pbuf, "N/A" );
          else if(!victim->fighting||(victim=victim->fighting->who)==NULL)
            strcpy( pbuf, "N/A" );
          else {
            if ( ch == victim )
                    strcpy ( pbuf, "You" );
            else if ( IS_NPC(victim) )
                    strcpy ( pbuf, victim->short_descr );
            else
                    strcpy ( pbuf, victim->name );
                pbuf[0] = UPPER( pbuf[0] );
          }
          break;
        case 'n':
	  if ( !IS_IMMORTAL(ch) ) break;
          if (!ch->fighting || (victim = ch->fighting->who) == NULL )
            strcpy( pbuf, "N/A" );
          else {
            if ( ch == victim )
                    strcpy ( pbuf, "You" );
            else if ( IS_NPC(victim) )
                    strcpy ( pbuf, victim->short_descr );
            else
                    strcpy ( pbuf, victim->name );
            pbuf[0] = UPPER( pbuf[0] );
          }
          break;
      case 'T':
        if      ( time_info.hour <  5 ) strcpy( pbuf, "night" );
        else if ( time_info.hour <  6 ) strcpy( pbuf, "dawn" );
        else if ( time_info.hour < 19 ) strcpy( pbuf, "day" );
        else if ( time_info.hour < 21 ) strcpy( pbuf, "dusk" );
        else                            strcpy( pbuf, "night" );
        break;
      case 'b':
	  stat = 0;
	break;
      case 'B':
	  stat = 0;
	break;
      case 'u':
	stat = num_descriptors;
	break;
      case 'U':
	stat = sysdata.maxplayers;
	break;
      case 'v':
	stat = ch->move;
	break;
      case 'V':
	stat = ch->max_move;
	break;
      case 'g':
	stat = ch->gold;
	break;
      case 'r':
	if ( IS_IMMORTAL(och) )
	  stat = ch->in_room->vnum;
	break;
      case 'F':
	if ( IS_IMMORTAL( och ) )
	  sprintf( pbuf, "%s", ext_flag_string( &ch->in_room->room_flags, r_flags) );
	break;
      case 'R':
	if ( xIS_SET(och->act, PLR_ROOMVNUM) )
	  sprintf(pbuf, "<#%d> ", ch->in_room->vnum);
	break;
      case 'x':
//	  sprintf ( pbuf, "%s", num_punct( stat_ldLong = ch->exp ));
	stat_ld = ch->exp;
	break;
      case 'X':
//	  sprintf ( pbuf, "%s", num_punct( stat_ldLong = exp_level(ch, ch->level+1) - ch->exp));
	stat = exp_level(ch, ch->level+1) - ch->exp;
      case 'y':
          if ( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
             strcpy( pbuf, "N/A" );
          else {
              	stat = victim->hit;
/*
              if ( victim->max_hit > 0 )
                    percent = (victim->hit);
              else
                    percent = -1;
                if (percent >= 100)
                       strcpy (pbuf, "|||\x1b[1;33m|||\x1b[1;32m||||");
                    else if (percent >= 90)
                       strcpy (pbuf, "|||\x1b[1;33m|||\x1b[1;32m|||\x1b[1;31m-");
                    else if (percent >= 80)
                       strcpy (pbuf, "|||\x1b[1;33m|||\x1b[1;32m||\x1b[1;31m--");
                    else if (percent >= 70)
                       strcpy (pbuf, "|||\x1b[1;33m|||\x1b[1;32m|\x1b[1;31m---");
                    else if (percent >= 60)
                       strcpy (pbuf, "|||\x1b[1;33m|||\x1b[1;31m----");
                    else if (percent >= 50)
                       strcpy (pbuf, "|||\x1b[1;33m||\x1b[1;31m-----");
                    else if (percent >= 40)
                       strcpy (pbuf, "|||\x1b[1;33m|\x1b[1;31m------");
                    else if (percent >= 30)
                       strcpy (pbuf, "|||-------");
                    else if (percent >= 20)
                       strcpy (pbuf, "||--------");
                    else if (percent >= 10)
                       strcpy (pbuf, "|---------");
                    else
                       strcpy (pbuf, "----------");
*/
           }
          break;

      case 'o':         /* display name of object on auction */
        if ( auction->item )
           strcpy( pbuf, auction->item->name );
        break;
      case 'S':
        if      ( ch->style == STYLE_BERSERK )    strcpy( pbuf, "B" );
        else if ( ch->style == STYLE_AGGRESSIVE ) strcpy( pbuf, "A" );
        else if ( ch->style == STYLE_DEFENSIVE )  strcpy( pbuf, "D" );
        else if ( ch->style == STYLE_EVASIVE )    strcpy( pbuf, "E" );
        else if ( ch->style == STYLE_PORTAL )    strcpy( pbuf, "P" );
        else                                      strcpy( pbuf, "S" );
	break;
      case 'i':
	if ( (!IS_NPC(ch) && xIS_SET(ch->act, PLR_WIZINVIS)) ||
	      (IS_NPC(ch) && xIS_SET(ch->act, ACT_MOBINVIS)) )
	  sprintf(pbuf, "(Invis %d) ", (IS_NPC(ch) ? ch->mobinvis : ch->pcdata->wizinvis));
	else
	if ( IS_AFFECTED(ch, AFF_INVISIBLE) )
	  sprintf(pbuf, "(Invis) " );
	break;
      case 'I':
	stat = (IS_NPC(ch) ? (xIS_SET(ch->act, ACT_MOBINVIS) ? ch->mobinvis : 0)
	     : (xIS_SET(ch->act, PLR_WIZINVIS) ? ch->pcdata->wizinvis : 0));
	break;
	case 'z':
		stat = get_armor(ch);
		break;
	case 'Z':
		stat = get_maxarmor(ch);
		break;
	case 'f':
		stat = ch->focus;
		break;
      }


	if ( stat != 0x80000000 ) {
		if (stat >= 1000)
			sprintf(pbuf, "%s", num_punct(stat));
		else
			sprintf(pbuf, "%d", stat);
	}
	if (stat_ld != -1) {
		sprintf(pbuf, "%s", abbNumLD(stat_ld));
		stat_ld = -1;
	}
	else if (stat_ldLong != -1) {
		sprintf(pbuf, "%s", num_punct_ld(stat_ldLong));
		stat_ldLong = -1;
	}

//		sprintf(pbuf, "%s", abbNumLD(stat_ld));
//		sprintf(pbuf, "%s", num_punct_ld(stat_ld));



      pbuf += strlen(pbuf);
      break;
    }
  }
  *pbuf = '\0';
  send_to_char( buf, ch );
  return;
}

int make_color_sequence( const char *col, char *code, DESCRIPTOR_DATA *d )
{
//   CHAR_DATA *ch = (d->original ? d->original : d->character);
   const char *ctype = col;
   int ln;
//   bool ansi = ( !IS_NPC(ch) && xIS_SET( ch->act, PLR_ANSI ) );

   col++;

   if( !*col )
      ln = -1;
   else if ( *ctype != '&' && *ctype != '^' && *ctype != '}' )
   {
      bug( "colorcode: command '%c' not '&', '^' or '}'", *ctype );
      ln = -1;
   }
   else if( *col == *ctype )
   {
      code[0] = *col;
      code[1] = '\0';
      ln = 1;
   }
   else if( !d->ansi )
      ln = 0;
   else
   {
	/* Foreground text - non-blinking */
	if( *ctype == '&' )
	{
         switch( *col )
         {
            default:
		   code[0] = *ctype;
		   code[1] = *col;
		   code[2] = '\0';
		   return 2;
		case 'i': /* Italic text */
		case 'I':
		   strcpy( code, ANSI_ITALIC );
		   break;
		case 'v': /* Reverse colors */
		case 'V':
		   strcpy( code, ANSI_REVERSE );
		   break;
		case 'u': /* Underline */
		case 'U':
		   strcpy( code, ANSI_UNDERLINE );
		   break;
	      case 'd': /* Player's client default color */
		   strcpy( code, ANSI_RESET );
		   break;
	      case 'D': /* Reset to custom color for whatever is being displayed */
		   strcpy( code, ANSI_RESET ); /* Yes, this reset here is quite necessary to cancel out other things */
		   strcat( code, ANSI_GREY );
//		   strcat( code, color_str( ch->desc->pagecolor, ch ) );
		   break;
            case 'x': /* Black */
               strcpy( code, ANSI_BLACK );
               break;
            case 'O': /* Orange/Brown */
               strcpy( code, ANSI_ORANGE );
               break;
            case 'c': /* Cyan */
               strcpy( code, ANSI_CYAN );
               break;
            case 'z': /* Dark Grey */
               strcpy( code, ANSI_DGREY );
               break;
            case 'g': /* Dark Green */
               strcpy( code, ANSI_DGREEN );
               break;
            case 'G': /* Light Green */
               strcpy( code, ANSI_GREEN );
               break;
            case 'P': /* Pink/Light Purple */
               strcpy( code, ANSI_PINK );
               break;
            case 'r': /* Dark Red */
               strcpy( code, ANSI_DRED );
               break;
            case 'b': /* Dark Blue */
               strcpy( code, ANSI_DBLUE );
               break;
            case 'w': /* Grey */
               strcpy( code, ANSI_GREY );
               break;
            case 'Y': /* Yellow */
               strcpy( code, ANSI_YELLOW );
               break;
            case 'C': /* Light Blue */
               strcpy( code, ANSI_LBLUE );
               break;
            case 'p': /* Purple */
               strcpy( code, ANSI_PURPLE );
               break;
            case 'R': /* Red */
               strcpy( code, ANSI_RED );
               break;
            case 'B': /* Blue */
               strcpy( code, ANSI_BLUE );
               break;
            case 'W': /* White */
               strcpy( code, ANSI_WHITE );
               break;
         }
	}
	/* Foreground text - blinking */
	if( *ctype == '}' )
	{
         switch( *col )
         {
            default:
		   code[0] = *ctype;
		   code[1] = *col;
		   code[2] = '\0';
		   return 2;
            case 'x': /* Black */
               strcpy( code, BLINK_BLACK );
               break;
            case 'O': /* Orange/Brown */
               strcpy( code, BLINK_ORANGE );
               break;
            case 'c': /* Cyan */
               strcpy( code, BLINK_CYAN );
               break;
            case 'z': /* Dark Grey */
               strcpy( code, BLINK_DGREY );
               break;
            case 'g': /* Dark Green */
               strcpy( code, BLINK_DGREEN );
               break;
            case 'G': /* Light Green */
               strcpy( code, BLINK_GREEN );
               break;
            case 'P': /* Pink/Light Purple */
               strcpy( code, BLINK_PINK );
               break;
            case 'r': /* Dark Red */
               strcpy( code, BLINK_DRED );
               break;
            case 'b': /* Dark Blue */
               strcpy( code, BLINK_DBLUE );
               break;
            case 'w': /* Grey */
               strcpy( code, BLINK_GREY );
               break;
            case 'Y': /* Yellow */
               strcpy( code, BLINK_YELLOW );
               break;
            case 'C': /* Light Blue */
               strcpy( code, BLINK_LBLUE );
               break;
            case 'p': /* Purple */
               strcpy( code, BLINK_PURPLE );
               break;
            case 'R': /* Red */
               strcpy( code, BLINK_RED );
               break;
            case 'B': /* Blue */
               strcpy( code, BLINK_BLUE );
               break;
            case 'W': /* White */
               strcpy( code, BLINK_WHITE );
               break;
         }
	}
	/* Background color */

	if( *ctype == '^' )
	{
         switch( *col )
         {
            default:
		   code[0] = *ctype;
		   code[1] = *col;
		   code[2] = '\0';
		   return 2;
            case 'x': /* Black */
               strcpy( code, BACK_BLACK );
               break;
            case 'r': /* Dark Red */
               strcpy( code, BACK_DRED );
               break;
            case 'g': /* Dark Green */
               strcpy( code, BACK_DGREEN );
               break;
            case 'O': /* Orange/Brown */
               strcpy( code, BACK_ORANGE );
               break;
            case 'b': /* Dark Blue */
               strcpy( code, BACK_DBLUE );
               break;
            case 'p': /* Purple */
               strcpy( code, BACK_PURPLE );
               break;
            case 'c': /* Cyan */
               strcpy( code, BACK_CYAN );
               break;
            case 'w': /* Grey */
               strcpy( code, BACK_GREY );
               break;
         }
	}
      ln = strlen( code );
   }
   if ( ln <= 0 )
      *code = '\0';
   return ln;
}

void set_pager_input( DESCRIPTOR_DATA *d, char *argument )
{
  while ( isspace(*argument) )
    argument++;
  d->pagecmd = *argument;
  return;
}

bool pager_output( DESCRIPTOR_DATA *d )
{
  register char *last;
  CHAR_DATA *ch;
  int pclines;
  register int lines;
  bool ret;

  if ( !d || !d->pagepoint || d->pagecmd == -1 )
    return TRUE;
  ch = d->original ? d->original : d->character;
  pclines = UMAX(ch->pcdata->pagerlen, 5) - 1;
  switch(LOWER(d->pagecmd))
  {
  default:
    lines = 0;
    break;
  case 'b':
    lines = -1-(pclines*2);
    break;
  case 'r':
    lines = -1-pclines;
    break;
  case 'n':
    lines = 0;
    pclines = 0x7FFFFFFF;	/* As many lines as possible */
    break;
  case 'q':
    d->pagetop = 0;
    d->pagepoint = NULL;
    flush_buffer(d, TRUE);
    DISPOSE(d->pagebuf);
    d->pagesize = MAX_STRING_LENGTH;
    return TRUE;
  }
  while ( lines < 0 && d->pagepoint >= d->pagebuf )
    if ( *(--d->pagepoint) == '\n' )
      ++lines;
  if ( *d->pagepoint == '\n' && *(++d->pagepoint) == '\r' )
      ++d->pagepoint;
  if ( d->pagepoint < d->pagebuf )
    d->pagepoint = d->pagebuf;
  for ( lines = 0, last = d->pagepoint; lines < pclines; ++last )
    if ( !*last )
      break;
    else if ( *last == '\n' )
      ++lines;
  if ( *last == '\r' )
    ++last;
  if ( last != d->pagepoint )
  {
    if ( !write_to_descriptor(d->descriptor, d->pagepoint,
          (last-d->pagepoint)) )
      return FALSE;
    d->pagepoint = last;
  }
  while ( isspace(*last) )
    ++last;
  if ( !*last )
  {
    d->pagetop = 0;
    d->pagepoint = NULL;
    flush_buffer(d, TRUE);
    DISPOSE(d->pagebuf);
    d->pagesize = MAX_STRING_LENGTH;
    return TRUE;
  }
  d->pagecmd = -1;
   if( xIS_SET( ch->act, PLR_ANSI ) )
      if( write_to_descriptor( d->descriptor, ANSI_LBLUE, 0 ) == FALSE )
	   return FALSE;
  if ( (ret=write_to_descriptor(d->descriptor,
	"(C)ontinue, (N)on-stop, (R)efresh, (B)ack, (Q)uit: [C] ", 0)) == FALSE )
	return FALSE;
  if ( xIS_SET( ch->act, PLR_ANSI ) )
  {
      char buf[32];

	sprintf( buf, "%s", color_str( d->pagecolor, ch ) );
  }
  return ret;
}


#ifdef WIN32

void shutdown_mud( char *reason );

void bailout(void)
{
    echo_to_all( AT_IMMORT, "MUD shutting down by system operator NOW!!", ECHOTAR_ALL );
    shutdown_mud( "MUD shutdown by system operator" );
    log_string ("MUD shutdown by system operator");
    Sleep (5000);		/* give "echo_to_all" time to display */
    mud_down = TRUE;		/* This will cause game_loop to exit */
    service_shut_down = TRUE;	/* This will cause characters to be saved */
    fflush(stderr);
    return;
}

#endif

bool check_total_ip( DESCRIPTOR_DATA *dnew)
{
	DESCRIPTOR_DATA *d;
	CHAR_DATA *vch;
	int ip_total = 0;
	bool ch = TRUE;
	bool is_nolimit = FALSE;
	bool linkdead = FALSE;

	if (!sysdata.check_plimit)
		return FALSE;

	if (dnew->character->level >= sysdata.level_noplimit)
		return FALSE;

	for( d = first_descriptor; d; d= d->next )
	{

		if (d == dnew)
		{
			continue;
		}

		vch = d->character;

		if (!vch)
			ch = FALSE;

		if (ch)
		{
			if (!vch->desc)
				linkdead = TRUE;
			if (!linkdead && dnew->character)
			{
				if (!str_cmp(dnew->character->name, vch->name))
					return FALSE;
			}
		}

#ifdef DNS_SLAVE
		if (ch && !str_cmp( d->host, dnew->host )
			&& vch->level >= sysdata.level_noplimit)
			is_nolimit = TRUE;

		if (!linkdead && !str_cmp( d->host, dnew->host ))
			ip_total ++;

#else
		if (ch && !str_cmp( d->host, dnew->host )
			&& vch->level >= sysdata.level_noplimit)
			is_nolimit = TRUE;

		if (!linkdead && !str_cmp( d->host, dnew->host ))
			ip_total ++;

#endif

	}

	if (is_nolimit)
		return FALSE;

	if (ip_total >= sysdata.plimit)
		return TRUE;

	return FALSE;
}

char *color_str( sh_int AType, CHAR_DATA *ch )
{
   if( !ch )
   {
	bug( "color_str: NULL ch!", 0 );
	return( "" );
   }

   if( IS_NPC(ch) || !xIS_SET( ch->act, PLR_ANSI ) )
      return( "" );

   switch( AType )
   {
	case 0:  return( ANSI_BLACK );		break;
	case 1:  return( ANSI_DRED );		break;
	case 2:  return( ANSI_DGREEN );		break;
	case 3:  return( ANSI_ORANGE );		break;
	case 4:  return( ANSI_DBLUE );		break;
	case 5:  return( ANSI_PURPLE );		break;
	case 6:  return( ANSI_CYAN );		break;
	case 7:  return( ANSI_GREY );		break;
	case 8:  return( ANSI_DGREY );		break;
	case 9:  return( ANSI_RED );		break;
	case 10: return( ANSI_GREEN );		break;
	case 11: return( ANSI_YELLOW );		break;
	case 12: return( ANSI_BLUE );		break;
	case 13: return( ANSI_PINK );		break;
	case 14: return( ANSI_LBLUE );		break;
	case 15: return( ANSI_WHITE );		break;

	default: return( ANSI_RESET );	break;
   }
}

/*
 *  Remove Colour Codes - By Komarosu
 *
 *  Colour code removal function, removes color codes but wont remove
 *  double instances of chars ( &&, }}. ^^ etc.).
 *  Not sure about memleaks, but i doubt any.
 *  Modified from a C++ Class for string modification.
 *
 */

char *remove_color(char *str)
{
    /* List of chars to remove */
    char *list = "&}^";
    int ii, jj, kk;

    char *aa = (char *) strdup(str);
    for (ii = 0, jj = strlen(aa); ii < jj; ii++)
    {
        for (kk = 0; kk < 3; kk++)
        {
            if (aa[ii] == list[kk])
            {
                if (aa[ii+1] != list[kk])
                  strcpy(& aa[ii], & aa[ii+2]);
                ii += 2;
                break;
            }
        }
    }
    return aa;
}

#define NAME(ch)        (IS_NPC(ch) ? ch->short_descr : ch->name)

char *act2_string(const char *format, CHAR_DATA *to, CHAR_DATA *ch,
		 const void *arg1, const void *arg2, int flags, const void *arg3)
{
  static char * const he_she  [] = { "it",  "he",  "she" };
  static char * const him_her [] = { "it",  "him", "her" };
  static char * const his_her [] = { "its", "his", "her" };
  static char buf[MAX_STRING_LENGTH];
  char fname[MAX_INPUT_LENGTH];
  char temp[MAX_STRING_LENGTH];
  char *point = buf;
  const char *str = format;
  const char *i;
  CHAR_DATA *vch = (CHAR_DATA *) arg2;
  OBJ_DATA *obj1 = (OBJ_DATA  *) arg1;
  OBJ_DATA *obj2 = (OBJ_DATA  *) arg2;

  if ( str[0] == '$' )
  	DONT_UPPER = FALSE;

  while ( *str != '\0' )
  {
    if ( *str != '$' )
    {
      *point++ = *str++;
      continue;
    }
    ++str;
    if ( !arg2 && *str >= 'A' && *str <= 'Z' )
    {
      bug( "Act: missing arg2 for code %c:", *str );
      bug( format );
      i = " <@@@> ";
    }
    else
    {
      switch ( *str )
      {
      default:  bug( "Act: bad code %c.", *str );
		i = " <@@@> ";						break;
      case 't': i = (arg1 ? (char *) arg1 : " " );			break;
      case 'T': i = (arg3 ? (char *) arg3 : " " );			break;
      case 'n':
              if ( ch->morph == NULL )
                  i = (to ? PERS ( ch, to ): NAME ( ch ) );
              else if ( !IS_SET( flags, STRING_IMM ) )
                  i = (to ? MORPHPERS (ch, to) : MORPHNAME (ch));
              else
              {
                sprintf(temp, "(MORPH) %s", (to ? PERS(ch,to):NAME(ch)));
                i = temp;
              }
              break;
      case 'N':
              if ( vch->morph == NULL )
                   i = (to ? PERS ( vch, to ) : NAME( vch ) );
              else if ( !IS_SET( flags, STRING_IMM ) )
                   i = (to ? MORPHPERS (vch, to) : MORPHNAME (vch));
              else
              {
                sprintf(temp, "(MORPH) %s", (to ? PERS(vch,to):NAME(vch)));
                i = temp;
              }
              break;

      case 'e': if (ch->sex > 2 || ch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", ch->name,
		      ch->sex);
		  i = "it";
		}
		else
		  i = he_she [URANGE(0,  ch->sex, 2)];
		break;
      case 'E': if (vch->sex > 2 || vch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", vch->name,
		      vch->sex);
		  i = "it";
		}
		else
		  i = he_she [URANGE(0, vch->sex, 2)];
		break;
      case 'm': if (ch->sex > 2 || ch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", ch->name,
		      ch->sex);
		  i = "it";
		}
		else
		  i = him_her[URANGE(0,  ch->sex, 2)];
		break;
      case 'M': if (vch->sex > 2 || vch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", vch->name,
		      vch->sex);
		  i = "it";
		}
		else
		  i = him_her[URANGE(0, vch->sex, 2)];
		break;
      case 's': if (ch->sex > 2 || ch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", ch->name,
		      ch->sex);
		  i = "its";
		}
		else
		  i = his_her[URANGE(0,  ch->sex, 2)];
		break;
      case 'S': if (vch->sex > 2 || vch->sex < 0)
		{
		  bug("act_string: player %s has sex set at %d!", vch->name,
		      vch->sex);
		  i = "its";
		}
		else
		  i = his_her[URANGE(0, vch->sex, 2)];
		break;
      case 'q': i = (to == ch) ? "" : "s";				break;
      case 'Q': i = (to == ch) ? "your" :
		    his_her[URANGE(0,  ch->sex, 2)];			break;
      case 'p': i = (!to || can_see_obj(to, obj1)
		  ? obj_short(obj1) : "something");			break;
      case 'P': i = (!to || can_see_obj(to, obj2)
		  ? obj_short(obj2) : "something");			break;
      case 'd':
        if ( !arg2 || ((char *) arg2)[0] == '\0' )
          i = "door";
        else
        {
          one_argument((char *) arg2, fname);
          i = fname;
        }
        break;
      }
    }
    ++str;
    while ( (*point = *i) != '\0' )
      ++point, ++i;

   /*  #0  0x80c6c62 in act_string (
    format=0x81db42e "$n has reconnected, kicking off old link.", to=0x0,
    ch=0x94fcc20, arg1=0x0, arg2=0x0, flags=2) at comm.c:2901 */
  }
  strcpy(point, "\n\r");
  if ( !DONT_UPPER )
     buf[0] = UPPER(buf[0]);
  return buf;
}
#undef NAME

void act2( sh_int AType, const char *format, CHAR_DATA *ch, const void *arg1, const void *arg2, int type, const void *arg3)
{
    char *txt;
    CHAR_DATA *to;
    CHAR_DATA *vch = (CHAR_DATA *)arg2;

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

    if ( !ch )
    {
	bug( "Act: null ch. (%s)", format );
	return;
    }

    if ( !ch->in_room )
      to = NULL;
    else if ( type == TO_CHAR )
      to = ch;
    else
      to = ch->in_room->first_person;

    /*
     * ACT_SECRETIVE handling
     */
    if ( IS_NPC(ch) && xIS_SET(ch->act, ACT_SECRETIVE) && type != TO_CHAR )
	return;

    if ( type == TO_VICT )
    {
	if ( !vch )
	{
	    bug( "Act: null vch with TO_VICT." );
	    bug( "%s (%s)", ch->name, format );
	    return;
	}
	if ( !vch->in_room )
	{
	    bug( "Act: vch in NULL room!" );
	    bug( "%s -> %s (%s)", ch->name, vch->name, format );
	    return;
	}
	to = vch;
/*	to = vch->in_room->first_person;*/
    }

    if ( MOBtrigger && type != TO_CHAR && type != TO_VICT && to )
    {
      OBJ_DATA *to_obj;

      txt = act2_string(format, NULL, ch, arg1, arg2, STRING_IMM, arg3);
      if ( HAS_PROG(to->in_room, ACT_PROG) )
        rprog_act_trigger(txt, to->in_room, ch, (OBJ_DATA *)arg1, (void *)arg2);
      for ( to_obj = to->in_room->first_content; to_obj;
            to_obj = to_obj->next_content )
        if ( HAS_PROG(to_obj->pIndexData, ACT_PROG) )
          oprog_act_trigger(txt, to_obj, ch, (OBJ_DATA *)arg1, (void *)arg2);
    }

    /* Anyone feel like telling me the point of looping through the whole
       room when we're only sending to one char anyways..? -- Alty */
    for ( ; to; to = (type == TO_CHAR || type == TO_VICT)
                     ? NULL : to->next_in_room )
    {
	if ((!to->desc
	&& (  IS_NPC(to) && !HAS_PROG(to->pIndexData, 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;
	if ( type == TO_CANSEE && ( to == ch ||
	    (!IS_IMMORTAL(to) && !IS_NPC(ch) && (xIS_SET(ch->act, PLR_WIZINVIS)
	    && (get_trust(to) < (ch->pcdata ? ch->pcdata->wizinvis : 0) ) ) ) ) )
	    continue;

        if ( IS_IMMORTAL(to) )
            txt = act2_string (format, to, ch, arg1, arg2, STRING_IMM, arg3);
        else
       	    txt = act2_string (format, to, ch, arg1, arg2, STRING_NONE, arg3);

	if (to->desc)
	{
	  if ( AType == AT_COLORIZE )
	  {
	     if ( txt[0] == '&' )
	     	send_to_char_color( txt, to );
	     else
	     {
	        set_char_color(AT_MAGIC, to );
	     	send_to_char_color( txt, to );
	     }
	  }
	  else {
	     set_char_color(AType, to);
	     send_to_char_color( txt, to );
	  }
	}
	if (MOBtrigger)
        {
          /* Note: use original string, not string with ANSI. -- Alty */
	  mprog_act_trigger( txt, to, ch, (OBJ_DATA *)arg1, (void *)arg2 );
        }
    }
    MOBtrigger = TRUE;
    return;
}

#ifdef MCCP
/*
 * Ported to SMAUG by Garil of DOTDII Mud
 * aka Jesse DeFer <dotd@dotd.com>  http://www.dotd.com
 *
 * revision 1: MCCP v1 support
 * revision 2: MCCP v2 support
 * revision 3: Correct MMCP v2 support
 * revision 4: clean up of write_to_descriptor() suggested by Noplex@CB
 *
 * See the web site below for more info.
 */

/*
 * mccp.c - support functions for mccp (the Mud Client Compression Protocol)
 *
 * see http://homepages.ihug.co.nz/~icecube/compress/ and README.Rom24-mccp
 *
 * Copyright (c) 1999, Oliver Jowett <icecube@ihug.co.nz>.
 *
 * This code may be freely distributed and used if this copyright notice is
 * retained intact.
 */

void *zlib_alloc(void *opaque, unsigned int items, unsigned int size)
{
    return calloc(items, size);
}

void zlib_free(void *opaque, void *address)
{
    DISPOSE(address);
}

bool process_compressed(DESCRIPTOR_DATA *d)
{
    int iStart = 0, nBlock, nWrite, len;

    if (!d->out_compress)
        return TRUE;

    // Try to write out some data..
    len = d->out_compress->next_out - d->out_compress_buf;

    if (len > 0)
    {
        // we have some data to write
        for (iStart = 0; iStart < len; iStart += nWrite)
        {
            nBlock = UMIN (len - iStart, 4096);
            if ((nWrite = write(d->descriptor, d->out_compress_buf + iStart, nBlock)) < 0)
            {
                if (errno == EAGAIN ||
                    errno == ENOSR)
                    break;

                return FALSE;
            }

            if (!nWrite)
                break;
        }

        if (iStart)
        {
            // We wrote "iStart" bytes
            if (iStart < len)
                memmove(d->out_compress_buf, d->out_compress_buf+iStart, len - iStart);

            d->out_compress->next_out = d->out_compress_buf + len - iStart;
        }
    }

    return TRUE;
}

char enable_compress[] =
{
    IAC, SB, TELOPT_COMPRESS, WILL, SE, 0
};
char enable_compress2[] =
{
    IAC, SB, TELOPT_COMPRESS2, IAC, SE, 0
};

bool compressStart(DESCRIPTOR_DATA *d, unsigned char telopt)
{
    z_stream *s;
	char buf[MAX_INPUT_LENGTH];

    if (d->out_compress)
        return TRUE;

		sprintf( buf, "Starting compression for descriptor %d", d->descriptor );
		log_string_plus( buf, LOG_COMM, LEVEL_SUPREME );
//    bug("Starting compression for descriptor %d", d->descriptor);

    CREATE(s, z_stream, 1);
    CREATE(d->out_compress_buf, unsigned char, COMPRESS_BUF_SIZE);

    s->next_in = NULL;
    s->avail_in = 0;

    s->next_out = d->out_compress_buf;
    s->avail_out = COMPRESS_BUF_SIZE;

    s->zalloc = zlib_alloc;
    s->zfree  = zlib_free;
    s->opaque = NULL;

    if (deflateInit(s, 9) != Z_OK)
    {
        DISPOSE(d->out_compress_buf);
        DISPOSE(s);
        return FALSE;
    }

    if (telopt == TELOPT_COMPRESS)
        write_to_descriptor(d->descriptor, enable_compress, 0);
    else if (telopt == TELOPT_COMPRESS2)
        write_to_descriptor(d->descriptor, enable_compress2, 0);
    else
        bug("compressStart: bad TELOPT passed");

    d->compressing = telopt;
    d->out_compress = s;

    return TRUE;
}

bool compressEnd(DESCRIPTOR_DATA *d)
{
    unsigned char dummy[1];
	char buf[MAX_INPUT_LENGTH];

    if (!d->out_compress)
        return TRUE;

		sprintf( buf, "Stopping compression for descriptor %d", d->descriptor );
		log_string_plus( buf, LOG_COMM, LEVEL_SUPREME );
//    bug("Stopping compression for descriptor %d", d->descriptor);

    d->out_compress->avail_in = 0;
    d->out_compress->next_in = dummy;

    if (deflate(d->out_compress, Z_FINISH) != Z_STREAM_END)
        return FALSE;

    if (!process_compressed(d)) /* try to send any residual data */
        return FALSE;

    deflateEnd(d->out_compress);
    DISPOSE(d->out_compress_buf);
    DISPOSE(d->out_compress);
    d->compressing = 0;

    return TRUE;
}

void do_compress( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_STRING_LENGTH];

    if (!ch->desc) {
        send_to_char("What descriptor?!\n", ch);
        return;
    }

	argument = one_argument( argument, arg );

    if (!str_cmp(arg, "on"))
    {
	    send_to_char("Initiating compression....", ch);
	    compressStart(ch->desc, TELOPT_COMPRESS2);
	    if (!ch->desc->out_compress)
	    	send_to_char("failure\n\r", ch);
		else
			send_to_char("success\n\r", ch);
	}
    else if (!str_cmp(arg, "off"))
    {
        send_to_char("Terminating compression...", ch);
        compressEnd(ch->desc);
	    if (!ch->desc->out_compress)
	    	send_to_char("success\n\r", ch);
		else
			send_to_char("failure\n\r", ch);
    }
    else
    {
    	send_to_char("Syntax: compression <on/off>\n\r", ch);
    	send_to_char("MCCP compression is currently: ", ch);
	    if (!ch->desc->out_compress)
	    	send_to_char("Off\n\r", ch);
		else
			send_to_char("On\n\r", ch);
	}

	return;
}
#endif
