eldhamud/boards/
eldhamud/clans/
eldhamud/classes/
eldhamud/councils/
eldhamud/deity/
eldhamud/doc/
eldhamud/doc/DIKU/
eldhamud/doc/MERC/
eldhamud/doc/mudprogs/
eldhamud/houses/
eldhamud/src/o/CVS/
/****************************************************************************
 * [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 <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"
#include "md5.h"

/*
 * 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'
  };

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


/*
 * 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;
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)";
sh_int client_speed( sh_int speed);



/*
 * OS-dependent local functions.
 */
void	game_loop		args( ( void ) );
int	init_socket		args( ( int mudport ) );
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 ) );
void	set_pager_input		args( ( DESCRIPTOR_DATA *d,
                              char *argument ) );
bool	pager_output		args( ( DESCRIPTOR_DATA *d ) );

void	mail_count		args( ( CHAR_DATA *ch ) );

int port;

#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 = FALSE;
#ifdef IMC

  int imcsocket = -1;
#endif

  /*
   * 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;
  boot_time = time(0);         /*  <-- I think this is what you wanted */
  strcpy( str_boot_time, ctime( &current_time ) );
  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 = 4600;
  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] );
#ifdef I3

      I3_socket = atoi( argv[4] );
#endif
#ifdef IMC

      imcsocket = atoi( argv[5] );
#endif

    }
    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 */

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

  /* 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 );

#ifdef I3
  /* Initialize and connect to Intermud-3 */
  I3_main( FALSE, port, fCopyOver );
#endif
#ifdef IMC
  /* Initialize and connect to IMC2 */
  imc_startup( FALSE, imcsocket, fCopyOver );
#endif

  log_string( log_buf );

  if( fCopyOver )
  {
    log_string( "Initiating hotboot recovery." );
    hotboot_recover();
  }

  game_loop( );

  closesocket( control  );

#ifdef I3

  I3_shutdown( 0 );
#endif
#ifdef IMC

  imc_shutdown( FALSE );
#endif

#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 );
      }
  }
  /* 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 mudport )
{
  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( mudport );

  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()
{
  abort();
}


/*
 * LAG alarm!							-Thoric
 */
void caught_alarm( int signum )
{
  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 == last_descriptor )
      break;
  }

  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( void )
{
  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 );
  gettimeofday( &last_time, NULL );
  current_time = (time_t) last_time.tv_sec;

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

    /*
     * 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 ( 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 > 2400)		  /* 10 mins */
             ||   ( d->connected != CON_PLAYING && d->idle > 4800) /* 20 mins */
             ||     d->idle > 115200 )				  /* 8 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 ) )
          {
            d->idle = 0;
            if ( d->character )
              d->character->timer = 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;
            }
          }

          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;
              }
          }
        }
      if ( d == last_descriptor )
        break;
    }

#ifdef I3
    I3_loop( );
#endif
#ifdef IMC

    imc_loop();
#endif

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

    /*
     * 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;

  }
  /*  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( 20 );
  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( 20 );
  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->outsize	= 2000;
  dnew->idle		= 0;
  dnew->lines		= 0;
  dnew->scrlen	= 24;
  dnew->port		= ntohs( sock.sin_port );
  dnew->newstate	= 0;
  dnew->prevcolor	= 0x07;

  CREATE( dnew->outbuf, char, dnew->outsize );

  strcpy( buf, inet_ntoa( sock.sin_addr ) );
  sprintf( log_buf, "Sock.sinaddr:  %s, port %hd.",
           buf, dnew->port );
  log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
  if ( sysdata.NO_NAME_RESOLVING )
    dnew->host = STRALLOC( buf );
  else
  {
    from = gethostbyaddr( (char *) &sock.sin_addr,
                          sizeof(sock.sin_addr), AF_INET );
    dnew->host = 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 );

  /*
   * Send the greeting.
   */
  {
    extern char * help_greeting;
    if ( help_greeting[0] == '.' )
      write_to_buffer( dnew, help_greeting+1, 0 );
    else
      write_to_buffer( dnew, help_greeting  , 0 );
  }

  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 )
{
  closesocket( d->descriptor );
  STRFREE( d->host );
  DISPOSE( d->outbuf );
  if ( d->pagebuf )
    DISPOSE( d->pagebuf );
  DISPOSE( d );
  return;
}

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

  /* 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_ROLL_STATS )
        ||  (dclose->connected >= CON_NOTE_TO && dclose->connected <= CON_NOTE_FINISH))
    {
      act( AT_ACTION, "$n has lost $s link.", ch, NULL, NULL, TO_CANSEE );
      ch->desc = NULL;
      ch->pcdata->mip_ver = STRALLOC( "" );
      ch->pcdata->sec_code = STRALLOC( "" );
      xREMOVE_BIT( ch->act, PLR_MIP );
    }
    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 );
  }

  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;

  /*
   * 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++ )
  {
    if ( k >= 508 )
    {
      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;
    }

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

  }

  /*
   * 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[1024 * UMAX( 1, d->speed)];



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

      buf[client_speed( d->speed )] = '\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, client_speed( d->speed ) ) )
    {
      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 ( xIS_SET(ch->act, PLR_PROMPT) )
      display_prompt(d);
    send_mip_points( ch );
    if ( ch->fighting )
      send_mip_attacker( 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;
      /* Bugfix by Samson - moved bug() call up */
      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;
}


