EmberMUD/
EmberMUD/clan/
EmberMUD/classes/
EmberMUD/doc/design/
EmberMUD/gods/
EmberMUD/log/
EmberMUD/notes/
EmberMUD/player/
EmberMUD/player/temp/
EmberMUD/src/MSVC/
EmberMUD/src/Sleep/
EmberMUD/src/StartMUD/
EmberMUD/src/Win32Common/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Thanks to abaddon for proof-reading our comm.c and pointing out bugs.  *
 *  Any remaining bugs are, of course, our work, not his.  :)              *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/**************************************************************************
 * Mudprogram's (Mobprogram, Objprogram and Roomprogram) originaly 	  *
 * by the SMAUG development team             				  *
 * Ported to EmberMUD by Thanatos and Tyrluk of ToED      		  *
 * (Temple of Eternal Death)    					  *
 * Tyrluk   - morn@telmaron.com or dajy@mindspring.com			  *
 * Thanatos - morn@telmaron.com or jonathan_w._rose@ffic.com              *
 **************************************************************************/

/*
 * This file contains all of the OS-dependent stuff:
 *   startup, signals, BSD sockets for tcp/ip, i/o, timing.
 *
 * The data flow for input is:
 *    Game_loop ---> Read_from_descriptor ---> Read
 *    Game_loop ---> Read_from_buffer
 *
 * The data flow for output is:
 *    Game_loop ---> Process_Output ---> Write_to_descriptor -> Write
 *
 * The OS-dependent functions are Read_from_descriptor and Write_to_descriptor.
 * -- Furey  26 Jan 1993
 */

#if defined(WIN32)
#include <windows.h>
#else
#include <sys/time.h>
#include <sys/wait.h>
#endif

#if defined(cbuilder)
#include <dir.h>
#endif

#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#include "merc.h"
#include <math.h>

/* command procedures needed */
DECLARE_DO_FUN( do_help );
DECLARE_DO_FUN( do_look );
DECLARE_DO_FUN( do_skills );
DECLARE_DO_FUN( do_outfit );
DECLARE_DO_FUN( do_afk );

/*
 * Malloc debugging stuff.
 */
#if defined(sun)
#undef MALLOC_DEBUG
#endif

#if defined(MALLOC_DEBUG)
#include <malloc.h>
extern int malloc_debug args( ( int ) );
extern int malloc_verify args( ( void ) );
#endif

/*
 * Signal handling.
 * Apollo has a problem with __attribute(atomic) in signal.h,
 *   I dance around it.
 */
#if defined(apollo)
#define __attribute(x)
#endif

#if defined(unix)
#include <signal.h>
#endif

#if defined(apollo)
#undef __attribute
#endif

/*
 * Socket and TCP/IP stuff.
 */
#if     defined(unix)
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/telnet.h>
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' };
const char will_suppress_ga_str[] = { IAC, WILL, TELOPT_SGA, '\0' };
const char wont_suppress_ga_str[] = { IAC, WONT, TELOPT_SGA, '\0' };
#endif

#if     defined(WIN32)
#include "Win32Common\telnet.h"
const char echo_off_str[] = { ( char ) IAC, ( char ) WILL, TELOPT_ECHO, '\0' };
const char echo_on_str[] = { ( char ) IAC, ( char ) WONT, TELOPT_ECHO, '\0' };
const char go_ahead_str[] = { ( char ) IAC, ( char ) GA, '\0' };
const char will_suppress_ga_str[] =
    { ( char ) IAC, ( char ) WILL, TELOPT_SGA, '\0' };
const char wont_suppress_ga_str[] =
    { ( char ) IAC, ( char ) WONT, TELOPT_SGA, '\0' };
#endif

#if	defined(cbuilder)
extern void AddUser( CHAR_DATA * ch );
extern void RemoveUser( CHAR_DATA * ch );
extern bool MudDown;
#endif

/*
 * OS-dependent declarations.
 */
#if     defined(_AIX)
#include <sys/select.h>
int accept args( ( int s, struct sockaddr * addr, int *addrlen ) );
int bind args( ( int s, struct sockaddr * name, int namelen ) );
void bzero args( ( char *b, int length ) );
int getpeername args( ( int s, struct sockaddr * name, int *namelen ) );
int getsockname args( ( int s, struct sockaddr * name, int *namelen ) );
int gettimeofday args( ( struct timeval * tp, struct timezone * tzp ) );
int listen args( ( int s, int backlog ) );
int setsockopt args( ( int s, int level, int optname, void *optval,
                       int optlen ) );
int socket args( ( int domain, int type, int protocol ) );
#endif

#if     defined(apollo)
#include <unistd.h>
void bzero args( ( char *b, int length ) );
#endif

#if     defined(__hpux)
int accept args( ( int s, void *addr, int *addrlen ) );
int bind args( ( int s, const void *addr, int addrlen ) );
void bzero args( ( char *b, int length ) );
int getpeername args( ( int s, void *addr, int *addrlen ) );
int getsockname args( ( int s, void *name, int *addrlen ) );
int gettimeofday args( ( struct timeval * tp, struct timezone * tzp ) );
int listen args( ( int s, int backlog ) );
int setsockopt args( ( int s, int level, int optname,
                       const void *optval, int optlen ) );
int socket args( ( int domain, int type, int protocol ) );
#endif

#if     defined(interactive)
#include <net/errno.h>
#include <sys/fnctl.h>
#endif

#if     defined(linux)
int close args( ( int fd ) );
/* int     getpeername     args( ( int s, struct sockaddr *name, int *namelen ) ); */
/* int     getsockname     args( ( int s, struct sockaddr *name, int *namelen ) ); */
int gettimeofday args( ( struct timeval * tp, struct timezone * tzp ) );
/* int     listen          args( ( int s, int backlog ) ); */
int select args( ( int width, fd_set * readfds, fd_set * writefds,
                   fd_set * exceptfds, struct timeval * timeout ) );
int socket args( ( int domain, int type, int protocol ) );
#endif

#if     defined(MIPS_OS)
extern int errno;
#endif

#if     defined(NeXT)
int close args( ( int fd ) );
int fcntl args( ( int fd, int cmd, int arg ) );
#if     !defined(htons)
u_short htons args( ( u_short hostshort ) );
#endif
#if     !defined(ntohl)
u_long ntohl args( ( u_long hostlong ) );
#endif
int read args( ( int fd, char *buf, int nbyte ) );
int select args( ( int width, fd_set * readfds, fd_set * writefds,
                   fd_set * exceptfds, struct timeval * timeout ) );
int write args( ( int fd, char *buf, int nbyte ) );
#endif

#if     defined(sequent)
int accept args( ( int s, struct sockaddr * addr, int *addrlen ) );
int bind args( ( int s, struct sockaddr * name, int namelen ) );
int close args( ( int fd ) );
int fcntl args( ( int fd, int cmd, int arg ) );
int getpeername args( ( int s, struct sockaddr * name, int *namelen ) );
int getsockname args( ( int s, struct sockaddr * name, int *namelen ) );
int gettimeofday args( ( struct timeval * tp, struct timezone * tzp ) );
#if     !defined(htons)
u_short htons args( ( u_short hostshort ) );
#endif
int listen args( ( int s, int backlog ) );
#if     !defined(ntohl)
u_long ntohl args( ( u_long hostlong ) );
#endif
int read args( ( int fd, char *buf, int nbyte ) );
int select args( ( int width, fd_set * readfds, fd_set * writefds,
                   fd_set * exceptfds, struct timeval * timeout ) );
int setsockopt args( ( int s, int level, int optname, caddr_t optval,
                       int optlen ) );
int socket args( ( int domain, int type, int protocol ) );
int write args( ( int fd, char *buf, int nbyte ) );
#endif

/* This includes Solaris Sys V as well */
#if defined(sun)
int accept args( ( int s, struct sockaddr * addr, int *addrlen ) );
int bind args( ( int s, struct sockaddr * name, int namelen ) );
void bzero args( ( char *b, int length ) );
int close args( ( int fd ) );
int getpeername args( ( int s, struct sockaddr * name, int *namelen ) );
int getsockname args( ( int s, struct sockaddr * name, int *namelen ) );
int gettimeofday args( ( struct timeval * tp, struct timezone * tzp ) );
int listen args( ( int s, int backlog ) );
int read args( ( int fd, char *buf, int nbyte ) );
int select args( ( int width, fd_set * readfds, fd_set * writefds,
                   fd_set * exceptfds, struct timeval * timeout ) );
#if defined(SYSV)
int setsockopt args( ( int s, int level, int optname,
                       const char *optval, int optlen ) );
#else
int setsockopt args( ( int s, int level, int optname, void *optval,
                       int optlen ) );
#endif
int socket args( ( int domain, int type, int protocol ) );
int write args( ( int fd, char *buf, int nbyte ) );
#endif

#if defined(ultrix)
int accept args( ( int s, struct sockaddr * addr, int *addrlen ) );
int bind args( ( int s, struct sockaddr * name, int namelen ) );
void bzero args( ( char *b, int length ) );
int close args( ( int fd ) );
int getpeername args( ( int s, struct sockaddr * name, int *namelen ) );
int getsockname args( ( int s, struct sockaddr * name, int *namelen ) );
int gettimeofday args( ( struct timeval * tp, struct timezone * tzp ) );
int listen args( ( int s, int backlog ) );
int read args( ( int fd, char *buf, int nbyte ) );
int select args( ( int width, fd_set * readfds, fd_set * writefds,
                   fd_set * exceptfds, struct timeval * timeout ) );
int setsockopt args( ( int s, int level, int optname, void *optval,
                       int optlen ) );
int socket args( ( int domain, int type, int protocol ) );
int write args( ( int fd, char *buf, int nbyte ) );
#endif

/*
 * Global variables.
 */
