/
Archipelago/
Archipelago/doc/
Archipelago/lib/misc/
Archipelago/lib/plrobjs/
Archipelago/lib/plrobjs/P-T/
Archipelago/lib/world/mob/
Archipelago/lib/world/obj/
Archipelago/lib/world/shp/
Archipelago/lib/world/wld/
Archipelago/lib/world/zon/
Archipelago/slave/
/* ************************************************************************
*   File: comm.c                                        Part of CircleMUD *
*  Usage: Communication, socket handling, main(), central game loop       *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993 by the Trustees of the Johns Hopkins University     *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */
/* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "limits.h"
#include "screen.h"
#include "spells.h"

#define MAX_HOSTNAME	256
#define OPT_USEC	250000  /* time delay corresponding to 4 passes/sec */

/* externs */
extern int	restrict;
extern int	mini_mud;
extern int	no_rent_check;
extern FILE	*player_fl;
extern int	DFLT_PORT;
extern char	*DFLT_DIR;
extern int	MAX_PLAYERS;
extern int	MAX_DESCRIPTORS_AVAILABLE;
extern struct   index_data *mob_index;
extern char     *revdirs[];
extern char     *dirs[];
extern struct   room_data *world;		
extern int	top_of_world;   	   	
extern struct   time_info_data time_info;	
extern char	help[];
extern int      slave_socket;
extern pid_t    slave_pid; 

struct spell_info_type spell_info[MAX_SPL_LIST];


/* local globals */
struct descriptor_data *descriptor_list, *next_to_process;
struct txt_block *bufpool = 0;	/* pool of large output buffers */
int	buf_largecount;		/* # of large buffers which exist */
int	buf_overflows;		/* # of overflows of output */
int	buf_switches;		/* # of switches from small to large buf */
int	circle_shutdown = 0;	/* clean shutdown*/
int	circle_reboot = 0;	/* reboot the game after a shutdown */
int	no_specials = 0;	/* Suppress ass. of special routines */
int	no_limited_check = 0;	/* Suppress ass. of special routines */
int	last_desc = 0;		/* last unique num assigned to a desc. */
int	mother_desc = 0;	/* file desc of the mother connection */
int	maxdesc;		/* highest desc num used */
int	avail_descs;		/* max descriptors available */
int	tics = 0;		/* for extern checkpointing */
int     pulse =0;               /* for fine grained event management */
int	port;
extern int	nameserver_is_slow;	/* see config.c */
extern int	auto_save;		/* see config.c */
extern int	autosave_time;		/* see config.c */
struct event_type *events[301];

/* functions in this file */
void    parse_prompt(struct char_data *ch, char *pmt);
void    parse_text(struct char_data *c,struct char_data *vict, int mode, char *pmt);
int	get_from_q(struct txt_q *queue, char *dest);
void	run_the_game(int port);
void	game_loop(int s);
int	init_socket(int port);
int	new_connection(int s);
int	new_descriptor(int s);
int	process_output(struct descriptor_data *t);
int	process_input(struct descriptor_data *t);
void	close_sockets(int s);
void	close_socket(struct descriptor_data *d);
struct timeval timediff(struct timeval *a, struct timeval *b);
void	flush_queues(struct descriptor_data *d);
void	nonblock(int s);
int	perform_subst(struct descriptor_data *t, char *orig, char *subst);
void    cmpact(char *input);
void    extract_event(struct char_data *ch);
void    add_event(int plse, int event, int inf1, int inf2, int inf3
	       , int inf4, char *arg, void *subj, void *vict);
void    process_events(int pulse);
static int get_slave_result(void);
/* extern fcnts */
char    *pluralise_string(char *arg);
void	boot_db(void);
void	zone_update(void);
void	affect_update(void); /* In spells.c */
void	point_update(void);  /* In limits.c */
void	mobile_activity(void);
void	room_activity(void);
void	string_add(struct descriptor_data *d, char *str);
void	perform_violence(void);
void	show_string(struct descriptor_data *d, char *input);
void	check_reboot(void);
int	isbanned(char *hostname);
void	weather_and_time(int mode);
void    shopedit(struct descriptor_data *d, char *str);
void    roomedit(struct descriptor_data *d, char *str);
void    mobedit(struct descriptor_data *d, char *str);
void    objedit(struct descriptor_data *d, char *str);
void    assign_levels();
char    *report_cost(int gold);
void    death_cry(struct char_data *ch);
int     spell_lev(struct char_data *caster, int spell);
char    *first_name(char *buf);
int     is_goditem(struct obj_data *j);
void    clear_queue(struct txt_q *queue);


/* *********************************************************************
*  main game loop and related stuff				       *
********************************************************************* */

int	main(int argc, char **argv)
{
    char	buf[512];
    int	pos = 1;
    char   *dir;

   port = DFLT_PORT;
   dir = DFLT_DIR;

   if (strstr(argv[0], "test")){
     no_limited_check = 1;
     logg("Suppressing check of limited equipment.");
   }
   while ((pos < argc) && (*(argv[pos]) == '-')) {
     switch (*(argv[pos] + 1)){
     case 'd':
       if (*(argv[pos] + 2))
	 dir = argv[pos] + 2;
       else if (++pos < argc)
	 dir = argv[pos];
       else {
	 logg("Directory arg expected after option -d.");
	 exit(0);
       }
       break;
     case 'm':
       mini_mud = 1;
       no_rent_check = 1;
       logg("Running in minimized mode & with no rent check.");
       break;
     case 'q':
       no_rent_check = 1;
       logg("Quick boot mode -- rent check suppressed.");
       break;
     case 'r':
       restrict = 1;
       logg("Restricting game -- no new players allowed.");
       break;
     case 's':
       no_specials = 1;
       logg("Suppressing assignment of special routines.");
       break;
     default:
       sprintf(buf, "SYSERR: Unknown option -%c in argument string.", *(argv[pos] + 1));
       logg(buf);
       break;
     }
     pos++;
   }
   
   if (pos < argc)
     if (!isdigit(*argv[pos])) {
       fprintf(stderr, "Usage: %s [-m] [-q] [-r] [-s] [-d pathname] [ port # ]\n", argv[0]);
       exit(0);
     }
   if (argv[pos])
     port = atoi(argv[pos]);

   assign_levels();
   sprintf(buf, "loading levels table");
   logg(buf);
   sprintf(buf, "Running game on port %d.", port);
   logg(buf);

   if (chdir(dir) < 0) {
      perror("Fatal error changing to data directory");
      exit(0);
   }

   sprintf(buf, "Using %s as data directory.", dir);
   logg(buf);

   srandom(time(0));

   run_the_game(port);
   return(0);
}





/* Init sockets, run game, and cleanup sockets */
void	run_the_game(int port)
{
   int	s;

   void	signal_setup(void);

   descriptor_list = NULL;

   logg("Signal trapping.");
   signal_setup();

   logg("Opening mother connection.");
   mother_desc = s = init_socket(port);

   boot_db();

   logg("Entering game loop.");

   game_loop(s);

   close_sockets(s);
   fclose(player_fl);

   if (circle_reboot) {
      logg("Rebooting.");
      exit(52);            /* what's so great about HHGTTG, anyhow? */
   }

   logg("Normal termination of game.");
}






/* Accept new connects, relay commands, and call 'heartbeat-functs' */
void	game_loop(int s)
{
   fd_set input_set, output_set, exc_set;
   struct timeval last_time, now, timespent, timeout, null_time;
   static struct timeval opt_time;
   char	comm[MAX_INPUT_LENGTH];
   char	prompt[MAX_INPUT_LENGTH];
   struct descriptor_data *point, *next_point;
   int mins_since_crashsave = 0, mask;
   int	i,sockets_connected, sockets_playing;
   char	buf[100];

   null_time.tv_sec = 0;
   null_time.tv_usec = 0;

   opt_time.tv_usec = OPT_USEC;  /* Init time values */
   opt_time.tv_sec = 0;
   gettimeofday(&last_time, (struct timezone *) 0);

   maxdesc = s;


#if defined (OPEN_MAX)
   avail_descs = OPEN_MAX - 8;
#elif defined (USE_TABLE_SIZE)
   {
      int retval;

      retval = setdtablesize(64);
      if (retval == -1)
        logg("SYSERR: unable to set table size");
      else {
         sprintf(buf, "%s %d\n", "dtablesize set to: ", retval);
         logg(buf);
      }
      avail_descs = getdtablesize() - 8;
   }
#else
   avail_descs = MAX_DESCRIPTORS_AVAILABLE;
#endif

   avail_descs = MIN(avail_descs, MAX_PLAYERS);

   mask = sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGINT) |  
      sigmask(SIGPIPE) | sigmask(SIGALRM) | sigmask(SIGTERM) | 
       sigmask(SIGURG) | sigmask(SIGXCPU) | sigmask(SIGHUP) | 	
       sigmask(SIGSEGV) | sigmask(SIGBUS);

   for (i=0;i<=300;i++)
       events[i] = 0;
   /* Main loop */
   while (!circle_shutdown) {
      /* Check what's happening out there */
      FD_ZERO(&input_set);
      FD_ZERO(&output_set);
      FD_ZERO(&exc_set);
      FD_SET(s, &input_set);
      if (slave_socket != -1) 
	FD_SET(slave_socket, &input_set);
      for (point = descriptor_list; point; point = point->next) {
	 FD_SET(point->descriptor, &input_set);
	 FD_SET(point->descriptor, &exc_set);
	 FD_SET(point->descriptor, &output_set);
      }

      /* check out the time */
      gettimeofday(&now, (struct timezone *) 0);
      timespent = timediff(&now, &last_time);
      timeout = timediff(&opt_time, &timespent);
      last_time.tv_sec = now.tv_sec + timeout.tv_sec;
      last_time.tv_usec = now.tv_usec + timeout.tv_usec;
      if (last_time.tv_usec >= 1000000) {
	 last_time.tv_usec -= 1000000;
	 last_time.tv_sec++;
      }

      sigsetmask(mask);

      if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time)
           < 0) {
	 perror("Select poll");
	 return;
      }

      if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) {
	 perror("Select sleep");
	 exit(1);
      }