/*
* 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.
*
* Added block checking to prevent random booting of the descriptor. Thanks go
* out to Rustry for his suggestions. -Orion
*/
bool write_to_descriptor( int desc, char *txt, int length )
{
  int iStart = 0;
  int nWrite = 0;
  int nBlock = 0;
  int iErr   = 0;

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

  for ( iStart = 0; iStart < length; iStart += nWrite )
  {
    nBlock = UMIN( length - iStart, 4096 );
    nWrite = send( desc, txt + iStart, nBlock, 0 );

    if ( nWrite == -1 )
    {
#ifdef WIN32
      iErr = WSAGetLastError ();
#else

      iErr = errno;
#endif

      if ( iErr == EWOULDBLOCK )
      {
        /*
         * This is a SPAMMY little bug error. I would suggest
         * not using it, but I've included it in case. -Orion
         *
        perror( "Write_to_descriptor: Send is blocking" );
         */
        nWrite = 0;
        continue;
      }
      else
      {
        perror( "Write_to_descriptor" );
        return FALSE;
      }
    }
  }

  return TRUE;
}



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

  ch = d->character;
  // removed all the guff from within here, dont see the need to have all the login spam tommi 2005
  /*   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 );
     }*/
  write_to_buffer( d, "Press [ENTER]\n\r ", 0 );
  d->connected = CON_CHECK_BOARDS;

}

char *smaug_crypt( const char *pwd )
{
  md5_state_t state;
  md5_byte_t digest[16];
  static char passwd[16];
  unsigned int x;

  md5_init( &state );
  md5_append( &state, (const md5_byte_t *)pwd, strlen( pwd ) );
  md5_finish( &state, digest );

  strncpy( passwd, (const char *)digest, 16 );

  /* The listed exceptions below will fubar the MD5 authentication packets, so change them */
  for( x = 0; x < strlen( passwd ); x++ )
  {
    if( passwd[x] == '\n' )
      passwd[x] = 'n';
    if( passwd[x] == '\r' )
      passwd[x] = 'r';
    if( passwd[x] == '\t' )
      passwd[x] = 't';
    if( passwd[x] == ' ' )
      passwd[x] = 's';
    if( (int)passwd[x] == 11 )
      passwd[x] = 'x';
    if( (int)passwd[x] == 12 )
      passwd[x] = 'X';
    if( passwd[x] == '~' )
      passwd[x] = '+';
    if( passwd[x] == EOF )
      passwd[x] = 'E';
  }
  return( passwd );
}