DESCRIPTOR_DATA *descriptor_free = 0;   /* Free list for descriptors    */
DESCRIPTOR_DATA *descriptor_list = 0;   /* All open descriptors         */
LAST_DATA *flast = 0;
LAST_DATA *last_list = 0;
LAST_DATA *flast_imm = 0;
LAST_DATA *last_imm = 0;
LAST_DATA *flast_admin = 0;
LAST_DATA *last_admin = 0;
LAST_DATA *flast_hero = 0;
LAST_DATA *last_hero = 0;
DESCRIPTOR_DATA *d_next = 0;    /* Next descriptor in loop       */
FILE *fpReserve = 0;            /* Reserved file handle          */
bool god;                       /* All new chars are gods!       */
bool merc_down;                 /* Shutdown                      */
bool wizlock;                   /* Game is wizlocked             */
bool newlock;                   /* Game is newlocked             */
bool chaos;                     /* Game in CHAOS!                */
bool reap_shells = FALSE;
bool silentmode;                /* Send no output to descriptors */
char str_boot_time[MAX_INPUT_LENGTH];
time_t current_time;            /* time of this pulse */
bool rolled = FALSE;
int stat1[5], stat2[5], stat3[5], stat4[5], stat5[5];
bool fCopyOver;
int port;
bool MOBtrigger;
int control;
#ifdef HEAVY_DEBUG
char last_file[MAX_INPUT_LENGTH];
#endif

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

/*
 * Other local functions (OS-independent).
 */
bool check_parse_name args( ( char *name ) );
bool check_reconnect args( ( DESCRIPTOR_DATA * d, char *name, bool fConn ) );
bool check_playing args( ( DESCRIPTOR_DATA * d, char *name ) );
#if defined(cbuilder)
int embermain args( ( int argc, char **argv ) );
#else
int main args( ( int argc, char **argv ) );
#endif
void nanny args( ( DESCRIPTOR_DATA * d, char *argument ) );
bool process_output args( ( DESCRIPTOR_DATA * d, bool fPrompt ) );
void read_from_buffer args( ( DESCRIPTOR_DATA * d, bool color ) );
void stop_idling args( ( CHAR_DATA * ch ) );
char *doparseprompt args( ( CHAR_DATA * ch ) );
int roll_stat args( ( int base_bonus, int stat_max ) );

bool can_read_descriptor( int fd )
{
    struct timeval tzero;
    fd_set R_SET;

    tzero.tv_sec = 0;
    tzero.tv_usec = 0;
    FD_ZERO( &R_SET );
    FD_SET( fd, &R_SET );
    while ( select( fd + 1, &R_SET, 0, 0, &tzero ) < 0 )
    {
#ifndef WIN32
        if ( errno == EINTR )
        {
            continue;
        }
        return FALSE;
#else
        int err = WSAGetLastError(  );
        fprintf( stderr, "select winsock error\n" );
        return FALSE;
#endif
    }

    return ( bool ) FD_ISSET( fd, &R_SET );
}

int figure_difference( int points )
{
    if ( points >= 28 )
        return ( ( int ) pow( ( double ) points, ( double ) 1.2 ) );
    if ( points < 28 )
        return ( 26 + points );
    return ( 0 );
}

#if defined(cbuilder)
int embermain( int argc, char **argv )
#else
int main( int argc, char **argv )
#endif
{
    struct timeval now_time = { 0, 0 };

    fCopyOver = FALSE;

    /*
     * Memory debugging if needed.
     */
#if defined(MALLOC_DEBUG)
    malloc_debug( 2 );
#endif

    /*
     * Init time.
     */

#if defined(cbuilder)
/*
    if (chdir("..\\area"))
        chdir("..\\..\\area");
*/
#endif

    gettimeofday( &now_time, NULL );
    current_time = ( time_t ) now_time.tv_sec;
    strcpy( str_boot_time, get_curtime(  ) );

    /*
     * Reserve one channel for our use.
     */
    if ( ( fpReserve = fopen( NULL_FILE, "r" ) ) == NULL )
    {
#if defined(cbuilder)
        log_string( "Error: fopen fpReserve" );
        return -1;
#else
        perror( NULL_FILE );
        exit( 1 );
#endif
    }

    /*
     * Get the port number.
     */
    port = 9000;
    fCopyOver = FALSE;
    if ( argc > 1 )
    {
        if ( !is_number( argv[1] ) )
        {
#if defined(cbuilder)
            logf_string( "Usage: %s [port #] %s", argv[0], argv[1] );
            return -1;
#else
            fprintf( stderr, "Usage: %s [port #]\n", argv[0] );
            exit( 1 );
#endif
        }
        else if ( ( port = atoi( argv[1] ) ) <= 1024 )
        {
#if defined(cbuilder)
            log_string( "Port number must be above 1024." );
            return -1;
#else
            fprintf( stderr, "Port number must be above 1024.\n" );
            exit( 1 );
#endif
        }
        if ( ( argc > 2 ) && ( argv[2] && argv[2][0] ) )
        {
            fCopyOver = TRUE;
            control = atoi( argv[3] );
        }
    }

    /*
     * Run the game.
     */

    if ( !fCopyOver )
    {
        control = init_socket( port );
        if ( control == -1 )
        {
            log_string( "Error: init_socket(), shutting down." );
            return -1;
        }
    }
    if ( boot_db(  ) )
    {
        log_string( "Error: boot_db(), shutting down." );
        return -1;
    }
    sprintf( log_buf, "EmberMUD is ready to rock on port %d.", port );
#if defined(cbuilder)
//  SetStatus("EmberMUD Running.");
#endif
    update_last( "boot_db: done", "", "" );
    log_string( log_buf );
    if ( game_loop( control ) )
    {
        log_string( "Error: game_loop(), shutting down." );
#if defined (WIN32)
        closesocket( control );
#else
        close( control );
#endif
        return -1;
    }
#if defined (WIN32)
    closesocket( control );
#else
    close( control );
#endif

    /*
     * That's all, folks.
     */
    log_string( "Normal termination of game." );
#if defined(cbuilder)
    return 0;
#else
    exit( 0 );
    return 0;
#endif
}

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

#if defined(WIN32)
    WSADATA wsaData;

    wsaData.wVersion = 2;

#if defined(cbuilder)
    char wsaError[MAX_STRING_LENGTH];
#endif

    WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
#endif
    if ( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
    {
#if defined(cbuilder)
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(  ), 0,
                       wsaError, MAX_STRING_LENGTH, NULL );
        logf_string( "Init_socket: %s", wsaError );
        merc_down = TRUE;
        return -1;
#else
        perror( "Init_socket: socket" );
        exit( 1 );
#endif
    }

    if ( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
                     ( char * ) &x, sizeof( x ) ) < 0 )
    {
#if defined(cbuilder)
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(  ), 0,
                       wsaError, MAX_STRING_LENGTH, NULL );
        logf_string( "Init_socket: %s", wsaError );
        closesocket( fd );
        merc_down = TRUE;
        return -1;
#else
        perror( "Init_socket: SO_REUSEADDR" );
#if defined(WIN32)
        closesocket( fd );
#else
        close( fd );
#endif
        exit( 1 );
#endif
    }

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

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

        if ( setsockopt( fd, SOL_SOCKET, SO_DONTLINGER,
                         ( char * ) &ld, sizeof( ld ) ) < 0 )
        {
#if defined(cbuilder)
            FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                           WSAGetLastError(  ), 0, wsaError, MAX_STRING_LENGTH,
                           NULL );
            logf_string( "Init_socket: %s", wsaError );
            closesocket( fd );
            merc_down = TRUE;
            return -1;
#else
            perror( "Init_socket: SO_DONTLINGER" );
#if defined(WIN32)
            closesocket( fd );
#else
            close( fd );
#endif
            exit( 1 );
#endif
        }
    }
#endif

    sa = sa_zero;
    sa.sin_family = AF_INET;
    sa.sin_port = htons( ( u_short ) port );

    if ( bind( fd, ( struct sockaddr * ) &sa, sizeof( sa ) ) < 0 )
    {
#if defined(cbuilder)
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(  ), 0,
                       wsaError, MAX_STRING_LENGTH, NULL );
        logf_string( "Init_socket: %s", wsaError );
        closesocket( fd );
        merc_down = TRUE;
        return -1;
#else
        perror( "Init socket: bind" );
#if defined(WIN32)
        closesocket( fd );
#else
        close( fd );
#endif
        exit( 1 );
#endif
    }

    if ( listen( fd, 3 ) < 0 )
    {
#if defined(cbuilder)
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(  ), 0,
                       wsaError, MAX_STRING_LENGTH, NULL );
        logf_string( "Init_socket: %s", wsaError );
        closesocket( fd );
        merc_down = TRUE;
        return -1;
#else
        perror( "Init socket: listen" );
#if defined(WIN32)
        closesocket( fd );
#else
        close( fd );
#endif
        exit( 1 );
#endif
    }

    return fd;
}

#if defined(unix)
void sigchld_handler( int sig )
{
    pid_t child_pid;
    int status;

    if ( ( child_pid = wait( &status ) ) < 0 )
    {
        return;
    }

    reap_shells = TRUE;
    return;
}
#endif