/*      sigsetmask(0);  changed AJN 28th Sept. 94 */
       sigsetmask(sigmask(SIGPIPE));

      /* Respond to whatver might be happening */
       if (slave_socket != -1 && FD_ISSET( slave_socket, &input_set)) {
	 while (get_slave_result() == 0)
	   ;
       }  

       /* New connection? */
       if (FD_ISSET(s, &input_set))
	 if (new_descriptor(s) < 0)
	   perror("New connection");
       
       /* kick out the freaky folks */
       for (point = descriptor_list; point; point = next_point) {
	 next_point = point->next;
	 if (FD_ISSET(point->descriptor, &exc_set)) {
	   FD_CLR(point->descriptor, &input_set);
	   FD_CLR(point->descriptor, &output_set);
	   close_socket(point);
	 }
       }

       for (point = descriptor_list; point; point = next_point) {
	 next_point = point->next;
	 if (FD_ISSET(point->descriptor, &input_set))
	   if (process_input(point) < 0)
	     close_socket(point);
       }

      /* process_commands; */
       for (point = descriptor_list; point; point = next_to_process) {
	 next_to_process = point->next;
	 
	 if ((--(point->wait) <= 0) && get_from_q(&point->input, comm)) {
	   if (point->character && point->connected == CON_PLYNG && 
	        point->character->specials.was_in_room != NOWHERE) {
	      if (point->character->in_room != NOWHERE)
		 char_from_room(point->character);
	       char_to_room(point->character, 
			    point->character->specials.was_in_room, FALSE);
	       point->character->specials.was_in_room = NOWHERE;
	       act("$n has returned.", 	TRUE, point->character, 0, 0, TO_ROOM);
	    }

	    point->wait = 1;
	    if (point->character)
	      point->character->specials.timer = 0;
	    point->prompt_mode = 1;
	    
	    if (point->str)
	      string_add(point, comm);
	    else if (point->mob_edit)
	      mobedit(point, comm);
	    else if (point->obj_edit)
	      objedit(point, comm);
	    else if (point->room_edit)
	      roomedit(point, comm);
	    else if (point->shop_edit)
	      shopedit(point, comm);	    	    	    
	    else if (point->zone_edit)
	      zoneedit(point, comm);	    	    	    
	    else if (!point->connected)
	      if (point->showstr_point)
		show_string(point, comm);
	      else
		  command_interpreter(point->character, comm);
	    else
	      nanny(point, comm);
	 }
      }


      for (point = descriptor_list; point; point = next_point) {
	 next_point = point->next;
	 if (FD_ISSET(point->descriptor, &output_set) && *(point->output))
	    if (process_output(point) < 0)
	       close_socket(point);
	    else
	       point->prompt_mode = 1;
      }

      /* kick out the Phreaky Pholks II  -JE */
      for (point = descriptor_list; point; point = next_to_process) {
	 next_to_process = point->next;
	 if (STATE(point) == CON_CLOSE) 
	    close_socket(point);
      }


      /* give the people some prompts */
      for (point = descriptor_list; point; point = point->next){
	if (point->prompt_mode) {
	  if (point->str) {
	    write_to_descriptor(point->descriptor, "] ");
	  }
	  else if (point->mob_edit
		   || point->shop_edit
		   || point->obj_edit
		   || point->room_edit)
	    strcpy(prompt, "**Enter Q to quit**");
	  else if (!point->connected) {
	    if (point->showstr_point) {
	      strcpy(prompt, "[*** Press return to continue, q to quit ***]");
	    }
	    else {
	      parse_prompt(point->character, prompt);
	    }
	    write_to_descriptor(point->descriptor,  prompt);
	    SET_BIT(PLR_FLAGS(point->character), PLR_NEEDCRLF);
	  }
	  point->prompt_mode = 0;
	}
      }
      
      
      /* handle heartbeat stuff */
      /* Note: pulse now changes every 1/4 sec  */
      process_events(pulse); 
      pulse++;

      if (!(pulse % PULSE_ZONE))	zone_update();
      if (!(pulse % (2*PULSE_VIOLENCE))) mobile_activity();
      if (!(pulse % PULSE_ROOM))	room_activity();
      if (!(pulse % PULSE_ROOM))	object_activity(); 
      if (!(pulse % PULSE_VIOLENCE))	perform_violence();

      if (!(pulse % (SECS_PER_MUD_HOUR * 4))) {
	 weather_and_time(1);
	 affect_update();
	 point_update();
	 fflush(player_fl);
      }


      if (auto_save)
         if (!(pulse % (60 * 4))) /* one minute */
	    if (++mins_since_crashsave >= autosave_time) {
	       mins_since_crashsave = 0;
	       Crash_save_all();
	    }

      if (time_info.hours && !((time_info.hours*SECS_PER_MUD_HOUR*4 + pulse)  % 1200)) {
	 sockets_connected = sockets_playing = 0;

	 for (point = descriptor_list; point; point = next_point) {
	    next_point = point->next;
	    sockets_connected++;
	    if (!point->connected)
	       sockets_playing++;
	 }

	 sprintf(buf, "nusage: %-3d sockets connected, %-3d sockets playing",
	     sockets_connected,
	     sockets_playing);
	 logg(buf);

#ifdef RUSAGE
	  {
	    struct rusage rusagedata;

	    getrusage(0, &rusagedata);
	    sprintf(buf, "rusage: %d %d %d %d %d %d %d",
	        rusagedata.ru_utime.tv_sec,
	        rusagedata.ru_stime.tv_sec,
	        rusagedata.ru_maxrss,
	        rusagedata.ru_ixrss,
	        rusagedata.ru_ismrss,
	        rusagedata.ru_idrss,
	        rusagedata.ru_isrss);
	    logg(buf);
	 }
#endif 

      }

      if (pulse >= 300) {
	 pulse = 0;
	 check_reboot();
      }

      tics++;        /* tics since last checkpoint signal */
   }
}


/* ******************************************************************
*  general utility stuff (for local use)			    *
****************************************************************** */

