/****************************************************************************
* Eldhamud Codebase V2.2 *
* ------------------------------------------------------------------------ *
* EldhaMUD code (C) 2003-2008 by Robert Powell (Tommi) *
* ------------------------------------------------------------------------ *
* Original SMAUG 1.4a written by Thoric (Derek Snider) with Altrag, *
* Blodkai, Haus, Narn, Scryn, Swordbearer, Tricops, Gorog, Rennard, *
* Grishnakh, Fireblade, and Nivek. *
* *
* Original MERC 2.1 code by Hatchet, Furey, and Kahn. *
* *
* Original DikuMUD code by: Hans Staerfeldt, Katja Nyboe, Tom Madsen, *
* Michael Seifert, and Sebastian Hammer. *
* ------------------------------------------------------------------------ *
* Low-level communication module *
****************************************************************************/
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include "./Headers/mud.h"
#include "./Headers/sha256.h"
/*
* Socket and TCP/IP stuff.
*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <netdb.h>
#define closesocket close
#ifdef sun
int gethostname ( char *name, int namelen );
#endif
const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, STRING_NULL };
const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, STRING_NULL };
const char go_ahead_str[] = { IAC, GA, STRING_NULL };
void auth_maxdesc args ( ( int *md, fd_set * ins, fd_set * outs, fd_set * excs ) );
void auth_check args ( ( fd_set * ins, fd_set * outs, fd_set * excs ) );
void set_auth args ( ( DESCRIPTOR_DATA * d ) );
void kill_auth args ( ( DESCRIPTOR_DATA * d ) );
void save_sysdata args ( ( SYSTEM_DATA sys ) );
void medit_parse args ( ( DESCRIPTOR_DATA *d, char *arg ) );
void redit_parse args ( ( DESCRIPTOR_DATA *d, char *arg ) );
void oedit_parse args ( ( DESCRIPTOR_DATA *d, char *arg ) );
bool is_inolc args ( ( DESCRIPTOR_DATA *d ) );
/*
* Global variables.
*/
DESCRIPTOR_DATA *first_descriptor; /* First descriptor */
DESCRIPTOR_DATA *last_descriptor; /* Last descriptor */
DESCRIPTOR_DATA *d_next; /* Next descriptor in loop */
int num_descriptors;
FILE *fpReserve; /* Reserved file handle */
bool mud_down; /* Shutdown */
bool service_shut_down; /* Shutdown by operator closing down service */
bool wizlock; /* Game is wizlocked */
time_t boot_time;
char str_boot_time[MAX_INPUT_LENGTH];
char lastplayercmd[MAX_INPUT_LENGTH * 2];
time_t current_time; /* Time of this pulse */
int control; /* Controlling descriptor */
int newdesc; /* New descriptor */
fd_set in_set; /* Set of desc's for reading */
fd_set out_set; /* Set of desc's for writing */
fd_set exc_set; /* Set of desc's with errors */
int maxdesc;
char *alarm_section = "(unknown)";
short client_speed ( short speed );
/*
* OS-dependent local functions.
*/
void game_loop args ( ( void ) );
int init_socket args ( ( int mudport ) );
void new_descriptor args ( ( int new_desc ) );
bool read_from_descriptor args ( ( DESCRIPTOR_DATA * d ) );
bool write_to_descriptor args ( ( int desc, char *txt, int length ) );
/*
* Other local functions (OS-independent).
*/
bool check_reconnect args ( ( DESCRIPTOR_DATA * d, char *name, bool fConn ) );
bool check_playing args ( ( DESCRIPTOR_DATA * d, char *name, bool kick ) );
int main args ( ( int argc, char **argv ) );
void nanny args ( ( DESCRIPTOR_DATA * d, char *argument ) );
bool flush_buffer args ( ( DESCRIPTOR_DATA * d, bool fPrompt ) );
void read_from_buffer args ( ( DESCRIPTOR_DATA * d ) );
void stop_idling args ( ( CHAR_DATA * ch ) );
void free_desc args ( ( DESCRIPTOR_DATA * d ) );
void display_prompt args ( ( DESCRIPTOR_DATA * d ) );
void set_pager_input args ( ( DESCRIPTOR_DATA * d, char *argument ) );
bool pager_output args ( ( DESCRIPTOR_DATA * d ) );
void mail_count args ( ( CHAR_DATA * ch ) );
int port;
int main ( int argc, char **argv )
{
struct timeval now_time;
char hostn[128];
bool fCopyOver = FALSE;
#ifdef IMC
int imcsocket = -1;
#endif
/*
* Memory debugging if needed.
*/
#if defined(MALLOC_DEBUG)
malloc_debug ( 2 );
#endif
DONT_UPPER = FALSE;
num_descriptors = 0;
first_descriptor = NULL;
last_descriptor = NULL;
sysdata.NO_NAME_RESOLVING = TRUE;
sysdata.WAIT_FOR_AUTH = TRUE;
/*
* Init time.
*/
gettimeofday ( &now_time, NULL );
current_time = ( time_t ) now_time.tv_sec;
boot_time = time ( 0 ); /* <-- I think this is what you wanted */
strcpy ( str_boot_time, ctime ( ¤t_time ) );
init_pfile_scan_time( ); /* Pfile autocleanup initializer - Samson 5-8-99 */
/*
* Reserve two channels for our use.
*/
if ( ( fpReserve = fopen ( NULL_FILE, "r" ) ) == NULL )
{
perror ( NULL_FILE );
exit ( 1 );
}
if ( ( fpLOG = fopen ( NULL_FILE, "r" ) ) == NULL )
{
perror ( NULL_FILE );
exit ( 1 );
}
/*
* Get the port number.
*/
port = 8000;
if ( argc > 1 )
{
if ( !is_number ( argv[1] ) )
{
fprintf ( stderr, "Usage: %s [port #]\n", argv[0] );
exit ( 1 );
}
else if ( ( port = atoi ( argv[1] ) ) <= 1024 )
{
fprintf ( stderr, "Port number must be above 1024.\n" );
exit ( 1 );
}
if ( argv[2] && argv[2][0] )
{
fCopyOver = TRUE;
control = atoi ( argv[3] );
#ifdef IMC
imcsocket = atoi ( argv[4] );
#endif
}
else
fCopyOver = FALSE;
}
/*
* Run the game.
*/
log_string ( "Booting Database" );
boot_db ( fCopyOver );
log_string ( "Initializing socket" );
if ( !fCopyOver ) /* We have already the port if copyover'ed */
control = init_socket ( port );
/*
* I don't know how well this will work on an unnamed machine as I don't
* have one handy, and the man pages are ever-so-helpful.. -- Alty
*/
if ( gethostname ( hostn, sizeof ( hostn ) ) < 0 )
{
perror ( "main: gethostname" );
strcpy ( hostn, "unresolved" );
}
sprintf ( log_buf, "%s ready at address %s on port %d.", sysdata.mud_name, hostn, port );
log_string ( log_buf );
#ifdef IMC
/* Initialize and connect to IMC2 */
imc_startup ( FALSE, imcsocket, fCopyOver );
#endif
if ( fCopyOver )
{
log_string ( "Initiating hotboot recovery." );
hotboot_recover( );
}
game_loop( );
closesocket ( control );
#ifdef IMC
imc_shutdown ( FALSE );
#endif
/*
* That's all, folks.
*/
log_string ( "Normal termination of game." );
exit ( 0 );
return 0;
}
int init_socket ( int mudport )
{
char hostname[64];
struct sockaddr_in sa;
int x = 1;
int fd;
gethostname ( hostname, sizeof ( hostname ) );
if ( ( fd = socket ( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
{
perror ( "Init_socket: socket" );
exit ( 1 );
}
if ( setsockopt ( fd, SOL_SOCKET, SO_REUSEADDR, ( void * ) &x, sizeof ( x ) ) < 0 )
{
perror ( "Init_socket: SO_REUSEADDR" );
closesocket ( fd );
exit ( 1 );
}
#if defined(SO_DONTLINGER) && !defined(SYSV)
{
struct linger ld;
ld.l_onoff = 1;
ld.l_linger = 1000;
if ( setsockopt ( fd, SOL_SOCKET, SO_DONTLINGER, ( void * ) &ld, sizeof ( ld ) ) < 0 )
{
perror ( "Init_socket: SO_DONTLINGER" );
closesocket ( fd );
exit ( 1 );
}
}
#endif
memset ( &sa, STRING_NULL, sizeof ( sa ) );
sa.sin_family = AF_INET;
sa.sin_port = htons ( mudport );
if ( bind ( fd, ( struct sockaddr * ) &sa, sizeof ( sa ) ) == -1 )
{
perror ( "Init_socket: bind" );
closesocket ( fd );
exit ( 1 );
}
if ( listen ( fd, 50 ) < 0 )
{
perror ( "Init_socket: listen" );
closesocket ( fd );
exit ( 1 );
}
return fd;
}
static void SegVio( )
{
abort( );
}
/*
* LAG alarm! -Thoric
*/
void caught_alarm ( int signum )
{
char buf[MAX_STRING_LENGTH];
sprintf ( buf, "ALARM CLOCK! In section %s", alarm_section );
bug ( buf );
strcpy ( buf, "Alas, the hideous malevalent entity known only as 'Lag' rises once more!\n\r" );
echo_to_all ( AT_IMMORT, buf, ECHOTAR_ALL );
if ( newdesc )
{
FD_CLR ( newdesc, &in_set );
FD_CLR ( newdesc, &out_set );
FD_CLR ( newdesc, &exc_set );
log_string ( "clearing newdesc" );
}
}
bool check_bad_desc ( int desc )
{
if ( FD_ISSET ( desc, &exc_set ) )
{
FD_CLR ( desc, &in_set );
FD_CLR ( desc, &out_set );
log_string ( "Bad FD caught and disposed." );
return TRUE;
}
return FALSE;
}
/*
* Determine whether this player is to be watched --Gorog
*/
bool chk_watch ( short player_level, char *player_name, char *player_site )
{
WATCH_DATA *pw;
/*
* char buf[MAX_INPUT_LENGTH];
* sprintf( buf, "che_watch entry: plev=%d pname=%s psite=%s",
* player_level, player_name, player_site);
* log_string(buf);
*/
if ( !first_watch )
return FALSE;
for ( pw = first_watch; pw; pw = pw->next )
{
if ( pw->target_name )
{
if ( !str_cmp ( pw->target_name, player_name ) && player_level < pw->imm_level )
return TRUE;
}
else if ( pw->player_site )
{
if ( !str_prefix ( pw->player_site, player_site ) && player_level < pw->imm_level )
return TRUE;
}
}
return FALSE;
}
void accept_new ( int ctrl )
{
static struct timeval null_time;
DESCRIPTOR_DATA *d;
/*
* int maxdesc; Moved up for use with id.c as extern
*/
#if defined(MALLOC_DEBUG)
if ( malloc_verify( ) != 1 )
abort( );
#endif
/*
* Poll all active descriptors.
*/
FD_ZERO ( &in_set );
FD_ZERO ( &out_set );
FD_ZERO ( &exc_set );
FD_SET ( ctrl, &in_set );
maxdesc = ctrl;
newdesc = 0;
for ( d = first_descriptor; d; d = d->next )
{
maxdesc = UMAX ( maxdesc, d->descriptor );
FD_SET ( d->descriptor, &in_set );
FD_SET ( d->descriptor, &out_set );
FD_SET ( d->descriptor, &exc_set );
if ( d == last_descriptor )
break;
}
if ( select ( maxdesc + 1, &in_set, &out_set, &exc_set, &null_time ) < 0 )
{
perror ( "accept_new: select: poll" );
exit ( 1 );
}
if ( FD_ISSET ( ctrl, &exc_set ) )
{
bug ( "Exception raise on controlling descriptor %d", ctrl );
FD_CLR ( ctrl, &in_set );
FD_CLR ( ctrl, &out_set );
}
else if ( FD_ISSET ( ctrl, &in_set ) )
{
newdesc = ctrl;
new_descriptor ( newdesc );
}
}
void game_loop ( void )
{
struct timeval last_time;
char cmdline[MAX_INPUT_LENGTH];
DESCRIPTOR_DATA *d;
/*
* time_t last_check = 0;
*/
signal ( SIGSEGV, SegVio );
gettimeofday ( &last_time, NULL );
current_time = ( time_t ) last_time.tv_sec;
/*
* Main loop
*/
while ( !mud_down )
{
accept_new ( control );
/*
* Kick out descriptors with raised exceptions
* or have been idle, then check for input.
*/
for ( d = first_descriptor; d; d = d_next )
{
if ( d == d->next )
{
bug ( "descriptor_loop: loop found & fixed" );
d->next = NULL;
}
d_next = d->next;
d->idle++; /* make it so a descriptor can idle out */
if ( FD_ISSET ( d->descriptor, &exc_set ) )
{
FD_CLR ( d->descriptor, &in_set );
FD_CLR ( d->descriptor, &out_set );
if ( d->character && ( d->connected == CON_PLAYING || d->connected == CON_EDITING ) )
save_char_obj ( d->character );
d->outtop = 0;
close_socket ( d, TRUE );
continue;
}
else if ( ( ( !d->character && d->idle > 1200 ) /* 5 mins */
|| ( d->connected != CON_PLAYING && d->idle > 2400 ) /* 10 mins */
|| d->idle > 57600 ) /* 4 hrs */
&& ! ( d->character && d->character->level >= LEVEL_IMMORTAL ) )
{
write_to_descriptor ( d->descriptor, "Idle timeout... disconnecting.\n\r", 0 );
d->outtop = 0;
close_socket ( d, TRUE );
continue;
}
else
{
d->fcommand = FALSE;
if ( FD_ISSET ( d->descriptor, &in_set ) )
{
d->idle = 0;
if ( d->character )
d->character->timer = 0;
if ( !read_from_descriptor ( d ) )
{
FD_CLR ( d->descriptor, &out_set );
if ( d->character && ( d->connected == CON_PLAYING || d->connected == CON_EDITING ) )
save_char_obj ( d->character );
d->outtop = 0;
close_socket ( d, FALSE );
continue;
}
}
if ( d->character && d->character->wait > 0 )
{
--d->character->wait;
continue;
}
read_from_buffer ( d );
if ( d->incomm[0] != STRING_NULL )
{
d->fcommand = TRUE;
stop_idling ( d->character );
strcpy ( cmdline, d->incomm );
d->incomm[0] = STRING_NULL;
if ( d->character )
set_cur_char ( d->character );
if ( d->pagepoint )
set_pager_input ( d, cmdline );
else
switch ( d->connected )
{
default:
nanny ( d, cmdline );
break;
case CON_PLAYING:
d->character->cmd_recurse = 0;
interpret ( d->character, cmdline );
break;
case CON_EDITING:
edit_buffer ( d->character, cmdline );
break;
}
}
}
if ( d == last_descriptor )
break;
}
#ifdef IMC
imc_loop();
#endif
/*
* Autonomous game motion.
*/
update_handler( );
/*
* Output.
*/
for ( d = first_descriptor; d; d = d_next )
{
d_next = d->next;
if ( ( d->fcommand || d->outtop > 0 ) && FD_ISSET ( d->descriptor, &out_set ) )
{
if ( d->pagepoint )
{
if ( !pager_output ( d ) )
{
if ( d->character && ( d->connected == CON_PLAYING || d->connected == CON_EDITING ) )
save_char_obj ( d->character );
d->outtop = 0;
close_socket ( d, FALSE );
}
}
else if ( !flush_buffer ( d, TRUE ) )
{
if ( d->character && ( d->connected == CON_PLAYING || d->connected == CON_EDITING ) )
save_char_obj ( d->character );
d->outtop = 0;
close_socket ( d, FALSE );
}
}
if ( d == last_descriptor )
break;
}
/*
* Synchronize to a clock.
* Sleep( last_time + 1/PULSE_PER_SECOND - now ).
* Careful here of signed versus unsigned arithmetic.
*/
{
struct timeval now_time;
long secDelta;
long usecDelta;
gettimeofday ( &now_time, NULL );
usecDelta = ( ( int ) last_time.tv_usec ) - ( ( int ) now_time.tv_usec ) + 1000000 / PULSE_PER_SECOND;
secDelta = ( ( int ) last_time.tv_sec ) - ( ( int ) now_time.tv_sec );
while ( usecDelta < 0 )
{
usecDelta += 1000000;
secDelta -= 1;
}
while ( usecDelta >= 1000000 )
{
usecDelta -= 1000000;
secDelta += 1;
}
if ( secDelta > 0 || ( secDelta == 0 && usecDelta > 0 ) )
{
struct timeval stall_time;
stall_time.tv_usec = usecDelta;
stall_time.tv_sec = secDelta;
if ( select ( 0, NULL, NULL, NULL, &stall_time ) < 0 && errno != EINTR )
{
perror ( "game_loop: select: stall" );
exit ( 1 );
}
}
}
gettimeofday ( &last_time, NULL );
current_time = ( time_t ) last_time.tv_sec;
}
fflush ( stderr ); /* make sure strerr is flushed */
return;
}
void new_descriptor ( int new_desc )
{
char buf[MAX_STRING_LENGTH];
DESCRIPTOR_DATA *dnew;
struct sockaddr_in sock;
struct hostent *from;
size_t desc, size;
char bugbuf[MAX_STRING_LENGTH];
size = sizeof ( sock );
if ( check_bad_desc ( new_desc ) )
{
set_alarm ( 0 );
return;
}
set_alarm ( 20 );
alarm_section = "new_descriptor::accept";
if ( ( desc = accept ( new_desc, ( struct sockaddr * ) &sock, &size ) ) < 0 )
{
perror ( "New_descriptor: accept" );
sprintf ( bugbuf, "[*****] BUG: New_descriptor: accept" );
log_string_plus ( bugbuf, LOG_COMM, sysdata.log_level );
set_alarm ( 0 );
return;
}
if ( check_bad_desc ( new_desc ) )
{
set_alarm ( 0 );
return;
}
#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif
set_alarm ( 20 );
alarm_section = "new_descriptor: after accept";
if ( fcntl ( desc, F_SETFL, FNDELAY ) == -1 )
{
perror ( "New_descriptor: fcntl: FNDELAY" );
set_alarm ( 0 );
return;
}
if ( check_bad_desc ( new_desc ) )
return;
CREATE ( dnew, DESCRIPTOR_DATA, 1 );
dnew->next = NULL;
dnew->descriptor = desc;
dnew->connected = CON_GET_NAME;
dnew->outsize = 2000;
dnew->idle = 0;
dnew->lines = 0;
dnew->scrlen = 24;
dnew->port = ntohs ( sock.sin_port );
dnew->newstate = 0;
dnew->prevcolor = 0x07;
CREATE ( dnew->outbuf, char, dnew->outsize );
strcpy ( buf, inet_ntoa ( sock.sin_addr ) );
sprintf ( log_buf, "Sock.sinaddr: %s, port %hd.", buf, dnew->port );
log_string_plus ( log_buf, LOG_COMM, sysdata.log_level );
if ( sysdata.NO_NAME_RESOLVING )
dnew->host = STRALLOC ( buf );
else
{
from = gethostbyaddr ( ( char * ) &sock.sin_addr, sizeof ( sock.sin_addr ), AF_INET );
dnew->host = STRALLOC ( ( char * ) ( from ? from->h_name : buf ) );
}
if ( check_total_bans ( dnew ) )
{
write_to_descriptor ( desc, "Your site has been banned from this Mud.\n\r", 0 );
free_desc ( dnew );
set_alarm ( 0 );
return;
}
/*
* Init descriptor data.
*/
if ( !last_descriptor && first_descriptor )
{
DESCRIPTOR_DATA *d;
bug ( "New_descriptor: last_desc is NULL, but first_desc is not! ...fixing" );
for ( d = first_descriptor; d; d = d->next )
if ( !d->next )
last_descriptor = d;
}
LINK ( dnew, first_descriptor, last_descriptor, next, prev );
/*
* Send the greeting.
*/
{
extern char *help_greeting;
if ( help_greeting[0] == '.' )
write_to_buffer ( dnew, help_greeting + 1, 0 );
else
write_to_buffer ( dnew, help_greeting, 0 );
}
if ( ++num_descriptors > sysdata.maxplayers )
sysdata.maxplayers = num_descriptors;
if ( sysdata.maxplayers > sysdata.alltimemax )
{
if ( sysdata.time_of_max )
DISPOSE ( sysdata.time_of_max );
sprintf ( buf, "%24.24s", ctime ( ¤t_time ) );
sysdata.time_of_max = str_dup ( buf );
sysdata.alltimemax = sysdata.maxplayers;
sprintf ( log_buf, "Broke all-time maximum player record: %d", sysdata.alltimemax );
log_string_plus ( log_buf, LOG_COMM, sysdata.log_level );
to_channel ( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
save_sysdata ( sysdata );
}
set_alarm ( 0 );
return;
}
void free_desc ( DESCRIPTOR_DATA * d )
{
closesocket ( d->descriptor );
STRFREE ( d->host );
DISPOSE ( d->outbuf );
if ( d->pagebuf )
DISPOSE ( d->pagebuf );
DISPOSE ( d );
return;
}
void close_socket ( DESCRIPTOR_DATA * dclose, bool force )
{
CHAR_DATA *ch;
DESCRIPTOR_DATA *d;
bool DoNotUnlink = FALSE;
/*
* flush outbuf
*/
if ( !force && dclose->outtop > 0 )
flush_buffer ( dclose, FALSE );
/*
* say bye to whoever's snooping this descriptor
*/
if ( dclose->snoop_by )
write_to_buffer ( dclose->snoop_by, "Your victim has left the game.\n\r", 0 );
/*
* stop snooping everyone else
*/
for ( d = first_descriptor; d; d = d->next )
if ( d->snoop_by == dclose )
d->snoop_by = NULL;
/*
* Check for switched people who go link-dead. -- Altrag
*/
if ( dclose->original )
{
if ( ( ch = dclose->character ) != NULL )
do_return ( ch, "" );
else
{
bug ( "Close_socket: dclose->original without character %s", ( dclose->original->name ? dclose->original->name : "unknown" ) );
dclose->character = dclose->original;
dclose->original = NULL;
}
}
ch = dclose->character;
/*
* sanity check :(
*/
if ( !dclose->prev && dclose != first_descriptor )
{
DESCRIPTOR_DATA *dp, *dn;
bug ( "Close_socket: %s desc:%p != first_desc:%p and desc->prev = NULL!", ch ? ch->name : d->host, dclose, first_descriptor );
dp = NULL;
for ( d = first_descriptor; d; d = dn )
{
dn = d->next;
if ( d == dclose )
{
bug ( "Close_socket: %s desc:%p found, prev should be:%p, fixing.", ch ? ch->name : d->host, dclose, dp );
dclose->prev = dp;
break;
}
dp = d;
}
if ( !dclose->prev )
{
bug ( "Close_socket: %s desc:%p could not be found!.", ch ? ch->name : dclose->host, dclose );
DoNotUnlink = TRUE;
}
}
if ( !dclose->next && dclose != last_descriptor )
{
DESCRIPTOR_DATA *dp, *dn;
bug ( "Close_socket: %s desc:%p != last_desc:%p and desc->next = NULL!", ch ? ch->name : d->host, dclose, last_descriptor );
dn = NULL;
for ( d = last_descriptor; d; d = dp )
{
dp = d->prev;
if ( d == dclose )
{
bug ( "Close_socket: %s desc:%p found, next should be:%p, fixing.", ch ? ch->name : d->host, dclose, dn );
dclose->next = dn;
break;
}
dn = d;
}
if ( !dclose->next )
{
bug ( "Close_socket: %s desc:%p could not be found!.", ch ? ch->name : dclose->host, dclose );
DoNotUnlink = TRUE;
}
}
if ( dclose->character )
{
sprintf ( log_buf, "Closing link to %s.", ch->pcdata->filename );
log_string_plus ( log_buf, LOG_COMM, UMAX ( sysdata.log_level, ch->level ) );
/*
* if ( ch->level < LEVEL_DEMI )
* to_channel( log_buf, CHANNEL_MONITOR, "Monitor", ch->level );
*/
if ( ( dclose->connected == CON_PLAYING || dclose->connected == CON_EDITING ) )
{
act ( AT_ACTION, "$n has lost $s link.", ch, NULL, NULL, TO_CANSEE );
ch->desc = NULL;
ch->pcdata->mip_ver = STRALLOC ( "" );
ch->pcdata->sec_code = STRALLOC ( "" );
xREMOVE_BIT ( ch->act, PLR_MIP );
}
else
{
/*
* clear descriptor pointer to get rid of bug message in log
*/
dclose->character->desc = NULL;
free_char ( dclose->character );
}
}
if ( !DoNotUnlink )
{
/*
* make sure loop doesn't get messed up
*/
if ( d_next == dclose )
d_next = d_next->next;
UNLINK ( dclose, first_descriptor, last_descriptor, next, prev );
}
if ( dclose->descriptor == maxdesc )
--maxdesc;
free_desc ( dclose );
--num_descriptors;
return;
}
bool read_from_descriptor ( DESCRIPTOR_DATA * d )
{
int iStart, iErr;
/*
* Hold horses if pending command already.
*/
if ( d->incomm[0] != STRING_NULL )
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" "You cannot enter the same command more than 20 consecutive times!\n\r", 0 );
return FALSE;
}
for ( ;; )
{
int nRead;
nRead = recv ( d->descriptor, d->inbuf + iStart, sizeof ( d->inbuf ) - 10 - iStart, 0 );
iErr = errno;
if ( nRead > 0 )
{
iStart += nRead;
if ( d->inbuf[iStart - 1] == '\n' || d->inbuf[iStart - 1] == '\r' )
break;
}
else if ( nRead == 0 )
{
log_string_plus ( "EOF encountered on read.", LOG_COMM, sysdata.log_level );
return FALSE;
}
else if ( iErr == EWOULDBLOCK )
break;
else
{
perror ( "Read_from_descriptor" );
return FALSE;
}
}
d->inbuf[iStart] = STRING_NULL;
return TRUE;
}
/*
* Transfer one line from input buffer to input line.
*/
void read_from_buffer ( DESCRIPTOR_DATA * d )
{
int i, j, k;
/*
* Hold horses if pending command already.
*/
if ( d->incomm[0] != STRING_NULL )
return;
/*
* Look for at least one new line.
*/
for ( i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r' && i < MAX_INBUF_SIZE; i++ )
{
if ( d->inbuf[i] == STRING_NULL )
return;
}
/*
* Canonical input processing.
*/
for ( i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ )
{
if ( k >= 508 )
{
write_to_descriptor ( d->descriptor, "Line too long.\n\r", 0 );
/*
* skip the rest of the line
*/
/*
* for ( ; d->inbuf[i] != STRING_NULL || i>= MAX_INBUF_SIZE ; i++ )
* {
* if ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' )
* break;
* }
*/
d->inbuf[i] = '\n';
d->inbuf[i + 1] = STRING_NULL;
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] = STRING_NULL;
/*
* Deal with bozos with #repeat 1000 ...
*/
if ( k > 1 || d->incomm[0] == '!' )
{
if ( d->incomm[0] != '!' && strcmp ( d->incomm, d->inlast ) )
{
d->repeat = 0;
}
}
/*
* Do '!' substitution.
*/
if ( d->incomm[0] == '!' )
strcpy ( d->incomm, d->inlast );
else
strcpy ( d->inlast, d->incomm );
/*
* Shift the input buffer.
*/
while ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' )
i++;
for ( j = 0; ( d->inbuf[j] = d->inbuf[i + j] ) != STRING_NULL; j++ )
;
return;
}
/*
* Low level output function.
*/
bool flush_buffer ( DESCRIPTOR_DATA * d, bool fPrompt )
{
char buf[MAX_INPUT_LENGTH * UMAX ( 1, d->speed ) ];
/*
* If buffer has more than 4K inside, spit out .5K at a time -Thoric
*/
if ( !mud_down && d->outtop > client_speed ( d->speed ) )
{
memcpy ( buf, d->outbuf, client_speed ( d->speed ) );
d->outtop -= client_speed ( d->speed );
memmove ( d->outbuf, d->outbuf + client_speed ( d->speed ), d->outtop );
if ( d->snoop_by )
{
char snoopbuf[MAX_INPUT_LENGTH * UMAX ( 1, d->speed ) ];
buf[client_speed ( d->speed ) ] = STRING_NULL;
if ( d->character && d->character->name )
{
if ( d->original && d->original->name )
sprintf ( snoopbuf, "%s (%s)", d->character->name, d->original->name );
else
sprintf ( snoopbuf, "%s", d->character->name );
write_to_buffer ( d->snoop_by, snoopbuf, 0 );
}
write_to_buffer ( d->snoop_by, "% ", 2 );
write_to_buffer ( d->snoop_by, buf, 0 );
}
if ( !write_to_descriptor ( d->descriptor, buf, client_speed ( d->speed ) ) )
{
d->outtop = 0;
return FALSE;
}
return TRUE;
}
/*
* Bust a prompt.
*/
if ( fPrompt && !mud_down && d->connected == CON_PLAYING )
{
CHAR_DATA *ch;
ch = d->original ? d->original : d->character;
if ( xIS_SET ( ch->act, PLR_BLANK ) )
write_to_buffer ( d, "\n\r", 2 );
if ( xIS_SET ( ch->act, PLR_PROMPT ) )
display_prompt ( d );
send_mip_points ( ch );
if ( ch->fighting )
send_mip_attacker ( ch );
if ( xIS_SET ( ch->act, PLR_TELNET_GA ) )
write_to_buffer ( d, go_ahead_str, 0 );
}
/*
* Short-circuit if nothing to write.
*/
if ( d->outtop == 0 )
return TRUE;
/*
* Snoop-o-rama.
*/
if ( d->snoop_by )
{
/*
* without check, 'force mortal quit' while snooped caused crash, -h
*/
if ( d->character && d->character->name )
{
/*
* Show original snooped names. -- Altrag
*/
if ( d->original && d->original->name )
sprintf ( buf, "%s (%s)", d->character->name, d->original->name );
else
sprintf ( buf, "%s", d->character->name );
write_to_buffer ( d->snoop_by, buf, 0 );
}
write_to_buffer ( d->snoop_by, "% ", 2 );
write_to_buffer ( d->snoop_by, d->outbuf, d->outtop );
}
/*
* OS-dependent output.
*/
if ( !write_to_descriptor ( d->descriptor, d->outbuf, d->outtop ) )
{
d->outtop = 0;
return FALSE;
}
else
{
d->outtop = 0;
return TRUE;
}
}
/*
* Append onto an output buffer.
*/
void write_to_buffer ( DESCRIPTOR_DATA * d, const char *txt, int length )
{
if ( !d )
{
bug ( "Write_to_buffer: NULL descriptor" );
return;
}
/*
* Normally a bug... but can happen if loadup is used.
*/
if ( !d->outbuf )
return;
/*
* Find length in case caller didn't.
*/
if ( length <= 0 )
length = strlen ( txt );
/*
* Uncomment if debugging or something
* if ( length != strlen(txt) )
* {
* bug( "Write_to_buffer: length(%d) != strlen(txt)!", length );
* length = strlen(txt);
* }
*/
/*
* Initial \n\r if needed.
*/
if ( d->outtop == 0 && !d->fcommand )
{
d->outbuf[0] = '\n';
d->outbuf[1] = '\r';
d->outtop = 2;
}
/*
* Expand the buffer as needed.
*/
while ( d->outtop + length >= d->outsize )
{
if ( d->outsize > 32000 )
{
/*
* empty buffer
*/
d->outtop = 0;
/*
* Bugfix by Samson - moved bug() call up
*/
bug ( "Buffer overflow. Closing (%s).", d->character ? d->character->name : "???" );
close_socket ( d, TRUE );
return;
}
d->outsize *= 2;
RECREATE ( d->outbuf, char, d->outsize );
}
/*
* Copy.
*/
strncpy ( d->outbuf + d->outtop, txt, length );
d->outtop += length;
d->outbuf[d->outtop] = STRING_NULL;
return;
}
/*
* Lowest level output function. Write a block of text to the file descriptor.
* If this gives errors on very long blocks (like 'ofind all'), try lowering
* the max block size.
*
* Added block checking to prevent random booting of the descriptor. Thanks go
* out to Rustry for his suggestions. -Orion
*/
bool write_to_descriptor ( int desc, char *txt, int length )
{
int iStart = 0;
int nWrite = 0;
int nBlock = 0;
int iErr = 0;
if ( length <= 0 )
length = strlen ( txt );
for ( iStart = 0; iStart < length; iStart += nWrite )
{
nBlock = UMIN ( length - iStart, 4096 );
nWrite = send ( desc, txt + iStart, nBlock, 0 );
if ( nWrite == -1 )
{
iErr = errno;
if ( iErr == EWOULDBLOCK )
{
/*
* This is a SPAMMY little bug error. I would suggest
* not using it, but I've included it in case. -Orion
*
perror( "Write_to_descriptor: Send is blocking" );
*/
nWrite = 0;
continue;
}
else
{
perror ( "Write_to_descriptor" );
return FALSE;
}
}
}
return TRUE;
}
/*
* Deal with sockets that haven't logged in yet.
*/
void nanny ( DESCRIPTOR_DATA * d, char *argument )
{
/*
* extern int lang_array[];
* extern char *lang_names[];
*/
char buf[MAX_STRING_LENGTH];
char arg[MAX_STRING_LENGTH];
CHAR_DATA *ch;
char *pwdnew;
char *p;
int iRace, Class;
bool fOld, chk;
if ( d->connected != CON_NOTE_TEXT )
{
while ( isspace ( *argument ) )
argument++;
}
ch = d->character;
switch ( d->connected )
{
default:
bug ( "Nanny: bad d->connected %d.", d->connected );
close_socket ( d, TRUE );
return;
case CON_OEDIT:
oedit_parse ( d, argument );
break;
case CON_REDIT:
redit_parse ( d, argument );
break;
case CON_MEDIT:
medit_parse ( d, argument );
break;
case CON_GET_NAME:
if ( argument[0] == STRING_NULL )
{
close_socket ( d, FALSE );
return;
}
argument[0] = UPPER ( argument[0] );
if ( !check_parse_name ( argument, ( d->newstate != 0 ) ) )
{
sprintf ( buf, "Sorry, %s does not pass the profanity filter, Please enter another name: ", argument );
write_to_buffer ( d, buf, 0 );
return;
}
if ( !str_cmp ( argument, "Register" ) )
{
if ( d->newstate == 0 )
{
if ( sysdata.DENY_NEW_PLAYERS == TRUE )
{
send_to_desc_color ( "\033[1;35mPlease be patient, the game server is currently undergoing routine system \n\r", d );
send_to_desc_color ( "\033[1;35mmaintainence. New players are not accepted at this time. Please try again \n\r", d );
send_to_desc_color ( "\033[1;35min 5 minuites.\n\r", d );
close_socket ( d, FALSE );
}
send_to_desc_color ( "\n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;36m New Character Name \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;37m Naming of your character is a very important aspect of playing The Fury .PvP. \n\r", d );
send_to_desc_color ( "\033[0;37m make sure that the name fits in a medieval martial arts style of game. The \n\r", d );
send_to_desc_color ( "\033[0;37m name should not contain profanity, be a common name or the name of an object, \n\r", d );
send_to_desc_color ( "\033[0;37m it should also not be a name of a movie, book or other fictional character. \n\r", d );
send_to_desc_color ( "\033[0;37m If your name does not meet these criteria, you will be asked to change it to \n\r", d );
send_to_desc_color ( "\033[0;37m something more appropriate by The Fury .PvP. the game staff [GM] or [GS] \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[1;37m Please enter the name of your new character: \n\r", d );
d->newstate++;
d->connected = CON_GET_NAME;
return;
}
else
{
send_to_desc_color ( "\033[1;35mSorry, %s does not pass the profanity filter, Please enter another name: \n\r", d );
return;
}
}
if ( check_playing ( d, argument, FALSE ) == BERR )
{
send_to_desc_color ( "\033[1;37mName: ", d );
return;
}
fOld = load_char_obj ( d, argument, TRUE, FALSE );
if ( !d->character )
{
sprintf ( log_buf, "Bad player file %s@%s.", argument, d->host );
log_string ( log_buf );
send_to_desc_color ( "\033[0;31mYour playerfile is corrupt...\033[1;31mPlease notify Tommi, \033[1;37meldhamud@gmail.com.\n\r", d );
close_socket ( d, FALSE );
return;
}
ch = d->character;
if ( check_bans ( ch, BAN_SITE ) )
{
send_to_desc_color ( "\033[0;32mCONGRATULATIONS...\033[1;31mYour site has been banned from this Mud.\n\r", d );
close_socket ( d, FALSE );
return;
}
if ( xIS_SET ( ch->act, PLR_DENY ) )
{
sprintf ( log_buf, "Denying access to %s@%s.", argument, d->host );
log_string_plus ( log_buf, LOG_COMM, sysdata.log_level );
if ( d->newstate != 0 )
{
sprintf ( buf, "Sorry, the name %s has been taken, Please enter another name: \n\r", argument );
write_to_buffer ( d, buf, 0 );
d->connected = CON_GET_NAME;
d->character->desc = NULL;
free_char ( d->character ); /* Big Memory Leak before --Shaddai */
d->character = NULL;
return;
}
send_to_desc_color ( "\033[0;31mYou are denied access. \033[1;31mPlease contact eldhamud@gmail.com\n\r", d );
close_socket ( d, FALSE );
return;
}
chk = check_reconnect ( d, argument, FALSE );
if ( chk == BERR )
return;
if ( chk )
{
fOld = TRUE;
}
else
{
if ( wizlock && !IS_IMMORTAL ( ch ) )
{
send_to_desc_color ( "\033[1;35mThe game is wizlocked. Only immortals can connect now.\n\r", d );
send_to_desc_color ( "\033[1;37mPlease try back later.\n\r", d );
close_socket ( d, FALSE );
return;
}
}
if ( fOld )
{
if ( d->newstate != 0 )
{
sprintf ( buf, "\033[1;35mSorry, the name %s has been taken, Please enter another name: \n\r", argument );
write_to_buffer ( d, buf, 0 );
d->connected = CON_GET_NAME;
d->character->desc = NULL;
free_char ( d->character ); /* Big Memory Leak before --Shaddai */
d->character = NULL;
return;
}
send_to_desc_color ( "\033[1;37mEnter Your Password: ", d );
d->connected = CON_GET_OLD_PASSWORD;
return;
}
else
{
if ( d->newstate == 0 )
{
send_to_desc_color ( "\n\r\033[1;35mNo such player exists.\n\r ", d );
send_to_desc_color ( "\033[1;35mPlease check your spelling or type \033[1;37mREGISTER \033[1;35mto start a new player.\n\r", d );
send_to_desc_color ( "\033[1;37mName: ", d );
d->connected = CON_GET_NAME;
d->character->desc = NULL;
free_char ( d->character ); /* Big Memory Leak before --Shaddai */
d->character = NULL;
return;
}
send_to_desc_color_args ( d, "\033[1;37mDo you wish for your name to be, %s \033[1;35m(Y/N)\033[1;37m?\n\r", argument );
d->connected = CON_CONFIRM_NEW_NAME;
return;
}
break;
case CON_GET_OLD_PASSWORD:
write_to_buffer ( d, "\r\n", 2 );
/* This if check is what you will want to keep once it is no longer necessary to convert pfiles */
if ( str_cmp ( sha256_crypt ( argument ), ch->pcdata->pwd ) )
{
write_to_buffer ( d, "Wrong password, disconnecting.\r\n", 0 );
/* clear descriptor pointer to get rid of bug message in log */
d->character->desc = NULL;
close_socket ( d, FALSE );
return;
}
write_to_buffer ( d, echo_on_str, 0 );
if ( check_playing ( d, ch->pcdata->filename, TRUE ) )
return;
chk = check_reconnect ( d, ch->pcdata->filename, TRUE );
if ( chk == BERR )
{
if ( d->character && d->character->desc )
d->character->desc = NULL;
close_socket ( d, FALSE );
return;
}
if ( chk == TRUE )
return;
strncpy ( buf, ch->pcdata->filename, MAX_STRING_LENGTH );
d->character->desc = NULL;
free_char ( d->character );
d->character = NULL;
fOld = load_char_obj ( d, buf, FALSE, FALSE );
ch = d->character;
if ( ch->position > POS_SITTING && ch->position < POS_STANDING )
ch->position = POS_STANDING;
sprintf ( log_buf, "%s@%s has connected.", ch->pcdata->filename, d->host );
log_string_plus ( log_buf, LOG_COMM, sysdata.log_level );
if ( ch->pcdata->version < 4 )
{
DISPOSE ( ch->pcdata->pwd );
ch->pcdata->pwd = str_dup ( sha256_crypt ( argument ) );
}
{
struct tm *tme;
time_t now;
char day[50];
now = time ( 0 );
tme = localtime ( &now );
strftime ( day, 50, "%a %b %d %H:%M:%S %Y", tme );
sprintf ( log_buf, "%-20s %-24s %s", ch->pcdata->filename, day, d->host );
write_last_file ( log_buf );
}
send_to_desc_color ( "\033[1;37mPress ENTER", d );
d->connected = CON_PRESS_ENTER;
break;
case CON_CONFIRM_NEW_NAME:
switch ( *argument )
{
case 'y':
case 'Y':
send_to_desc_color ( "\n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;36m Password \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;37m Your password should have a minimum of 8 characters and be a mixture of \n\r", d );
send_to_desc_color ( "\033[0;37m numbers and letters. The less obvious the password is the more secure your \n\r", d );
send_to_desc_color ( "\033[0;37m account will be and won't be easily guessed by anyone. Account security is \n\r", d );
send_to_desc_color ( "\033[0;37m your responcibility. \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color_args ( d, "\033[1;37m Please enter a password for %s: %s", ch->name, echo_off_str );
d->connected = CON_GET_NEW_PASSWORD;
break;
case 'n':
case 'N':
send_to_desc_color ( "\033[1;35mOk, what would you like your name to be: \n\r ", d );
d->character->desc = NULL;
free_char ( d->character );
d->character = NULL;
d->connected = CON_GET_NAME;
break;
default:
send_to_desc_color ( "\033[1;35mPlease type Yes or No: \n\r ", d );
break;
}
break;
case CON_GET_NEW_PASSWORD:
send_to_desc_color ( "\n\r", d );
if ( strlen ( argument ) < 8 )
{
send_to_desc_color ( "\033[1;35mThe minumum password lendth is 8 characters. Your password should\n\r", d );
send_to_desc_color ( "\033[1;35mcontain a mixture of Upper and Lower case letters and numbers.\n\r" "Password: ", d );
return;
}
if ( argument[0] == '!' )
{
send_to_desc_color ( "\033[1;35mNew password cannot begin with the '!' character.\n\r", d );
return;
}
pwdnew = sha256_crypt ( argument ); /* SHA-256 Encryption */
for ( p = pwdnew; *p != STRING_NULL; p++ )
{
if ( *p == '~' )
{
send_to_desc_color ( "\033[0;32mNew password not acceptable, cannot use the \033[1;37m~ \033[0;32mcharacter.\n\rPassword:\n\r", d );
return;
}
}
DISPOSE ( ch->pcdata->pwd );
ch->pcdata->pwd = str_dup ( pwdnew );
send_to_desc_color ( "\n\r\033[1;35mPlease retype the password to confirm:\n\r", d );
d->connected = CON_CONFIRM_NEW_PASSWORD;
break;
case CON_CONFIRM_NEW_PASSWORD:
write_to_buffer ( d, "\n\r", 2 );
if ( str_cmp ( sha256_crypt ( argument ), ch->pcdata->pwd ) )
{
send_to_desc_color ( "\033[1;35mPasswords don't match. \033[1;37mRetype password:\n\r", d );
d->connected = CON_GET_NEW_PASSWORD;
return;
}
send_to_desc_color ( "\n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;32m \033[0;36mSex \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;37m Would you like your character to be male or female? \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color_args ( d, "\033[0;37m Please enter the Sex of %s: ( M/F )? %s\n\r", ch->name, echo_on_str );
d->connected = CON_GET_NEW_SEX;
break;
case CON_GET_NEW_SEX:
switch ( argument[0] )
{
case 'm':
case 'M':
ch->sex = SEX_MALE;
break;
case 'f':
case 'F':
ch->sex = SEX_FEMALE;
break;
default:
write_to_buffer ( d, "\033[1;35mThat's not a sex. Would you like to be Male or Female?\n\r", 0 );
return;
}
send_to_desc_color ( "\n\r", d );
send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color( "\033[0;36m Race \n\r", d );
send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color( "\033[0;37m Race : Str+: Int+: Wis+: Dex+: Con+: Cha+: Lck+: Hp+: Ma+: Alignment: \n\r", d );
send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color( "\033[1;37m Default : 0 0 0 0 0 0 0 0 0 Good \n\r", d );
send_to_desc_color( "\033[1;37m You need to create your own races and edit the source in comm.c nanny function \n\r", d );
send_to_desc_color( "\033[0;36m \n\r", d );
send_to_desc_color( "\033[0;36m \n\r", d );
send_to_desc_color( "\033[1;31m \n\r", d );
send_to_desc_color( "\033[1;31m \n\r", d );
send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color_args( d, " \033[1;37mPlease enter the race of %s, or type help <name> for more information. \n\r", ch->name );
d->connected = CON_GET_NEW_RACE;
break;
case CON_GET_NEW_RACE:
argument = one_argument ( argument, arg );
if ( !str_cmp ( arg, "help" ) )
{
for ( iRace = 0; iRace < MAX_PC_RACE; iRace++ )
{
if ( toupper ( argument[0] ) == toupper ( race_table[iRace]->race_name[0] ) && !str_prefix ( argument, race_table[iRace]->race_name ) )
{
do_help ( ch, argument );
send_to_desc_color ( "\033[1;35mPlease choose a race:\033[0;37m\n\r", d );
return;
}
}
send_to_desc_color ( "\033[1;35mNo help on that topic. Please choose a race:\033[0;37m\n\r", d );
return;
}
for ( iRace = 0; iRace < MAX_PC_RACE; iRace++ )
{
if ( toupper ( arg[0] ) == toupper ( race_table[iRace]->race_name[0] ) && !str_prefix ( arg, race_table[iRace]->race_name ) )
{
ch->race = iRace;
break;
}
}
if ( iRace == MAX_PC_RACE || !race_table[iRace]->race_name
|| race_table[iRace]->race_name[0] == STRING_NULL || IS_SET ( race_table[iRace]->Class_restriction, 1 << ch->Class )
|| !str_cmp ( race_table[iRace]->race_name, "unused" ) )
{
send_to_desc_color ( "\033[1;35mThat's not a race. What would you like your race to be?\033[0;37m\n\r", d );
return;
}
send_to_desc_color ( "\n\r", d );
send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color( "\033[0;36m Class \n\r", d );
send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color( "\033[0;37m Class: Faction: School: Weapon: Armor: Major Ability \n\r", d );
send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color( "\033[1;31m Default Death Fire Blades Brigandine Physical \n\r", d );
send_to_desc_color( "\033[1;31m You need to create your own classes and edit the code in comm.c nanny function \n\r", d );
send_to_desc_color( "\033[0;36m \n\r", d );
send_to_desc_color( "\033[0;36m \n\r", d );
send_to_desc_color( "\033[1;34m \n\r", d );
send_to_desc_color( "\033[1;34m \n\r", d );
send_to_desc_color( "\033[1;32m \n\r", d );
send_to_desc_color( "\033[1;32m \n\r", d );
send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color_args( d, " \033[1;37mPlease enter the class of %s, or type help <name> for more information.\n\r", ch->name );
d->connected = CON_GET_NEW_CLASS;
break;
case CON_GET_NEW_CLASS:
argument = one_argument ( argument, arg );
if ( !str_cmp ( arg, "help" ) )
{
for ( Class = 0; Class < MAX_PC_CLASS; Class++ )
{
if ( Class_table[Class]->who_name && Class_table[Class]->who_name[0] != '\0' )
{
if ( toupper ( argument[0] ) == toupper ( Class_table[Class]->who_name[0] ) && !str_prefix ( argument, Class_table[Class]->who_name ) )
{
do_help ( ch, argument );
send_to_desc_color ( "\033[1;35mPlease choose a Class:\033[0;37m \n\r", d );
return;
}
}
}
send_to_desc_color ( "\033[1;35mSorry there is no such help topic for that class. Please choose a Class:\033[0;37m \n\r", d );
return;
}
for ( Class = 0; Class < MAX_PC_CLASS; Class++ )
{
if ( Class_table[Class]->who_name && Class_table[Class]->who_name[0] != '\0' )
{
if ( toupper ( arg[0] ) == toupper ( Class_table[Class]->who_name[0] ) && !str_prefix ( arg, Class_table[Class]->who_name ) )
{
ch->Class = Class;
break;
}
}
}
if ( Class == MAX_PC_CLASS || !Class_table[Class]->who_name || Class_table[Class]->who_name[0] == '\0' || !str_cmp ( Class_table[Class]->who_name, "unused" ) )
{
send_to_desc_color ( "\033[1;35mThat's not a Class. Please enter the name of the Class you would like to be? \033[0;37m \n\r", d );
return;
}
send_to_desc_color ( "\n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;36m Ansi Color \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;37m Would you like to start with ANSI Color turned on? Yes/No \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
d->connected = CON_GET_WANT_RIPANSI;
break;
case CON_GET_WANT_RIPANSI:
sprintf ( log_buf, "%s@%s new %s %s.", ch->name, d->host, race_table[ch->race]->race_name, Class_table[ch->Class]->who_name );
log_string_plus ( log_buf, LOG_COMM, sysdata.log_level );
to_channel ( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
ch->level = 0;
ch->position = POS_STANDING;
set_pager_color ( AT_PLAIN, ch );
switch ( argument[0] )
{
case 'y':
case 'Y':
xSET_BIT ( ch->act, PLR_ANSI );
send_to_char ( "Press ENTER", ch );
d->connected = CON_PRESS_ENTER;
break;
case 'n':
case 'N':
send_to_char ( "Press ENTER", ch );
d->connected = CON_PRESS_ENTER;
break;
default:
send_to_desc_color ( "\n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;36m Ansi Color \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
send_to_desc_color ( "\033[0;37m Would you like to start with ANSI Color turned on? Yes/No \n\r", d );
send_to_desc_color ( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
d->connected = CON_GET_WANT_RIPANSI;
break;
}
break;
case CON_PRESS_ENTER:
if ( IS_IMMORTAL ( ch ) )
{
send_to_desc_color ( "\n\r", d );
do_last ( ch, "10" );
do_help ( ch, "imotd" );
}
else
{
do_help ( ch, "motd" );
}
d->connected = CON_READ_MOTD;
break;
case CON_READ_MOTD:
{
if ( !IS_IMMORTAL ( ch ) )
{
sprintf ( buf, "\033[0;32m[&CANNOUNCEMENT\033[0;32m]&c %s has entered the game.", ch->name );
talk_info ( AT_GREEN, buf );
}
}
add_char ( ch );
d->connected = CON_PLAYING;
ch->desc->speed = 5;
if ( ch->level == 0 )
{
OBJ_DATA *obj;
int iLang;
ch->pcdata->clan = NULL;
// need to sort out attribute prime here.......
ch->perm_str += race_table[ch->race]->str_plus;
ch->perm_int += race_table[ch->race]->int_plus;
ch->perm_wis += race_table[ch->race]->wis_plus;
ch->perm_dex += race_table[ch->race]->dex_plus;
ch->perm_con += race_table[ch->race]->con_plus;
ch->perm_cha += race_table[ch->race]->cha_plus;
ch->affected_by = race_table[ch->race]->affected;
ch->perm_lck += race_table[ch->race]->lck_plus;
ch->armor += race_table[ch->race]->ac_plus;
ch->alignment += race_table[ch->race]->alignment;
ch->attacks = race_table[ch->race]->attacks;
ch->defenses = race_table[ch->race]->defenses;
ch->saving_poison_death = race_table[ch->race]->saving_poison_death;
ch->saving_wand = race_table[ch->race]->saving_wand;
ch->saving_para_petri = race_table[ch->race]->saving_para_petri;
ch->saving_breath = race_table[ch->race]->saving_breath;
ch->saving_spell_staff = race_table[ch->race]->saving_spell_staff;
ch->height = number_range ( race_table[ch->race]->height * .9, race_table[ch->race]->height * 1.1 );
ch->weight = number_range ( race_table[ch->race]->weight * .9, race_table[ch->race]->weight * 1.1 );
if ( ( iLang = skill_lookup ( "common" ) ) < 0 )
bug ( "Nanny: cannot find common language." );
else
ch->pcdata->learned[iLang] = 100;
for ( iLang = 0; lang_array[iLang] != LANG_UNKNOWN; iLang++ )
if ( lang_array[iLang] == race_table[ch->race]->language )
break;
if ( lang_array[iLang] == LANG_UNKNOWN )
bug ( "Nanny: invalid racial language." );
else
{
if ( ( iLang = skill_lookup ( lang_names[iLang] ) ) < 0 )
bug ( "Nanny: cannot find racial language." );
else
ch->pcdata->learned[iLang] = 100;
}
reset_colors ( ch );
ch->level = 1;
ch->exp = 0;
ch->max_hit += race_table[ch->race]->hit;
ch->max_mana += race_table[ch->race]->mana;
ch->hit = UMAX ( 1, ch->max_hit );
ch->mana = UMAX ( 1, ch->max_mana );
ch->move = ch->max_move;
ch->practice = 10; /*no more mr meany, better give them something to start with Tommi Aug 2005 */
ch->gold = 5000;
sprintf ( buf, "Fighter of Fury .PvP." );
set_title ( ch, buf );
/*
* Added by Narn. Start new characters with autoexit and autgold
* already turned on. Very few people don't use those.
*/
xSET_BIT ( ch->act, PLR_AUTOLOOT );
xSET_BIT ( ch->act, PLR_AUTOGOLD );
xSET_BIT ( ch->act, PLR_AUTOEXIT );
xSET_BIT ( ch->act, PLR_COMPASS );
xSET_BIT ( ch->act, PLR_AUTOMAP );
xSET_BIT ( ch->act, PLR_AUTOSAC );
SET_BIT ( ch->pcdata->flags, PCFLAG_DEADLY );
/*
* Outfit all players in basic equipments Tommi.
*/
if ( ch->Class == CLASS_BUSHI || ch->Class == CLASS_SHUGENJA )
{
{
OBJ_INDEX_DATA *obj_ind = get_obj_index ( 4014 );
if ( obj_ind != NULL )
{
obj = create_object ( obj_ind, 0 );
obj_to_char ( obj, ch );
equip_char ( ch, obj, WEAR_WIELD );
}
}
}
else if ( ch->Class == CLASS_WU_JEN || ch->Class == CLASS_KENSEI )
{
{
OBJ_INDEX_DATA *obj_ind = get_obj_index ( 4012 );
if ( obj_ind != NULL )
{
obj = create_object ( obj_ind, 0 );
obj_to_char ( obj, ch );
equip_char ( ch, obj, WEAR_WIELD );
}
}
}
else if ( ch->Class == CLASS_IAIJUTSU || ch->Class == CLASS_HENSHIN )
{
{
OBJ_INDEX_DATA *obj_ind = get_obj_index ( 4016 );
if ( obj_ind != NULL )
{
obj = create_object ( obj_ind, 0 );
obj_to_char ( obj, ch );
equip_char ( ch, obj, WEAR_WIELD );
}
}
}
else if ( ch->Class == CLASS_KISHI || ch->Class == CLASS_SOHEI )
{
{
OBJ_INDEX_DATA *obj_ind = get_obj_index ( 4018 );
if ( obj_ind != NULL )
{
obj = create_object ( obj_ind, 0 );
obj_to_char ( obj, ch );
equip_char ( ch, obj, WEAR_WIELD );
}
}
}
if ( !sysdata.WAIT_FOR_AUTH )
char_to_room ( ch, get_room_index ( ROOM_VNUM_SCHOOL ) );
}
else if ( !IS_IMMORTAL ( ch ) && ch->pcdata->release_date > 0 && ch->pcdata->release_date > current_time )
{
if ( ch->in_room->vnum == 6 || ch->in_room->vnum == 8 || ch->in_room->vnum == 1206 )
char_to_room ( ch, ch->in_room );
else
char_to_room ( ch, get_room_index ( 8 ) );
}
else if ( ch->in_room && ( IS_IMMORTAL ( ch ) || !xIS_SET ( ch->in_room->room_flags, ROOM_PROTOTYPE ) ) )
{
char_to_room ( ch, ch->in_room );
}
else if ( IS_IMMORTAL ( ch ) )
{
char_to_room ( ch, get_room_index ( ROOM_VNUM_CHAT ) );
}
else
{
char_to_room ( ch, get_room_index ( ROOM_VNUM_TEMPLE ) );
}
if ( get_timer ( ch, TIMER_SHOVEDRAG ) > 0 )
remove_timer ( ch, TIMER_SHOVEDRAG );
if ( get_timer ( ch, TIMER_PKILLED ) > 0 )
remove_timer ( ch, TIMER_PKILLED );
act ( AT_ACTION, "$n has entered the game.", ch, NULL, NULL, TO_CANSEE );
if ( ch->pcdata->pet )
{
act ( AT_ACTION, "$n returns to $s master from the Void.", ch->pcdata->pet, NULL, ch, TO_NOTVICT );
act ( AT_ACTION, "$N returns with you to the realms.", ch, NULL, ch->pcdata->pet, TO_CHAR );
}
send_to_char ( "Welcome to The Fury .PvP. Let the fury begin", ch );
do_look ( ch, "auto" );
if ( !ch->was_in_room && ch->in_room == get_room_index ( ROOM_VNUM_TEMPLE ) )
ch->was_in_room = get_room_index ( ROOM_VNUM_TEMPLE );
else if ( ch->was_in_room == get_room_index ( ROOM_VNUM_TEMPLE ) )
ch->was_in_room = get_room_index ( ROOM_VNUM_TEMPLE );
else if ( !ch->was_in_room )
ch->was_in_room = ch->in_room;
break;
}
return;
}
bool is_reserved_name ( char *name )
{
RESERVE_DATA *res;
for ( res = first_reserved; res; res = res->next )
if ( ( *res->name == '*' && !str_infix ( res->name + 1, name ) ) || !str_cmp ( res->name, name ) )
return TRUE;
return FALSE;
}
/*
* Parse a name for acceptability.
*/
bool check_parse_name ( char *name, bool newchar )
{
/*
* Names checking should really only be done on new characters, otherwise
* we could end up with people who can't access their characters. Would
* have also provided for that new area havoc mentioned below, while still
* disallowing current area mobnames. I personally think that if we can
* have more than one mob with the same keyword, then may as well have
* players too though, so I don't mind that removal. -- Alty
*/
if ( is_reserved_name ( name ) && newchar )
return FALSE;
/*
* Length restrictions.
*/
if ( strlen ( name ) < 3 )
return FALSE;
if ( strlen ( name ) > 12 )
return FALSE;
/*
* Alphanumerics only.
* Lock out IllIll twits.
*/
{
char *pc;
bool fIll;
fIll = TRUE;
for ( pc = name; *pc != STRING_NULL; pc++ )
{
if ( !isalpha ( *pc ) )
return FALSE;
if ( LOWER ( *pc ) != 'i' && LOWER ( *pc ) != 'l' )
fIll = FALSE;
}
if ( fIll )
return FALSE;
}
/*
* Code that followed here used to prevent players from naming
* themselves after mobs... this caused much havoc when new areas
* would go in...
*/
return TRUE;
}
/*
* Look for link-dead player to reconnect.
*/
bool check_reconnect ( DESCRIPTOR_DATA * d, char *name, bool fConn )
{
CHAR_DATA *ch;
for ( ch = first_char; ch; ch = ch->next )
{
if ( !IS_NPC ( ch ) && ( !fConn || !ch->desc ) && ch->pcdata->filename && !str_cmp ( name, ch->pcdata->filename ) )
{
if ( fConn && ch->switched )
{
write_to_buffer ( d, "Already playing.\n\rName: ", 0 );
d->connected = CON_GET_NAME;
if ( d->character )
{
/*
* clear descriptor pointer to get rid of bug message in log
*/
d->character->desc = NULL;
free_char ( d->character );
d->character = NULL;
}
return BERR;
}
if ( fConn == FALSE )
{
DISPOSE ( d->character->pcdata->pwd );
d->character->pcdata->pwd = str_dup ( ch->pcdata->pwd );
}
else
{
/*
* clear descriptor pointer to get rid of bug message in log
*/
d->character->desc = NULL;
free_char ( d->character );
d->character = ch;
ch->desc = d;
ch->timer = 0;
send_to_char ( "Reconnecting... Welcome back.\n\r", ch );
do_look ( ch, "auto" );
act ( AT_ACTION, "$n has reconnected.", ch, NULL, NULL, TO_CANSEE );
sprintf ( log_buf, "%s (%s) reconnected.", ch->name, d->host );
log_string_plus ( log_buf, LOG_COMM, UMAX ( sysdata.log_level, ch->level ) );
d->connected = CON_PLAYING;
}
return TRUE;
}
}
return FALSE;
}
/*
* Check if already playing.
*/
bool check_playing ( DESCRIPTOR_DATA * d, char *name, bool kick )
{
CHAR_DATA *ch;
DESCRIPTOR_DATA *dold;
int cstate;
for ( dold = first_descriptor; dold; dold = dold->next )
{
if ( dold != d && ( dold->character || dold->original ) && !str_cmp ( name, dold->original ? dold->original->pcdata->filename : dold->character->pcdata->filename ) )
{
cstate = dold->connected;
ch = dold->original ? dold->original : dold->character;
if ( !ch->name || ( cstate != CON_PLAYING && cstate != CON_EDITING ) )
{
write_to_buffer ( d, "Already connected - try again.\n\r", 0 );
sprintf ( log_buf, "%s already connected.", ch->pcdata->filename );
log_string_plus ( log_buf, LOG_COMM, sysdata.log_level );
return BERR;
}
if ( !kick )
return TRUE;
write_to_buffer ( d, "Already playing... Kicking off old connection.\n\r", 0 );
write_to_buffer ( dold, "Kicking off old connection... bye!\n\r", 0 );
close_socket ( dold, FALSE );
/*
* clear descriptor pointer to get rid of bug message in log
*/
d->character->desc = NULL;
free_char ( d->character );
d->character = ch;
ch->desc = d;
ch->timer = 0;
if ( ch->switched )
do_return ( ch->switched, "" );
ch->switched = NULL;
send_to_char ( "Reconnecting... Welcome back.\n\r", ch );
do_look ( ch, "auto" );
act ( AT_ACTION, "$n has reconnected, kicking off old link.", ch, NULL, NULL, TO_CANSEE );
sprintf ( log_buf, "%s@%s reconnected, kicking off old link.", ch->pcdata->filename, d->host );
log_string_plus ( log_buf, LOG_COMM, UMAX ( sysdata.log_level, ch->level ) );
/*
* if ( ch->level < LEVEL_SAVIOR )
* to_channel( log_buf, CHANNEL_MONITOR, "Monitor", ch->level );
*/
d->connected = cstate;
return TRUE;
}
}
return FALSE;
}
void stop_idling ( CHAR_DATA * ch )
{
ROOM_INDEX_DATA *was_in_room;
/*
* if ( !ch
* || !ch->desc
* || ch->desc->connected != CON_PLAYING
* || !ch->was_in_room
* || ch->in_room != get_room_index( ROOM_VNUM_LIMBO ) )
* return;
*/
if ( !ch || !ch->desc || ch->desc->connected != CON_PLAYING || !IS_IDLE ( ch ) )
return;
/*
* if ( IS_IMMORTAL(ch) )
* return;
*/
ch->timer = 0;
was_in_room = ch->was_in_room;
char_from_room ( ch );
char_to_room ( ch, was_in_room );
ch->was_in_room = ch->in_room;
/*
* ch->was_in_room = NULL;
*/
REMOVE_BIT ( ch->pcdata->flags, PCFLAG_IDLE );
act ( AT_ACTION, "$n has returned from the void.", ch, NULL, NULL, TO_ROOM );
return;
}
/*
* Function to strip off the "a" or "an" or "the" or "some" from an object's
* short description for the purpose of using it in a sentence sent to
* the owner of the object. (Ie: an object with the short description
* "a long dark blade" would return "long dark blade" for use in a sentence
* like "Your long dark blade". The object name isn't always appropriate
* since it contains keywords that may not look proper. -Thoric
*/
char *myobj ( OBJ_DATA * obj )
{
if ( !str_prefix ( "a ", obj->short_descr ) )
return obj->short_descr + 2;
if ( !str_prefix ( "an ", obj->short_descr ) )
return obj->short_descr + 3;
if ( !str_prefix ( "the ", obj->short_descr ) )
return obj->short_descr + 4;
if ( !str_prefix ( "some ", obj->short_descr ) )
return obj->short_descr + 5;
return obj->short_descr;
}
char *obj_short ( OBJ_DATA * obj )
{
static char buf[MAX_STRING_LENGTH];
if ( obj->count > 1 )
{
sprintf ( buf, "%s (%d)", obj->short_descr, obj->count );
return buf;
}
return obj->short_descr;
}
#define NAME(ch) (IS_NPC(ch) ? ch->short_descr : ch->name)
char *act_string ( const char *format, CHAR_DATA * to, CHAR_DATA * ch, void *arg1, void *arg2, int flags )
{
static char *const he_she[] = { "it", "he", "she" };
static char *const him_her[] = { "it", "him", "her" };
static char *const his_her[] = { "its", "his", "her" };
static char buf[MAX_STRING_LENGTH];
char fname[MAX_INPUT_LENGTH];
char *point = buf;
const char *str = format;
const char *i;
CHAR_DATA *vch = ( CHAR_DATA * ) arg2;
OBJ_DATA *obj1 = ( OBJ_DATA * ) arg1;
OBJ_DATA *obj2 = ( OBJ_DATA * ) arg2;
if ( str[0] == '$' )
DONT_UPPER = FALSE;
while ( *str != '\0' )
{
if ( *str != '$' )
{
*point++ = *str++;
continue;
}
++str;
if ( !arg2 && *str >= 'A' && *str <= 'Z' )
{
bug ( "Act: missing arg2 for code %c:", *str );
bug ( format );
i = " <@@@> ";
}
else
{
switch ( *str )
{
default:
bug ( "Act: bad code %c.", *str );
i = " <@@@> ";
break;
case 't':
i = ( char * ) arg1;
break;
case 'T':
i = ( char * ) arg2;
break;
case 'n':
i = ( to ? PERS ( ch, to, TRUE ) : NAME ( ch ) );
break;
case 'N':
i = ( to ? PERS ( vch, to, FALSE ) : NAME ( vch ) );
break;
case 'e':
if ( ch->sex > 2 || ch->sex < 0 )
{
bug ( "act_string: player %s has sex set at %d!", ch->name, ch->sex );
i = "it";
}
else
i = he_she[URANGE ( 0, ch->sex, 2 ) ];
break;
case 'E':
if ( vch->sex > 2 || vch->sex < 0 )
{
bug ( "act_string: player %s has sex set at %d!", vch->name, vch->sex );
i = "it";
}
else
i = he_she[URANGE ( 0, vch->sex, 2 ) ];
break;
case 'm':
if ( ch->sex > 2 || ch->sex < 0 )
{
bug ( "act_string: player %s has sex set at %d!", ch->name, ch->sex );
i = "it";
}
else
i = him_her[URANGE ( 0, ch->sex, 2 ) ];
break;
case 'M':
if ( vch->sex > 2 || vch->sex < 0 )
{
bug ( "act_string: player %s has sex set at %d!", vch->name, vch->sex );
i = "it";
}
else
i = him_her[URANGE ( 0, vch->sex, 2 ) ];
break;
case 's':
if ( ch->sex > 2 || ch->sex < 0 )
{
bug ( "act_string: player %s has sex set at %d!", ch->name, ch->sex );
i = "its";
}
else
i = his_her[URANGE ( 0, ch->sex, 2 ) ];
break;
case 'S':
if ( vch->sex > 2 || vch->sex < 0 )
{
bug ( "act_string: player %s has sex set at %d!", vch->name, vch->sex );
i = "its";
}
else
i = his_her[URANGE ( 0, vch->sex, 2 ) ];
break;
case 'q':
i = ( to == ch ) ? "" : "s";
break;
case 'Q':
i = ( to == ch ) ? "your" : his_her[URANGE ( 0, ch->sex, 2 ) ];
break;
case 'p':
i = ( !to || can_see_obj ( to, obj1 ) ? obj_short ( obj1 ) : "something" );
break;
case 'P':
i = ( !to || can_see_obj ( to, obj2 ) ? obj_short ( obj2 ) : "something" );
break;
case 'd':
if ( !arg2 || ( ( char * ) arg2 ) [0] == '\0' )
i = "door";
else
{
one_argument ( ( char * ) arg2, fname );
i = fname;
}
break;
}
}
++str;
while ( ( *point = *i ) != '\0' )
++point, ++i;
}
strcpy ( point, "\n\r" );
if ( !DONT_UPPER )
buf[0] = UPPER ( buf[0] );
return buf;
}
#undef NAME
void act ( short AType, const char *format, CHAR_DATA * ch, void *arg1, void *arg2, int type )
{
char *txt;
CHAR_DATA *to;
CHAR_DATA *vch = ( CHAR_DATA * ) arg2;
/*
* Discard null and zero-length messages.
*/
if ( !format || format[0] == STRING_NULL )
return;
if ( !ch )
{
bug ( "Act: null ch. (%s)", format );
return;
}
if ( !ch->in_room )
to = NULL;
else if ( type == TO_CHAR )
to = ch;
else
to = ch->in_room->first_person;
/*
* ACT_SECRETIVE handling
*/
if ( IS_NPC ( ch ) && xIS_SET ( ch->act, ACT_SECRETIVE ) && type != TO_CHAR )
return;
if ( type == TO_VICT )
{
if ( !vch )
{
bug ( "Act: null vch with TO_VICT." );
bug ( "%s (%s)", ch->name, format );
return;
}
if ( !vch->in_room )
{
bug ( "Act: vch in NULL room!" );
bug ( "%s -> %s (%s)", ch->name, vch->name, format );
return;
}
to = vch;
}
if ( MOBtrigger && type != TO_CHAR && type != TO_VICT && to )
{
OBJ_DATA *to_obj;
txt = act_string ( format, NULL, ch, arg1, arg2, STRING_IMM );
if ( HAS_PROG ( to->in_room, ACT_PROG ) )
rprog_act_trigger ( txt, to->in_room, ch, ( OBJ_DATA * ) arg1, ( void * ) arg2 );
for ( to_obj = to->in_room->first_content; to_obj; to_obj = to_obj->next_content )
if ( HAS_PROG ( to_obj->pIndexData, ACT_PROG ) )
oprog_act_trigger ( txt, to_obj, ch, ( OBJ_DATA * ) arg1, ( void * ) arg2 );
}
/*
* Anyone feel like telling me the point of looping through the whole
* room when we're only sending to one char anyways..? -- Alty
*/
for ( ; to; to = ( type == TO_CHAR || type == TO_VICT ) ? NULL : to->next_in_room )
{
if ( ( !to->desc && ( IS_NPC ( to ) && !HAS_PROG ( to->pIndexData, ACT_PROG ) ) ) || !IS_AWAKE ( to ) )
continue;
if ( type == TO_CHAR )
{
if ( to != ch )
continue;
if ( !is_same_map ( ch, to ) )
continue;
}
if ( type == TO_VICT && ( to != vch || to == ch ) )
continue;
if ( type == TO_ROOM )
{
if ( to == ch )
continue;
if ( !is_same_map ( ch, to ) )
continue;
}
if ( type == TO_NOTVICT )
{
if ( to == ch || to == vch )
continue;
if ( !is_same_map ( ch, to ) )
continue;
}
if ( type == TO_CANSEE )
{
if ( to == ch )
continue;
if ( IS_IMMORTAL ( ch ) && IS_PLR_FLAG ( ch, PLR_WIZINVIS ) )
{
if ( to->level < ch->pcdata->wizinvis )
continue;
if ( to->desc && is_inolc ( to->desc ) )
continue;
}
if ( !is_same_map ( ch, to ) )
continue;
}
if ( IS_IMMORTAL ( to ) )
txt = act_string ( format, to, ch, arg1, arg2, STRING_IMM );
else
txt = act_string ( format, to, ch, arg1, arg2, STRING_NONE );
if ( to->desc )
{
set_char_color ( AType, to );
send_to_char ( txt, to );
}
if ( MOBtrigger )
{
/*
* Note: use original string, not string with ANSI. -- Alty
*/
mprog_act_trigger ( txt, to, ch, ( OBJ_DATA * ) arg1, ( void * ) arg2 );
}
}
MOBtrigger = TRUE;
return;
}
char *default_fprompt ( CHAR_DATA * ch )
{
static char buf[60];
strcpy ( buf, "\033[1;31m<\033[1;33m%hhp &C%mm &G%vmv\033[1;31m %E> " );
if ( IS_NPC ( ch ) || IS_IMMORTAL ( ch ) )
strcat ( buf, "%i%R" );
return buf;
}
char *default_prompt ( CHAR_DATA * ch )
{
static char buf[60];
strcpy ( buf, "\033[1;31m<\033[1;33m%hhp &C%mm &G%vmv\033[1;31m> " );
if ( IS_NPC ( ch ) || IS_IMMORTAL ( ch ) )
strcat ( buf, "%i%R" );
return buf;
}
int getcolor ( char clr )
{
static const char colors[16] = "xrgObpcwzRGYBPCW";
int r;
for ( r = 0; r < 16; r++ )
if ( clr == colors[r] )
return r;
return -1;
}
void display_prompt ( DESCRIPTOR_DATA * d )
{
CHAR_DATA *ch = d->character;
CHAR_DATA *och = ( d->original ? d->original : d->character );
CHAR_DATA *victim;
bool ansi = ( !IS_NPC ( och ) && xIS_SET ( och->act, PLR_ANSI ) );
const char *prompt;
const char *helpstart = "<Type HELP START>";
char buf[MAX_STRING_LENGTH];
char *pbuf = buf;
int pstat, percent;
if ( !ch )
{
bug ( "display_prompt: NULL ch" );
return;
}
if ( !IS_NPC ( ch ) && !IS_SET ( ch->pcdata->flags, PCFLAG_HELPSTART ) )
prompt = helpstart;
else if ( !IS_NPC ( ch ) && ch->substate != SUB_NONE && ch->pcdata->subprompt && ch->pcdata->subprompt[0] != STRING_NULL )
prompt = ch->pcdata->subprompt;
else if ( IS_NPC ( ch ) || ( !ch->fighting && ( !ch->pcdata->prompt || !*ch->pcdata->prompt ) ) )
prompt = default_prompt ( ch );
else if ( ch->fighting )
{
if ( !ch->pcdata->fprompt || !*ch->pcdata->fprompt )
prompt = default_fprompt ( ch );
else
prompt = ch->pcdata->fprompt;
}
else
prompt = ch->pcdata->prompt;
if ( ansi )
{
strcpy ( pbuf, ANSI_RESET );
d->prevcolor = 0x08;
pbuf += 4;
}
/*
* Clear out old color stuff
*/
for ( ; *prompt; prompt++ )
{
/*
* '%' = prompt commands
* Note: foreground changes will revert background to 0 (black)
*/
if ( *prompt != '%' )
{
* ( pbuf++ ) = *prompt;
continue;
}
++prompt;
if ( !*prompt )
break;
if ( *prompt == * ( prompt - 1 ) )
{
* ( pbuf++ ) = *prompt;
continue;
}
switch ( * ( prompt - 1 ) )
{
default:
bug ( "Display_prompt: bad command char '%c'.", * ( prompt - 1 ) );
break;
case '%':
*pbuf = STRING_NULL;
pstat = 0x80000000;
switch ( *prompt )
{
case '%':
*pbuf++ = '%';
*pbuf = STRING_NULL;
break;
case 'a':
if ( ch->level >= 10 )
pstat = ch->alignment;
else if ( IS_GOOD ( ch ) )
strcpy ( pbuf, "good" );
else if ( IS_EVIL ( ch ) )
strcpy ( pbuf, "evil" );
else
strcpy ( pbuf, "neutral" );
break;
case 'A':
sprintf ( pbuf, "%s%s%s", IS_AFFECTED ( ch, AFF_INVISIBLE ) ? "I" : "", IS_AFFECTED ( ch, AFF_HIDE ) ? "H" : "", IS_AFFECTED ( ch, AFF_SNEAK ) ? "S" : "" );
break;
case 'C': /* Tank */
if ( !IS_IMMORTAL ( ch ) )
break;
if ( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
strcpy ( pbuf, "N/A" );
else if ( !victim->fighting || ( victim = victim->fighting->who ) == NULL )
strcpy ( pbuf, "N/A" );
else
{
if ( victim->max_hit > 0 )
percent = ( 100 * victim->hit ) / victim->max_hit;
else
percent = -1;
if ( percent >= 100 )
strcpy ( pbuf, "perfect health" );
else if ( percent >= 90 )
strcpy ( pbuf, "slightly scratched" );
else if ( percent >= 80 )
strcpy ( pbuf, "few bruises" );
else if ( percent >= 70 )
strcpy ( pbuf, "some cuts" );
else if ( percent >= 60 )
strcpy ( pbuf, "several wounds" );
else if ( percent >= 50 )
strcpy ( pbuf, "nasty wounds" );
else if ( percent >= 40 )
strcpy ( pbuf, "bleeding freely" );
else if ( percent >= 30 )
strcpy ( pbuf, "covered in blood" );
else if ( percent >= 20 )
strcpy ( pbuf, "leaking guts" );
else if ( percent >= 10 )
strcpy ( pbuf, "almost dead" );
else
strcpy ( pbuf, "DYING" );
}
break;
case 'c':
if ( !IS_IMMORTAL ( ch ) )
break;
if ( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
strcpy ( pbuf, "N/A" );
else
{
if ( victim->max_hit > 0 )
percent = ( 100 * victim->hit ) / victim->max_hit;
else
percent = -1;
if ( percent >= 100 )
strcpy ( pbuf, "perfect health" );
else if ( percent >= 90 )
strcpy ( pbuf, "slightly scratched" );
else if ( percent >= 80 )
strcpy ( pbuf, "few bruises" );
else if ( percent >= 70 )
strcpy ( pbuf, "some cuts" );
else if ( percent >= 60 )
strcpy ( pbuf, "several wounds" );
else if ( percent >= 50 )
strcpy ( pbuf, "nasty wounds" );
else if ( percent >= 40 )
strcpy ( pbuf, "bleeding freely" );
else if ( percent >= 30 )
strcpy ( pbuf, "covered in blood" );
else if ( percent >= 20 )
strcpy ( pbuf, "leaking guts" );
else if ( percent >= 10 )
strcpy ( pbuf, "almost dead" );
else
strcpy ( pbuf, "DYING" );
}
break;
case 'e':
if ( ( victim = who_fighting ( ch ) ) != NULL )
{
if ( victim->max_hit > 0 )
percent = ( 100 * victim->hit ) / victim->max_hit;
else
percent = -1;
if ( percent >= 65 )
sprintf ( pbuf, " \033[1;31mEnemy: &g%d%%", percent );
else if ( percent >= 25 && percent < 65 )
sprintf ( pbuf, " \033[1;31mEnemy: \033[1;33m%d%%", percent );
else
sprintf ( pbuf, " \033[1;31mEnemy: \033[0;31m%d%%", percent );
}
break;
case 'E':
if ( ( victim = who_fighting ( ch ) ) != NULL )
{
if ( victim->max_hit > 0 )
percent = ( 100 * victim->hit ) / victim->max_hit;
else
percent = -1;
if ( percent >= 100 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++&g++++\033[1;31m]&D" );
else if ( percent >= 90 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++&g+++ \033[1;31m]&D" );
else if ( percent >= 80 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++&g++ \033[1;31m]&D" );
else if ( percent >= 70 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++&g+ \033[1;31m]&D" );
else if ( percent >= 58 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++ \033[1;31m]&D" );
else if ( percent >= 45 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m++ \033[1;31m]&D" );
else if ( percent >= 30 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+ \033[1;31m]&D" );
else if ( percent >= 28 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m+++&x \033[1;31m]&D" );
else if ( percent >= 15 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m++&x \033[1;31m]&D" );
else if ( percent >= 8 )
strcpy ( pbuf, "\033[1;31mEnemy: [\033[0;31m+&x \033[1;31m]&D" );
else
strcpy ( pbuf, "\033[1;31mEnemy: [ \033[1;31m]&D" );
}
break;
case 'h':
pstat = ch->hit;
break;
case 'H':
pstat = ch->max_hit;
break;
case 'm':
pstat = ch->mana;
break;
case 'M':
pstat = ch->max_mana;
break;
case 'N': /* Tank */
if ( !IS_IMMORTAL ( ch ) )
break;
if ( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
strcpy ( pbuf, "N/A" );
else if ( !victim->fighting || ( victim = victim->fighting->who ) == NULL )
strcpy ( pbuf, "N/A" );
else
{
if ( ch == victim )
strcpy ( pbuf, "You" );
else if ( IS_NPC ( victim ) )
strcpy ( pbuf, victim->short_descr );
else
strcpy ( pbuf, victim->name );
pbuf[0] = UPPER ( pbuf[0] );
}
break;
case 'n':
if ( !IS_IMMORTAL ( ch ) )
break;
if ( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
strcpy ( pbuf, "N/A" );
else
{
if ( ch == victim )
strcpy ( pbuf, "You" );
else if ( IS_NPC ( victim ) )
strcpy ( pbuf, victim->short_descr );
else
strcpy ( pbuf, victim->name );
pbuf[0] = UPPER ( pbuf[0] );
}
break;
case 'T':
if ( time_info.hour < 5 )
strcpy ( pbuf, "night" );
else if ( time_info.hour < 6 )
strcpy ( pbuf, "dawn" );
else if ( time_info.hour < 19 )
strcpy ( pbuf, "day" );
else if ( time_info.hour < 21 )
strcpy ( pbuf, "dusk" );
else
strcpy ( pbuf, "night" );
break;
case 'b':
pstat = 0;
break;
case 'B':
pstat = 0;
break;
case 'u':
pstat = num_descriptors;
break;
case 'U':
pstat = sysdata.maxplayers;
break;
case 'v':
pstat = ch->move;
break;
case 'V':
pstat = ch->max_move;
break;
case 'g':
pstat = ch->gold;
break;
case 'r':
if ( IS_IMMORTAL ( och ) )
pstat = ch->in_room->vnum;
break;
case 'F':
if ( IS_IMMORTAL ( och ) )
sprintf ( pbuf, "%s", ext_flag_string ( &ch->in_room->room_flags, r_flags ) );
break;
case 'R':
if ( xIS_SET ( och->act, PLR_ROOMVNUM ) )
sprintf ( pbuf, "\033[1;31m<#%d>&D ", ch->in_room->vnum );
break;
case 'x':
pstat = ch->exp;
break;
case 'X':
pstat = exp_level ( ch, ch->level + 1 ) - ch->exp;
break;
case 'o': /* display name of object on auction */
if ( auction->item )
strcpy ( pbuf, auction->item->name );
break;
case 'S':
if ( ch->style == STYLE_BERSERK )
strcpy ( pbuf, "B" );
else if ( ch->style == STYLE_AGGRESSIVE )
strcpy ( pbuf, "A" );
else if ( ch->style == STYLE_DEFENSIVE )
strcpy ( pbuf, "D" );
else if ( ch->style == STYLE_EVASIVE )
strcpy ( pbuf, "E" );
else
strcpy ( pbuf, "S" );
break;
case 'i':
if ( ( !IS_NPC ( ch ) && xIS_SET ( ch->act, PLR_WIZINVIS ) ) || ( IS_NPC ( ch ) && xIS_SET ( ch->act, ACT_MOBINVIS ) ) )
sprintf ( pbuf, "(Invis %d) ", ( IS_NPC ( ch ) ? ch->mobinvis : ch->pcdata->wizinvis ) );
else if ( IS_AFFECTED ( ch, AFF_INVISIBLE ) )
sprintf ( pbuf, "(Invis) " );
break;
case 'I':
pstat = ( IS_NPC ( ch ) ? ( xIS_SET ( ch->act, ACT_MOBINVIS ) ? ch->mobinvis : 0 ) : ( xIS_SET ( ch->act, PLR_WIZINVIS ) ? ch->pcdata->wizinvis : 0 ) );
break;
}
if ( pstat != 0x80000000 )
sprintf ( pbuf, "%d", pstat );
pbuf += strlen ( pbuf );
break;
}
}
*pbuf = STRING_NULL;
send_to_char ( buf, ch );
send_to_char ( "\n\r", ch );
return;
}
void set_pager_input ( DESCRIPTOR_DATA * d, char *argument )
{
while ( isspace ( *argument ) )
argument++;
d->pagecmd = *argument;
return;
}
bool pager_output ( DESCRIPTOR_DATA * d )
{
register char *last;
CHAR_DATA *ch;
int pclines;
register int lines;
bool ret;
if ( !d || !d->pagepoint || d->pagecmd == -1 )
return TRUE;
ch = d->original ? d->original : d->character;
pclines = UMAX ( ch->pcdata->pagerlen, 5 ) - 1;
switch ( LOWER ( d->pagecmd ) )
{
default:
lines = 0;
break;
case 'b':
lines = -1 - ( pclines * 2 );
break;
case 'r':
lines = -1 - pclines;
break;
case 'n':
lines = 0;
pclines = 0x7FFFFFFF; /* As many lines as possible */
break;
case 'q':
d->pagetop = 0;
d->pagepoint = NULL;
flush_buffer ( d, TRUE );
DISPOSE ( d->pagebuf );
d->pagesize = MAX_STRING_LENGTH;
return TRUE;
}
while ( lines < 0 && d->pagepoint >= d->pagebuf )
if ( * ( --d->pagepoint ) == '\n' )
++lines;
if ( *d->pagepoint == '\n' && * ( ++d->pagepoint ) == '\r' )
++d->pagepoint;
if ( d->pagepoint < d->pagebuf )
d->pagepoint = d->pagebuf;
for ( lines = 0, last = d->pagepoint; lines < pclines; ++last )
if ( !*last )
break;
else if ( *last == '\n' )
++lines;
if ( *last == '\r' )
++last;
if ( last != d->pagepoint )
{
if ( !write_to_descriptor ( d->descriptor, d->pagepoint, ( last - d->pagepoint ) ) )
return FALSE;
d->pagepoint = last;
}
while ( isspace ( *last ) )
++last;
if ( !*last )
{
d->pagetop = 0;
d->pagepoint = NULL;
flush_buffer ( d, TRUE );
DISPOSE ( d->pagebuf );
d->pagesize = MAX_STRING_LENGTH;
return TRUE;
}
d->pagecmd = -1;
if ( xIS_SET ( ch->act, PLR_ANSI ) )
if ( write_to_descriptor ( d->descriptor, ANSI_LBLUE, 0 ) == FALSE )
return FALSE;
if ( ( ret = write_to_descriptor ( d->descriptor, "(C)ontinue, (N)on-stop, (R)efresh, (B)ack, (Q)uit: [C] ", 0 ) ) == FALSE )
return FALSE;
if ( xIS_SET ( ch->act, PLR_ANSI ) )
{
char buf[32];
sprintf ( buf, "%s", color_str ( d->pagecolor, ch ) );
ret = write_to_descriptor ( d->descriptor, buf, 0 );
}
return ret;
}
void do_speed ( CHAR_DATA * ch, char *argument )
{
short speed = atoi ( argument );
if ( !ch->desc )
return; /* Don't send messages to people who don't exist. duh. */
if ( argument[0] == STRING_NULL )
{
ch_printf ( ch, "Your present speed is a %d, which equates to %d bytes per second.\n\r", ch->desc->speed, client_speed ( ch->desc->speed ) );
return;
}
if ( speed > 8 || speed < 0 )
{
send_to_char ( "Speed is between 0 and 8.\n\r", ch );
return;
}
ch->desc->speed = speed;
ch_printf ( ch, "The MUD will now send output to you at %d bytes per second.\n\r", client_speed ( speed ) );
if ( client_speed ( speed ) > 2048 )
ch_printf ( ch, "You should be aware %d is fast enough to lag you if you have a slow connection.\n\r", client_speed ( speed ) );
return;
}
short client_speed ( short speed )
{
switch ( speed )
{
default:
break;
case 1:
return 512;
case 2:
return 1024;
case 3:
return 2048;
case 4:
return 3072;
case 5:
return 4096;
case 6:
return 5120;
case 7:
return 6144;
case 8:
return 7168;
}
return 512; /* Better than a mere default case. */
}
// const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, STRING_NULL };
// const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, STRING_NULL };
const char echo_new_str[] = { IAC, SB, 102 };
void do_telnet ( CHAR_DATA * ch, char *argument )
{
char buf[MAX_STRING_LENGTH];
sprintf ( buf, " %s %s: %s \n\r",echo_on_str ,argument ,echo_on_str );
write_to_buffer ( ch->desc, buf, 0 );
ch_printf ( ch, "%s sent", echo_new_str );
return;
}