int game_loop( int control )
{
    static struct timeval null_time;
    struct timeval last_time;
    bool color;

#if defined(unix)
    static struct sigaction sa;
    sigemptyset( &sa.sa_mask );
#ifndef SA_RESTART
#define SA_RESTART 0
#endif
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = sigchld_handler;
    if ( sigaction( SIGCHLD, &sa, 0 ) < 0 )
        return -1;
    signal( SIGPIPE, SIG_IGN );
#endif
    gettimeofday( &last_time, NULL );
    current_time = ( time_t ) last_time.tv_sec;

    /* Main loop */
    while ( !merc_down )
    {
        fd_set in_set;
        fd_set out_set;
        fd_set exc_set;
        DESCRIPTOR_DATA *d;
        int maxdesc;

#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( control, &in_set );
        maxdesc = control;
        for ( d = descriptor_list; d; d = d->next )
        {
            maxdesc = UMAX( maxdesc, d->descriptor );
            FD_SET( d->descriptor, &in_set );
            FD_SET( d->descriptor, &out_set );
            FD_SET( d->descriptor, &exc_set );
        }

        if ( select( maxdesc + 1, &in_set, &out_set, &exc_set, &null_time ) <
             0 )
        {
#if defined(cbuilder)
            log_string( "Error: Game_loop: select: poll" );
            return -1;
#else
            perror( "Game_loop: select: poll" );
            exit( 1 );
#endif
        }

#ifndef WIN32
        /* Guess this is as good a place as any for shell reaping? */
        if ( reap_shells )
        {
            CHAR_DATA *ch;
            /* No one in the shell list?  Then don't reap shells anymore! */
            if ( !shell_char_list )
            {
                reap_shells = FALSE;
            }
            else
            {
                /* Check the "in" pipe for each character in the list.
                 * If any of them are readable, put them back into the game. */
                for ( ch = shell_char_list; ch; ch = ch->next_in_shell )
                {
                    CHAR_DATA *prev;

                    if ( can_read_descriptor( ch->fdpair[0] ) )
                    {
                        close( ch->fdpair[0] );
                        if ( ch == shell_char_list )
                        {
                            shell_char_list = NULL;
                        }
                        else
                        {
                            for ( prev = shell_char_list;
                                  prev && prev->next != ch; prev = prev->next );

                            if ( prev )
                            {
                                prev->next = ch->next;
                            }
                            else
                            {
                                bug( "reap_shell: reaped char not found in the shell_char_list", 0 );
                            }
                        }
                        ch->next = char_list;
                        char_list = ch;
                        ch->next_player = player_list;
                        ch->next_in_shell = NULL;
                        player_list = ch;
                        send_to_char( "MUD I/O resumed.\n\r", ch );
                        ch->desc->connected = CON_PLAYING;
                        continue;
                    }
                }
            }
        }
#endif

        /*
         * New connection?
         */
        if ( FD_ISSET( control, &in_set ) )
            new_descriptor( control );

        /*
         * Kick out the freaky folks.
         */
        for ( d = descriptor_list; d != NULL; d = d_next )
        {
            d_next = d->next;
            if ( FD_ISSET( d->descriptor, &exc_set ) )
            {
                FD_CLR( ( unsigned int ) d->descriptor, &in_set );
                FD_CLR( ( unsigned int ) d->descriptor, &out_set );
                if ( d->character && d->character->level > 1 )
                    save_char_obj( d->character );
#if defined(cbuilder)
                if ( d->character && d->character->TNode )
                    RemoveUser( d->character );
#endif
                d->outtop = 0;
                close_socket( d );
            }
        }

        /*
         * Process input.
         */
        for ( d = descriptor_list; d != NULL; d = d_next )
        {
            d_next = d->next;
            d->fcommand = FALSE;

            if ( FD_ISSET( d->descriptor, &in_set ) )
            {
                if ( d->character != NULL )
                {
                    d->character->timer = 0;
                    if ( IS_SET( d->character->act, PLR_COLOR ) )
                        color = TRUE;
                    else
                        color = FALSE;
                }
                else
                    color = FALSE;
                if ( !read_from_descriptor( d, color ) )
                {
                    FD_CLR( ( unsigned int ) d->descriptor, &out_set );
                    if ( d->character != NULL && d->character->level > 1 )
                        save_char_obj( d->character );
#if defined(cbuilder)
                    if ( d->character && d->character->TNode )
                        RemoveUser( d->character );
#endif
                    d->outtop = 0;
                    close_socket( d );
                    continue;
                }
            }

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

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

                /* OLC */
                if ( d->showstr_point )
                    show_string( d, d->incomm );
                else if ( d->pString )
                    string_add( d->character, d->incomm );
                else
                    switch ( d->connected )
                    {
                    case CON_PLAYING:
                        if ( !run_olc_editor( d ) )
                            substitute_alias( d, d->incomm );
                        break;
                    default:
                        nanny( d, d->incomm );
                        break;
                    }

                d->incomm[0] = '\0';
            }
        }

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

        /*
         * Output.
         */
        for ( d = descriptor_list; d != NULL; d = d_next )
        {
            d_next = d->next;

            if ( ( d->fcommand || d->outtop > 0 )
                 && FD_ISSET( d->descriptor, &out_set ) )
            {
                if ( !process_output( d, TRUE ) )
                {
                    if ( d->character != NULL && d->character->level > 1 )
                        save_char_obj( d->character );
#if defined(cbuilder)
                    if ( d->character && d->character->TNode )
                        RemoveUser( d->character );
#endif
                    d->outtop = 0;
                    close_socket( d );
                }
            }
        }

#if defined(cbuilder)
        /*
         * Checks for GUI commands, updates GUI stats.
         */
        update_gui(  );
#endif

        /*
         * 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 );
#if defined(WIN32)
            if ( usecDelta > 0 )
                Sleep( UMIN( usecDelta, 250000 ) / 1000 );
#else
            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;
                if ( select( 0, NULL, NULL, NULL, &stall_time ) < 0 )
                {
#if defined(cbuilder)
                    log_string( "Error: Game_loop: select: stall" );
                    return -1;
#else
                    if ( errno != EINTR )
                    {
                        perror( "Game_loop: select: stall" );
                        exit( 1 );
                    }

#endif
                }
            }
#endif
        }

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

    return 0;
}

void new_descriptor( int control )
{
    char buf[MAX_STRING_LENGTH];
    static DESCRIPTOR_DATA d_zero;
    DESCRIPTOR_DATA *dnew;
    struct sockaddr_in sock;
#ifndef NO_RDNS
    struct hostent *from;
#endif
    int desc;
    int size;
#if defined(WIN32)
    int OptVal;
#endif

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

#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif

#if defined(WIN32)
    if ( setsockopt( desc, IPPROTO_TCP, TCP_NODELAY, ( char * ) &OptVal,
                     sizeof( int ) ) )
    {
        perror( "New_descriptor: setsockopt: TCP_NODELAY" );
        return;
    }
#else
    if ( fcntl( desc, F_SETFL, FNDELAY ) == -1 )
    {
        perror( "New_descriptor: fcntl: FNDELAY" );
        return;
    }
#endif

    /*
     * Cons a new descriptor.
     */
    if ( descriptor_free == NULL )
    {
        dnew = alloc_perm( sizeof( *dnew ) );
    }
    else
    {
        dnew = descriptor_free;
        descriptor_free = descriptor_free->next;
    }

    *dnew = d_zero;
    dnew->descriptor = desc;
    dnew->connected = CON_GET_ANSI;
    dnew->showstr_head = NULL;
    dnew->showstr_point = NULL;
    dnew->outsize = 2000;
    dnew->pEdit = NULL;         /* OLC */
    dnew->pString = NULL;       /* OLC */
    dnew->editor = 0;           /* OLC */
    dnew->outbuf = alloc_mem( dnew->outsize );

    size = sizeof( sock );
    if ( getpeername( desc, ( struct sockaddr * ) &sock, &size ) < 0 )
    {
        perror( "New_descriptor: getpeername" );
        dnew->host = str_dup( "(unknown)" );
    }
    else
    {
        /*
         * Would be nice to use inet_ntoa here but it takes a struct arg,
         * which ain't very compatible between gcc and system libraries.
         */
        int addr;

        addr = ntohl( sock.sin_addr.s_addr );
        sprintf( buf, "%d.%d.%d.%d",
                 ( addr >> 24 ) & 0xFF, ( addr >> 16 ) & 0xFF,
                 ( addr >> 8 ) & 0xFF, ( addr ) & 0xFF );
        sprintf( log_buf, "Sock.sinaddr:  %s", buf );
        log_string( log_buf );

        /* 
         * If NO_RDNS is defined don't try to look up the host name by IP.
         * Documented in config.h
         */
#ifndef NO_RDNS
        from = gethostbyaddr( ( char * ) &sock.sin_addr,
                              sizeof( sock.sin_addr ), AF_INET );
        if ( from && ( !str_cmp( from->h_name, "ursula.uoregon.edu" )
                       || !str_cmp( from->h_name, "monet.ucdavis.edu" ) ) )
            dnew->host = str_dup( "white.nextwork.rose-hulman.edu" );
        else
            dnew->host = str_dup( from ? from->h_name : buf );
#else
        dnew->host = str_dup( buf );
#endif
    }

    /*
     * Swiftest: I added the following to ban sites.  I don't
     * endorse banning of sites, but Copper has few descriptors now
     * and some people from certain sites keep abusing access by
     * using automated 'autodialers' and leaving connections hanging.
     *
     * Furey: added suffix check by request of Nickel of HiddenWorlds.
     */

    /*
     * Init descriptor data.
     */
    dnew->next = descriptor_list;
    descriptor_list = dnew;

    write_to_buffer( dnew, CFG_CONNECT_MSG, 0 );
    write_to_buffer( dnew, CFG_ASK_ANSI, 0 );

    return;
}

void close_socket( DESCRIPTOR_DATA * dclose )
{
    CHAR_DATA *ch;

    if ( dclose->outtop > 0 )
        process_output( dclose, FALSE );

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

    {
        DESCRIPTOR_DATA *d;

        for ( d = descriptor_list; d != NULL; d = d->next )
        {
            if ( d->snoop_by == dclose )
                d->snoop_by = NULL;
        }
    }

    if ( ( ch = dclose->character ) != NULL )
    {
        /* If ch is switched, unswitch them */
        if ( dclose->original && dclose->original != ch )
        {
            dclose->character = dclose->original;
            dclose->original = NULL;
            dclose->character->desc = dclose;
            ch = dclose->character;
        }

        sprintf( log_buf, "Closing link to %s.", ch->name );
        log_string( log_buf );

        /* If ch is writing note or playing, just lose link otherwise clear char */
        if ( ( dclose->connected == CON_PLAYING ) ||
             ( ( dclose->connected >= CON_NOTE_TO ) &&
               ( dclose->connected <= CON_NOTE_FINISH ) ) )
        {
            act( "$n has lost $s link.", ch, NULL, NULL, TO_ROOM );
            ch->pcdata->ticks = 1;
            if ( IS_SET( ch->act, PLR_BUILDING ) );
            {
                REMOVE_BIT( ch->act, PLR_BUILDING );
            }

            ch->desc = NULL;
        }
        else
        {
            free_char( dclose->character );
        }
    }

    if ( d_next == dclose )
        d_next = d_next->next;

    if ( dclose == descriptor_list )
    {
        descriptor_list = descriptor_list->next;
    }
    else
    {
        DESCRIPTOR_DATA *d;

        for ( d = descriptor_list; d && d->next != dclose; d = d->next )
            ;
        if ( d != NULL )
            d->next = dclose->next;
        else
            bug( "Close_socket: dclose not found.", 0 );
    }

#if defined(WIN32)
    closesocket( dclose->descriptor );
#else
    close( dclose->descriptor );
#endif
    free_string( &dclose->host );
    /* RT socket leak fix -- I hope */
    free_mem( &dclose->outbuf );
    dclose->next = descriptor_free;
    descriptor_free = dclose;
    return;
}