/*
 * 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 arg[MAX_STRING_LENGTH];
  CHAR_DATA *ch;
  char *pwdnew;
  char *p;
  //  int iClass;
  int iRace;
  bool fOld, chk;

  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_OEDIT:
    oedit_parse( d, argument );
    break;

  case CON_REDIT:
    redit_parse( d, argument );
    break;

  case CON_MEDIT:
    medit_parse( d, argument );
    break;

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

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

    /* Old players can keep their characters. -- Alty */
    if ( !check_parse_name( argument, (d->newstate != 0) ) )
    {
      write_to_buffer( d, "Illegal name, try another.\n\rName: ", 0 );
      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" );
          write_to_buffer( d, buf, 0 );
          sprintf( buf, "New players are not accepted during this time.\n\r" );
          write_to_buffer( d, buf, 0 );
          sprintf( buf, "Please try again in a few minutes.\n\r" );
          write_to_buffer( d, buf, 0 );
          close_socket( d, FALSE );
        }
        sprintf( buf, "\n\rChoosing 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 suits a medieval theme.\n\r"
                 "If the name you select is not acceptable, you will be asked to choose\n\r"
                 "another one.\n\r\n\rPlease choose a name for your character: ");
        write_to_buffer( d, buf, 0 );
        d->newstate++;
        d->connected = CON_GET_NAME;
        return;
      }
      else
      {
        write_to_buffer(d, "Illegal name, try another.\n\rName: ", 0);
        return;
      }
    }

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

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

    if ( fOld )
    {
      if ( check_bans( ch, BAN_CLASS ) )
      {
        write_to_buffer (d,
                         "Your class has been banned from this Mud.\n\r", 0);
        close_socket (d, FALSE);
        return;
      }
      if ( check_bans( ch, BAN_RACE ) )
      {
        write_to_buffer (d,
                         "Your race has been banned from this Mud.\n\r", 0);
        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)
      {
        write_to_buffer( d, "That name is already taken.  Please choose another: ", 0 );
        d->connected = CON_GET_NAME;
        d->character->desc = NULL;
        free_char( d->character ); /* Big Memory Leak before --Shaddai */
        d->character = NULL;
        return;
      }
      write_to_buffer( d, "You are denied access.\n\r", 0 );
      close_socket( d, FALSE );
      return;
    }
    /*
     *  Make sure the immortal host is from the correct place.
     *  Shaddai
     */

    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);
      write_to_buffer (d, "This hacking attempt has been logged.\n\r", 0);
      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) )
      {
        write_to_buffer( d, "The game is wizlocked.  Only immortals can connect now.\n\r", 0 );
        write_to_buffer( d, "Please try back later.\n\r", 0 );
        close_socket( d, FALSE );
        return;
      }
    }

    if ( fOld )
    {
      if (d->newstate != 0)
      {
        write_to_buffer( d, "That name is already taken.  Please choose another: ", 0 );
        d->connected = CON_GET_NAME;
        d->character->desc = NULL;
        free_char( d->character ); /* Big Memory Leak before --Shaddai */
        d->character = NULL;
        return;
      }
      /* Old player */
      write_to_buffer( d, "Password: ", 0 );
      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 */
        write_to_buffer( d, "\n\rNo such player exists.\n\rPlease check your spelling, or type new to start a new player.\n\r\n\rName: ", 0 );
        d->connected = CON_GET_NAME;
        d->character->desc = NULL;
        free_char( d->character ); /* Big Memory Leak before --Shaddai */
        d->character = NULL;
        return;
      }

      sprintf( buf, "Did I get that right, %s (Y/N)? ", argument );
      write_to_buffer( d, buf, 0 );
      d->connected = CON_CONFIRM_NEW_NAME;
      return;
    }
    break;

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

    if( str_cmp( smaug_crypt( argument ), ch->pcdata->pwd ) )
    {
      write_to_buffer( d, "Wrong password, disconnecting.\n\r", 0 );
      /* 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;

    strcpy( buf, ch->pcdata->filename );
    d->character->desc = NULL;
    free_char( d->character );
    d->character = NULL;
    fOld = load_char_obj( d, buf, FALSE, 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_STANDING;

    sprintf( log_buf, "%s (%s) has connected.", ch->name, d->host );
    if ( ch->level < LEVEL_DEMI )
    {
      log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
    }
    else
      log_string_plus( log_buf, LOG_COMM, ch->level );
    show_title(d);
    {
      struct tm *tme;
      time_t now;
      char day[50];
      now = time(0);
      tme = localtime(&now);
      strftime(day, 50, "%a %b %d %H:%M:%S %Y", tme);
      sprintf(log_buf, "%-20s     %-24s    %s", ch->pcdata->filename, day, d->host);
      write_last_file(log_buf);
    }

    break;

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

    case 'n':
    case 'N':
      write_to_buffer( d, "Ok, what IS it, then? ", 0 );
      /* 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:
      write_to_buffer( d, "Please type Yes or No. ", 0 );
      break;
    }
    break;

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

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

    if (argument[0] == '!' )
    {
      send_to_char( "New password cannot begin with the '!' character.",ch);
      return;
    }
    pwdnew = smaug_crypt( argument ); /* MD5 Encryption */
    for( p = pwdnew; *p != '\0'; p++ )
    {
      if( *p == '~' )
      {
        write_to_buffer( d, "New password not acceptable, cannot use the ~ character.\n\rPassword: ", 0 );
        return;
      }
    }
    DISPOSE( ch->pcdata->pwd );
    ch->pcdata->pwd = str_dup( pwdnew );
    write_to_buffer( d, "\n\rPlease retype the password to confirm: ", 0 );
    d->connected = CON_CONFIRM_NEW_PASSWORD;
    break;

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

    if( str_cmp( smaug_crypt( argument ), ch->pcdata->pwd ) )
    {
      write_to_buffer( d, "Passwords don't match.\n\rRetype password: ", 0 );
      d->connected = CON_GET_NEW_PASSWORD;
      return;
    }

    write_to_buffer( d, echo_on_str, 0 );
    write_to_buffer( d, "\n\rWhat is your sex (M/F/N)? ", 0 );
    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:
      write_to_buffer( d, "That's not a sex.\n\rWhat IS your sex? ", 0 );
      return;
    }

    write_to_buffer( d, "\n\rYou may choose from the following races, or type help [race] to learn more:\n\r[", 0 );
    buf[0] = '\0';
    for ( iRace = 0; iRace < MAX_PC_RACE; iRace++ )
    {
      if (race_table[iRace]->race_name && race_table[iRace]->race_name[0] != '\0'
          && !IS_SET(race_table[iRace]->class_restriction, 1 << ch->class)
          && str_cmp(race_table[iRace]->race_name,"unused") )
      {
        if ( iRace > 0 )
        {
          if ( strlen(buf)+strlen(race_table[iRace]->race_name) > 77 )
          {
            strcat( buf, "\n\r" );
            write_to_buffer( d, buf, 0 );
            buf[0] = '\0';
          }
          else
            strcat( buf, " " );
        }
        strcat( buf, race_table[iRace]->race_name );
      }
    }
    strcat( buf, "]\n\r: " );
    write_to_buffer( d, buf, 0 );
    d->connected = CON_GET_NEW_RACE;
    break;

    case CON_GET_NEW_RACE:
    argument = one_argument(argument, arg);
    ch->class =  0;
    if (!str_cmp( arg, "help") )
    {
      for ( iRace = 0; iRace < MAX_PC_RACE; iRace++ )
      {
        if ( toupper(argument[0]) == toupper(race_table[iRace]->race_name[0])
             &&  !str_prefix( argument, race_table[iRace]->race_name) )
        {
          do_help(ch, argument);
          write_to_buffer( d, "Please choose a race: ", 0);
          return;
        }
      }
      write_to_buffer( d, "No help on that topic.  Please choose a race: ", 0 );
      return;
    }


    for ( iRace = 0; iRace < MAX_PC_RACE; iRace++ )
    {
      if ( toupper(arg[0]) == toupper(race_table[iRace]->race_name[0])
           &&   !str_prefix( arg, race_table[iRace]->race_name ) )
      {
        ch->race = iRace;
        break;
      }
    }

    if ( iRace == MAX_PC_RACE
         ||  !race_table[iRace]->race_name || race_table[iRace]->race_name[0] == '\0'

         ||   IS_SET(race_table[iRace]->class_restriction, 1 << ch->class )
         ||   !str_cmp(race_table[iRace]->race_name,"unused")
       )
    {
      write_to_buffer( d,"That's not a race.\n\rWhat IS your race? ", 0 );
      return;
    }
    /* Rerolling of stats added by Samson 2-8-98. Code courtesy of Kermit's Mud Page */

    write_to_buffer( d, "You may now roll for your character's stats.\n\rYou may roll as often as you like.\n\r",0);

    name_stamp_stats(ch);

    snprintf( buf, MAX_STRING_LENGTH, "\n\r Strength:     %d\n\r Intelligence: %d\n\r Wisdom:       %d\n\r Dexterity:    %d\n\r Constution:   %d\n\r Charisma:     %d\n\r Luck:         %d\n\r Keep? (Y/N)", ch->perm_str, ch->perm_int, ch->perm_wis, ch->perm_dex, ch->perm_con, ch->perm_cha, ch->perm_lck );
    write_to_buffer( d, buf, 0 );

    d->connected = CON_ROLL_STATS;
    break;

  case CON_ROLL_STATS:
    switch (argument[0] )
    {
    case 'y':
    case 'Y':
      write_to_buffer( d, "\n\rWould you like ANSI, or no graphic/color support, (A/N)? \n\r", 0 );
      d->connected = CON_GET_WANT_RIPANSI;
      break;

    case 'n':
    case 'N':

      name_stamp_stats(ch);

      snprintf( buf, MAX_STRING_LENGTH, "\n\r Strength:     %d\n\r Intelligence: %d\n\r Wisdom:       %d\n\r Dexterity:    %d\n\r Constution:   %d\n\r Charisma:     %d\n\r Luck:         %d\n\r Keep? (Y/N)", ch->perm_str, ch->perm_int, ch->perm_wis, ch->perm_dex, ch->perm_con, ch->perm_cha, ch->perm_lck );
      write_to_buffer( d, buf, 0 );
      return;
    default:
      write_to_buffer( d, "Yes or No? ", 0 );
      return;
    }
    break;

  case CON_GET_WANT_RIPANSI:
    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_pager( "\n\rPress [ENTER] 11", ch );
    show_title(d);
    ch->level = 0;
    ch->position = POS_STANDING;
    d->connected = CON_CHECK_BOARDS;
    set_pager_color( AT_PLAIN, ch );
    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;
    }

    return;
    break;



  case CON_CHECK_BOARDS:
    if ( IS_IMMORTAL(ch) )
    {
      send_to_pager( "\n\rThe last 10 logins have been attempted by:\n\r", ch );
      do_last(ch, "10");
    }
    do_global_boards( ch, "" );
    send_to_pager( "Press [ENTER]", ch );

    d->connected = CON_PRESS_ENTER;
    break;


  case CON_PRESS_ENTER:
    if ( IS_IMMORTAL(ch) )
    {
      do_help(ch, "imotd");
    }
    else
      do_help(ch, "motd");

    send_to_pager( "\n\rPress [ENTER]", ch );
    d->connected = CON_READ_MOTD;
    break;


  case CON_READ_MOTD:
    {
      if(!IS_IMMORTAL(ch))
      {
        sprintf(buf, "&R[&CANNOUNCEMENT&R]&c %s has entered the game.", ch->name);
        talk_info(AT_GREEN, buf);
      }
    }
    add_char( ch );
    d->connected	= CON_PLAYING;

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

      ch->pcdata->clan	  = NULL;
      switch ( class_table[ch->class]->attr_prime )
      {
      case APPLY_STR:
        ch->perm_str = 16;
        break;
      case APPLY_INT:
        ch->perm_int = 16;
        break;
      case APPLY_WIS:
        ch->perm_wis = 16;
        break;
      case APPLY_DEX:
        ch->perm_dex = 16;
        break;
      case APPLY_CON:
        ch->perm_con = 16;
        break;
      case APPLY_CHA:
        ch->perm_cha = 16;
        break;
      case APPLY_LCK:
        ch->perm_lck = 16;
        break;
      }

      ch->perm_str	 += race_table[ch->race]->str_plus;
      ch->perm_int	 += race_table[ch->race]->int_plus;
      ch->perm_wis	 += race_table[ch->race]->wis_plus;
      ch->perm_dex	 += race_table[ch->race]->dex_plus;
      ch->perm_con	 += race_table[ch->race]->con_plus;
      ch->perm_cha	 += race_table[ch->race]->cha_plus;
      ch->affected_by	  = race_table[ch->race]->affected;
      ch->perm_lck	 += race_table[ch->race]->lck_plus;

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

      reset_colors( ch );
    
      ch->level		= 1;
      ch->exp		= 0;
      ch->max_hit    	+= race_table[ch->race]->hit;
      ch->max_mana   	+= race_table[ch->race]->mana;
      ch->hit		= UMAX(1,ch->max_hit);
      ch->mana		= UMAX(1,ch->max_mana);
      ch->move		= ch->max_move;
      ch->practice	= 10; //no more mr meany, better give them something to start with Tommi Aug 2005
      ch->gold 		= 5000;
      sprintf( buf, "the %s",
               title_table [ch->class] [ch->level]
               [ch->sex == SEX_FEMALE ? 1 : 0] );
      set_title( ch, buf );

      /* 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_AUTOLOOT );
      xSET_BIT( ch->act, PLR_AUTOGOLD );
      xSET_BIT( ch->act, PLR_AUTOEXIT );
      xSET_BIT( ch->act, PLR_COMPASS );
      xSET_BIT( ch->act, PLR_AUTOMAP );
       /*
      Outfit all players in basic equipments Tommi.         
      */

      {
        OBJ_INDEX_DATA *obj_ind = get_obj_index( 840 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_WIELD );
        }
      }
      {
        OBJ_INDEX_DATA *obj_ind = get_obj_index( 833 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_HEAD );
        }
      }
      {
        OBJ_INDEX_DATA *obj_ind = get_obj_index( 839 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_SHIELD );
        }
      }
      {
        OBJ_INDEX_DATA *obj_ind = get_obj_index( 836 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_HANDS );
        }
      }
      {
        OBJ_INDEX_DATA *obj_ind = get_obj_index( 835 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_FEET );
        }
      }
      {

        OBJ_INDEX_DATA *obj_ind = get_obj_index( 832 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_BODY );
        }
      }
      {

        OBJ_INDEX_DATA *obj_ind = get_obj_index( 831 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_NECK_1 );
        }
      }
      {

        OBJ_INDEX_DATA *obj_ind = get_obj_index( 831 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_NECK_2 );
        }
      }
      {

        OBJ_INDEX_DATA *obj_ind = get_obj_index( 834 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_LEGS );
        }
      }
      {

        OBJ_INDEX_DATA *obj_ind = get_obj_index( 838 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_ABOUT );
        }
      }
      {

        OBJ_INDEX_DATA *obj_ind = get_obj_index( 839 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_WRIST_L );
        }
      }
      {

        OBJ_INDEX_DATA *obj_ind = get_obj_index( 839 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_WRIST_R );
        }
      }
      {

        OBJ_INDEX_DATA *obj_ind = get_obj_index( 820 );
        if ( obj_ind != NULL )
        {
          obj = create_object( obj_ind, 0 );
          obj_to_char( obj, ch );
          equip_char( ch, obj, WEAR_ARMS );
        }
      }

      if (!sysdata.WAIT_FOR_AUTH)
        char_to_room( ch, get_room_index( ROOM_VNUM_SCHOOL ) );
      else
      {
        char_to_room( ch, get_room_index( ROOM_AUTH_START ) );
        ch->pcdata->auth_state = 0;
        SET_BIT(ch->pcdata->flags, PCFLAG_UNAUTHED);
      }
    }
    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 ( 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 ) );
          }


    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 );
    }

    do_look( ch, "auto" ); //do_look

    mail_count(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... Welcome back.\n\r", ch );
        do_look( ch, "auto" );
        act( AT_ACTION, "$n has reconnected.", ch, NULL, NULL, TO_CANSEE );
        sprintf( log_buf, "%s (%s) reconnected.", ch->name, d->host );
        log_string_plus( log_buf, LOG_COMM, UMAX( sysdata.log_level, 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 && cstate != CON_ROLL_STATS ))
      {
        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... Welcome back.\n\r", ch );
      do_look( ch, "auto" ); //do_look
      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;
}

/*
 * 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;
}

#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, void *arg1, void *arg2, int flags )
{
  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;
#ifdef I3

      case '$':
        i = "$";
        break;
#endif

      case 't':
        i = (char *) arg1;
        break;
      case 'T':
        i = (char *) arg2;
        break;
      case 'n':
        if ( ch->morph == NULL )
          i = (to ? PERS ( ch, to, TRUE ): NAME ( ch ) );
        else if ( !IS_SET( flags, STRING_IMM ) )
          i = (to ? MORPHPERS (ch, to, FALSE) : MORPHNAME (ch));
        else
        {
          sprintf(temp, "(MORPH) %s", (to ? PERS(ch,to, FALSE):NAME(ch)));
          i = temp;
        }
        break;
      case 'N':
        if ( vch->morph == NULL )
          i = (to ? PERS ( vch, to, FALSE ) : NAME( vch ) );
        else if ( !IS_SET( flags, STRING_IMM ) )
          i = (to ? MORPHPERS (vch, to, FALSE) : MORPHNAME (vch));
        else
        {
          sprintf(temp, "(MORPH) %s", (to ? PERS(vch,to, FALSE):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;
  }
  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, void *arg1, 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;
  }

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

    txt = act_string(format, NULL, ch, arg1, arg2, STRING_IMM);
    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 )
    {
      if( to != ch )
        continue;

      if( !is_same_map( ch, to ) )
        continue;
    }
    if ( type == TO_VICT && ( to != vch || to == ch ) )
      continue;
    if( type == TO_ROOM )
    {
      if( to == ch )
        continue;

      if( !is_same_map( ch, to ) )
        continue;
    }
    if( type == TO_NOTVICT )
    {
      if( to == ch || to == vch )
        continue;

      if( !is_same_map( ch, to ) )
        continue;
    }
    if( type == TO_CANSEE )
    {
      if( to == ch )
        continue;

      if( IS_IMMORTAL(ch) && IS_PLR_FLAG( ch, PLR_WIZINVIS ) )
      {
        if( to->level < ch->pcdata->wizinvis )
          continue;
      }

      if ( to->desc && is_inolc(to->desc) )
        continue;

      if( !is_same_map( ch, to ) )
        continue;
    }


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

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

  strcpy(buf, "&w<&Y%hhp ");
  if ( IS_VAMPIRE(ch) )
    strcat(buf, "&R%bbp");
  else
    strcat(buf, "&C%mm");
  strcat(buf, " &G%vmv&w %E> ");
  if ( IS_NPC(ch) || IS_IMMORTAL(ch) )
    strcat(buf, "%i%R");
  return buf;
}

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

  strcpy(buf, "&w<&Y%hhp ");
  if ( IS_VAMPIRE(ch) )
    strcat(buf, "&R%bbp");
  else
    strcat(buf, "&C%mm");
  strcat(buf, " &G%vmv&w> ");
  if ( IS_NPC(ch) || IS_IMMORTAL(ch) )
    strcat(buf, "%i%R");
  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 pstat, percent;

  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, ANSI_RESET );
    d->prevcolor = 0x08;
    pbuf += 4;
  }
  /* Clear out old color stuff */
  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';
      pstat = 0x80000000;
      switch(*prompt)
      {
      case '%':
        *pbuf++ = '%';
        *pbuf = '\0';
        break;
      case 'a':
        if ( ch->level >= 10 )
          pstat = 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 'e':
        if (( victim = who_fighting( ch ) ) != NULL)
        {
          if (victim->max_hit > 0)
            percent =  (100 * victim->hit ) / victim->max_hit;
          else
            percent = -1;
          if(percent >= 65)
            sprintf(pbuf," &wEnemy: &g%d%%",percent);
          else if(percent >= 25 && percent < 65)
            sprintf(pbuf," &wEnemy: &Y%d%%",percent);
          else
            sprintf(pbuf," &wEnemy: &r%d%%",percent);

        }
        break;

      case 'E':
        if (( victim = who_fighting( ch ) ) != NULL)
        {
          if (victim->max_hit > 0)
            percent =  (100 * victim->hit ) / victim->max_hit;
          else
            percent = -1;
          if (percent >= 100)
            strcpy(pbuf,"&wEnemy: [&r+++&Y+++&g++++&D]");
          else if (percent >= 90)
            strcpy(pbuf,"&wEnemy: [&r+++&Y+++&g+++ &D]");
          else if (percent >= 80)
            strcpy(pbuf,"&wEnemy: [&r+++&Y+++&g++  &D]");
          else if (percent >= 70)
            strcpy(pbuf,"&wEnemy: [&r+++&Y+++&g+   &D]");
          else if (percent >= 58)
            strcpy(pbuf,"&wEnemy: [&r+++&Y+++    &D]");
          else if (percent >= 45)
            strcpy(pbuf,"&wEnemy: [&r+++&Y++     &D]");
          else if (percent >= 30)
            strcpy(pbuf,"&wEnemy: [&r+++&Y+      &D]");
          else if (percent >= 28)
            strcpy(pbuf,"&wEnemy: [&r+++&x       &D]");
          else if (percent >= 15)
            strcpy(pbuf,"&wEnemy: [&r++&x        &D]");
          else if (percent >= 8)
            strcpy(pbuf,"&wEnemy: [&r+&x         &D]");
          else
            strcpy(pbuf,"&wEnemy: [          &D]");
        }
        break;

      case 'h':
        pstat = ch->hit;
        break;
      case 'H':
        pstat = ch->max_hit;
        break;
      case 'm':
        if ( IS_VAMPIRE(ch) )
          pstat = 0;
        else
          pstat = ch->mana;
        break;
      case 'M':
        if ( IS_VAMPIRE(ch) )
          pstat = 0;
        else
          pstat = ch->max_mana;
        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':
        if ( IS_VAMPIRE(ch) )
          pstat = ch->pcdata->condition[COND_BLOODTHIRST];
        else
          pstat = 0;
        break;
      case 'B':
        if ( IS_VAMPIRE(ch) )
          pstat = ch->level + 10;
        else
          pstat = 0;
        break;
      case 'u':
        pstat = num_descriptors;
        break;
      case 'U':
        pstat = sysdata.maxplayers;
        break;
      case 'v':
        pstat = ch->move;
        break;
      case 'V':
        pstat = ch->max_move;
        break;
      case 'g':
        pstat = ch->gold;
        break;
      case 'r':
        if ( IS_IMMORTAL(och) )
          pstat = 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':
        pstat = ch->exp;
        break;
      case 'X':
        pstat = exp_level(ch, ch->level+1) - ch->exp;
        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
          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':
        pstat = (IS_NPC(ch) ? (xIS_SET(ch->act, ACT_MOBINVIS) ? ch->mobinvis : 0)
                     : (xIS_SET(ch->act, PLR_WIZINVIS) ? ch->pcdata->wizinvis : 0));
        break;
      }
      if ( pstat != 0x80000000 )
        sprintf(pbuf, "%d", pstat);
      pbuf += strlen(pbuf);
      break;
    }
  }
  *pbuf = '\0';
  send_to_char( buf, ch );
  send_to_char( "\n\r", ch );
  return;
}

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 ) );
    ret = write_to_descriptor( d->descriptor, buf, 0 );
  }
  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

void do_speed( CHAR_DATA *ch, char *argument)
{
  sh_int speed=atoi(argument);

  if (!ch->desc)
    return;   // Don't send messages to people who don't exist. duh.

  if (argument[0] == '\0')
  {
    ch_printf( ch, "Your present speed is a %d, which equates to %d bytes per second.\n\r", ch->desc->speed, client_speed(ch->desc->speed) );
    return;
  }

  if (speed > 5 || speed < 0)
  {
    send_to_char("Speed is between 0 and 5.\n\r", ch);
    return;
  }
  ch->desc->speed = speed;
  ch_printf( ch, "The MUD will now send output to you at %d bytes per second.\n\r", client_speed( speed) );
  if ( client_speed(speed) > 2048)
    ch_printf( ch, "You should be aware %d is fast enough to lag you if you have a slow connection.\n\r", client_speed( speed) );
  return;
}

sh_int client_speed( sh_int speed)
{
  switch ( speed )
  {
  default:
    break;
  case 1:
    return 512;
  case 2:
    return 1024;
  case 3:
    return 2048;
  case 4:
    return 3584;
  case 5:
    return 5120;

  }
  return 512; // Better than a mere default case.
}