void parse_prompt(struct char_data *ch, char *pmt)
{
    static const char *default_prompt = "H: %1%h%0/%H V: %5%v%0/%V Ex: %X%n>";
    char bff[512];
    char bff2[512];
    const char *string;
    const char *i;
    char *point;
    int percent, slev;

    if( ch->player.prmpt == NULL || ch->player.prmpt[0] == '\0' ) {
        string = default_prompt;
    }
    else{
        string = ch->player.prmpt;
    }
    bzero(bff,512);
    bzero(bff2,512);
    point = bff;
    while( *string != '\0' )
	{
	    if( *string != '%' )
		{
		    *point++ = *string++;
		    continue;
		}
	    ++string;
	    switch( *string )
		{
		default:
		    i = " "; break;
		case '0' :
		    sprintf(bff2,CCNRM(ch, C_NRM));
		    i = bff2; break;
		case '1' :
		    sprintf(bff2,CCRED(ch, C_NRM));
		    i = bff2; break;
		case '2' :
		    sprintf(bff2,CCGRN(ch, C_NRM));
		    i = bff2; break;
		case '3' :
		    sprintf(bff2,CCYEL(ch, C_NRM));
		    i = bff2; break;
		case '4' :
		    sprintf(bff2,CCBLU(ch, C_NRM));
		    i = bff2; break;
		case '5' :
		    sprintf(bff2,CCMAG(ch, C_NRM));
		    i = bff2; break;
		case '6' :
		    sprintf(bff2,CCCYN(ch, C_NRM));
		    i = bff2; break;
		case '7' :
		    sprintf(bff2,CCWHT(ch, C_NRM));
		    i = bff2; break;
		case '8' :
		    sprintf(bff2,CCBLK(ch, C_NRM));
		    i = bff2; break;		    
		case 'b' :
		    sprintf(bff2,CCBLD(ch, C_NRM));
		    i = bff2; break;
		case 'u' :
		    sprintf(bff2,CCUND(ch, C_NRM));
		    i = bff2; break;
		case 'n':
		    sprintf(bff2,"\r\n");
		    i = bff2; break;
		case 'i':
		    if (IS_AFFECTED(ch,AFF_INVISIBLE)){
			sprintf(bff2,"*invis*");
			i=bff2;break;}
		    else if (GET_INVIS_LEV(ch)){
			sprintf(bff2,"<i%d>",GET_INVIS_LEV(ch));
			i = bff2; break;}
                    *bff2 = '\0';
                    i = bff2;
		    break;
		case 'f':
		    if (IS_AFFECTED(ch,AFF_FLY)){
			sprintf(bff2,"*fly*");
			i = bff2; break;}
		    break;
		case 'w':
		    if (IS_AFFECTED(ch,AFF_WATER_BREATH)){
			sprintf(bff2,"*water*");
			i = bff2; break;}
		    break;
		case 'h' :
		    sprintf( bff2, "%d", ch->points.hit );
		    i = bff2; break;
		case 'H' :
		    sprintf( bff2, "%d", ch->points.max_hit );
		    i = bff2; break;
		case 'm' :
		    break;
		case 'M' :
		    break;
		case 'p' :
		    break;
		case 'P' :
		    break;
		case 'v' :
		    sprintf( bff2, "%d", ch->points.move );
		    i = bff2; break;
		case 'V' :
		    sprintf( bff2, "%d", ch->points.max_move );
		    i = bff2; break;
		case 'x' :
		    sprintf( bff2, "%d", ch->points.exp );
		    i = bff2; break;
		case 'X' :
		  slev = (levels_table[GET_LEVEL(ch)+1]
			   - levels_table[GET_LEVEL(ch)])/10;
		  sprintf( bff2, "%d",levels_table[GET_LEVEL(ch)] +
			   slev*(GET_SUB_LEVEL(ch) +1)
			    - GET_EXP(ch) );
		    i = bff2; break;
		case 'l':
		    if(ch->master && !IS_NPC(ch))
			sprintf( bff2, "%s",GET_NAME(ch->master));
		    else if (ch->master && !IS_NPC(ch))
			sprintf( bff2, "%s",PERS(ch->master, ch));
		    else
			sprintf( bff2,"%s", "nobody");
		    i = bff2; break;
		case 'L':
		    if (ch->master)
			sprintf( bff2, "%d",100*GET_HIT(ch->master)
				 /GET_MAX_HIT(ch->master));
		    else
			sprintf( bff2, " ");
		    i = bff2; break;
		case 'o' :
		    if (ch->specials.fighting)
			sprintf( bff2,"%s",PERS(ch->specials.fighting, ch));
		    else
			sprintf( bff2,"%s","nobody");
		    i = bff2; break;
		case 'O':
		    if (ch->specials.fighting){
			if (GET_MAX_HIT(ch->specials.fighting) >0)
			    percent = (100 * GET_HIT(ch->specials.fighting)/
				       GET_MAX_HIT(ch->specials.fighting));
			else
			    percent = -1;
			if (percent >= 100)
			    sprintf(bff2,"%s","unhurt");
			else if (percent >= 90)
			    sprintf(bff2,"%s","a few scratches");
			else if (percent >= 75)
			    sprintf(bff2,"%s","small wounds");
			else if (percent >= 50)
			    sprintf(bff2,"%s","quite a few wounds");
			else if (percent >= 30)
			    sprintf(bff2,"%s","big nasty wounds");
			else if (percent >= 15)
			    sprintf(bff2,"%s","pretty hurt");
			else if (percent >= 0)
			    sprintf(bff2,"%s","awful");
			else
			    sprintf(bff2,"%s","unconscious"); }
		    else
			sprintf(bff2,"%s"," ");
		    i = bff2; break;
		case 'g' :
		    sprintf( bff2, "%s",report_cost(ch->points.gold) );
		    i = bff2; break;
		case 'G' :
		    sprintf( bff2, "%s",report_cost(GET_BANK_GOLD(ch))); 
		    i = bff2; break;
		case 'a' :
		    if( ch->player.level < 5 )
			sprintf( bff2, "%d", ch->specials2.alignment );
		    else
			sprintf( bff2, "%s", IS_GOOD(ch) ? "good" : IS_EVIL(ch) ? 
				 "evil" : "neutral" );
		    i = bff2; break;
		case 'r' :
		    if( world[ch->in_room].name != NULL ){
			if (IS_DARK(ch->in_room) &&
			    !PRF_FLAGGED(ch, PRF_HOLYLIGHT))
			    sprintf( bff2, "Pitch Black");
			else
			    sprintf( bff2, "%s", world[ch->in_room].name );
		    }
		    else
			sprintf( bff2, " " );
		    i = bff2; break;		    
		case 'R' :
		    if( GET_LEVEL( ch ) >= LEVEL_BUILDER && world[ch->in_room].name != NULL )
			sprintf( bff2, "%d", world[ch->in_room].number );
		    else
			sprintf( bff2, " " );
		    i = bff2; break;
		case '%' :
		    sprintf( bff2, "%%" );
		    i = bff2; break;
		}
	    if (strlen(bff) + strlen(bff2) >= 510){
		    strcpy(pmt,bff);
		    return;}
	    ++string;
	    while( (*point = *i) != '\0')
		++point, ++i;      
	}
    point = bff;
    for ( ; *point != '\0'; point++ )
	{
	    if ( *point == '~' )
		*point = '-';
	}
    
    strcpy(pmt,bff);
    return; 
}
void parse_text(struct char_data *ch, struct char_data *vict, int mode,char *text)
{
  char bff[1024];
  char bff2[1024];
  char bff4[1024];
  char def[512];
  char defi[512];    
  const char *string;
  const char *i,*j;
  char *point,*pmt;
  bool start = TRUE, include = TRUE;
  int len=0;


  switch(mode){
  case 0:
    strcpy(defi,"%n the %R %l");
    strcpy(def,"%n the %l %c");
    if( ch->player.title == NULL || ch->player.title[0] == '\0')
      if ( GET_LEVEL(ch) < LEVEL_BUILDER)
	{
	  CREATE(GET_TITLE(ch), char, strlen(def) + 1);
	  strcpy(GET_TITLE(ch), def);
		    
	}
      else
	{
	  CREATE(GET_TITLE(ch), char, strlen(defi) + 1);
	  strcpy(GET_TITLE(ch), defi);
	}
    string = ch->player.title;
    break;
  case 1:
    strcpy(defi,"%n appears with an ear-splitting %bbang!%0");
    if( ch->specials.poofIn == NULL || ch->specials.poofIn[0] == '\0')
      {
	CREATE(ch->specials.poofIn, char, strlen(defi) + 1);
	strcpy(ch->specials.poofIn, defi);
      }
    string = ch->specials.poofIn;
    break;
  case 2:
    strcpy(defi,"%n disappears in a puff of logic.");
    if( ch->specials.poofOut == NULL || ch->specials.poofOut[0] == '\0')
      {
	CREATE(ch->specials.poofOut, char, strlen(defi) + 1);
	strcpy(ch->specials.poofOut, defi);
      }
    string = ch->specials.poofOut;
    break;
  }

  bzero(bff,1024);
  bzero(bff2,1024);
  point = bff;
  pmt = bff4;

  while( *string != '\0' )
    {
      if( *string != '%' )
	{
	  *point++ = *string++;
	  len++;
	  continue;
	}
      while(*(++string) == '%')
	;
      include  = TRUE;
      switch( *string )
	{
	default:
	  i = " "; break;
	case '0' :
	  sprintf(bff2,CCNRM(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case '1' :
	  sprintf(bff2,CCRED(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case '2' :
	  sprintf(bff2,CCGRN(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case '3' :
	  sprintf(bff2,CCYEL(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case '4' :
	  sprintf(bff2,CCBLU(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case '5' :
	  sprintf(bff2,CCMAG(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case '6' :
	  sprintf(bff2,CCCYN(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case '7' :
	  sprintf(bff2,CCWHT(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case '8' :
	  sprintf(bff2,CCBLK(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;		    
	case 'b' :
	  sprintf(bff2,CCBLD(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case 'u' :
	  sprintf(bff2,CCUND(vict, C_NRM));
	  include = FALSE;
	  i = bff2; break;
	case 'n':
	  if (GET_LEVEL(ch) < LEVEL_BUILDER)
	    sprintf(bff2,"%s",GET_NAME(ch));
	  else if (GET_INVIS_LEV(ch) > GET_LEVEL(vict))
	    {
	      for (j = bff;*j != '\0';j++) 
		if (*j != ' ')
		  start = FALSE; 
	      sprintf(bff2,(start ? "Someone": "someone"));
	    }
	  else
	    sprintf(bff2,"%s",GET_NAME(ch));
	  i = bff2; break;
	case 'o' :
	  if (ch->specials.fighting)
	    sprintf( bff2,"%s",PERS(ch->specials.fighting, ch));
	  else
	    sprintf( bff2,"%s","nobody");
	  i = bff2; break;
	case 'r' :
	  if( world[ch->in_room].name != NULL ){
	    if (IS_DARK(ch->in_room) &&
		!PRF_FLAGGED(ch, PRF_HOLYLIGHT))
	      sprintf( bff2, "Pitch Black");
	    else
	      sprintf( bff2, "%s", world[ch->in_room].name );
	  }
	  else
	    sprintf( bff2, " " );
	  i = bff2; break;
	case 'l':
	  if (GET_LEVEL(ch) < 4)
	    sprintf( bff2, "newbie");
	  else if (GET_LEVEL(ch) < 20)
	    sprintf( bff2, "wily");
	  else if (GET_LEVEL(ch) < 50)
	    sprintf( bff2, "tough");
	  else if (GET_LEVEL(ch) < 100)
	    sprintf( bff2, "experienced");
	  else if (GET_LEVEL(ch) < 150)
	    sprintf( bff2, "hardened");
	  else if (GET_LEVEL(ch) < LEVEL_IMMORT)
	    sprintf( bff2, "veteran");	  
	  else if (GET_LEVEL(ch) >= LEVEL_IMPL)
	    sprintf( bff2, "%s","Implementor");
	  else if (GET_LEVEL(ch) >= LEVEL_ASS_IMPL)
	    sprintf( bff2, "%s","Overlord");
	  else if (GET_LEVEL(ch) >= LEVEL_GRGOD)
	    sprintf( bff2, "%s","Djinn");
	  else if (GET_LEVEL(ch) >= LEVEL_GOD)
	    sprintf( bff2, "%s","Daemon");
	  else if (GET_LEVEL(ch) >= LEVEL_MBUILDER)
	    sprintf( bff2, "%s","Bodhisattva");
	  else if (GET_LEVEL(ch) >= LEVEL_BUILDER)
	    sprintf( bff2, "%s","Builder");
	  else if (GET_LEVEL(ch) >= LEVEL_IMMORT)
	    sprintf( bff2, "%s","Avatar");
	  else
	    sprintf( bff2, "%s","bug");
	  i = bff2; break;
	case 'c' :
	  switch(GET_RACE(ch)){
	  case 0:
	    sprintf( bff2, "%s", "error");
	    break;
	  case 1:
	    sprintf( bff2, "%s", "Human");
	    break;
	  case 2:
	    sprintf( bff2, "%s", "Elf");
	    break;
	  case 3:
	    sprintf( bff2, "%s", "Halfling");
	    break;
	  case 4:
	    sprintf( bff2, "%s", "Giant");
	    break;
	  case 5:
	    sprintf( bff2, "%s", "Gnome");
	    break;
	  case 6:
	    sprintf( bff2, "%s", "Half-elf");
	    break;
	  case 7:
	    sprintf( bff2, "%s", "Ogier");
	    break;
	  case 8:
	    sprintf( bff2, "%s", "Dwarf");
	    break;
	  case 9:
	    sprintf( bff2, "%s", "Selkie");
	    break;
	  case 10:
	    sprintf( bff2, "%s", "Pixie");
	    break;
	  case 11:
	    sprintf( bff2, "%s", "Amarya");
	    break;			
	  case 12:
	    sprintf( bff2, "%s", "Troll");
	    break;			
	  }
	  i = bff2; break;
	case 'R' :
	  switch(GET_RACE(ch)){
	  case 0:
	    sprintf( bff2, "%s", "error");
	    break;
	  case 1:
	    sprintf( bff2, "%s", "Human");
	    break;
	  case 2:
	    sprintf( bff2, "%s", "Elven");
	    break;
	  case 3:
	    sprintf( bff2, "%s", "Halfling");
	    break;
	  case 4:
	    sprintf( bff2, "%s", "Gargantuan");
	    break;
	  case 5:
	    sprintf( bff2, "%s", "Gnomish");
	    break;
	  case 6:
	    sprintf( bff2, "%s", "Half-elven");
	    break;
	  case 7:
	    sprintf( bff2, "%s", "Ogier");
	    break;
	  case 8:
	    sprintf( bff2, "%s", "Dwarven");
	    break;
	  case 9:
	    sprintf( bff2, "%s", "Selkan");
	    break;
	  case 10:
	    sprintf( bff2, "%s", "Pixie");
	    break;
	  case 11:
	    sprintf( bff2, "%s", "Amaryan");
	    break;
	  case 12:
	    sprintf( bff2, "%s", "Trollan");
	    break;			
	    
	  }
	  i = bff2; break;
	case '%' :
	  sprintf( bff2, "%%" );
	  i = bff2; break;
	}
      if (len + (include ? strlen(bff2) : 0) > 79){
	sprintf(text,"%s%s",bff,CCNRM(vict, C_NRM));
	return;
      }
      ++string;
      while( (*point = *i) != '\0'){
	++point, ++i;
	if (include)
	  ++len;
      }
    }
  point = bff;
  for ( ; *point != '\0'; point++ )
    {
      if ( *point == '~' )
	*point = '-';
    }
  sprintf(text,"%s%s",bff,CCNRM(vict, C_NRM));
  
}

int	get_from_q(struct txt_q *queue, char *dest)
{
   struct txt_block *tmp;

   /* Q empty? */
   if (!queue->head)
      return(0);

   tmp = queue->head;
   strcpy(dest, queue->head->text);
   queue->head = queue->head->next;
   
   free(tmp->text);
   free(tmp);
   
   return(1);
}
void clear_queue(struct txt_q *queue)
{
  struct txt_block *tmp, *tmp2;
  for (tmp = queue->head; tmp; tmp = tmp2){
    tmp2 = tmp->next;
    free(tmp->text);
    free(tmp);
  }
  
}
void cmpact(char *input)
{
    int count=0, size;
    char j[24576],priv_buf[4096],*pp, *end;

    if ((size = strlen(input)) < 80)
	return; /* don't compact one liners */
    *j = '\0';
    end = input + strlen(input);

    bzero(priv_buf,4096);
    if (!(pp = (char *)  strtok(input,"\n"))) 
	return;
    strcpy(priv_buf,pp);
    while ((pp = strtok(0,"\n"))){
	if (strcmp(priv_buf,pp) != 0)
	  { 
	    if ((count > 0)){
	      if (!strcmp(priv_buf,"\r"))
		while (1 + count--)
		  strcat(j,"\r\n"); 
	      else
		sprintf(j,"%s(%i*)%s\n",j,++count,priv_buf); 
	      strcpy(priv_buf,pp);
	      count=0; 
	    } 
	    else { 
	      sprintf(j,"%s%s\n",j,priv_buf);
	      strcpy(priv_buf,pp);
	      count = 0;
	    }
	  }
	else {
	  strcpy(priv_buf,pp);
	  count++;
	}
    }
    sprintf(input,"%s", j); 	
    if (count) {
      if (!strcmp(priv_buf,"\r")) 
	  while (1 + count--)
	    strcat(input,"\r\n");
      else 
	sprintf(input,"%s(%i*)%s\n",input,++count,priv_buf);
    }
    else {
      if (!strcmp(priv_buf,"\r"))
	  strcat(input,"\r\n"); 	
      else {
	size = strlen(priv_buf);
	if (*(priv_buf + size - 1 ) == '\r')
	  sprintf(input,"%s%s\n",input,priv_buf);
	else
	  sprintf(input,"%s%s",input,priv_buf);
      }
    }
}



void	write_to_output(char *txt, struct descriptor_data *t)
{
   int size = 0;
   if (!txt)
       return;
   size = strlen(txt);
   
   if (t->character && PLR_FLAGGED(t->character, PLR_NEEDCRLF))
       size += 2;    


   /* if we're in the overflow state already, ignore this */
   if (t->bufptr < 0)
      return;

   /* if we have enough space, just write to buffer and that's it! */
   if (t->bufspace >= size) {
       if (t->character && PLR_FLAGGED(t->character, PLR_NEEDCRLF)){
	   sprintf(t->output+t->bufptr,"\r\n%s", txt);
       }
       else
	   strcpy(t->output+t->bufptr, txt);
      t->bufspace -= size;
      t->bufptr += size;
   }   else {      /* otherwise, try to switch to a large buffer */
      if (t->large_outbuf || ((size + strlen(t->output)) > LARGE_BUFSIZE)) {
	 /* we're already using large buffer, or even the large buffer
	    in't big enough -- switch to overflow state */
	 t->bufptr = -1;
	 buf_overflows++;
	 return;
      }

      buf_switches++;
      /* if the pool has a buffer in it, grab it */
      if (bufpool) {
	 t->large_outbuf = bufpool;
	 bufpool = bufpool->next;
      } else { /* else create one */
	 CREATE(t->large_outbuf, struct txt_block, 1);
	 CREATE(t->large_outbuf->text, char, LARGE_BUFSIZE);
	 buf_largecount++;
      }
      strcpy(t->large_outbuf->text, t->output);
      t->output = t->large_outbuf->text;
      strcat(t->output, txt);
      t->bufspace = LARGE_BUFSIZE-1 - strlen(t->output);
      t->bufptr = strlen(t->output);
   }
   if (t->character && PLR_FLAGGED(t->character, PLR_NEEDCRLF))
       REMOVE_BIT(PLR_FLAGS(t->character), PLR_NEEDCRLF);
}



void	write_to_q(char *txt, struct txt_q *queue)
{
   struct txt_block *new;
   if (!txt)
       return;
   CREATE(new, struct txt_block, 1);
   CREATE(new->text, char, strlen(txt) + 1);

   strcpy(new->text, txt);
   /* Q empty? */
   if (!queue->head) {
      new->next = NULL;
      queue->head = queue->tail = new;
   } else {
      queue->tail->next = new;
      queue->tail = new;
      new->next = NULL;
   }
}




struct timeval timediff(struct timeval *a, struct timeval *b)
{
   struct timeval rslt, tmp;

   tmp = *a;

   if ((rslt.tv_usec = tmp.tv_usec - b->tv_usec) < 0) {
      rslt.tv_usec += 1000000;
      --(tmp.tv_sec);
   }
   if ((rslt.tv_sec = tmp.tv_sec - b->tv_sec) < 0) {
      rslt.tv_usec = 0;
      rslt.tv_sec = 0;
   }
   return(rslt);
}





/* Empty the queues before closing connection */
void	flush_queues(struct descriptor_data *d)
{
   if (d->large_outbuf) {
      d->large_outbuf->next = bufpool;
      bufpool = d->large_outbuf;
   }

   while (get_from_q(&d->input, buf2)) 
      ;
}





/* ******************************************************************
*  socket handling						    *
****************************************************************** */



int	init_socket(int port)
{
   int	s;
   int  opt = 1; 
   char	hostname[MAX_HOSTNAME+1];
   struct sockaddr_in sa;
   struct hostent *hp;

   bzero(&sa, sizeof(struct sockaddr_in ));
   gethostname(hostname, MAX_HOSTNAME);

   hp = gethostbyname(hostname);
   if (hp == NULL) {
      perror("gethostbyname");
      exit(1);
   }
   sa.sin_family = hp->h_addrtype;
   sa.sin_port	 = htons(port);

   if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
      perror("Init-socket");
      exit(1);
   }

   if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof (opt)) < 0) {
      perror ("setsockopt REUSEADDR");
      exit (1);
   }

#ifdef USE_LINGER 
   {
      struct linger ld;

      ld.l_onoff = 0;
      ld.l_linger = 1000;
      if (setsockopt(s, SOL_SOCKET, SO_LINGER, &ld, sizeof(ld)) < 0) {
         perror("setsockopt LINGER");
         exit(1);
      }
   }
#endif 

   if (bind(s, (struct sockaddr *) & sa, sizeof(sa)) < 0) {
      perror("bind");
      close(s);
      exit(1);
   }
   listen(s, 5);
   return(s);
}





int	new_connection(int s)
{
   struct sockaddr_in isa;
   int	i, t;

   i = sizeof(isa);
   getsockname(s, (struct sockaddr *) & isa, &i);

   if ((t = accept(s, (struct sockaddr *)(&isa), &i)) < 0) {
      perror("Accept");
      return(-1);
   }
   nonblock(t);
   return(t);
}



char *format_inet_addr( char *dest, long addr )
{
    dest += sprintf( dest, "%ld.%ld.%ld.%ld", 
                (addr & 0xFF000000) >> 24,
                (addr & 0x00FF0000) >> 16,
                (addr & 0x0000FF00) >> 8,
                (addr & 0x000000FF) );
    return( dest );
}


int	new_descriptor(int s)
{
   int	desc;
   char *pbuf;
   struct descriptor_data *newd, *point, *next_point;
   int	size, sockets_connected, sockets_playing, i;
   struct sockaddr_in sock;
   struct hostent *from, *p;
   char buf[4097];

   if ((desc = new_connection(s)) < 0)
      return (-1);

   sockets_connected = sockets_playing = 0;

   for (point = descriptor_list; point; point = next_point) {
      next_point = point->next;
      sockets_connected++;
      if (!point->connected)
	 sockets_playing++;
   }

   /* if ((maxdesc + 1) >= avail_descs) 
      if (sockets_connected >= avail_descs) {
      write_to_descriptor(desc, "Sorry, Archipelago is full right now... try again later!\r\n");
      close(desc);
      return(0);
      } else */
   if (desc > maxdesc)
     maxdesc = desc;
   
   CREATE(newd, struct descriptor_data, 1);
   
   /* find info */
   size = sizeof(sock);
   if (getpeername(desc, (struct sockaddr *) &sock, &size) < 0) {
      perror("getpeername");
      *newd->host = '\0';
   }
   else if (nameserver_is_slow ||!(from = gethostbyaddr((char *)&sock.sin_addr,
      sizeof(sock.sin_addr), PF_INET))) {
         if (!nameserver_is_slow)
	    perror("gethostbyaddr");
         i = sock.sin_addr.s_addr;
	 sprintf(newd->host, "%d.%d.%d.%d", (i & 0x000000FF),
		 (i & 0x0000FF00) >> 8, (i & 0x00FF0000) >> 16,
		 (i & 0xFF000000) >> 24 );
   }
   else {
      strncpy(newd->host, from->h_name, 49);
      *(newd->host + 49) = '\0';
   }
   if (slave_socket != -1) {
     buf[0] = 'i';
     pbuf = format_inet_addr(buf+1, ntohl(sock.sin_addr.s_addr));
     pbuf += sprintf(pbuf, ",%d,%d\n", ntohs(sock.sin_port), port);
     if (write(slave_socket, buf, pbuf - buf + 1) != (pbuf - buf + 1)) {
       logg("[SLAVE] loosing slave on write:");
       close(slave_socket);
       slave_socket = -1;
     }
   }
   
   if (isbanned(newd->host) == BAN_ALL) {
      close(desc);
      /*      sprintf(buf2, "Connection attempt denied from [%s]", newd->host);
      mudlog(buf2, CMP, LEVEL_GOD, TRUE);*/
      free(newd);
      return(0);
   }

/*  Uncomment this if you want new connections logged.  It's usually not
    necessary, and just adds a lot of unnecessary bulk to the logs.
    */
   newd->port = ntohs(sock.sin_port);
   sprintf(buf2, "New connection from [%s], port: %d", newd->host,
	   newd->port);
   mudlog(buf2, CMP, LEVEL_IMPL, TRUE);
   /*   logg(buf2);*/

   /* init desc data */
   newd->addr = ntohl(sock.sin_addr.s_addr);
   newd->user_id[0] = '\0';
   newd->descriptor = desc;
   newd->connected = CON_QCOLOR;
   newd->bad_pws = 0;
   newd->pos = -1;
   newd->wait = 1;
   newd->color = TRUE;
   newd->prompt_mode = 0;
   *newd->buf = '\0';
   newd->str = 0;
   newd->replace = 0;
   newd->with = 0;   
   newd->showstr_head = 0;
   newd->showstr_point = 0;
   *newd->last_input = '\0';
   newd->output = newd->small_outbuf;
   *(newd->output) = '\0';
   newd->bufspace = SMALL_BUFSIZE-1;
   newd->large_outbuf = NULL;
   newd->input.head = NULL;
   newd->next = descriptor_list;
   newd->character = 0;
   newd->original = 0;
   newd->snoop.snooping = 0;
   newd->snoop.snoop_by = 0;
   newd->login_time = time(0);

   if (++last_desc == 1000)
      last_desc = 1;
   newd->desc_num = last_desc;
   newd->mob_edit = 0;
   newd->medit_mode = 0;
   newd->obj_edit = 0;
   newd->oedit_mode = 0;
   newd->room_edit = 0;
   newd->redit_mode = 0;      
   newd->zone_edit = 0;
   newd->zedit_mode = 0;
   newd->shop_edit = 0;
   newd->sedit_mode = 0;      
   
   /* prepend to list */

   descriptor_list = newd;

   SEND_TO_Q("***Colour [Y/n]? (default is yes)\r\n*Note colour is not recommended if your connection is slow/laggy*: ", newd); 
   return(0);
}

static int get_slave_result(void)
{
  char *p, buf[4097], token[4096], os[4096], userid[4096];
  int octet[4], local_port, remote_port, len;
  struct descriptor_data *d;
  long addr;
  
  len = read(slave_socket, buf, 4096);
  if (len < 0) {
    if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
      return(-1);
    logg("[SLAVE] loosing slave on read");
    close(slave_socket);
    slave_socket = -1;
    return(-1);
  }
  else if (len==0)
    return(-1);
  buf[len] = 0;
  if (sscanf(buf + 1, "%d.%d.%d.%d %d , %d : %[^: ] : %[^: ] : %s",
	     &octet[0], &octet[1], &octet[2], &octet[3],
	     &remote_port, &local_port, token, os, userid) != 9) {
    if((sscanf(buf + 1, "%d.%d.%d.%d %d , %d : %[^: ] : %s",
	     &octet[0], &octet[1], &octet[2], &octet[3],
	     &remote_port, &local_port, token, userid) != 8) ||
       strncmp(token, "ERROR", 5)) {
      logg("[SLAVE] invalid");
    }
    return(0);
  }
  if (local_port != port) {
    logg ("[SLAVE] local port != game port");
    return(0);
  }
  addr = (octet[0] << 24) + (octet[1] << 16) + (octet[2] << 8) + octet[3];

  for (d = descriptor_list; d; d = d->next) {
    if (d->port != remote_port ) continue;
    if (d->addr != addr) continue;
    strncpy(d->user_id, userid, (MAX_USER_ID - 1));
    d->user_id[MAX_USER_ID-1] = '\0';
    return(0);
  }
    
}


int	process_output(struct descriptor_data *t)
{
   static char	i[LARGE_BUFSIZE + 20];


   /* start writing at the 2nd space so we can prepend "% " for snoop */
   if (t->character &&
       !t->connected &&
       !(!IS_NPC(t->character) && 
	 (PRF_FLAGGED(t->character, PRF_COMPACT)))){
      strcpy(i+2, "\r\n"); 
      strcpy(i+2, t->output);}
   else
      strcpy(i+2, t->output);
   
   if (t->bufptr < 0)
      strcat(i+2, "**OVERFLOW**");
   if (t->character &&
       !t->connected &&
       !(!IS_NPC(t->character) && 
	 (PRF_FLAGGED(t->character, PRF_COMPACT))))
     strcat(i+2, "\r\n");
   if (write_to_descriptor(t->descriptor, i+2) < 0) 
      return -1;

   if (t->snoop.snoop_by) {
      i[0] = '%';
      i[1] = ' ';
      SEND_TO_Q(i, t->snoop.snoop_by->desc);
   }

   /* if we were using a large buffer, put the large buffer on the buffer
      pool and switch back to the small one */
   if (t->large_outbuf) {
      t->large_outbuf->next = bufpool;
      bufpool = t->large_outbuf;
      t->large_outbuf = NULL;
      t->output = t->small_outbuf;
   }

   /* reset total bufspace back to that of a small buffer */
   t->bufspace = SMALL_BUFSIZE-1;
   t->bufptr = 0;
   *(t->output) = '\0';

   return 1;
}



int	write_to_descriptor(int desc, char *txt)
{
   int	sofar, thisround, total;

   if (!txt)
       return(0);
   
   cmpact(txt);
   
   total = strlen(txt);
   sofar = 0;

   do {
       thisround = write(desc, txt + sofar, total - sofar);
       if (thisround < 0) {
	   perror("Write to socket");
	   return(-1);
       }
       sofar += thisround;
   } while (sofar < total);
   return(0);
}





int	process_input(struct descriptor_data *t)
{
   int	sofar, thisround, begin, squelch, i, k, flag, failed_subst = 0;
   char	tmp[MAX_INPUT_LENGTH+2], buffer[MAX_INPUT_LENGTH + 60];

   sofar = 0;
   flag = 0;
   begin = strlen(t->buf);

   /* Read in some stuff */
   do {
      if ((thisround = read(t->descriptor, t->buf + begin + sofar, 
          MAX_STRING_LENGTH - (begin + sofar) - 1)) > 0)
	 sofar += thisround;
      else if (thisround < 0)
	 if (errno != EWOULDBLOCK) {
	    perror("Read1 - ERROR");
	    return(-1);
	 } 
	 else
	    break;
      else {
	 logg("EOF encountered on socket read.");
	 return(-1);
      }
   } while (!ISNEWL(*(t->buf + begin + sofar - 1)));

   *(t->buf + begin + sofar) = 0;

   /* if no newline is contained in input, return without proc'ing */
   for (i = begin; !ISNEWL(*(t->buf + i)); i++)
      if (!*(t->buf + i))
	 return(0);

   /* input contains 1 or more newlines; process the stuff */
   for (i = 0, k = 0; *(t->buf + i); ) {
      if (!ISNEWL(*(t->buf + i)) && !(flag = (k >= (MAX_INPUT_LENGTH - 2))))
	 if (*(t->buf + i) == '\b')	 /* backspace */
	    if (k) {  /* more than one char ? */
	       if (*(tmp + --k) == '$')
		  k--;
	       i++;
	    } 
	    else
	       i++;  /* no or just one char.. Skip backsp */
	 else if (isascii(*(t->buf + i)) && isprint(*(t->buf + i))) {
	    /* trans char, double for '$' (printf)	*/
	    if ((*(tmp + k) = *(t->buf + i)) == '$' && !t->str)
	       *(tmp + ++k) = '$';
	    k++;
	    i++;
	 } 
	 else
	    i++;
      else {
	 *(tmp + k) = 0;
	 if (*tmp == '!')
	    strcpy(tmp, t->last_input);
	 else if (*tmp == '^') {
	    if (!(failed_subst = perform_subst(t, t->last_input, tmp)))
	       strcpy(t->last_input, tmp);
	 } else
	    strcpy(t->last_input, tmp);

	 if (!failed_subst)
	   {
	     if (!strncmp(tmp, "abort",5) || !strncmp(tmp, "flee",4))
	       while(get_from_q(&t->input, buf2))
		 ;
	     write_to_q(tmp, &t->input);
	   }
	 
	 if (t->snoop.snoop_by) {
	    SEND_TO_Q("% ", t->snoop.snoop_by->desc);
	    SEND_TO_Q(tmp, t->snoop.snoop_by->desc);
	    SEND_TO_Q("\r\n", t->snoop.snoop_by->desc);
	 }

	 if (flag) {
	    sprintf(buffer, "Line too long.  Truncated to:\r\n%s\r\n", tmp);
	    if (write_to_descriptor(t->descriptor, buffer) < 0)
	       return(-1);

	    /* skip the rest of the line */
	    for (; !ISNEWL(*(t->buf + i)); i++)
	       ;
	 }

	 /* find end of entry */
	 for (; ISNEWL(*(t->buf + i)); i++)
	    ;

	 /* squelch the entry from the buffer */
	 for (squelch = 0; ; squelch++)
	    if ((*(t->buf + squelch) = *(t->buf + i + squelch)) == '\0')
	       break;
	 k = 0;
	 i = 0;
      }
   }
   return(1);
}



int	perform_subst(struct descriptor_data *t, char *orig, char *subst)
{
   char new[MAX_INPUT_LENGTH+5];

   char *first, *second, *strpos;

   first = subst+1;
   if (!(second = strchr(first, '^'))) {
      SEND_TO_Q("Invalid substitution.\r\n", t);
      return 1;
   }

   *(second++) = '\0';

   if (!(strpos = strstr(orig, first))) {
      SEND_TO_Q("Invalid substitution.\r\n", t);
      return 1;
   }

   strncpy(new, orig, (strpos-orig));
   new[(strpos-orig)] = '\0';
   strcat(new, second);
   if (((strpos-orig) + strlen(first)) < strlen(orig))
      strcat(new, strpos+strlen(first));
   strcpy(subst, new);

   return 0;
}



void	close_sockets(int s)
{
   logg("Killing Slave");
   if (slave_socket != -1)
     kill( slave_pid, SIGKILL);
   
   logg("Closing all sockets.");   
   while (descriptor_list)
      close_socket(descriptor_list);

   close(s);
}




void	close_socket(struct descriptor_data *d)
{
  struct descriptor_data *tmp;
  char	buf[100];
  
  close(d->descriptor);
  flush_queues(d);
  if (d->descriptor == maxdesc)
    --maxdesc;
  
  /* Forget snooping */
  if (d->snoop.snooping)
    d->snoop.snooping->desc->snoop.snoop_by = 0;

  if (d->snoop.snoop_by) {
    send_to_char("Your victim is no longer among us.\r\n", d->snoop.snoop_by);
    d->snoop.snoop_by->desc->snoop.snooping = 0;
  }
  
  if (d->character)
    if (d->connected == CON_PLYNG) {
      save_char(d->character, NOWHERE);
      act("$n has lost $s link.", TRUE, d->character, 0, 0, TO_ROOM);
      sprintf(buf, "Closing link to: %s.", GET_NAME(d->character));
      mudlog(buf, NRM, MAX(LEVEL_BUILDER, GET_INVIS_LEV(d->character)), TRUE);
      d->character->desc = 0;
    }
    else {
      sprintf(buf, "Losing player: %s.", GET_NAME(d->character));
      mudlog(buf, CMP, MAX(LEVEL_BUILDER, GET_INVIS_LEV(d->character)), TRUE);
      free_char(d->character);
    }
  else
    mudlog("Losing descriptor without char.", CMP, LEVEL_BUILDER, TRUE);
  
  if (next_to_process == d)  /* to avoid crashing the process loop */
    next_to_process = next_to_process->next;
  
  if (d == descriptor_list) /* this is the head of the list */
    descriptor_list = descriptor_list->next;
  else {  /* This is somewhere inside the list */
    /* Locate the previous element */
    for (tmp = descriptor_list; (tmp->next != d) && tmp; tmp = tmp->next)
      ;
    tmp->next = d->next;
  }
  
  if (d->showstr_head)
    free(d->showstr_head);
  if (d->name)
    free(d->name);
  if (d->replace)
    free(d->replace);
  if (d->with)
     free(d->with);   
  free(d);
}


#if defined(SVR4) || defined(LINUX)

void	nonblock(int s)
{
   int	flags;
   flags = fcntl(s, F_GETFL);
   flags |= O_NONBLOCK;
   if (fcntl(s, F_SETFL, flags) < 0) {
      perror("Fatal error executing nonblock (comm.c)");
      exit(1);
   }
}

#else

void	nonblock(int s)
{
   if (fcntl(s, F_SETFL, FNDELAY) == -1) {
      perror("Fatal error executing nonblock (comm.c)");
      exit(1);
   }
}

#endif



/* ****************************************************************
*	Public routines for system-to-player-communication	  *
*******************************************************************/



void	send_to_char(char *messg, struct char_data *ch)
{
    if (ch->desc && messg)
	SEND_TO_Q(messg, ch->desc);
}




void	send_to_all(char *messg)
{
    struct descriptor_data *i;
    if (messg)
	for (i = descriptor_list; i; i = i->next)
	    if (!i->connected)
		SEND_TO_Q(messg, i);
}


void	send_to_outdoor(char *messg)
{
   struct descriptor_data *i;

   if (messg)
      for (i = descriptor_list; i; i = i->next)
	 if (!i->connected)
	    if (OUTSIDE(i->character) &&
		(world[i->character->in_room].sector_type != SECT_UNDER_WATER)
		&& !PLR_FLAGGED(i->character, PLR_WRITING) &&
		!PLR_FLAGGED(i->character, PLR_BUILDING) && AWAKE(i->character))
	       SEND_TO_Q(messg, i);
}


void	send_to_except(char *messg, struct char_data *ch)
{
   struct descriptor_data *i;

   if (messg)
      for (i = descriptor_list; i; i = i->next)
	 if (ch->desc != i && !i->connected
	     && !PLR_FLAGGED(i->character, PLR_WRITING) &&
		!PLR_FLAGGED(i->character, PLR_BUILDING))
	    SEND_TO_Q(messg, i);
}



void	send_to_room(char *messg, int room, bool virtual)
{
   struct char_data *i;

   if (virtual)
       room = real_room(room);
   
   if (messg)
      for (i = world[room].people; i; i = i->next_in_room)
	  if (i->desc && !PLR_FLAGGED(i, PLR_WRITING) &&
		!PLR_FLAGGED(i, PLR_BUILDING))
	    SEND_TO_Q(messg, i->desc);
}




void	send_to_room_except(char *messg, int room, struct char_data *ch, bool virtual)
{
   struct char_data *i;

   if (virtual)
       room = real_room(room);   
   
   if (messg)
      for (i = world[room].people; i; i = i->next_in_room)
	 if (i != ch && i->desc && !PLR_FLAGGED(i, PLR_WRITING) &&
		!PLR_FLAGGED(i, PLR_BUILDING))
	    SEND_TO_Q(messg, i->desc);
}


void	send_to_room_except_two
(char *messg, int room, struct char_data *ch1, struct char_data *ch2, bool virtual)
{
   struct char_data *i;
   
   if (virtual)
       room = real_room(room);
   if (messg)
      for (i = world[room].people; i; i = i->next_in_room)
	 if (i != ch1 && i != ch2 && i->desc
	     && !PLR_FLAGGED(i, PLR_WRITING) &&
		!PLR_FLAGGED(i, PLR_BUILDING))
	    SEND_TO_Q(messg, i->desc);
}



/* higher-level communication */

void	act(char *str, int hide_invisible, struct char_data *ch,
struct obj_data *obj, void *vict_obj, int type)
{
   register char	*strp, *point, *i;
   struct char_data *to, *ignore=0;
   static char	buf[MAX_STRING_LENGTH];

   if (!str || !*str)
      return;
   if (!ch && !obj && !vict_obj)
       return;
   
   if (type == TO_VICT)
       to = (struct char_data *) vict_obj;
   else if (type == TO_CHAR)
       to = ch;
   else if (type == TO_ROOM){
     if (ch){
       if (ch->in_room >= 0)
	 to = world[ch->in_room].people;
       else
	 return;
     }
     else if (obj){
       if (obj->in_room >=0)
	 to = world[obj->in_room].people;
       else if (obj->worn_by)
	 to = world[obj->worn_by->in_room].people;
       else if (obj->carried_by)
	 to = world[obj->carried_by->in_room].people;
       else
	 return;
     }
   }
   if (vict_obj && type == TO_NOTVICT){
     ignore = (struct char_data *) vict_obj;
     if (ignore->in_room >= 0)
       to = world[ignore->in_room].people;
     else
       return;
   }
   if (ch && type == TO_NOTCHAR){
       to = world[ch->in_room].people;
       ignore = ch;
   }
   
   if (!to)
       return;
   for (; to; to = to->next_in_room) {
      if (to && to->desc && ((to != ch) || (type == TO_CHAR)) &&  
          ((ch && CAN_SEE(to, ch)) || !hide_invisible || (type == TO_VICT)) &&
	  AWAKE(to) && !PLR_FLAGGED(to, PLR_WRITING) &&
	  !PLR_FLAGGED(to, PLR_BUILDING) &&  !(to == ignore))
	  {
	      for (strp = str, point = buf; ; )
		  if (*strp == '$') {
		      switch (*(++strp)) {
		      case 'n':
			  i = PERS(ch, to);
			  break;
		      case 'N':
			  i = PERS((struct char_data *) vict_obj, to);
			  break;
		      case 'm':
			  i = HMHR(ch);
			  break;
		      case 'M':
			  i = HMHR((struct char_data *) vict_obj);
			  break;
		      case 's':
			  i = HSHR(ch);
			  break;
		      case 'S':
			  i = HSHR((struct char_data *) vict_obj);
			  break;
		      case 'e':
			  i = HSSH(ch);
			  break;
		      case 'E':
			  i = HSSH((struct char_data *) vict_obj);
			  break;
		      case 'o':
			  i = OBJN(obj, to);
			  break;
		      case 'O':
			  i = OBJN((struct obj_data *) vict_obj, to);
			  break;
		      case 'p':
			  i = OBJS(obj, to);
			  break;
		      case 'P':
			  i = OBJS((struct obj_data *) vict_obj, to);
			  break;
		      case 'a':
			  i = SANA(obj);
			  break;
		      case 'A':
			  i = SANA((struct obj_data *) vict_obj);
			  break;
		      case 'T':
			  i = (char *) vict_obj;
			  break;
		      case 'F':
			  i = fname((char *) vict_obj);
			  if (!(strcmp(i,"")))
			      strcpy(i,"hidden passage");
			  break;
		      case '$':
			  i = "$";
			  break;
		      default:
			  logg("SYSERR: Illegal $-code to act():");
			  strcpy(buf1, "SYSERR: ");
			  strcat(buf1, str);
			  logg(buf1);
			  break;
		      }
		      while ((*point = *(i++)))
			  ++point;
		      ++strp;
		  }
		  else if (!(*(point++) = *(strp++)))
		      break;
	      
	      *(--point) = '\n';
	      *(++point) = '\r';
	      *(++point) = '\0';
	      SEND_TO_Q(CAP(buf), to->desc);
	  }
      if ((type == TO_VICT) || (type == TO_CHAR))
	  return;
   }
}


void process_events(int pulse)
{
  ACMD(do_look);
  ACMD(do_wear);
  ACMD(do_get);
  struct event_type *tmp,*tmp2;
  struct char_data *subj, *vict, *next_vict;
  struct obj_data *obj_vict, *obj_subj;
  struct room_data *rm_subj;
  int spl, to_room, n = 0;
  struct follow_type *k;
  char buf_loc[256];

  tmp = events[pulse];
  while (tmp)
    {
      switch(tmp->event){
      case EVENT_IGNORE:
	break;
      case EVENT_REBOOT:
	subj = (struct char_data *) tmp->subject;
	tmp->info1--;
	if (tmp->info1 < 1) {
	  if (tmp->info3 > 0) {
	    circle_shutdown = 1;
	    break;
	  }
	  else {
	    do_force(subj, "all save",0,0);
	    sprintf(buf,"\007\007(GC) Shutdown by %s, now.\r\n",
		    GET_NAME(subj),tmp->info1-1);
	    send_to_all(buf);
	    logg(buf);
	    if (tmp->info2 > 0)
	      send_to_all("Rebooting.. come back in a minute or two.\r\n");
	    add_event(1, EVENT_REBOOT, tmp->info1,tmp->info2,1,0,"",subj,0);
	  }
	}
	else {
	  sprintf(buf,"\007\007(GC) %s by %s, in %d %s.\r\n",
		  (tmp->info2 ? "Reboot" : "Shutdown"),
		  GET_NAME(subj),tmp->info1,
		  ((tmp->info1 == 1) ? "minute" : "minutes"));
	  send_to_all(buf);
	  logg(buf);
	  add_event(250, EVENT_REBOOT, tmp->info1,tmp->info2,0,0,"",subj,0);
	}
	break;
      case EVENT_ATTACK:
	subj = (struct char_data *) tmp->subject;
	vict = (struct char_data *) tmp->target;
	if (CAN_SPEAK(subj) && (GET_INT(subj) < 15))
	  act("$n screams, '$N! I'm gonna kill you'",FALSE,subj,0,vict,TO_ROOM);
	else if (CAN_SPEAK(subj) && (GET_INT(subj) >= 15))
	  act("$n says, '$N! Time to die!'",FALSE,subj,0,vict,TO_ROOM);
	else if (!CAN_SPEAK(subj))
	  act("$n roars! and attacks $N!",FALSE,subj,0,vict, TO_NOTVICT);
	if (vict->in_room == subj->in_room)
	  hit(subj, vict,0);
	break;
      case EVENT_OBJTIMER:
	obj_subj = (struct obj_data *) tmp->subject;
	subj = obj_subj->worn_by;
	if (is_goditem(obj_subj))
	  break;
	if (obj_subj->obj_flags2.aff_timer == 1){
	  if (subj){
	    affect_modify(subj, APPLY_NONE,0,
			  obj_subj->obj_flags2.bitvector_aff,
			  FALSE);
	    obj_subj->obj_flags2.bitvector_aff = 0;
	    SET_BIT(PLR_FLAGS(subj), PLR_CRASH);
			    
	  }
	  else
	    obj_subj->obj_flags2.bitvector_aff = 0;
	  obj_subj->obj_flags2.aff_timer = 0;
	  if (obj_subj->obj_flags2.no_use_timer > 0)
	    obj_subj->obj_flags2.no_use_timer--;	
	}
	else{
	  if (obj_subj->obj_flags2.aff_timer > 0)
	    obj_subj->obj_flags2.aff_timer--;
	  switch (GET_ITEM_TYPE(obj_subj))
	    {
	    case ITEM_WAND:
	      obj_subj->obj_flags2.no_use_timer -= 3;
	      break;
	    case ITEM_STAFF:
	      obj_subj->obj_flags2.no_use_timer -= 2;
	      break;
	    default:
	      obj_subj->obj_flags2.no_use_timer--;
	      break;
	    }
	  obj_subj->obj_flags2.no_use_timer
	    =MAX(0,obj_subj->obj_flags2.no_use_timer);
	}
	if (obj_subj->obj_flags2.no_use_timer > 0)
	  if (GET_ITEM_TYPE(obj_subj) != ITEM_WAND &&
	      GET_ITEM_TYPE(obj_subj) != ITEM_STAFF )
	    add_event(-1,EVENT_OBJTIMER,0,0,0,0,0,obj_subj,0);
	  else 
	    add_event(2,EVENT_OBJTIMER,0,0,0,0,0,obj_subj,0);
	break;
      case EVENT_LOOT:
	subj = (struct char_data *) tmp->subject;
	obj_vict = (struct obj_data *) tmp->target;
	if (obj_vict && (GET_ITEM_TYPE(obj_vict) == ITEM_CONTAINER)
	    && obj_vict->obj_flags.value[3] < 0
	    && obj_vict->contains){
	  do_get(subj,"all corpse", 0,0);
	  if (GET_INT(subj) > 10)
	    do_wear(subj,"all",0,0);
	}
	break;
      case EVENT_LEAVE:
	if (!tmp->subject)
	  break;
	subj = (struct char_data *) tmp->subject;

	if (subj->specials.fighting)
	  break;
		    
	if (!IS_AFFECTED(subj, AFF_SNEAK) && !(subj->specials.mount)
	    && !(subj->specials.rider) && !(subj->specials.carried_by)){
	  if (IS_CLIMB(subj,tmp->info1)
	      && !IS_AFFECTED(subj,AFF_FLY))
	    {
	      sprintf(buf2, "$n starts to climb %s.", dirs[tmp->info1]);
	      act(buf2, TRUE, subj, 0, 0, TO_ROOM);
	      sprintf(buf2, "You start to climb.");
	      act(buf2, TRUE, subj, 0, 0, TO_CHAR);
	    }
	  else if (world[subj->in_room].sector_type
		   == SECT_UNDER_WATER
		   || world[subj->in_room].sector_type
		   == SECT_WATER_SWIM
		   || (world[subj->in_room].sector_type
		       == SECT_WATER_NOSWIM && !tmp->info2)) {
	    sprintf(buf2, "$n swims %s.",dirs[tmp->info1]);
	    act(buf2, TRUE, subj,0,0,TO_ROOM);
	    sprintf(buf2, "You swim %s.",dirs[tmp->info1]);
	    act(buf2, TRUE, subj,0,0,TO_CHAR);
	  }
	  else if (IS_AFFECTED(subj,AFF_FLY)) {
	    sprintf(buf2, "$n flies %s.",dirs[tmp->info1]);
	    act(buf2, TRUE, subj,0,0,TO_ROOM);
	    sprintf(buf2, "You fly %s.",dirs[tmp->info1]);
	    act(buf2, TRUE, subj,0,0,TO_CHAR);
	  }
	  else {
	    sprintf(buf2, "$n sets off %s.",dirs[tmp->info1]);
	    act(buf2, TRUE, subj,0,0,TO_ROOM);
	    sprintf(buf2, "You set off %s.",dirs[tmp->info1]);
	    act(buf2, TRUE, subj,0,0,TO_CHAR);
	  }
	}
	else if (subj->specials.mount)
	  {
	    if (world[subj->in_room].sector_type
		== SECT_UNDER_WATER
		|| world[subj->in_room].sector_type
		== SECT_WATER_SWIM
		|| (world[subj->in_room].sector_type
		    == SECT_WATER_NOSWIM && !tmp->info2)) {
	      sprintf(buf2, "$n swims %s on $N.",dirs[tmp->info1]);
	      act(buf2, TRUE, subj,0,subj->specials.mount,TO_ROOM);
	      sprintf(buf2, "You swim %s on $N.",dirs[tmp->info1]);
	      act(buf2, TRUE, subj,0,subj->specials.mount,TO_CHAR);
	    }
	    else if (IS_AFFECTED(subj->specials.mount,AFF_FLY)) {
	      sprintf(buf2, "$n flies %s on $N.",dirs[tmp->info1]);
	      act(buf2, TRUE, subj,0,subj->specials.mount,TO_ROOM);
	      sprintf(buf2, "You fly %s on $N.",dirs[tmp->info1]);
	      act(buf2, TRUE, subj,0,subj->specials.mount,TO_CHAR);
	    }
	    else {
	      sprintf(buf2, "$n rides off %s on $N.",dirs[tmp->info1]);
	      act(buf2, TRUE, subj,0,subj->specials.mount,TO_ROOM);
	      sprintf(buf2, "You ride off %s on $N.",dirs[tmp->info1]);
	      act(buf2, TRUE, subj,0,subj->specials.mount,TO_CHAR);
	    }
	  }
	break;
      case EVENT_CART_LEAVE:
	if (!tmp->subject)
	  break;
	obj_subj = (struct obj_data *) tmp->subject;
	bzero(buf_loc,256);
	for (k = obj_subj->pulled_by;k;k = k->next){
	  strcat(buf_loc,
		 first_name(k->follower->player.name ? k->follower->player.name :
			    k->follower->player.short_descr));
	  strcat(buf_loc, " ");
	  n++;}
	if (n > 1)
	  sprintf(buf2,"The %s pull $p %s.",pluralise_string(buf_loc),dirs[tmp->info1]);
	else{	
	  half_chop(buf_loc,buf,buf1);
	  sprintf(buf2,"The %s pulls $p %s.",buf,dirs[tmp->info1]);}
	act(buf2,TRUE,world[obj_subj->in_room].people,obj_subj,0,TO_ROOM);
	act(buf2,TRUE,world[obj_subj->in_room].people,obj_subj,0,TO_CHAR);
	to_room = -1;
	if (GET_ITEM_TYPE(obj_subj) == ITEM_CONTAINER)
	  to_room = real_room(obj_subj->obj_flags.value[3]);
	if (world[to_room].people)
	  if (!IS_SET(obj_subj->obj_flags.value[1],CONT_CLOSED)){
	    if (to_room > 0 && to_room <= top_of_world){
	      sprintf(buf1,"The %s moves off %s.",
		      first_name(obj_subj->name)
		      ,dirs[tmp->info1]);
	      act(buf1,TRUE,world[to_room].people
		  ,obj_subj,0
		  ,TO_CHAR);
	      act(buf1,TRUE,world[to_room].people
		  ,obj_subj,0
		  ,TO_ROOM);
	    }
	  }
	  else if (to_room > 0 && to_room <= top_of_world){
	    sprintf(buf1,"The %s rattles and bumps about."
		    ,first_name(obj_subj->name));
	    act(buf1,TRUE,world[to_room].people
		,obj_subj,0
		,TO_CHAR);
	    act(buf1,TRUE,world[to_room].people
		,obj_subj,0
		,TO_ROOM);				
	  }
	break;
      case EVENT_CART_ARRIVE:
	if (!tmp->subject)
	  break;
	obj_subj = (struct obj_data *) tmp->subject;
	if (!obj_subj->pulled_by)
	  break;
	obj_from_room(obj_subj);
	obj_to_room(obj_subj, tmp->info2, TRUE);
	sprintf(buf2, "$p arrives from %s.",revdirs[tmp->info1]);
	act(buf2,FALSE, 0,obj_subj,0,TO_ROOM);
	break;
      case EVENT_ARRIVE:
	if (!tmp->subject)
	  break;
	subj = (struct char_data *) tmp->subject;
	if (subj->specials.fighting)
	  break;
	if (subj->desc)
	  subj->desc->prompt_mode =1;
	if (subj->specials.cart
	    && (subj->specials.cart->in_room == subj->in_room)){
	  obj_from_room(subj->specials.cart);
	  obj_to_room(subj->specials.cart, tmp->info2, TRUE);
	}
	char_from_room(subj);
	char_to_room(subj,tmp->info2, TRUE);
	if (subj->specials.carrying){
	  char_from_room(subj->specials.carrying);
	  char_to_room(subj->specials.carrying,tmp->info2, TRUE);
	}
	if (!IS_AFFECTED(subj, AFF_SNEAK) && !subj->specials.mount
	    && !subj->specials.rider && !subj->specials.carried_by) {
	  sprintf(buf2, "$n arrives from %s.",revdirs[tmp->info1]);
	  act(buf2, TRUE, subj,0,0,TO_ROOM);}
	else if (subj->specials.mount) {
	  sprintf(buf2, "$n rides in from %s on $N.",revdirs[tmp->info1]);
	  act(buf2, TRUE, subj,0,subj->specials.mount,TO_ROOM);}
	do_look(subj,"",0,0);
	if (IS_SET(world[subj->in_room].room_flags, DEATH) && 
	    GET_LEVEL(subj) < LEVEL_BUILDER) {
	  log_death_trap(subj);
	  death_cry(subj);
	  extract_char(subj, TRUE);
	  break;
	}
	for (vict = world[subj->in_room].people;
	     vict;vict = vict->next_in_room){
	  if (IS_MOB(vict) && mob_index[vict->nr].func)
	    (*mob_index[vict->nr].func)(subj,vict,SPEC_ARRIVE,"");
	}
	break;
      case EVENT_TELEPORT:
	rm_subj = (struct room_data *) tmp->subject;
	vict    = (struct char_data *) tmp->target;
	if (!(rm_subj) || !(vict)){
	  tmp->event = EVENT_IGNORE;
	  break;}
	if (rm_subj->number != world[vict->in_room].number){
	  tmp->event = EVENT_IGNORE;
	  break;}
	if (rm_subj->tele_mesg1)
	  act(rm_subj->tele_mesg1,FALSE,vict,0,0,TO_CHAR);
	if (real_room(rm_subj->tele_to_room) != vict->in_room){
	  if (rm_subj->tele_mesg2)
	    act(rm_subj->tele_mesg2,FALSE,vict,0,0,TO_ROOM);
	  char_from_room(vict);
	  char_to_room(vict,rm_subj->tele_to_room, TRUE);
	  do_look(vict,"",0,0);
	  if (rm_subj->tele_mesg3)   	    
	    act(rm_subj->tele_mesg3,FALSE,vict,0,0,TO_ROOM);}
	tmp->event = EVENT_IGNORE;
	break;
      case EVENT_COMBAT:
	if (tmp->subject)
	  subj = (struct char_data *) tmp->subject;
	else
	  break;
	if (GET_POS(subj) <=  POSITION_SLEEPING)
	  break;
	vict = (struct char_data *) tmp->target;
	if (vict->in_room != subj->in_room)
	  break;
	if (vict)
	  if (affected_by_spell(vict, SPELL_ENCASE_IN_ICE)){
	    if (!number(0,3) && (tmp->info1 > 5)){
	      act("$n's blow shatters the ice encasing $N!",
		  TRUE,subj,0,vict,TO_NOTVICT);
	      act("$n's blow shatters the ice encasing you!",
		  TRUE,subj,0,vict,TO_VICT);
	      act("Your blow shatters the ice encasing $N.",
		  TRUE,subj,0,vict,TO_CHAR);
	      damage(subj,vict,tmp->info1/4,tmp->info2,tmp->info3,0);
	      affect_from_char(vict, SPELL_ENCASE_IN_ICE);
	    }
	    else {
	      act("$n's blow glances off the ice encasing $N!",
		  TRUE,subj,0,vict,TO_NOTVICT);
	      act("$n's blow glances off the ice encasing you!",
		  TRUE,subj,0,vict,TO_VICT);
	      act("Your blow glances off the ice encasing $N.",
		  TRUE,subj,0,vict,TO_CHAR);
	    }
	  }
	  else
	    damage(subj, vict, tmp->info1, tmp->info2, tmp->info3,0);

	tmp->event = EVENT_IGNORE;
	break;
      case EVENT_ROOM_DAMAGE:
	{
	  int dam, damtype;

	  if (tmp->info3 == SPELL_BREATH_OF_VULCAN)
	    damtype = SKILL_IGNEM;
	  else
	    damtype = tmp->info3;
	  if (real_room(tmp->info2)){
	    rm_subj = world + real_room(tmp->info2);
	    if ((tmp->info3 < SPELL_FROST_BREATH) && 
		IS_SET(rm_subj->room_flags, PEACEFULL)
		|| IS_SET(rm_subj->room_flags, NO_MAGIC)){
	      tmp->event = EVENT_IGNORE;
	      break;
	    }
	  }
	  else{
	    tmp->event = EVENT_IGNORE;
	    break;
	  }
	  subj = (struct char_data *) tmp->subject;
	  vict = rm_subj->people;
	  next_vict = ((vict) ? vict->next_in_room : 0);
	  for (; next_vict; vict= next_vict){
	    next_vict = vict->next_in_room;
	    act(tmp->arg,FALSE,subj,0,vict,TO_ROOM);
	    dam = tmp->info1;
	    if (!tmp->info4 && vict == subj)
	      dam = 0;
	    if (((tmp->info3 == SPELL_BREATH_OF_VULCAN) ||
		 (tmp->info3 == SPELL_FIRE_BREATH)) &&
		(affected_by_spell(vict, SPELL_ENCASE_IN_ICE))){
	      reduce_ice(vict, dam);
	      dam = 0;
	    }
	    if (tmp->info3 == SPELL_CALL_OF_THE_WATERY_GRAVE
		&& IS_AFFECTED(vict, AFF_WATER_BREATH))
	      dam = 0;
	    else if (((tmp->info3 == SPELL_BREATH_OF_VULCAN) ||
		      (tmp->info3 == SPELL_FIRE_BREATH)) &&
		      IS_AFFECTED(vict, AFF_RESIST_HEAT))
	      dam /= 2;
	    else if (((tmp->info3 == SPELL_BREATH_OF_VULCAN) ||
		      (tmp->info3 == SPELL_FIRE_BREATH)) &&
		      IS_AFFECTED(vict, AFF_RESIST_COLD))
	      dam *= 2;
	    if (saves_spell(vict, SAVING_SPELL, GET_LEVEL(subj))) {
	      dam /= 3;
	      damage(subj,vict,dam,tmp->info3,-1,1);
	      if ((IS_MOB(subj) &&
		   !IS_MOB(vict))
		  || (!IS_MOB(subj)  &&  IS_MOB(vict)))
		spell_damage_equipment(subj, vict, damtype, 3*dam);	      
	    }
	    else{
	      damage(subj,vict,dam,tmp->info3,-1,0);
	      if ((IS_MOB(subj) &&
		   !IS_MOB(vict))
		  || (!IS_MOB(subj)  &&  IS_MOB(vict)))
		spell_damage_equipment(subj, vict, damtype, 2*dam);
	    }
	  }
	  tmp->event = EVENT_IGNORE;
	}
	break;
      case EVENT_SPELL:
	spl = tmp->info4;
	if ((!(spell_info[spl].spell_pointer) && !tmp->info2)
	    || (!(spell_info[spl].spll_pointer) && tmp->info2))
	  break;
	if (tmp->subject)
	  subj = (struct char_data *) tmp->subject;
	else
	  break;
	if (tmp->target)
	  switch(tmp->info3){
	  case 0:
	    vict = (struct char_data *) tmp->target;
	    obj_vict =0;
	    break;
	  case 1:
	    vict = 0;
	    obj_vict = (struct obj_data *) tmp->target;
	    break;
	  }
	else{
	  vict =0;
	  obj_vict = 0;
	  if (!IS_SET(spell_info[spl].targets,TAR_IGNORE)){
	    send_to_char("Your target seems to have wandered off.\r\n",subj);
			    
	    break;
	  }
	}
	if (vict)
	  if (vict->in_room != subj->in_room
	      && spell_info[spl].targets != TAR_CHAR_WORLD ){
	    send_to_char("Your target seems to have wandered off.\r\n",subj);
	    tmp->event = EVENT_IGNORE;
	    break;
	  }
	if (tmp->info2){
	  if (spell_info[spl].spll_pointer)
	    ((*spell_info[spl].spll_pointer) (spl, tmp->info2, subj, tmp->arg, SPELL_TYPE_SPELL, vict, obj_vict));
	  break;
	}
	break;
      default:
	break;
      }
      tmp = tmp->next;
    }
  tmp = events[pulse];
  while (tmp){
    tmp2 = tmp->next;
    free(tmp);
    tmp = tmp2;}
    
  events[pulse] = 0; 

    
  return;
}

void add_event(int plse, int event, int inf1, int inf2, int inf3
	       , int inf4, char *arg, void *subj, void *vict)
{
    struct event_type *evnt,*tmp;
    
    CREATE(evnt, struct event_type, 1);
    evnt->event = event;
    evnt->info1 = inf1;
    evnt->info2 = inf2;    
    evnt->info3 = inf3;	
    evnt->info4 = inf4;
    evnt->arg   = arg;
    evnt->subject = subj;
    evnt->target = vict;
    evnt->next = 0;

    plse += pulse;
    
    plse = plse % 300;
    /* now locate end of event stack
     and add the event on the end */
    if (events[plse]){
	tmp = events[plse];
	while(tmp->next)
	    tmp = tmp->next;

	tmp->next = evnt;}
    else
	events[plse] = evnt;
    return;

}

void extract_event(struct char_data *ch)
{
    int i;
    struct event_type *tmp;
    
    for (i=0;i< 300; i++)
	if ((tmp = events[i]) != NULL)
	    while(tmp)
		{
		    if(tmp->event != EVENT_IGNORE)
			if ((ch == (struct char_data *) tmp->subject)
			    || (ch == (struct char_data *) tmp->target))
			    tmp->event = EVENT_IGNORE;
		    tmp = tmp->next;
		}
}