bool read_from_descriptor( DESCRIPTOR_DATA * d, bool color )
{
    int iStart;
    bool bOverflow = FALSE;

    /* Don't read from people in shells */
    if ( d->connected == CON_SHELL )
        return TRUE;

    /* 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\r", 0, color );
        return FALSE;
    }

    /* Snarf input. */

    for ( ;; )
    {
        int nRead;
        int nBufSize;

        nBufSize = sizeof( d->inbuf ) - 10 - iStart;

#if defined(WIN32)
        nRead = recv( d->descriptor, d->inbuf + iStart, nBufSize, 0 );
#else
        nRead = read( d->descriptor, d->inbuf + iStart, nBufSize );
#endif
        if ( nRead > 0 )
        {
            iStart += nRead;
            if ( bOverflow )
            {
                iStart = 0;
                if ( nRead < sizeof( d->inbuf ) - 10 )
                    break;
            }
            else if ( d->inbuf[iStart - 1] == '\n'
                      || d->inbuf[iStart - 1] == '\r' || nRead < nBufSize )
                /* If we hit a CR/LF or read less than the buffer size then return */
                break;
            else if ( iStart >= sizeof( d->inbuf ) - 10 )
            {
                if ( iStart - nRead > 0 )
                    iStart -= nRead;
                else
                    iStart = 0;
                bOverflow = TRUE;
            }
        }
        else if ( nRead == 0 )
        {
            log_string( "EOF encountered on read." );
            return FALSE;
        }
#if defined(unix)
        else if ( errno == EWOULDBLOCK )
            break;
#endif
        else
        {
            perror( "Read_from_descriptor" );
            return FALSE;
        }
    }

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

    if ( bOverflow )
    {
        sprintf( log_buf, "%s input overflow!", d->host );
        log_string( log_buf );
        write_to_descriptor( d->descriptor,
                             "\n\rLine too long, ignored.\n\r", 0, color );
    }

    return TRUE;
}

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

    /* Don't read from people in shells. */
    if ( d->connected == CON_SHELL )
        return;

    /*
     * 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++ )
    {
        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 >= MAX_INPUT_LENGTH - 2 )
        {
            write_to_descriptor( d->descriptor, "Line too long.\n\r", 0,
                                 color );

            /* skip the rest of the line */
            for ( ; d->inbuf[i] != '\0'; 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;
	}
	else
	{
	    if ( ++d->repeat >= 25 )
	    {
		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\r", 0 , color);
		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 process_output( DESCRIPTOR_DATA * d, bool fPrompt )
{
    char buf[MAX_STRING_LENGTH];
    extern bool merc_down;
    bool color = TRUE;

    /* If you're in a shell, then no output for you! */
    if ( d->connected == CON_SHELL )
        return TRUE;

    /*
     * Bust a prompt.
     */
    if ( ( d->character != NULL ) && IS_SET( d->character->act, PLR_COLOR ) )
        color = TRUE;
    else
        color = FALSE;

    if ( !merc_down )
    {
        if ( d->showstr_point )
            write_to_buffer( d, "`W\n\r[Hit Return to continue]\n\r`0", 0 );
        else if ( fPrompt && d->pString && d->connected == CON_PLAYING )
            write_to_buffer( d, "> ", 2 );
        else if ( fPrompt && d->connected == CON_PLAYING )
        {
            CHAR_DATA *ch;

            ch = d->original ? d->original : d->character;
            if ( !IS_SET( ch->comm, COMM_COMPACT ) )
                write_to_buffer( d, "\n\r", 2 );

            if ( IS_SET( ch->comm, COMM_PROMPT ) )
            {
                ch = d->character;
                if ( !IS_NPC( ch ) )
                    sprintf( buf, "%s", doparseprompt( ch ) );
                else
                    sprintf( buf, "<H%d/%d M%d/%d V%d/%d>", ch->hit,
                             ch->max_hit, ch->mana, ch->max_mana, ch->move,
                             ch->max_move );
                write_to_buffer( d, buf, 0 );
            }

            if ( IS_SET( ch->comm, COMM_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 != NULL )
    {
        if ( d->character != NULL )
            write_to_buffer( d->snoop_by, d->character->name, 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, color ) )
    {
        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 )
{
    extern bool silentmode;

    /* Don't do ANYTHING if global silentmode is on. */
    if ( silentmode )
        return;

    /*
     * Find length in case caller didn't.
     */
    if ( length <= 0 )
        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 )
    {
        char *outbuf;

        if ( d->outsize + 2048 > MAX_OUTPUT_BUFFER )
            return;

        outbuf = alloc_mem( d->outsize + 2048 );
        strncpy( outbuf, d->outbuf, d->outtop );
        free_mem( &d->outbuf );
        d->outbuf = outbuf;
        d->outsize += 2048;
    }

    /*
     * Copy.
     */

    strcpy( d->outbuf + d->outtop, txt );
    d->outtop += length;
    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.
 */
bool write_to_descriptor( int desc, char *txt, int length, bool color )
{
    int iStart;
    int nWrite;
    int nBlock;
    static char colorbuf[MAX_OUTPUT_BUFFER];

    /* Run through the color filter */
    do_color( txt, length, colorbuf, sizeof( colorbuf ), color );
    length = strlen( colorbuf );

    for ( iStart = 0; iStart < length; iStart += nWrite )
    {
        nBlock = UMIN( length - iStart, sizeof( colorbuf ) );
#if defined(WIN32)
        if ( ( nWrite = send( desc, colorbuf + iStart, nBlock, 0 ) ) < 0 )
        {
            perror( "Write_to_descriptor" );
            return FALSE;
        }
#else
        if ( ( nWrite = write( desc, colorbuf + iStart, nBlock ) ) < 0 )
        {
            perror( "Write_to_descriptor" );
            return FALSE;
        }
#endif
    }

    return TRUE;
}

/*
 * Deal with sockets that haven't logged in yet.
 */
void nanny( DESCRIPTOR_DATA * d, char *argument )
{
    char buf[MAX_STRING_LENGTH];
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *ch;
    BAN_DATA *pban;
    int ban_type;
    char *pwdnew;
    char *p;
    int iClass, race, i, x;
    bool fOld;
    DESCRIPTOR_DATA *d_old;

    /* Delete leading spaces UNLESS character is writing a note */
    if ( d->connected != CON_NOTE_TEXT )
    {
        while ( isspace( *argument ) )
            argument++;
    }

    ch = d->character;
    update_last( "Nanny:", ch ? ch->name : "new", argument );

    switch ( d->connected )
    {

    default:
        bug( "Nanny: bad d->connected %d.", d->connected );
#if defined(cbuilder)
        if ( d->character && d->character->TNode )
            RemoveUser( d->character );
#endif
        close_socket( d );
        return;

    case CON_GET_ANSI:

        if ( ( argument[0] == 'n' ) || ( argument[0] == 'N' ) )
            d->ansi = FALSE;
        else
            d->ansi = TRUE;

        /*
         * Send the greeting.
         */
        {
            extern char *help_greeting;
            extern char *ansi_greeting;

            if ( d->ansi )
                write_to_buffer( d, ansi_greeting, 0 );
            else
            {
                if ( help_greeting[0] == '.' )
                    write_to_buffer( d, help_greeting + 1, 0 );
                else
                    write_to_buffer( d, help_greeting, 0 );
            }
        }
        d->connected = CON_GET_NAME;
        return;

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

        argument[0] = UPPER( argument[0] );
        if ( !check_parse_name( argument ) )
        {
            write_to_buffer( d, "Illegal name, try another.\n\rName: ", 0 );
            return;
        }

        fOld = load_char_obj( d, argument );
        ch = d->character;

/* I just could NOT get the check_ban function from the rom ban.c file to
work, so I got fed up with it and wrote some new ban checks. They are not
the best in the world, but it was alot quicker then writting a new
check_ban function.  
		-Lancelight 
*/

        for ( pban = ban_list; pban != NULL; pban = pban->next )
        {
            ban_type = pban->ban_flags;
            if ( !str_suffix( ( pban->name ), d->host ) )
            {
                if ( IS_SET( pban->ban_flags, BAN_PERMANENT )
                     || IS_SET( pban->ban_flags, BAN_ALL ) )
                {
                    write_to_buffer( d,
                                     "Your site has been banned from this mud.\n\r",
                                     0 );
                    close_socket( d );
                    return;
                }
            }

        }
        for ( pban = ban_list; pban != NULL; pban = pban->next )
        {
            ban_type = pban->ban_flags;
            if ( !str_suffix( ( pban->name ), d->host ) )
            {
                if ( ban_type == 16 && !IS_SET( ch->act, PLR_PERMIT ) )
                {
                    sprintf( buf,
                             "You do not have permission to play on this mud.\n\r Please email %s to get permission to play here.",
                             MUD_ADMIN_EMAIL );
                    send_to_char( buf, ch );
                    close_socket( d );
                    return;
                }
            }

        }
        if ( IS_SET( ch->act, PLR_DENY ) )
        {
            sprintf( log_buf, "Denying access to %s@%s.", argument, d->host );
            log_string( log_buf );
            write_to_buffer( d, "You are denied access.\n\r", 0 );
            close_socket( d );
            return;
        }

        if ( check_reconnect( d, argument, FALSE ) )
        {
            fOld = TRUE;
        }
        else
        {
            if ( wizlock && !IS_HERO( ch ) )
            {
                write_to_buffer( d, "The game is wizlocked.\n\r", 0 );
                close_socket( d );
                return;
            }
            else if ( chaos && !IS_HERO( ch ) )
            {
                write_to_buffer( d, "The game is in CHAOS!\n\r", 0 );
                close_socket( d );
                return;
            }
        }

        if ( fOld )
        {
            write_to_buffer( d, "Password: ", 0 );
            write_to_buffer( d, echo_off_str, 0 );
            d->connected = CON_GET_OLD_PASSWORD;
            return;
        }
        else
        {
            /* New player */
            if ( newlock )
            {
                write_to_buffer( d, "The game is newlocked.\n\r", 0 );
                close_socket( d );
                return;
            }
            for ( pban = ban_list; pban != NULL; pban = pban->next )
            {
                ban_type = pban->ban_flags;
                if ( !str_suffix( ( pban->name ), d->host ) )
                {
                    if ( ban_type == 4 )
                    {
                        write_to_buffer( d,
                                         "New players from your site have been banned from this mud.\n\r",
                                         0 );
                        close_socket( d );
                        return;
                    }
                }

            }
/*
    for ( pban = ban_list; pban != NULL; pban = pban->next )
    {
	if ( !str_suffix( pban->name, d->host ) && 
	     (pban->ban_flags,BAN_NEWBIES))
	    {
		write_to_buffer(d,
		    "New players are not allowed from your site.\n\r",0);
		close_socket(d);
		return;
	    }
	else 
		write_to_buffer(d,
		    "I dont work.\n\r",0);
	
	
}*/
            sprintf( buf, "Did I get that right, %s (Y/N)? ", argument );
            write_to_buffer( d, buf, 0 );
            d->connected = CON_CONFIRM_NEW_NAME;
            return;
        }

        if ( check_playing( d, ch->name ) )
            return;

        break;

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

        if ( strcmp( crypt( argument, ch->pcdata->pwd ), ch->pcdata->pwd ) )
        {
            write_to_buffer( d, "Wrong password.\n\r", 0 );
            close_socket( d );
            return;
        }

        if ( ch->pcdata->pwd[0] == 0 )
        {
            write_to_buffer( d, "Warning! Null password!\n\r", 0 );
            write_to_buffer( d, "Please report old password with bug.\n\r", 0 );
            write_to_buffer( d,
                             "Type 'password null <new password>' to fix.\n\r",
                             0 );
        }

        write_to_buffer( d, echo_on_str, 0 );

        if ( check_reconnect( d, ch->name, TRUE ) )
            return;

        if ( check_playing( d, ch->name ) )
            return;

        sprintf( log_buf, "%s@%s has connected.", ch->name, d->host );
        log_string( log_buf );
        if ( IS_SET( ch->act, PLR_BUILDING ) );
        {
            REMOVE_BIT( ch->act, PLR_BUILDING );
        }

        if ( IS_HERO( ch ) )
        {
            do_help( ch, "imotd" );
            d->connected = CON_READ_IMOTD;
        }
        else
        {
            do_help( ch, "motd" );
            d->connected = CON_READ_MOTD;
        }
        break;

/* RT's code for breaking link sucked... you could cheat and duplicate all your gear. 
   This version disconnects the old character and prompts you to re-enter your name 
   password.  This way you won't have loaded the pfile containing objects and such
   which you may have dropped on the ground, resulting in two copies of any object,
   even special or quest objects. -- Kyle */

    case CON_BREAK_CONNECT:
        switch ( *argument )
        {
        case 'y':
        case 'Y':
            for ( d_old = descriptor_list; d_old != NULL; d_old = d_next )
            {
                d_next = d_old->next;
                if ( d_old == d || d_old->character == NULL )
                    continue;

                if ( str_cmp( ch->name, d_old->character->name ) )
                    continue;

                close_socket( d_old );
                if ( d->character != NULL )
                {
                    free_char( d->character );
                    d->character = NULL;
                }
                d->connected = CON_GET_NAME;
                break;
            }

            write_to_buffer( d, "Disconnected.   Re-enter name: ", 0 );
            if ( d->character != NULL )
            {
                free_char( d->character );
                d->character = NULL;
            }
            d->connected = CON_GET_NAME;
            break;

        case 'n':
        case 'N':
            write_to_buffer( d, "Name: ", 0 );
            if ( d->character != NULL )
            {
                free_char( d->character );
                d->character = NULL;
            }
            d->connected = CON_GET_NAME;
            break;

        default:
            write_to_buffer( d, "Please type Y or N? ", 0 );
            break;
        }
        break;

    case CON_CONFIRM_NEW_NAME:
        switch ( *argument )
        {
        case 'y':
        case 'Y':
            sprintf( buf, "New character.\n\rGive me a password for %s: %s",
                     ch->name, echo_off_str );
            write_to_buffer( d, buf, 0 );
            d->connected = CON_GET_NEW_PASSWORD;
            if ( d->ansi )
                SET_BIT( ch->act, PLR_COLOR );
            break;

        case 'n':
        case 'N':
            write_to_buffer( d, "Ok, what IS it, then? ", 0 );
            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;
        }

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

        free_string( &ch->pcdata->pwd );
        ch->pcdata->pwd = str_dup( pwdnew );
        write_to_buffer( d, "Please retype password: ", 0 );
        d->connected = CON_CONFIRM_NEW_PASSWORD;
        break;

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

        if ( strcmp( crypt( argument, ch->pcdata->pwd ), 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, "The following races are available:\n\r  ", 0 );
        for ( race = 1; race_table[race].name != NULL; race++ )
        {
            if ( !race_table[race].pc_race )
                break;
            if ( !race_table[race].remort_race ||
                 ( race_table[race].remort_race
                   && IS_SET( ch->act, PLR_REMORT ) ) )
            {

                write_to_buffer( d, race_table[race].name, 0 );
                write_to_buffer( d, " ", 1 );
            }

        }
        write_to_buffer( d, "\n\r", 0 );
        write_to_buffer( d, "What is your race (help for more information)? ",
                         0 );
        d->connected = CON_GET_NEW_RACE;
        break;

    case CON_GET_NEW_RACE:
        one_argument( argument, arg );

        if ( !strcmp( arg, "help" ) )
        {
            argument = one_argument( argument, arg );
            if ( argument[0] == '\0' )
                do_help( ch, "race help" );
            else
                do_help( ch, argument );
            write_to_buffer( d,
                             "What is your race (help for more information)? ",
                             0 );
            break;
        }

        race = race_lookup( argument );

        if ( race == 0 || !race_table[race].pc_race
             || ( race_table[race].remort_race
                  && !IS_SET( ch->act, PLR_REMORT ) ) )
        {
            write_to_buffer( d, "That is not a valid race.\n\r", 0 );
            write_to_buffer( d, "The following races are available:\n\r  ", 0 );
            for ( race = 1; race_table[race].name != NULL; race++ )
            {
                if ( !race_table[race].pc_race )
                    break;
                if ( !race_table[race].remort_race ||
                     ( race_table[race].remort_race
                       && IS_SET( ch->act, PLR_REMORT ) ) )
                {
                    write_to_buffer( d, race_table[race].name, 0 );
                    write_to_buffer( d, " ", 1 );
                }
            }
            write_to_buffer( d, "\n\r", 0 );
            write_to_buffer( d,
                             "What is your race? (help for more information) ",
                             0 );
            break;
        }

        ch->race = race;
        /* initialize stats */
        for ( i = 0; i < MAX_STATS; i++ )
            ch->perm_stat[i] = pc_race_table[race].stats[i];
        ch->affected_by = ch->affected_by | race_table[race].aff;
        ch->imm_flags = ch->imm_flags | race_table[race].imm;
        ch->res_flags = ch->res_flags | race_table[race].res;
        ch->vuln_flags = ch->vuln_flags | race_table[race].vuln;
        ch->form = race_table[race].form;
        ch->parts = race_table[race].parts;

        /* add skills */
        for ( i = 0; i < 5; i++ )
        {
            if ( pc_race_table[race].skills[i] == NULL )
                break;
            group_add( ch, pc_race_table[race].skills[i], FALSE );
        }
        /* add cost */
        ch->pcdata->points = pc_race_table[race].points;
        ch->size = pc_race_table[race].size;
        write_to_buffer( d, "What is your sex (M/F)? ", 0 );
        d->connected = CON_GET_NEW_SEX;
        break;

    case CON_GET_NEW_SEX:
        switch ( argument[0] )
        {
        case 'm':
        case 'M':
            ch->sex = SEX_MALE;
            ch->pcdata->true_sex = SEX_MALE;
            break;
        case 'f':
        case 'F':
            ch->sex = SEX_FEMALE;
            ch->pcdata->true_sex = SEX_FEMALE;
            break;
        default:
            write_to_buffer( d, "That's not a sex.\n\rWhat IS your sex? ", 0 );
            return;
        }
        write_to_buffer( d, "Press enter to start rolling your stats. ", 0 );
        d->connected = CON_GET_STATS;
        break;

    case CON_GET_STATS:
        if ( ( rolled == TRUE ) && atoi( argument ) < 5
             && atoi( argument ) >= 0 )
            switch ( argument[0] )
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
                ch->perm_stat[0] = stat1[atoi( argument )];
                ch->perm_stat[1] = stat2[atoi( argument )];
                ch->perm_stat[2] = stat3[atoi( argument )];
                ch->perm_stat[3] = stat4[atoi( argument )];
                ch->perm_stat[4] = stat5[atoi( argument )];

                strcpy( buf, "Select a class [" );
                for ( iClass = 0; iClass < MAX_CLASS; iClass++ )
                {
                    if ( !class_table[iClass].remort_class ||
                         ( class_table[iClass].remort_class
                           && IS_SET( ch->act, PLR_REMORT ) ) )
                    {
                        if ( iClass > 0
                             && str_cmp( class_table[iClass].name, "Console" ) )
                            strcat( buf, " " );
                        if ( str_cmp( class_table[iClass].name, "Console" ) )
                            strcat( buf, class_table[iClass].name );
                    }
                }
                strcat( buf, "]: " );
                write_to_buffer( d, buf, 0 );
                d->connected = CON_GET_NEW_CLASS;
                break;
            default:
                write_to_buffer( d,
                                 "                       0    1    2    3    4\n\r",
                                 0 );
                write_to_buffer( d, "     Strength     :", 0 );
                for ( x = 0; x < 5; x++ )
                {
                    stat1[x] =
                        roll_stat( pc_race_table[ch->race].stats[0],
                                   pc_race_table[ch->race].max_stats[0] -
                                   REROLL_PENALTY );
                    sprintf( buf, "   %2d", stat1[x] );
                    write_to_buffer( d, buf, 0 );
                }
                write_to_buffer( d, "\n\r     Intelligence :", 0 );
                for ( x = 0; x < 5; x++ )
                {
                    stat2[x] =
                        roll_stat( pc_race_table[ch->race].stats[1],
                                   pc_race_table[ch->race].max_stats[1] -
                                   REROLL_PENALTY );
                    sprintf( buf, "   %2d", stat2[x] );
                    write_to_buffer( d, buf, 0 );
                }
                write_to_buffer( d, "\n\r     Wisdom       :", 0 );
                for ( x = 0; x < 5; x++ )
                {
                    stat3[x] =
                        roll_stat( pc_race_table[ch->race].stats[2],
                                   pc_race_table[ch->race].max_stats[2] -
                                   REROLL_PENALTY );
                    sprintf( buf, "   %2d", stat3[x] );
                    write_to_buffer( d, buf, 0 );
                }
                write_to_buffer( d, "\n\r     Dexterity    :", 0 );
                for ( x = 0; x < 5; x++ )
                {
                    stat4[x] =
                        roll_stat( pc_race_table[ch->race].stats[3],
                                   pc_race_table[ch->race].max_stats[3] -
                                   REROLL_PENALTY );
                    sprintf( buf, "   %2d", stat4[x] );
                    write_to_buffer( d, buf, 0 );
                }
                write_to_buffer( d, "\n\r     Constitution :", 0 );
                for ( x = 0; x < 5; x++ )
                {
                    stat5[x] =
                        roll_stat( pc_race_table[ch->race].stats[4],
                                   pc_race_table[ch->race].max_stats[4] -
                                   REROLL_PENALTY );
                    sprintf( buf, "   %2d", stat5[x] );
                    write_to_buffer( d, buf, 0 );
                }
                write_to_buffer( d,
                                 "\n\r\n\r     Press enter to roll again, else enter number of column: ",
                                 0 );
                rolled = TRUE;
                break;
            }
        else
        {
            write_to_buffer( d,
                             "                       0    1    2    3    4\n\r",
                             0 );
            write_to_buffer( d, "     Strength     :", 0 );
            for ( x = 0; x < 5; x++ )
            {
                stat1[x] =
                    roll_stat( pc_race_table[ch->race].stats[0],
                               pc_race_table[ch->race].max_stats[0] );
                sprintf( buf, "   %2d", stat1[x] );
                write_to_buffer( d, buf, 0 );
            }
            write_to_buffer( d, "\n\r     Intelligence :", 0 );
            for ( x = 0; x < 5; x++ )
            {
                stat2[x] =
                    roll_stat( pc_race_table[ch->race].stats[1],
                               pc_race_table[ch->race].max_stats[1] );
                sprintf( buf, "   %2d", stat2[x] );
                write_to_buffer( d, buf, 0 );
            }
            write_to_buffer( d, "\n\r     Wisdom       :", 0 );
            for ( x = 0; x < 5; x++ )
            {
                stat3[x] =
                    roll_stat( pc_race_table[ch->race].stats[2],
                               pc_race_table[ch->race].max_stats[2] );
                sprintf( buf, "   %2d", stat3[x] );
                write_to_buffer( d, buf, 0 );
            }
            write_to_buffer( d, "\n\r     Dexterity    :", 0 );
            for ( x = 0; x < 5; x++ )
            {
                stat4[x] =
                    roll_stat( pc_race_table[ch->race].stats[3],
                               pc_race_table[ch->race].max_stats[3] );
                sprintf( buf, "   %2d", stat4[x] );
                write_to_buffer( d, buf, 0 );
            }
            write_to_buffer( d, "\n\r     Constitution :", 0 );
            for ( x = 0; x < 5; x++ )
            {
                stat5[x] =
                    roll_stat( pc_race_table[ch->race].stats[4],
                               pc_race_table[ch->race].max_stats[4] );
                sprintf( buf, "   %2d", stat5[x] );
                write_to_buffer( d, buf, 0 );
            }
            write_to_buffer( d,
                             "\n\r\n\r     Press enter to roll again, else enter number of column: ",
                             0 );
            rolled = TRUE;
        }
        break;

    case CON_GET_NEW_CLASS:
        iClass = class_lookup( argument );

        if ( iClass == -1 || !str_cmp( argument, "console" )
             || ( class_table[iClass].remort_class
                  && !IS_SET( ch->act, PLR_REMORT ) ) )
        {
            write_to_buffer( d,
                             "That's not a class.\n\rWhat IS your class? ", 0 );
            return;
        }

        ch->Class = iClass;

        sprintf( log_buf, "%s@%s new player.", ch->name, d->host );
        log_string( log_buf );
        write_to_buffer( d, "\n\r", 2 );
        /*if (ch->race == 1) write_to_buffer( d, "Your class is evil by nature.\n\r",0);
           else { */
        write_to_buffer( d, "You may be good, neutral, or evil.\n\r", 0 );
        write_to_buffer( d, "Which alignment (G/N/E)? ", 0 );
        /*} */
        d->connected = CON_GET_ALIGNMENT;
        break;

    case CON_GET_ALIGNMENT:
        /* if (ch->race == 1) ch->alignment = -750; */
        switch ( argument[0] )
        {
        case 'g':
        case 'G':
            ch->alignment = 750;
            break;
        case 'n':
        case 'N':
            ch->alignment = 0;
            break;
        case 'e':
        case 'E':
            ch->alignment = -750;
            break;
        default:
            write_to_buffer( d, "That's not a valid alignment.\n\r", 0 );
            write_to_buffer( d, "Which alignment (G/N/E)? ", 0 );
            return;
        }

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

        group_add( ch, "rom basics", FALSE );
        group_add( ch, class_table[ch->Class].base_group, FALSE );
        ch->pcdata->learned[gsn_recall] = 50;
        write_to_buffer( d, "Do you wish to customize this character?\n\r", 0 );
        write_to_buffer( d,
                         "Customization takes time, but allows a wider range of skills and abilities.\n\r",
                         0 );
        write_to_buffer( d, "Customize (Y/N)? ", 0 );
        d->connected = CON_DEFAULT_CHOICE;
        break;

    case CON_DEFAULT_CHOICE:
        write_to_buffer( d, "\n\r", 2 );
        switch ( argument[0] )
        {
        case 'y':
        case 'Y':
            ch->gen_data = alloc_perm( sizeof( *ch->gen_data ) );
            ch->gen_data->points_chosen = ch->pcdata->points;
            do_help( ch, "group header" );
            list_group_costs( ch );
            write_to_buffer( d, "You already have the following skills:\n\r",
                             0 );
            do_skills( ch, "" );
            do_help( ch, "menu choice" );
            d->connected = CON_GEN_GROUPS;
            break;
        case 'n':
        case 'N':
            group_add( ch, class_table[ch->Class].default_group, TRUE );
            write_to_buffer( d, "\n\r", 2 );
            do_help( ch, "motd" );
            d->connected = CON_READ_MOTD;
            break;
        default:
            write_to_buffer( d, "Please answer (Y/N)? ", 0 );
            return;
        }
        break;

    case CON_GEN_GROUPS:
        send_to_char( "\n\r", ch );
        if ( !str_cmp( argument, "done" ) )
        {
            sprintf( buf, "Creation points: %d\n\r", ch->pcdata->points );
            send_to_char( buf, ch );
            sprintf( buf,
                     "Experience modifier: %d (Percent of difference from the norm)\n\r",
                     figure_difference( ch->gen_data->points_chosen ) );
            if ( ch->pcdata->points < 40 )
                ch->train = ( 40 - ch->pcdata->points + 1 ) / 2;
            send_to_char( buf, ch );
            write_to_buffer( d, "\n\r", 2 );
            do_help( ch, "motd" );
            d->connected = CON_READ_MOTD;
            break;
        }

        if ( !parse_gen_groups( ch, argument ) )
            send_to_char
                ( "Choices are: list,learned,premise,add,drop,info,help, and done.\n\r",
                  ch );

        do_help( ch, "menu choice" );
        break;

    case CON_BEGIN_REMORT:
        write_to_buffer( d, "Now beginning the remorting process.\n\r\n\r", 0 );
        write_to_buffer( d, "The following races are available:\n\r  ", 0 );
        for ( race = 1; race_table[race].name != NULL; race++ )
        {
            if ( !race_table[race].pc_race )
                break;
            write_to_buffer( d, race_table[race].name, 0 );
            write_to_buffer( d, " ", 1 );
        }
        write_to_buffer( d, "\n\r", 0 );
        write_to_buffer( d, "What is your race (help for more information)? ",
                         0 );
        d->connected = CON_GET_NEW_RACE;
        break;

    case CON_READ_IMOTD:
#ifdef IMMS_CAN_HIDE
        if ( !str_cmp( argument, "hidden" ) )
        {
            SET_BIT( ch->act, PLR_NO_ANNOUNCE );
        }

        if ( !str_cmp( argument, "wizi" ) )
        {
            SET_BIT( ch->act, PLR_NO_ANNOUNCE );
            SET_BIT( ch->act, PLR_WIZINVIS );
            ch->invis_level = get_trust( ch );
        }

#endif
        write_to_buffer( d, "\n\r", 2 );
        do_help( ch, "motd" );
        d->connected = CON_READ_MOTD;
        break;

    case CON_READ_MOTD:
#if defined(cbuilder)
        AddUser( ch );
#endif

        write_to_buffer( d, "\n\rWelcome to EmberMUD.\n\r", 0 );
        /* Add to list of PCs and NPCs */
        ch->next = char_list;
        char_list = ch;
        /* Add to list of PCs */
        ch->next_player = player_list;
        player_list = ch;

        d->connected = CON_PLAYING;
        reset_char( ch );

        if ( ch->level == 0 )
        {

            ch->level = 1;
            ch->exp = 0;
            ch->hit = ch->max_hit;
            ch->mana = ch->max_mana;
            ch->move = ch->max_move;
            ch->train = STARTING_TRAINS;
            ch->practice = STARTING_PRACTICES;
            set_title( ch, STARTING_TITLE );

            do_outfit( ch, "" );
            obj_to_char( create_object( get_obj_index( OBJ_VNUM_MAP ), 0 ),
                         ch );

            ch->pcdata->learned[get_weapon_sn( ch )] = 40;

            char_to_room( ch, get_room_index( ROOM_VNUM_SCHOOL ) );
            send_to_char( "\n\r", ch );
            save_char_obj( ch );
            do_help( ch, "NEWBIE INFO" );
            send_to_char( "\n\r", ch );
        }
        else if ( ch->in_room != NULL )
        {
            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 ) );
        }

#ifdef ANNOUNCE_CONNECTIONS
        if ( ch->played == 0 )
        {
            sprintf( buf, "%s has entered the game for the first time.",
                     ch->name );
            do_sendinfo( ch, buf );
        }
        sprintf( buf, "%s has entered the game.", ch->name );
#ifdef IMMS_CAN_HIDE
        if ( IS_SET( ch->act, PLR_NO_ANNOUNCE ) )
        {
            REMOVE_BIT( ch->act, PLR_NO_ANNOUNCE );
        }
        else
        {
#endif
            if ( !IS_SET( ch->act, PLR_WIZINVIS ) )
                do_sendinfo( ch, buf );
#ifdef IMMS_CAN_HIDE
        }
#endif
#endif

        act( "$n has entered the game.", ch, NULL, NULL, TO_ROOM );
        do_look( ch, "auto" );
        do_board( ch, "" );     /* Show board status */

        if ( ch->pet != NULL )
        {
            char_to_room( ch->pet, ch->in_room );
            act( "$n has entered the game.", ch->pet, NULL, NULL, TO_ROOM );
        }

        if ( IS_SET( ch->act, PLR_AFK ) )
            do_afk( ch, NULL );

        ch->pcdata->chaos_score = 0;

        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;

    case CON_SHELL:
        break;
    }

    return;
}

/*
 * Parse a name for acceptability.
 */
bool check_parse_name( char *name )
{
    /*
     * Reserved words.
     */
    if ( is_exact_name
         ( name, "all auto immortal self someone something the you fuck shit" )
         || is_name( name, "fuck" ) || is_name( name, "shit" )
         || is_name( name, "imm" ) )
        return FALSE;

    /*
     * Length restrictions.
     */

    if ( strlen( name ) < 2 )
        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;
    }

    /*
     * Prevent players from naming themselves after mobs.
     */
#ifdef NO_MOB_NAMES
    {
        extern MOB_INDEX_DATA *mob_index_hash[MAX_KEY_HASH];
        MOB_INDEX_DATA *pMobIndex;
        int iHash;

        for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ )
        {
            for ( pMobIndex = mob_index_hash[iHash];
                  pMobIndex != NULL; pMobIndex = pMobIndex->next )
            {
                if ( is_exact_name( name, pMobIndex->player_name ) )
                    return FALSE;
            }
        }
    }
#endif

    return TRUE;
}

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

    for ( ch = player_list; ch != NULL; ch = ch->next_player )
    {
        if ( ( !fConn || ch->desc == NULL )
             && !str_cmp( d->character->name, ch->name ) )
        {
            if ( fConn == FALSE )
            {
                free_string( &d->character->pcdata->pwd );
                d->character->pcdata->pwd = str_dup( ch->pcdata->pwd );
            }
            else
            {
                free_char( d->character );
                d->character = ch;
                ch->desc = d;
                ch->timer = 0;
                if ( IS_SET( ch->act, PLR_BUILDING ) );
                {
                    REMOVE_BIT( ch->act, PLR_BUILDING );
                }

                send_to_char( "Reconnecting.\n\r", ch );
#if defined(cbuilder)
                AddUser( ch );
#endif
                act( "$n has reconnected.", ch, NULL, NULL, TO_ROOM );
                ch->pcdata->ticks = 0;
                sprintf( log_buf, "%s@%s reconnected.", ch->name, d->host );
                log_string( log_buf );
                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 NWRITE to continue it.\n\r",
                          ch );
            }
            return TRUE;
        }
    }

    return FALSE;
}

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

    for ( dold = descriptor_list; dold; dold = dold->next )
    {
        if ( dold != d
             && dold->character != NULL
             && dold->connected != CON_GET_NAME
             && dold->connected != CON_GET_OLD_PASSWORD
             && !str_cmp( name, dold->original
                          ? dold->original->name : dold->character->name ) )
        {
            write_to_buffer( d,
                             "That character is already playing.\n\rDisconnect that player? ",
                             0 );
            d->connected = CON_BREAK_CONNECT;
            return TRUE;
        }
    }

    return FALSE;
}

void stop_idling( CHAR_DATA * ch )
{
    if ( ch == NULL
         || ch->desc == NULL
         || ch->desc->connected != CON_PLAYING
         || ch->was_in_room == NULL
         || ch->in_room != get_room_index( ROOM_VNUM_LIMBO ) )
        return;

    ch->timer = 0;
    char_from_room( ch );
    char_to_room( ch, ch->was_in_room );
    ch->was_in_room = NULL;
    act( "$n has returned from the void.", ch, NULL, NULL, TO_ROOM );
    return;
}

/*
 * Write to one char.
 */
void send_to_char( const char *txt, CHAR_DATA * ch )
{
    if ( txt != NULL && ch->desc != NULL )
        write_to_buffer( ch->desc, txt, strlen( txt ) );
    return;
}

void printf_to_char( CHAR_DATA * ch, char *fmt, ... )
{
    char param[MAX_STRING_LENGTH * 4];
    {
        va_list args;
        va_start( args, fmt );
        vsprintf( param, fmt, args );
        va_end( args );
    }
    if ( param[0] && ch->desc )
        write_to_buffer( ch->desc, param, strlen( param ) );

    return;
}

/*
 * Send a page to one char.
 */
void page_to_char( const char *txt, CHAR_DATA * ch )
{
    if ( txt == NULL || ch->desc == NULL )
        return;

    if ( ch->desc == NULL )
        return;
    ch->desc->showstr_head = alloc_mem( strlen( txt ) + 1 );
    strcpy( ch->desc->showstr_head, txt );
    ch->desc->showstr_point = ch->desc->showstr_head;
    show_string( ch->desc, "" );
}

/* string pager */
void show_string( struct descriptor_data *d, char *input )
{
    char buf[MAX_STRING_LENGTH];
    char buffer[4 * MAX_STRING_LENGTH];
    register char *scan, *chk;
    int lines = 0, toggle = 1;
    int show_lines;

    one_argument( input, buf );
    if ( buf[0] != '\0' )
    {
        if ( d->showstr_head )
            free_mem( &d->showstr_head );
        d->showstr_point = 0;
        return;
    }

    if ( d->character )
        show_lines = d->character->lines;
    else
        show_lines = 0;

    for ( scan = buffer;; scan++, d->showstr_point++ )
    {
        if ( ( ( *scan = *d->showstr_point ) == '\n' || *scan == '\r' )
             && ( toggle = -toggle ) < 0 )
            lines++;

        else if ( !*scan || ( show_lines > 0 && lines >= show_lines ) )
        {
            *scan = '\0';
            write_to_buffer( d, buffer, strlen( buffer ) );
            for ( chk = d->showstr_point; isspace( *chk ); chk++ );
            {
                if ( !*chk )
                {
                    if ( d->showstr_head )
                        free_mem( &d->showstr_head );
                    d->showstr_point = 0;
                }
            }
            return;
        }
    }
    return;
}

/* quick sex fixer */
void fix_sex( CHAR_DATA * ch )
{
    if ( ch->sex < 0 || ch->sex > 2 )
        ch->sex = IS_NPC( ch ) ? 0 : ch->pcdata->true_sex;
}

void act( const char *format, CHAR_DATA * ch, const void *arg1,
          const void *arg2, int type )
{
    /* to be compatible with older code */
    act_new( format, ch, arg1, arg2, type, POS_RESTING );
}

#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 )
{
    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 *point;
    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;

    bzero( buf, sizeof( buf ) );
    point = buf;
    while ( *str )
    {
        if ( *str != '$' )
        {
            *point++ = *str++;
            continue;
        }
        ++str;

        if ( !arg2 && *str >= 'A' && *str <= 'Z' )
        {
            bug( "Act: missing arg2 for code %d.", *str );
            i = " <@@@> ";
        }
        else
        {
            switch ( *str )
            {
            default:
                bug( "Act: bad code %d.", *str );
                i = " <@@@> ";
                break;
                /* Thx alex for 't' idea */
            case 't':
                i = ( char * ) arg1;
                break;
            case 'T':
                i = ( char * ) arg2;
                break;
            case 'n':
                i = ( to ? PERS( ch, to ) : NAME( ch ) );
                break;
            case 'N':
                i = ( to ? PERS( vch, to ) : NAME( vch ) );
                break;
            case 'e':
                i = he_she[URANGE( 0, ch->sex, 2 )];
                break;
            case 'E':
                i = he_she[URANGE( 0, vch->sex, 2 )];
                break;
            case 'm':
                i = him_her[URANGE( 0, ch->sex, 2 )];
                break;
            case 'M':
                i = him_her[URANGE( 0, vch->sex, 2 )];
                break;
            case 's':
                i = his_her[URANGE( 0, ch->sex, 2 )];
                break;
            case 'S':
                i = his_her[URANGE( 0, vch->sex, 2 )];
                break;
            case 'p':
                i = ( !to || can_see_obj( to, obj1 )
                      ? obj1->short_descr : "something" );
                break;
            case 'P':
                i = ( !to || can_see_obj( to, obj2 )
                      ? obj2->short_descr : "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 ) )
            ++point, ++i;
    }
    *point++ = '`';
    *point++ = 'w';
    *point++ = '\n';
    *point++ = '\r';
    *point = '\0';
    buf[0] = UPPER( buf[0] );

    return buf;
}

char *act_new( const char *format, CHAR_DATA * ch, const void *arg1,
               const void *arg2, int type, int min_pos )
{
    char *txt = NULL;
    CHAR_DATA *to;
    CHAR_DATA *vch = ( CHAR_DATA * ) arg2;

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

    /* discard null rooms and chars */
    if ( !ch )
    {
        bug( "Act: null ch. (%s)", format );
        return NULL;
    }

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

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

        if ( !vch->in_room )
        {
            bug( "Act: vch in NULL room!" );
            bug( "%s -> %s (%s)", ch->name, vch->name, format );
            return NULL;
        }

        to = vch;
/*        to = vch->in_room->people;*/
    }

    /* room progs & obj progs */
    if ( MOBtrigger && type != TO_CHAR && type != TO_VICT && to )
    {
/*        OBJ_DATA *to_obj; *//* FIXME! - Zane */
        txt = act_string( format, NULL, ch, arg1, arg2 );
        /* FIXME! - Zane */
/*        if ( IS_SET( to->in_room->progtypes, ACT_PROG ) )
            rprog_act_trigger( txt, to->in_room, ch, (OBJ_DATA *)arg1, (void *)arg2 );
        for ( to_obj = to->in_room->contents; to_obj;
              to_obj = to_obj->next_content )
            if ( IS_SET(to_obj->pIndexData->progtypes, ACT_PROG) )
               oprog_act_trigger(txt, to_obj, ch, (OBJ_DATA *)arg1, (void *)arg2);*/
    }

    for ( ; to; to = ( type == TO_CHAR || type == TO_VICT )
          ? NULL : to->next_in_room )
    {
        if ( ( !to->desc && ( IS_NPC( to ) &&
                              !IS_SET( to->pIndexData->progtypes, ACT_PROG ) ) )
             || to->position < min_pos )
            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;

        txt = act_string( format, to, ch, arg1, arg2 );
        if ( to->desc )
            write_to_buffer( to->desc, txt, strlen( txt ) );

        mprog_act_trigger( txt, ch );

        oprog_act_trigger( txt, ch );

        rprog_act_trigger( txt, ch );

        /* FIXME! - Zane */
/*        if (MOBtrigger)
               mprog_act_trigger( txt, to, ch, (OBJ_DATA *)arg1, vch );*/

    }

    MOBtrigger = TRUE;
    return txt;
}

void do_color( register char *inbuf, int inlen, register char *outbuf,
               int outlen, bool color )
{
    char *origin = inbuf;
    char *origout = outbuf;

    while ( *inbuf != '\0' && origin + inlen > inbuf
            && origout + outlen > outbuf )
    {
        if ( *inbuf != '`' )
        {
            *outbuf++ = *inbuf++;
            continue;
        }

        if ( origin + inlen - 1 <= ++inbuf )
        {
            *outbuf = '\0';
            return;
        }

        if ( !color )
        {
            if ( *inbuf++ == '`' )
                *outbuf++ = '`';
        }
        else
            switch ( *inbuf++ )
            {
            case '0':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 4;
                break;
            case 'k':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'K':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'r':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'R':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'g':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'G':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'y':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'Y':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'b':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'B':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'm':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'M':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'c':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'C':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'w':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case 'W':
                if ( origout + outlen - 7 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                strcpy( outbuf, "" );
                outbuf += 7;
                break;
            case '`':
                if ( origout + outlen - 2 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                *outbuf++ = '`';
                break;
            default:
                if ( origout + outlen - 2 <= outbuf )
                {
                    *outbuf = '\0';
                    return;
                }
                *outbuf++ = '`';
                *outbuf++ = *( inbuf - 1 );
                break;
            }
    }

    if ( color )
    {
        if ( origout + outlen - 7 <= outbuf )
        {
            *outbuf = '\0';
            return;
        }
        strcpy( outbuf, "\0" );
    }
    else
        *outbuf = '\0';
}

char *figurestate( int current, int max )
{
    static char status[40];

    bzero( status, sizeof( status ) );
    if ( current >= ( max / 3 * 2 ) )
        sprintf( status, "`W%d`w", current );
    else if ( current >= max / 3 )
        sprintf( status, "`Y%d`w", current );
    else
        sprintf( status, "`R%d`w", current );
    return ( status );
}

char *damstatus( CHAR_DATA * ch )
{
    int percent;
    static char wound[40];

    bzero( wound, sizeof( wound ) );
    if ( ch->max_hit > 0 )
        percent = ch->hit * 100 / ch->max_hit;
    else
        percent = -1;
    if ( percent >= 100 )
        sprintf( wound, "excellent condition" );
    else if ( percent >= 90 )
        sprintf( wound, "few scratches" );
    else if ( percent >= 75 )
        sprintf( wound, "small wounds" );
    else if ( percent >= 50 )
        sprintf( wound, "quite a few wounds" );
    else if ( percent >= 30 )
        sprintf( wound, "nasty wounds" );
    else if ( percent >= 15 )
        sprintf( wound, "pretty hurt" );
    else if ( percent >= 0 )
        sprintf( wound, "awful condition" );
    else
        sprintf( wound, "bleeding to death" );
    return ( wound );
}

char *doparseprompt( CHAR_DATA * ch )
{
    CHAR_DATA *tank, *victim;
    static char finished_prompt[240];
    char workstr[100];
    char *fp_point;
    char *orig_prompt;

    bzero( finished_prompt, sizeof( finished_prompt ) );
    orig_prompt = ch->pcdata->prompt;
    fp_point = finished_prompt;
    while ( *orig_prompt != '\0' )
    {
        if ( *orig_prompt != '%' )
        {
            *fp_point = *orig_prompt;
            orig_prompt++;
            fp_point++;
            continue;
        }
        orig_prompt++;
        switch ( *orig_prompt )
        {
        case 'h':
            sprintf( workstr, "%d", ch->hit );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'H':
            sprintf( workstr, "%d", ch->max_hit );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'm':
            sprintf( workstr, "%d", ch->mana );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'M':
            sprintf( workstr, "%d", ch->max_mana );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'v':
            sprintf( workstr, "%d", ch->move );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'V':
            sprintf( workstr, "%d", ch->max_move );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'r':
            strcat( finished_prompt, "\n\r" );
            fp_point++;
            fp_point++;
            orig_prompt++;
            break;
        case 'i':
            sprintf( workstr, "%s", figurestate( ch->hit, ch->max_hit ) );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'n':
            sprintf( workstr, "%s", figurestate( ch->mana, ch->max_mana ) );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'w':
            sprintf( workstr, "%s", figurestate( ch->move, ch->max_move ) );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'l':
            if ( ( tank = ch->fighting ) != NULL )
                if ( ( tank = tank->fighting ) != NULL )
                {
                    sprintf( workstr, "%s", damstatus( tank ) );
                    strcat( finished_prompt, workstr );
                    fp_point += strlen( workstr );
                }
            orig_prompt++;
            break;
        case 'e':
            if ( ( victim = ch->fighting ) != NULL )
            {
                sprintf( workstr, "%s", damstatus( victim ) );
                strcat( finished_prompt, workstr );
                fp_point += strlen( workstr );
            }
            orig_prompt++;
            break;
        case 's':
            sprintf( workstr, "%s", damstatus( ch ) );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'T':
            sprintf( workstr, "%s", get_curtime(  ) );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case '#':
            if ( IS_IMMORTAL( ch ) && ch->in_room != NULL )
            {
                sprintf( workstr, "%d", ch->in_room->vnum );
                strcat( finished_prompt, workstr );
                fp_point += strlen( workstr );
                orig_prompt++;
                break;
            }
            else
                break;
        case 'a':
            sprintf( workstr, "%d", ch->alignment );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'B':
            if ( IS_SET( ch->act, PLR_BUILDING ) )
            {
                sprintf( workstr, "`Y[`RBuilding`Y]`w" );
                strcat( finished_prompt, workstr );
                fp_point += strlen( workstr );
            }
            orig_prompt++;
            break;
        case 'A':
            if ( IS_SET( ch->act, PLR_AFK ) )
            {
                sprintf( workstr, "`W(AFK)" );
                strcat( finished_prompt, workstr );
                fp_point += strlen( workstr );
            }
            if ( IS_SET( ch->act, PLR_KILLER ) )
            {
                sprintf( workstr, "`R(PK)" );
                strcat( finished_prompt, workstr );
                fp_point += strlen( workstr );
            }
            if ( IS_SET( ch->act, PLR_THIEF ) )
            {
                sprintf( workstr, "`K(THIEF)" );
                strcat( finished_prompt, workstr );
                fp_point += strlen( workstr );
            }
            if ( IS_IMMORTAL( ch ) && IS_SET( ch->act, PLR_WIZINVIS ) )
            {
                sprintf( workstr, "`B(Wizi:%d)", ch->invis_level );
                strcat( finished_prompt, workstr );
                fp_point += strlen( workstr );
            }
            orig_prompt++;
            break;
        case 'q':
            sprintf( workstr, "%s", ch->in_room->area->name );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'x':
            sprintf( workstr, "%ld", ch->exp );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        case 'X':
            sprintf( workstr, "%ld",
                     exp_per_level( ch, ch->pcdata->points ) - ch->exp );
            strcat( finished_prompt, workstr );
            fp_point += strlen( workstr );
            orig_prompt++;
            break;
        default:
            strcat( finished_prompt, "%" );
            fp_point++;
            break;
        }
    }
    return ( finished_prompt );
}

/* Roll a six sided die four times, discard the lowest roll and total the other three,
 * add the base_bonus and return the result */
int roll_stat( int base_bonus, int stat_max )
{
    int roll = 0;
    int min_roll = 99;
    int total = 0;
    int counter;

    /* Roll a six sided die four times, total the result and remember the lowest roll */
    for ( counter = 0; counter < 4; counter++ )
    {
        roll = number_range( 1, 6 );
        total += roll;

        if ( roll < min_roll )
            min_roll = roll;
    }

    /* 
     * Discard the lowest roll of the four and return the total plus the base_bonus 
     * If the result is less than 1 return 1
     */
    return UMAX( total - min_roll + base_bonus, 1 );
}