sunder2.1/clan/
sunder2.1/class/
sunder2.1/class/bak/
sunder2.1/doc/ideas/
sunder2.1/gods/
sunder2.1/log/
sunder2.1/msgbase/
sunder2.1/src/o/
sunder2.1/time/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/
/**********************************************************
 *************** S U N D E R M U D *** 2 . 0 **************
 **********************************************************
 * The unique portions of the SunderMud code as well as   *
 * the integration efforts for code from other sources is *
 * based primarily on the efforts of:                     *
 *                                                        *
 * Lotherius <aelfwyne@operamail.com> (Alvin W. Brinson)  *
 *    and many others, see "help sundermud" in the mud.   *
 **********************************************************/

#include "everything.h"

/* command procedures needed */
DECLARE_DO_FUN ( do_quit );

/* prototypes needed */
void real_acomm args ( ( CHAR_DATA *ch, long flag, char *text ) );

/*
 * Local functions.
 */

char *scramble_word args ( ( char *argument, int modifier ) );

/* Text scrambler -- Altrag */

char *scramble ( char *argument, int modifier )
{
     char                word[MAX_INPUT_LENGTH];
     static char         final[2 * MAX_INPUT_LENGTH];
     char               *tmp;

     if ( modifier == 100 )
          return argument;

     final[0] = '\0';

     while ( argument != NULL && argument[0] != '\0' )
     {
          argument = one_argument_nl ( argument, word );
          if ( number_percent (  ) > modifier )
          {
               tmp = scramble_word ( word, modifier );
               SLCAT ( final, tmp );
          }
          else
               SLCAT ( final, word );
          SLCAT ( final, " " );
     }
     /* Whack last extra space */
     final[strlen ( final ) - 1] = '\0';

     return final;
}

char *scramble_word ( char *argument, int modifier )
{
     static char         arg[MAX_INPUT_LENGTH];
     sh_int              position;
     sh_int              conversion = 0;

     modifier %= number_range ( 80, 300 );

     for ( position = 0; position < MAX_INPUT_LENGTH; position++ )
     {
          if ( argument[position] == '\0' )
          {
               arg[position] = '\0';
               return arg;
          }
          else if ( argument[position] >= 'A' && argument[position] <= 'Z' )
          {
               conversion = -conversion + position - modifier + argument[position] - 'A';
               conversion = number_range ( conversion - 5, conversion + 5 );
               while ( conversion > 25 )
                    conversion -= 26;
               while ( conversion < 0 )
                    conversion += 26;
               arg[position] = conversion + 'A';
          }
          else if ( argument[position] >= 'a' && argument[position] <= 'z' )
          {
               conversion = -conversion + position - modifier + argument[position] - 'a';
               conversion = number_range ( conversion - 5, conversion + 5 );
               while ( conversion > 25 )
                    conversion -= 26;
               while ( conversion < 0 )
                    conversion += 26;
               arg[position] = conversion + 'a';
          }
          else if ( argument[position] >= '0' && argument[position] <= '9' )
          {
               conversion = -conversion + position - modifier + argument[position] - '0';
               conversion = number_range ( conversion - 2, conversion + 2 );
               while ( conversion > 9 )
                    conversion -= 10;
               while ( conversion < 0 )
                    conversion += 10;
               arg[position] = conversion + '0';
          }
          else
          {
               arg[position] = argument[position];
          }

     }
     arg[position] = '\0';
     return arg;
}

char *drunk_speech ( const char *argument, CHAR_DATA * ch )
{
     const char         *arg = argument;
     static char         buf[MAX_INPUT_LENGTH * 2];
     char                buf1[MAX_INPUT_LENGTH * 2];
     sh_int              drunk;
     char               *txt;
     char               *txt1;

     if ( IS_NPC ( ch ) || !ch->pcdata )
          return ( char * ) argument;

     drunk = ch->pcdata->condition[COND_DRUNK];

     if ( drunk <= 0 )
          return ( char * ) argument;

     buf[0] = '\0';
     buf1[0] = '\0';

     if ( !argument )
     {
          bugf ( "Drunk_speech: NULL argument (%s)", ch->name );
          return "";
     }

     /*
      * if ( *arg == '\0' )
      * return (char *) argument;
      */

     txt = buf;
     txt1 = buf1;

     while ( *arg != '\0' )
     {
          if ( toupper ( *arg ) == 'S' )
          {
               if ( number_percent (  ) < ( drunk * 2 ) )	/* add 'h' afteran 's' */
               {
                    *txt++ = *arg;
                    *txt++ = 'h';
               }
               else *txt++ = *arg;
          }
          else if ( toupper ( *arg ) == 'X' )
          {
               if ( number_percent (  ) < ( drunk * 2 / 2 ) )
               {
                    *txt++ = 'c', *txt++ = 's', *txt++ = 'h';
               }
               else *txt++ = *arg;
          }
          else if ( number_percent (  ) < ( drunk * 2 / 5 ) )	/* slurred letters */
          {
               sh_int              slurn = number_range ( 1, 2 );
               sh_int              currslur = 0;

               while ( currslur < slurn )
                    *txt++ = *arg, currslur++;
          }
          else *txt++ = *arg;
          arg++;
     };

     *txt = '\0';

     txt = buf;

     while ( *txt != '\0' )	/* Let's mess with the string's caps */
     {
          if ( number_percent (  ) < ( 2 * drunk / 2.5 ) )
          {
               if ( isupper ( *txt ) )
                    *txt1 = tolower ( *txt );
               else if ( islower ( *txt ) )
                    *txt1 = toupper ( *txt );
               else
                    *txt1 = *txt;
          }
          else
               *txt1 = *txt;
          txt1++, txt++;
     };

     *txt1 = '\0';
     txt1 = buf1;
     txt = buf;

     while ( *txt1 != '\0' )	/* Let's make them stutter */
     {
          if ( *txt1 == ' ' )	/* If there's a space, then there's gotta be a */
          {			/* along there somewhere soon */

               while ( *txt1 == ' ' )	/* Don't stutter on spaces */
                    *txt++ = *txt1++;

               if ( ( number_percent (  ) < ( 2 * drunk / 4 ) ) && *txt1 != '\0' )
               {
                    sh_int              offset = number_range ( 0, 2 );
                    sh_int              pos = 0;

                    while ( *txt1 != '\0' && pos < offset )
                         *txt++ = *txt1++, pos++;

                    if ( *txt1 == ' ' )	/* Make sure not to stutter a space after */

                    {		/* the initial offset into the word */
                         *txt++ = *txt1++;
                         continue;
                    }

                    pos = 0;
                    offset = number_range ( 2, 4 );
                    while ( *txt1 != '\0' && pos < offset )
                    {
                         *txt++ = *txt1;
                         pos++;
                         if ( *txt1 == ' ' || pos == offset )	/* Make sure we don't stick */
                         {		/* A hyphen right before a space */
                              txt1--;
                              break;
                         }
                         *txt++ = '-';

                    }
                    if ( *txt1 != '\0' )
                         txt1++;
               }
          }
          else
               *txt++ = *txt1++;
     }

     *txt = '\0';

     return buf;
}

/* RT code to delete yourself */

void do_delet ( CHAR_DATA * ch, char *argument )
{
     send_to_char( "You must type the full command to delete yourself.\n\r", ch );
}

void do_delete ( CHAR_DATA * ch, char *argument )
{
     if ( IS_NPC ( ch ) )
          return;

     if ( ch->pcdata->confirm_delete )
     {
          if ( argument[0] != '\0' )
          {
               send_to_char ( "Delete status removed.\n\r", ch );
               ch->pcdata->confirm_delete = FALSE;
               return;
          }
          else
          {
               send_to_char ( "Deleting!\n\r", ch );
               sound ("taps.wav", ch);

			   /* Zeran - notify_message */
               notify_message ( ch, NOTIFY_DELETE, TO_ALL, NULL );

               real_delete (ch);
               return;
          }
     }

     if ( argument[0] != '\0' )
     {
          send_to_char ( "Just type delete. No argument.\n\r", ch );
          return;
     }

     send_to_char ( "Type delete again to confirm this command.\n\r", ch );
     send_to_char ( "WARNING: this command is irreversible.\n\r", ch );
     send_to_char ( "Typing delete with an argument will undo delete status.\n\r",  ch );
     ch->pcdata->confirm_delete = TRUE;
}

/* So we make sure to remove everything the character is associated with when */
/* The pfile is removed, not leaving anything dangling. */
/* do_fastquit handles the temporary stuff of course. */

void real_delete ( CHAR_DATA *ch )
{
     char                strsave[MAX_INPUT_LENGTH];
     int i;

	 /* Remove char from account */

     for (i = 0 ; i < MAX_CHARS ; i++)
     {
          if (ch->pcdata->account->char_name[i] && !strcmp(ch->name, ch->pcdata->account->char_name[i]) )
          {
			   /* Set character name to NULL */
               ch->pcdata->account->char_name[i] = NULL;
               if ( !ch->pcdata->mortal )			// A demigod is deleting! Decrement the counter
                    --ch->pcdata->account->demigods;
          }
     }

     fwrite_accounts();

	 /* Remove char from any clans */

     if (ch->pcdata->clan)
     {
          ch->pcdata->clan->membercount--; 	/* Decrement the Counter */
          ch->pcdata->clan = NULL;		/* Clear pointer just in case. */
     }

     save_clans ( );

#if defined (COMPRESS_PFILES)
     SNP ( strsave, "%s%s.gz", PLAYER_DIR,
               capitalize ( ch->name ) );
#else
     SNP ( strsave, "%s%s", PLAYER_DIR,
               capitalize ( ch->name ) );
#endif

     do_fastquit ( ch );
     unlink ( strsave );
}

/* Always call fwrite_accounts after calling offline_delete */

void offline_delete ( char *argument )
{
     char		strdel[MAX_INPUT_LENGTH];
     struct account_type  *tmp = NULL;
     int i;
     bool acc_found = FALSE;

	 /* We need to find the associated account first - Pretty intensive*/
	 /* But required since we don't have a pointer. */

     for ( tmp = account_list; tmp != NULL; tmp = tmp->next )
     {
          for (i = 0 ; i < MAX_CHARS ; i++)
          {
               if (tmp->char_name[i] && !strcmp (argument, tmp->char_name[i]) )
               {
                    tmp->char_name[i] = NULL; /* Remove name */
                    acc_found = TRUE;
                    break;
               }
          }
          /* end of for i = 0 */
     }
     /* end of for tmp = account_list */

     if (!acc_found)
     {
          bugf ( "Tried to delete %s offline, account not found.", argument );
          return;
     }

	 /* Remove char from any clans */
	 /* NEED ROSTER FOR THIS!  */
     //    {
     //        clan->membercount--;
     //    }
     //
#if defined (COMPRESS_PFILES)
     SNP ( strdel, "%s%s.gz", PLAYER_DIR, capitalize ( argument ) );
#else
     SNP ( strdel, "%s%s", PLAYER_DIR,  capitalize ( argument ) );
#endif

     unlink ( strdel );
}

void do_beep ( CHAR_DATA * ch, char *argument )
{
     CHAR_DATA          *victim;

     if ( IS_SET ( ch->comm, COMM_NOTELL ) )
     {
          send_to_char ( "Your beep didn't get through.\n\r", ch );
          return;
     }

     if ( IS_SET ( ch->comm, COMM_QUIET ) )
     {
          send_to_char ( "You must turn off quiet mode first.\n\r", ch );
          return;
     }

     if ( argument[0] == '\0' )
     {
          if ( IS_SET ( ch->comm, COMM_BEEP ) )
          {
               REMOVE_BIT ( ch->comm, COMM_BEEP );
               send_to_char ( "You now accept beeps (This includes beep codes).\n\r", ch );
          }
          else
          {
               SET_BIT ( ch->comm, COMM_BEEP );
               send_to_char ( "You refuse to accept beeps or beep codes.\n\r", ch );
          }
          return;
     }

     if ( IS_SET ( ch->comm, COMM_BEEP ) )
     {
          send_to_char ( "You have to turn on the beep channel first.\n\r", ch );
          return;
     }

     if ( ( victim = get_char_world ( ch, argument ) ) == NULL )
     {
          send_to_char ( "Nobody like that.\n\r", ch );
          return;
     }

     if ( IS_SET ( victim->comm, COMM_BEEP ) )
     {
          act_new ( "$N is not receiving beeps.", ch, NULL, victim,	TO_CHAR, POS_DEAD );
          return;
     }

     act_new ( "\a{WYou beep to $N.{x", ch, NULL, victim, TO_CHAR, POS_DEAD );
     act_new ( "\a{W$n beeps you.{x", ch, NULL, victim, TO_VICT, POS_DEAD );

     return;
}

void do_deaf ( CHAR_DATA * ch, char *argument )
{
     if ( IS_NPC ( ch ) )
          return;
     
     if ( IS_SET ( ch->comm, COMM_NOSHOUT ) )
     {
          send_to_char ( "The gods have taken away your ability to shout.\n\r", ch );
          return;
     }

     real_acomm ( ch, COMM_DEAF, "Deaf Mode (Block Shouts)" );
     
}

void do_quiet ( CHAR_DATA * ch, char *argument )
{
     real_acomm ( ch, COMM_QUIET, "Quiet Mode" );
}

/* Channels block */

void do_auction ( CHAR_DATA * ch, char *argument )
{
     channel_message ( ch, argument, "auction" );
     return;
}

void do_gossip ( CHAR_DATA * ch, char *argument )
{
     channel_message ( ch, argument, "gossip" );
     return;
}

void do_question ( CHAR_DATA * ch, char *argument )
{
     channel_message ( ch, argument, "question" );
     return;
}

void do_answer ( CHAR_DATA * ch, char *argument )
{
     channel_message ( ch, argument, "answer" );
     return;
}

void do_music ( CHAR_DATA * ch, char *argument )
{
     channel_message ( ch, argument, "music" );
     return;
}

void do_immtalk ( CHAR_DATA * ch, char *argument )
{
     channel_message ( ch, argument, "immtalk" );
     return;
}

void do_imptalk ( CHAR_DATA * ch, char *argument )
{
     if ( argument[0] == '\0' ) // because imps can't turn off imptalk.
          return;
     channel_message ( ch, argument, "imptalk" );
}

void do_say ( CHAR_DATA * ch, char *argument )
{
     OBJ_DATA *obj, *obj_next;

     if ( argument[0] == '\0' )
     {
          send_to_char ( "Say what?\n\r", ch );
          return;
     }
     if ( CAN_DETECT ( ch, AFF_MUTE ) )
     {
          send_to_char ( "Mute people cannot use 'say'.\n\r", ch );
          return;
     }
     channel_message ( ch, argument, "say" );

     if ( !IS_NPC(ch) )
     {
          CHAR_DATA *mob, *mob_next;
          OBJ_DATA *obj, *obj_next; // progs

          for ( mob = ch->in_room->people; mob != NULL; mob = mob_next )
          {
               mob_next = mob->next_in_room;
               if ( IS_NPC(mob) && HAS_TRIGGER_MOB( mob, TRIG_SPEECH ) && mob->position == mob->pIndexData->default_pos )
                    p_act_trigger( argument, mob, NULL, NULL, ch, NULL, NULL, TRIG_SPEECH );
               for ( obj = mob->carrying; obj; obj = obj_next )
               {
                    obj_next = obj->next_content;
                    if ( HAS_TRIGGER_OBJ( obj, TRIG_SPEECH ) )
                         p_act_trigger( argument, NULL, obj, NULL, ch, NULL, NULL, TRIG_SPEECH );
               }
          }
     }
     for ( obj = ch->in_room->contents; obj; obj = obj_next )
     {
          obj_next = obj->next_content;
          if ( HAS_TRIGGER_OBJ( obj, TRIG_SPEECH ) )
               p_act_trigger( argument, NULL, obj, NULL, ch, NULL, NULL, TRIG_SPEECH );
     }
     if ( HAS_TRIGGER_ROOM( ch->in_room, TRIG_SPEECH ) )
          p_act_trigger( argument, NULL, NULL, ch->in_room, ch, NULL, NULL, TRIG_SPEECH );
     return;
}

void do_shout ( CHAR_DATA * ch, char *argument )
{
     if ( IS_SET ( ch->comm, COMM_NOSHOUT ) )
     {
          send_to_char ( "You can't shout.\n\r", ch );
          return;
     }
     if ( IS_SET ( ch->comm, COMM_DEAF ) )
     {
          send_to_char ( "Deaf people can't shout.\n\r", ch );
          return;
     }
     if ( argument[0] == '\0' )
     {
          send_to_char ( "Shout what?\n\r", ch );
          return;
     }

     channel_message ( ch, argument, "shout" );
     WAIT_STATE ( ch, 12 );
}

void do_tell ( CHAR_DATA * ch, char *argument )
{
     char                arg[MAX_INPUT_LENGTH];
     CHAR_DATA          *victim;

     if ( !IS_NPC (ch ) && IS_SET ( ch->comm, COMM_NOTELL ) )
     {
          send_to_char ( "Your message didn't get through.\n\r", ch );
          return;
     }

     if ( !IS_NPC ( ch ) && IS_SET ( ch->comm, COMM_QUIET ) )
     {
          send_to_char ( "You must turn off quiet mode first.\n\r", ch );
          return;
     }

     argument = one_argument ( argument, arg );

     if ( arg[0] == '\0' || argument[0] == '\0' )
     {
          send_to_char ( "Tell whom what?\n\r", ch );
          return;
     }

     if ( ( victim = get_char_world ( ch, arg ) ) == NULL || ( IS_NPC ( victim ) && victim->in_room != ch->in_room ) )
     {
          send_to_char ( "They aren't here.\n\r", ch );
          return;
     }

     if ( victim->desc == NULL && !IS_NPC ( victim ) )
     {
          act ( "$N seems to have misplaced $S link...try again later.", ch, NULL, victim, TO_CHAR );
          return;
     }

     if ( IS_SET ( victim->comm, COMM_QUIET ) && !IS_IMMORTAL ( ch ) )
     {
          act ( "$E is not receiving tells.", ch, 0, victim, TO_CHAR );
          return;
     }

     if ( !IS_NPC ( victim ) && IS_SET ( victim->act, PLR_AFK ) )
     {
          struct afk_tell_type *tmp;
          char   buf[MIL*2];

          act ( "$N is AFK...your tell may not be seen immediately.", ch, NULL, victim, TO_CHAR );
          SNP ( buf, "%s tells you '%s'\n\r", PERSMASK ( ch, victim ), argument );

          tmp = alloc_mem ( sizeof ( struct afk_tell_type ), "afk_tell_type" );
          tmp->message = str_dup ( buf );
          tmp->next = NULL;
		  /* attach to list */
          if ( victim->pcdata->afk_tell_first == NULL )
          {
               victim->pcdata->afk_tell_first = tmp;
               victim->pcdata->afk_tell_last = tmp;
          }
          else
          {
               victim->pcdata->afk_tell_last->next = tmp;
               victim->pcdata->afk_tell_last = tmp;
          }
     }

     act ( "You tell $N '{W$t{x'", ch, argument, victim, TO_CHAR );
     act_new ( "$n tells you '{W$t{x'", ch, argument, victim, TO_VICT, POS_DEAD );
     victim->reply = ch;

     if ( !IS_NPC(ch) && IS_NPC(victim) && HAS_TRIGGER_MOB(victim,TRIG_SPEECH) )
          p_act_trigger( argument, victim, NULL, NULL, ch, NULL, NULL, TRIG_SPEECH );

     return;
}

// Need to make reply a wrapper for tell so changes to tell don't need to be mirrored here.
//
void do_reply ( CHAR_DATA * ch, char *argument )
{
     CHAR_DATA          *victim;

     if ( IS_SET ( ch->comm, COMM_NOTELL ) )
     {
          send_to_char ( "Your message didn't get through.\n\r", ch );
          return;
     }

     if ( ( victim = ch->reply ) == NULL )
     {
          send_to_char ( "They aren't here.\n\r", ch );
          return;
     }

     if ( victim->desc == NULL && !IS_NPC ( victim ) )
     {
          act ( "$N seems to have misplaced $S link...try again later.", ch, NULL, victim, TO_CHAR );
          return;
     }

     if ( !IS_IMMORTAL ( ch ) && !IS_AWAKE ( victim ) )
     {
          act ( "$E can't hear you.", ch, 0, victim, TO_CHAR );
          return;
     }

     if ( IS_SET ( victim->comm, COMM_QUIET ) && !IS_IMMORTAL ( ch ) )
     {
          act ( "$E is not receiving tells.", ch, 0, victim, TO_CHAR );
          return;
     }

     if ( !IS_NPC ( victim ) && IS_SET ( victim->act, PLR_AFK ) )
     {
          struct afk_tell_type *tmp;
          char   buf[MIL*2];

          act ( "$N is AFK...your tell may not be seen immediately.", ch, NULL, victim, TO_CHAR );
          SNP ( buf, "%s tells you '%s'\n\r", PERSMASK ( ch, victim ), argument );

          tmp = alloc_mem ( sizeof ( struct afk_tell_type ), "afk_tell_type" );
          tmp->message = str_dup ( buf );
          tmp->next = NULL;
		  /* attach to list */
          if ( victim->pcdata->afk_tell_first == NULL )
          {
               victim->pcdata->afk_tell_first = tmp;
               victim->pcdata->afk_tell_last = tmp;
          }
          else
          {
               victim->pcdata->afk_tell_last->next = tmp;
               victim->pcdata->afk_tell_last = tmp;
          }
     }

     act ( "You tell $N '{W$t{x'", ch, argument, victim, TO_CHAR );
     act_new ( "$n tells you '{W$t{w'", ch, argument, victim, TO_VICT, POS_DEAD );
     victim->reply = ch;

     if ( !IS_NPC(ch) && IS_NPC(victim) && HAS_TRIGGER_MOB(victim,TRIG_SPEECH) )
          p_act_trigger( argument, victim, NULL, NULL, ch, NULL, NULL, TRIG_SPEECH );
     return;
}

void do_yell ( CHAR_DATA * ch, char *argument )
{

     if ( IS_SET ( ch->comm, COMM_NOSHOUT ) )
     {
          send_to_char ( "You can't yell.\n\r", ch );
          return;
     }

     if ( argument[0] == '\0' )
     {
          send_to_char ( "Yell what?\n\r", ch );
          return;
     }

     channel_message ( ch, argument, "yell" );
     return;
}

void do_emote ( CHAR_DATA * ch, char *argument )
{
     if ( !IS_NPC ( ch ) && IS_SET ( ch->comm, COMM_NOEMOTE ) )
     {
          send_to_char ( "You can't show your emotions.\n\r", ch );
          return;
     }

     if ( argument[0] == '\0' )
     {
          send_to_char ( "Emote what?\n\r", ch );
          return;
     }

     MOBtrigger = FALSE;
     act ( "$n $T", ch, NULL, argument, TO_ROOM );
     act ( "$n $T", ch, NULL, argument, TO_CHAR );
     MOBtrigger = TRUE;
     return;
}

/*
 * Structure for a Quote
 */

struct quote_type
{
     char               *text;
     char               *by;
};

/*
 * The Quotes - insert yours in, and increase MAX_QUOTES in merc.h
 * Someday I'll move this to a savefile.
 */

const struct quote_type quote_table[MAX_QUOTES] =
{
     {"Cogito Ergo Sum", "Descartes"},	/* 1 */
     {"Your lucky color has faded.", "Unknown"},
     {"Don't mess with Dragons, for thou art Crunchy and go well with milk.", "Unknown Source"},
     {"... and furthermore ... I don't like your trousers.", "Unknown"},	/* 4 */
     {"They're only kobolds!", "Dragon Magazine 'Famous Last Words'"},	/* 5 */
     {"I wouldn't want to be around when a mage says 'Oops'", "Unknown"},	/*6 */
     {"Of course I'm a wizard, son.  I've got a tall pointy hat!", "Unknown"},	/* 7 */
     {"Oh, wizardry has really very little to do with magic.", "Ingold"},	/* 8 */
     {"Would you like to be my new experiment?", "Kask the Evil Wizard"},	/* 9 */
     {"A wizard, huh?  I throw my drink at him.", "Unkown"},	/* 10 */
     {"No true wizard ever breaks his word.", "Dragon Magazine 'Famous Last Words'"},	/* 11 */
     {"Just how many 30th-level evil wizards are there in this village?", "Unknown"},	/* 12 */
     {"I take the wizard's wand, and snap it in two.", "Dragon Magazine 'Famous Last Words'"},	/* 13 */
     {"No matter how subtle the sorcerer, a knife in the back will seriously cramp his style.", "Unknown"}	/* 14 */

};

void do_quote ( CHAR_DATA * ch )
{
     int                 quote = 0;

     quote = number_range ( 0, MAX_QUOTES - 1 );

     if ( quote_table[quote].text == NULL )
          bugf ( "DO_QUOTE: Null Quote %d", quote );
     form_to_char ( ch, "\n\r{W%s\n\r{Y - %s{x\n\r",
                    quote_table[quote].text, quote_table[quote].by );
     return;
}

void do_bug ( CHAR_DATA * ch, char *argument )
{
     append_file ( ch, BUG_FILE, argument );
     send_to_char ( "Bug logged.\n\r", ch );
     notify_message ( NULL, WIZNET_BUG, TO_IMM, argument );
     return;
}

void do_typo ( CHAR_DATA * ch, char *argument )
{
     append_file ( ch, TYPO_FILE, argument );
     send_to_char ( "Typo logged.\n\r", ch );
     return;
}

void do_rent ( CHAR_DATA * ch, char *argument )
{
     send_to_char ( "Circles and Dikus and Rents Oh My!!!\n\r", ch );
     send_to_char ( "But this is Sunder. You don't have to rent to quit here.\n\r", ch );
     send_to_char ( "If you are looking to rent a room, not quit, try LEASE.\n\r", ch );
     return;
}

void do_qui ( CHAR_DATA * ch, char *argument )
{
     send_to_char ( "If you want to QUIT, you have to spell it out.\n\r", ch );
     return;
}

void do_quit ( CHAR_DATA * ch, char *argument )
{
     DESCRIPTOR_DATA    *d;
     OBJ_DATA *obj;
     OBJ_DATA *obj_next = NULL;

     if ( IS_NPC ( ch ) )
          return;
     
     if ( ch->position == POS_FIGHTING )
     {
          send_to_char ( "No way! You are fighting.\n\r", ch );
          return;
     }

     if ( ch->position < POS_STUNNED )
     {
          send_to_char ( "You're not DEAD yet.\n\r", ch );
          return;
     }

     for ( obj = ch->carrying; obj != NULL; obj = obj_next )
     {
          if (obj->owner && !belongs (ch, obj) )
          {
               send_to_char("Sorry, you may not quit while holding someone else's property.\n\r", ch);
               return;
          }
     }

     if ( ch->pcdata->in_progress )
          free_note ( ch->pcdata->in_progress );

     /* Zeran - notify message */
     notify_message ( ch, NOTIFY_QUITGAME, TO_ALL, NULL );
     act ( "$n has left the game.", ch, NULL, NULL, TO_ROOM );
     log_string ( "%s has quit.", ch->name );
     do_quote ( ch );
     ch->quitting = TRUE;

     /*
      * After extract_char the ch is no longer valid!
      */
     
     save_char_obj( ch );
     d = ch->desc;
     extract_char( ch, TRUE );
     if ( d != NULL )
          close_socket( d );     

     return;
}

/* Fastquit only for internal usage, since it doesn't do some normal sanity checks.
 * Also no output to user.
 */

void do_fastquit ( CHAR_DATA * ch )
{
     DESCRIPTOR_DATA    *d;
     OBJ_DATA *obj;
     OBJ_DATA *obj_next = NULL;

     if ( IS_NPC ( ch ) )
          return;

     if ( ch->position == POS_FIGHTING )
     {
          bugf ("fastquit called while %s was fighting.\n\r", ch->name);
     }

     if ( ch->position < POS_STUNNED )
     {
          bugf ("fastquit called while %s was stunned or worse.\n\r", ch->name);
     }

     /* For fastquit, since likely outcome is deletion, character will drop */
     /* anything belonging to someone else automatically. */
     /* We will do some logging for this incase items are lost when a character is made */
     /* to fastquit for reasons other than deletion */

     for ( obj = ch->carrying; obj != NULL; obj = obj_next )
     {
          if (obj->owner && !belongs (ch, obj) )
          {
               obj_from_char ( obj );
               obj_to_room ( obj, ch->in_room );
               act ( "$n drops $p.", ch, obj, NULL, TO_ROOM );
               log_string ( "%s dropped %s (level %d) belonging to %s while being fastquit.",
                         ch->name, obj->name, obj->level, obj->owner);
          }
     }

     if ( ch->pcdata->in_progress )
          free_note ( ch->pcdata->in_progress );

     log_string ( "%s has been fastquit.", ch->name );

     ch->quitting = TRUE;

     /*
      * After extract_char the ch is no longer valid!
      */
     save_char_obj( ch );
     d = ch->desc;
     extract_char( ch, TRUE );
     if ( d != NULL )
          close_socket( d );     
     
     return;
}

void do_save ( CHAR_DATA * ch, char *argument )
{
     if ( IS_NPC ( ch ) )
          return;

     save_char_obj ( ch );
     send_to_char ( "Saving. Remember your character is automatically saved anyway.\n\r", ch );
     WAIT_STATE ( ch, PULSE_VIOLENCE );
     return;
}

/* Groups code rewritten totally by Lotherius to be more efficient. Maybe. */

void do_follow ( CHAR_DATA * ch, char *argument )
{
     char                arg[MAX_INPUT_LENGTH];
     CHAR_DATA          *victim;

     one_argument ( argument, arg );

     if ( arg[0] == '\0' )
     {
          send_to_char ( "Follow whom?\n\r", ch );
          return;
     }

     if ( ( victim = get_char_room ( ch, NULL, arg ) ) == NULL )
     {
          send_to_char ( "They aren't here.\n\r", ch );
          return;
     }

     if ( IS_AFFECTED ( ch, AFF_CHARM ) && ch->master != NULL )
     {
          act ( "But you'd rather follow $N!", ch, NULL, ch->master, TO_CHAR );
          return;
     }

     if ( victim == ch )
     {
          if ( ch->master == NULL )
          {
               send_to_char ( "You already follow yourself.\n\r", ch );
               return;
          }
          stop_follower ( ch );
          return;
     }

     if ( !IS_NPC ( victim ) && IS_SET ( victim->act, PLR_NOFOLLOW ) && !IS_HERO ( ch ) )
     {
          act ( "$N doesn't seem to want any followers.\n\r", ch, NULL, victim, TO_CHAR );
          return;
     }

     REMOVE_BIT ( ch->act, PLR_NOFOLLOW );

     if ( ch->master != NULL )
          stop_follower ( ch );

     if ( ch->leader != NULL )
          leave_group ( ch );

     add_follower ( ch, victim );
     return;
}

void add_follower ( CHAR_DATA * ch, CHAR_DATA * master )
{
     if ( ch->master != NULL )
     {
          bugf ( "Add_follower: non-null master. (%s)", ch->name );
          return;
     }

     if ( ch->leader != NULL )
     {
          bugf ( "Add_follower: Trying to follow while still in group.(%s)", ch->name );
          return;
     }

     ch->master = master;

     if ( can_see ( master, ch ) )
          act ( "$n now follows you.", ch, NULL, master, TO_VICT );

     act ( "You now follow $N.", ch, NULL, master, TO_CHAR );

     return;
}

// This func is more complicated than it looks.
// So I decided to document it -- Lotherius
//
void leave_group ( CHAR_DATA * ch )
{
     if ( ch->leader != NULL )   /* Remove Member from Leader's Group - Lotherius */
     {
          CHAR_DATA          *leader;
          int                 counter;
          bool                match = FALSE;

          leader = ch->leader;    // whoever ch->leader is, can be self if not in a group.

          // Look for self in leader's group, and remove self.
          //
          for ( counter = 0; counter < MAX_GMEMBERS; counter++ )
          {
               if ( leader->group[counter] && ( leader->group[counter]->gch == ch ) ) // We found ourselves. Bail.
               {
                    match = TRUE;
                    break;
               }
          }
		  /* Zeran - Uhm...Loth, try checking your match variable first */
          if ( match )
          {
               leader->group[counter]->gch = NULL;
               free_group ( leader->group[counter] );
          }
          else
          {
               bugf ( "%s had no group on leave_group (Should always have a group)", ch->name );
          }
          ch->leader = NULL;
     }
     else
     {
          bugf ("We got a null ch->leader on leave_group. (%s)", ch->name );
     }

     return;
}

void stop_follower ( CHAR_DATA * ch )
{
     struct char_group  *tmp;

     if ( ch->master == NULL )
     {
          bugf ( "Stop_follower: %s null master.", ch->name );
          return;
     }

     if ( IS_AFFECTED ( ch, AFF_CHARM ) )
     {
          REMOVE_BIT( ch->affected_by, AFF_CHARM );
          affect_strip ( ch, gsn_charm_person );
     }

     if ( can_see ( ch->master, ch ) && ch->in_room != NULL )
     {
          act ( "$n stops following you.", ch, NULL, ch->master, TO_VICT );
          act ( "You stop following $N.", ch, NULL, ch->master, TO_CHAR );
     }
     if ( ch->master->pet == ch )
          ch->master->pet = NULL;

     ch->master = NULL;

     leave_group ( ch );

     /* Put back in own group */

     tmp = newgroup();

     ch->group[MAX_GMEMBERS - 1] = tmp;
     tmp->gch = ch;

     ch->leader = ch;

     return;
}

/* nukes charmed monsters and pets when master leaves */
void nuke_pets ( CHAR_DATA * ch )
{
     CHAR_DATA          *pet;

     if ( ( pet = ch->pet ) != NULL )
     {
          stop_follower ( pet );
          if ( pet->in_room != NULL )
               act ( "$N slowly fades away.", ch, NULL, pet, TO_NOTVICT );
          sound ("taps.wav", ch);
          extract_char ( pet, TRUE );
     }
     ch->pet = NULL;

     return;
}

void die_follower ( CHAR_DATA * ch )
{
     CHAR_DATA          *fch;

     if ( ch->master != NULL )
     {
          if ( ch->master->pet == ch )
               ch->master->pet = NULL;
          stop_follower ( ch );
     }

	 /* Remove Follower from Leader's Group - Lotherius */
     if ( ch->leader != NULL && ch->leader != ch )
          leave_group ( ch );

     for ( fch = char_list; fch != NULL; fch = fch->next )
     {
          if ( fch->master == ch )
               stop_follower ( fch );
          if ( fch->leader == ch )
               fch->leader = fch;
     }

     return;
}

void do_order ( CHAR_DATA * ch, char *argument )
{
     char                buf[MAX_INPUT_LENGTH];
     char                arg[MAX_INPUT_LENGTH];
     char                arg2[MAX_INPUT_LENGTH];
     CHAR_DATA          *victim;
     CHAR_DATA          *och;
     CHAR_DATA          *och_next;
     bool                found;
     bool                fAll;

     argument = one_argument ( argument, arg );
     one_argument ( argument, arg2 );

     if ( arg[0] == '\0' || argument[0] == '\0' )
     {
          send_to_char ( "Order whom to do what?\n\r", ch );
          return;
     }
     if ( !str_cmp (arg2,"delete") || !str_cmp(arg2,"mob"))
     {
          send_to_char ( "That will NOT be done.\n\r", ch );
          return;
     }
     if ( strlen ( argument ) > 100 )
     {
          send_to_char ("No novels, please.\n\r", ch );
          return;
     }
     if ( IS_AFFECTED ( ch, AFF_CHARM ) )
     {
          send_to_char ( "You feel like taking, not giving, orders.\n\r", ch );
          return;
     }
     if ( !str_cmp ( arg, "all" ) )
     {
          fAll = TRUE;
          victim = NULL;
     }
     else
     {
          fAll = FALSE;
          if ( ( victim = get_char_room ( ch, NULL, arg ) ) == NULL )
          {
               send_to_char ( "They aren't here.\n\r", ch );
               return;
          }

          if ( victim == ch )
          {
               send_to_char ( "Aye aye, right away!\n\r", ch );
               return;
          }

          if ( !IS_AFFECTED ( victim, AFF_CHARM ) || victim->master != ch )
          {
               send_to_char ( "Do it yourself!\n\r", ch );
               return;
          }
     }
     found = FALSE;
     for ( och = ch->in_room->people; och != NULL; och = och_next )
     {
          och_next = och->next_in_room;

          if ( IS_AFFECTED ( och, AFF_CHARM )
               && och->master == ch && ( fAll || och == victim ) )
          {
               if ( och->wait == 0 )
               {
                    found = TRUE;
                    SNP ( buf, "$n orders you to '%s'.", argument );
                    act ( buf, ch, NULL, och, TO_VICT );
                    interpret ( och, argument );
               }
               else
               {
                    form_to_char ( ch, "%s is too busy right now.\n\r", 
                                   ( IS_NPC ( och ) ? och->short_descr : och->name ) );
               }

          }
     }

     if ( found )
          send_to_char ( "Ok.\n\r", ch );
     else
          send_to_char ( "You have no followers here.\n\r", ch );
     return;
}

/* Biggest changes here - Lotherius */
void do_group ( CHAR_DATA * ch, char *argument )
{
     struct char_group  *tmp;
     char                arg[MAX_INPUT_LENGTH];
     CHAR_DATA          *victim;
     int                 counter;
     int                 last_free = -1;
     bool                match = FALSE;

     one_argument ( argument, arg );

	 /* List Group members */

     if ( arg[0] == '\0' )
     {
          CHAR_DATA          *leader;

          leader = ( ch->leader != NULL ) ? ch->leader : ch;

          form_to_char ( ch, "{W%s{w's Group:\n\r{Y-------------------------\n\r",
                    leader->name );

          for ( counter = 0; counter < MAX_GMEMBERS; counter++ )
          {
               tmp = leader->group[counter];
               if ( tmp != NULL )
               {
                    CHAR_DATA          *gch;

                    gch = leader->group[counter]->gch;
                    form_to_char ( ch, "{C[%2d %s] %-16s %4d/%4d hp "
                                   "%4d/%4d mana %4d/%4d mv %5d tnl{x\n\r",
                                   gch->level,
                                   IS_NPC ( gch ) ? "Mob" :
                                   class_table[gch->pcdata->pclass].who_name,
                                   capitalize ( PERSMASK ( gch, ch ) ),
                                   gch->hit, gch->max_hit, gch->mana,
                                   gch->max_mana, gch->move, gch->max_move,
                                   IS_NPC (gch) ? 0 :
                                   ( (gch->level +1) * exp_per_level (gch, gch->pcdata->points) - gch->exp) );
                    match = TRUE;
               }
          }
          if ( !match )
               send_to_char ( "There is nobody else in the group.\n\r", ch );
          return;
     }
     /* If victim name is char, bail out */
     if ( !str_cmp ( arg, ch->name ) )
     {
          send_to_char ( "You can't group yourself!", ch );
          return;
     }
     /* Set target, verify target is in room */
     if ( ( victim = get_char_room ( ch, NULL, arg ) ) == NULL )
     {
          send_to_char ( "They aren't here.\n\r", ch );
          return;
     }
     /* See if character is following someone else, can't start a group */
     if ( ch->master != NULL || ( ch->leader != NULL && ch->leader != ch ) )
     {
          send_to_char ( "But you are following someone else!\n\r", ch );
          return;
     }

     /* Trying to group someone who hasn't followed you */
     if ( victim->master != ch && ch != victim )
     {
          act ( "$N isn't following you.", ch, NULL, victim, TO_CHAR );
          return;
     }
     /* Can't remove charmed mobs from your group, they're automatic. */
     if ( IS_AFFECTED ( victim, AFF_CHARM ) && is_same_group(victim, ch) )
     {
          send_to_char ( "You can't remove charmed mobs from your group.\n\r", ch );
          return;
     }
     /* If you are charmed, you can't leave the group. */
     if ( IS_AFFECTED ( ch, AFF_CHARM ) )
     {
          act ( "You like your master too much to leave $m!", ch, NULL, victim, TO_VICT );
          return;
     }

     /* If player is already in the group, this will remove player from group */
     if ( is_same_group ( victim, ch ) && ch != victim )
     {
          leave_group ( victim );

          act ( "$n removes $N from $s group.", ch, NULL, victim, TO_NOTVICT );
          act ( "$n removes you from $s group.", ch, NULL, victim, TO_VICT );
          act ( "You remove $N from your group.", ch, NULL, victim, TO_CHAR );
          return;
     }

	 /* Add member to group. */

     victim->leader = ch;
	 /* Find free member Slot */
     for ( counter = MAX_GMEMBERS - 1; counter >= 0; counter-- )
     {
          if ( ch->group[counter] == NULL )
               last_free = counter;
     }
     if ( last_free == -1 )
     {
          send_to_char ( "Your group is as large as possible.\n\r", ch );
          return;
     }

     tmp = newgroup();

     ch->group[last_free] = tmp;
     tmp->gch = victim;

     act ( "$N joins $n's group.", ch, NULL, victim, TO_NOTVICT );
     act ( "You join $n's group.", ch, NULL, victim, TO_VICT );
     act ( "$N joins your group.", ch, NULL, victim, TO_CHAR );
     return;
}

void do_split ( CHAR_DATA * ch, char *argument, bool tax )
{
     char                buf[MSL];
     char                arg[MAX_INPUT_LENGTH];
     CHAR_DATA          *gch;
     int                 members;
     int                 amount;
     int                 share;
     int                 extra;

     one_argument ( argument, arg );

     if ( arg[0] == '\0' )
     {
          send_to_char ( "Split how much?\n\r", ch );
          return;
     }

     amount = atoi ( arg );

     if ( amount < 0 )
     {
          send_to_char ( "Your group wouldn't like that.\n\r", ch );
          return;
     }
     if ( amount == 0 )
     {
          send_to_char ( "You hand out zero coins, but no one notices.\n\r", ch );
          return;
     }
     if ( ch->gold < amount )
     {
          send_to_char ( "You don't have that much gold.\n\r", ch );
          return;
     }
     members = 0;
     for ( gch = ch->in_room->people; gch != NULL; gch = gch->next_in_room )
     {
          if ( is_same_group ( gch, ch ) && !IS_AFFECTED ( gch, AFF_CHARM ) )
               members++;
     }
     if ( members < 2 )
     {
          send_to_char ( "Just keep it all.\n\r", ch );
          return;
     }
     share = amount / members;
     extra = amount % members;
     if ( share == 0 )
     {
          send_to_char ( "Don't even bother, cheapskate.\n\r", ch );
          return;
     }
     ch->gold -= amount;
     if ( tax )
          do_pay ( ch, ( share + extra ) );
     else
          ch->gold += ( share + extra );
     form_to_char ( ch, "You split %d gold coins.  Your share is %d gold coins.\n\r", amount, share + extra );
     SNP ( buf, "$n splits %d gold coins.  Your share is %d gold coins.", amount, share );
     for ( gch = ch->in_room->people; gch != NULL; gch = gch->next_in_room )
     {
          if ( gch != ch && is_same_group ( gch, ch ) && !IS_AFFECTED ( gch, AFF_CHARM ) )
          {
               act ( buf, ch, NULL, gch, TO_VICT );
               do_pay ( gch, share );
          }
     }

     return;
}

// Wrapper so that the player command of do_split won't cause player to have
// to pay a tax on money he/she already has.
void do_splitc ( CHAR_DATA *ch, char *argument )
{
     do_split ( ch, argument, FALSE );
     return;
}

void do_gtell ( CHAR_DATA * ch, char *argument )
{
     char                buf[MAX_STRING_LENGTH];
     CHAR_DATA          *gch;

     if ( argument[0] == '\0' )
     {
          send_to_char ( "Tell your group what?\n\r", ch );
          return;
     }
     if ( IS_SET ( ch->comm, COMM_NOTELL ) )
     {
          send_to_char ( "Your message didn't get through!\n\r", ch );
          return;
     }
     /*
      * Note use of send_to_char, so gtell works on sleepers.
      * Also, don't use form_to_char here because would have to repeatedly format
      */
     SNP ( buf, "%s tells the group '%s'.\n\r", ch->name, argument );
     for ( gch = char_list; gch != NULL; gch = gch->next )
     {
          if ( is_same_group ( gch, ch ) )
               send_to_char ( buf, gch );
     }

     return;
}

/*
 * It is very important that this be an equivalence relation:
 * (1) A ~ A
 * (2) if A ~ B then B ~ A
 * (3) if A ~ B  and B ~ C, then A ~ C
 */
bool is_same_group ( CHAR_DATA * ach, CHAR_DATA * bch )
{
     if ( ach->leader != NULL )
          ach = ach->leader;
     if ( bch->leader != NULL )
          bch = bch->leader;
     return ach == bch;
}

/*
 * Colour setting and unsetting, way cool, Lope Oct '94
 * Modified by Lotherius
 */
void do_colour ( CHAR_DATA * ch, char *argument )
{
     char                arg[MAX_STRING_LENGTH];

     if ( IS_NPC ( ch ) )
          return;

     argument = one_argument ( argument, arg );

     if ( !*arg )
     {
          if ( !ch->desc->ansi )
          {
               ch->desc->ansi = TRUE;
               SET_BIT ( ch->comm, COMM_COLOUR );
               send_to_char ( "{bC{ro{yl{co{mu{gr{x is now {rON{x, Way Cool!\n\r", ch );

          }
          else
          {
               ch->desc->ansi = FALSE;
               send_to_char ( "Colour is now OFF, <sigh>\n\r", ch );
               REMOVE_BIT ( ch->comm, COMM_COLOUR );
          }
          return;
     }
     else
          send_to_char ( "Sorry, configuration is not available in this version of colour.\n\r", ch );
     return;
}

/* Here's a function I need to find a good use for.
 * I really thought this was cool till I found out how many mud clients can't
 * handle full-screen telnet.
 * -Lotherius
 */

void do_cursor ( CHAR_DATA * ch, char *argument )
{
     char                arg[MIL];

     argument = one_argument ( argument, arg );

     if ( !*arg )
     {
          if ( !IS_SET ( ch->act, PLR_CURSOR ) )
          {
               SET_BIT ( ch->act, PLR_CURSOR );
               send_to_char ( VT_CLS, ch );
               gotoxy ( ch, 0, 0 );
               send_to_char ( "Cursor Control is Now On!\n\r", ch );
               send_to_char ( "The following numbers should run down the screen, moving to the right.", ch );
               gotoxy ( ch, 4, 1 );
               send_to_char ( "1", ch );
               gotoxy ( ch, 5, 3 );
               send_to_char ( "2", ch );
               gotoxy ( ch, 6, 5 );
               send_to_char ( "3", ch );
               gotoxy ( ch, 7, 7 );
               send_to_char ( "4\n\r", ch );
               send_to_char ( "If the numbers 1-4 run together, please turn cursor off, your terminal\n\r"
                              "doesn't support cursor control.", ch );
          }
          else
          {
               send_to_char_bw ( "Cursor control is now off.\n\r", ch );
               REMOVE_BIT ( ch->act, PLR_CURSOR );
          }
          return;
     }
     else
     {
          send_to_char ( "Just type cursor by itself.", ch );
     }
     return;
}

void do_alias ( CHAR_DATA * ch, char *argument )
{
     struct alias_data  *tmp = NULL;
     char                alias_name[MAX_INPUT_LENGTH];
     char               *alias_string;
     char                d_alias[MAX_INPUT_LENGTH];
     int                 counter, number;
     bool                got_one = FALSE;
     int                 last_free = -1;

     if ( IS_NPC ( ch ) )
          return;

     /* if no arguments, just print out current aliases */
     if ( argument == NULL || argument[0] == '\0' )
     {
          for ( counter = 0; counter < MAX_ALIAS; counter++ )
          {
               tmp = ch->pcdata->aliases[counter];
               if ( tmp != NULL )
               {
                    form_to_char ( ch, "Alias %d:  (%s) = (%s)\n\r",
                                   counter,
                                   ch->pcdata->aliases[counter]->name,
                                   ch->pcdata->aliases[counter]->
                                   command_string );
                    got_one = TRUE;
               }
          }
          if ( !got_one )
               send_to_char ( "You have no aliases defined.\n\r", ch );
          return;
     }

     alias_string = one_argument ( argument, alias_name );

     if ( alias_string == NULL || alias_string[0] == '\0' )
     {
          send_to_char ( "Syntax:  alias 'name' 'command string'\n\r", ch );
          return;
     }

     if ( !str_cmp ( alias_name, "delete" ) )
     {
          if ( alias_string[0] == '\0' )
          {
               send_to_char ( "Syntax for alias deletion:  alias delete <number>\n\r", ch );
               return;
          }
          one_argument ( alias_string, d_alias );
          /* delete designated alias if exits */
          for ( counter = 0; counter < MAX_ALIAS; counter++ )
          {
               tmp = ch->pcdata->aliases[counter];
               if ( tmp && !str_cmp ( tmp->name, d_alias ) )
               {
                    got_one = TRUE;
                    break;
               }
          }
          if ( !got_one )
          {
               send_to_char ( "No alias found with that name.\n\r", ch );
               return;
          }
          free_string ( tmp->name );
          free_string ( tmp->command_string );
          ch->pcdata->aliases[counter] = NULL;
          free_mem ( tmp, sizeof ( struct alias_data ), "alias_data" );
          send_to_char ( "Alias deleted.\n\r", ch );
          got_one = FALSE;
          /* check if all deleted, set has_alias to false */
          for ( number = 0; number < MAX_ALIAS; number++ )
          {
               if ( ch->pcdata->aliases[number] != NULL )
               {
                    got_one = TRUE;
                    break;
               }
          }
          if ( !got_one )
               ch->pcdata->has_alias = FALSE;
          return;
     }
     
     /*check for ridiculous size of alias */
     if ( strlen ( alias_string ) > MAX_ALIAS_LENGTH )
     {
          send_to_char ( "Alias too long, limit is 50 characters.\n\r", ch );
          return;
     }
     /* find first open alias in array and check for duplication of name */
     for ( counter = MAX_ALIAS - 1; counter >= 0; counter-- )
     {
          if ( ch->pcdata->aliases[counter] == NULL )
               last_free = counter;
          else if ( !str_cmp ( ch->pcdata->aliases[counter]->name, alias_name ) )
          {
               send_to_char ( "An alias with that name is already defined.\n\r", ch );
               return;
          }
     }
     /* if no free aliases, tell player to delete an alias first */
     if ( last_free == -1 )
     {
          send_to_char ( "All your alias slots are in use, please delete an alias first.\n\r", ch );
          return;
     }
     /* alloc_mem alias and assign its values */
     tmp = alloc_mem ( sizeof ( struct alias_data ), "alias_data" );
     ch->pcdata->aliases[last_free] = tmp;
     tmp->name = str_dup ( alias_name );
     tmp->command_string = str_dup ( alias_string );
     send_to_char ( "Alias set.\n\r", ch );
     ch->pcdata->has_alias = TRUE;
     return;
}

// See how insiduous buffer overruns can be? The next routine could possibly
// have been crashable because final was MIL, and it was strcpy'd to unsafely,
// thus making it potentially MIL+7 long, and boom, crash. Stuff like this is
// why i'm implementing buffer overrun protection even on seemingly innocent
// functions, because sometimes the obvious is missed. The hit to performance
// is nothing compared to safety gained. - Lotherius

void do_unalias ( CHAR_DATA * ch, char *argument )
{
     char                final[MAX_INPUT_LENGTH+8];

     if ( argument == NULL || argument[0] == '\0' )
     {
          send_to_char ( "Usage:  unalias <alias_name>\n\r", ch );
          return;
     }
     SLCPY ( final, "delete " );
     SLCAT ( final, argument );
     do_alias ( ch, final );
}

void do_replay ( CHAR_DATA * ch, char *argument )
{
     struct afk_tell_type *tmp;
     BUFFER *outbuf;

     if ( IS_NPC ( ch ) )
          return;

	 /* check if no buffered tells waiting */
     if ( ch->pcdata->afk_tell_first == NULL )
     {
          send_to_char ( "You have no tells waiting.\n\r", ch );
          return;
     }

     outbuf = buffer_new ( 256 );

	 /* send all tells */
     while ( ch->pcdata->afk_tell_first != NULL )
     {
          bprintf ( outbuf, ch->pcdata->afk_tell_first->message );
          bprintf ( outbuf, "\n\r" );
          /* free up buffered tell */
          tmp = ch->pcdata->afk_tell_first;
          ch->pcdata->afk_tell_first = ch->pcdata->afk_tell_first->next;
          free_string ( tmp->message );
          free_mem ( tmp, sizeof ( struct afk_tell_type ), "afk_tell_type" );
     }
     page_to_char ( outbuf->data, ch );
     ch->pcdata->afk_tell_last = NULL;
     buffer_free ( outbuf );
     return;
}

void do_language ( CHAR_DATA * ch, char *argument )
{
     char                lang_name[MIL*2];
     int                 ch_lang_skill;
     int                 skill;

     if ( IS_NPC ( ch ) )
          return;

     if ( argument[0] == '\0' )
     {
          form_to_char ( ch, "You are currently speaking {Y%s{x tongue.\n\r", ch->speaking );
          return;
     }

     SNP ( lang_name, "%s tongue", argument );

     skill = skill_lookup ( lang_name );
     if ( skill == -1 )
     {
          form_to_char ( ch, "Sorry, '{Y%s{x' is not a language.\n\r", argument );
          return;
     }

     free_string ( ch->speaking );
     ch->speaking = str_dup ( argument );

     form_to_char ( ch, "You switch to speaking in {Y%s{x tongue.\n\r", argument );

     ch_lang_skill = ch->pcdata->learned[skill];

     if ( ch_lang_skill == 100 )
          send_to_char ( "You speak it perfectly.\n\r", ch );
     else if ( ch_lang_skill > 90 )
          send_to_char ( "You speak it extremely well.\n\r", ch );
     else if ( ch_lang_skill > 70 )
          send_to_char ( "You speak it better than average.\n\r", ch );
     else if ( ch_lang_skill > 40 )
          send_to_char ( "You have some grasp of the language.\n\r", ch );
     else if ( ch_lang_skill > 10 )
          send_to_char ( "You know a few words, but not much more.\n\r", ch );
     else
          send_to_char ( "You would be better off grunting and using hand signals.\n\r", ch );

     return;
}

// Translate a csc (Custom String Code)
// takes arguments:
// 
// *ch        = user
// *argument  = string to translate
// *vch       = a possible target char
// *vob       = a possible target object
// *vrm       = a possible target room
// 
// This is a very speed sensitive function... It needs speed improvement before
// it can go into as widespread use as planned.

char *csc_translate ( CHAR_DATA *ch, const char *argument, CHAR_DATA *vch, OBJ_DATA *vob, ROOM_INDEX_DATA *vrm )
{
     static char    buf[MSL*4]; // since this is returned, it is static
     char  	    st[MSL];
     char	    csc_st[MSL];
     register char *csc_str = csc_st;
     register int   count, clen;

     if ( IS_NPC ( ch ) ) // No NPC's for now.
          return "Not on NPC's";

     buf[0] = '\0';

     strcpy ( csc_str, argument );
     clen = strlen ( csc_str );

     for ( count = 0; count <= clen; count++ )
     {
          if ( (csc_str[count] != '#') &&
               (csc_str[count] != '@' ) )
          {
               int temp = strlen ( buf );
               buf[temp] = csc_str[count];
               buf[temp + 1] = '\0';
          }
          else
          {
               switch ( csc_str[count] )
               {
               case '#':	// Do the # codes
                    {
                         switch ( csc_str[count+1] )
                         {
                         case 'a':	// Age in years
                              SNP ( st, "%d", get_age ( ch ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'A':	// Age in hours;
                              SNP ( st, "%d", ( ch->played + ( int ) ( current_time - ch->logon ) ) / 3600 );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'b':	// Overall score
                              SNP ( st, "%d", score_calc ( ch ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'B':	// Birth Month
                              SNP ( st, "%s", month_name[ch->pcdata->startmonth] );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'c':	// Class
                              SNP ( st, "%s", class_table[ch->pcdata->pclass].name );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'C':	// Birth Year
                              SNP ( st, "%d", ch->pcdata->startyear );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'd':	// Description
                              SNP ( st, "%s", ch->description );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'D':	// Birth Day
                              SNP ( st, "%d",  ch->pcdata->startday + 1 );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'e':	// Total xp
                              SNP ( st, "%d", ch->exp );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'E':	// Xp to level
                              {
                                   int need;
                                   need = ( ( ch->level + 1 ) * exp_per_level ( ch, ch->pcdata->points )- ch->exp );
                                   SNP ( st, "%d", need );
                                   strcat ( buf, st );
                                   count += 1;
                                   break;
                              }
                         case 'f':	// Fight target
                              if ( ch->fighting == NULL )
                              {
                                   if ( ch->fighting == ch )
                                        SNP ( st, "Yourself?" );
                                   else if ( ch->in_room == ch->fighting->in_room )                                   
                                        SNP ( st, "%s.", PERSMASK ( ch, ch->fighting ) );
                                   else
                                        SNP ( st, "someone who left??" );
                                   strcat ( buf, st );
                                   count += 1;
                              }
                              else
                              {
                                   int temp  = strlen ( buf );
                                   buf[temp] = csc_str[count];
                                   buf[temp + 1] = csc_str[count + 1];
                                   buf[temp + 2] = '\0';
                                   count += 1;
                                   break;                                   
                              }
                              break;
                         case 'F':	// Opponent Condition
                              break;                              
                         case 'g':	// Gold on hand
                              SNP ( st, "%ld", ch->gold );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'h':	// current hp
                              SNP ( st, "%d", ch->hit );
                              strcat ( buf, st );
                              count += 1;                              
                              break;
                         case 'H':	// Max hp
                              SNP ( st, "%d", ch->max_hit );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'i':	// qtime remain
                              SNP ( st, "%d", ch->pcdata->countdown );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'I':	// qtime to next
                              SNP ( st, "%d", ch->pcdata->nextquest );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'j':	// clan name
                              if ( ch->pcdata->clan )
                              {
                                   SNP ( st, "%s", ch->pcdata->clan->clan_name );
                              }
                              else
                                   SNP ( st, "None" );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'J':	// clan rank
                              if ( ch->pcdata->clan )
                              {
                                   SNP ( st, "%s",
                                         ch->sex == 2 ? ch->pcdata->clan->franks[ch->pcdata->clrank] :
                                         ch->pcdata->clan->mranks[ch->pcdata->clrank] );
                                   // ^^^ Gets the properly gendered rank.
                              }
                              else
                                   SNP ( st, "Nobody" );
                              strcat ( buf, st );
                              count += 1;                              
                              break;
                         case 'k':	// pk wins
                              SNP ( st, "%d", ch->pcdata->pkill_wins );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'K':	// pk losses
                              SNP ( st, "%d", ch->pcdata->pkill_losses );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'l':	// current level
                              SNP ( st, "%d", ch->level );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'L':	// Group leader
                              if ( ch->leader == NULL )                    
                              {
                                   bugf ( "%s has null leader", ch->name );
                                   SNP ( st, "Nobody" );
                              }
                              else
                                   SNP ( st, "%s", ch->leader->name );
                              strcat ( buf, st );
                              count += 1;                              
                              break;
                         case 'm':	// current mana
                              SNP ( st, "%d", ch->mana );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'M':	// Max mana
                              SNP ( st, "%d", ch->max_mana );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'n':	// your name
                              SNP ( st, "%s", ch->name );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'N':	// name of current area
                               SNP ( st, "%s", ch->in_room->area->name );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'o':	// encumbrance level
                              SNP ( st, "%d", total_encumbrance ( ch ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'p':	// practices
                              SNP ( st, "%d", ch->pcdata->practice );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'P':	// position
                              {
                                   switch ( ch->position )
                                   {
                                   case POS_DEAD:
                                        SNP ( st, "dead" );
                                        break;
                                   case POS_MORTAL:
                                        SNP ( st, "mortally wounded" );
                                        break;
                                   case POS_INCAP:
                                        SNP ( st, "incapacitated" );
                                        break;
                                   case POS_STUNNED:
                                        SNP ( st, "stunned" );
                                        break;
                                   case POS_SLEEPING:
                                        SNP ( st, "sleeping" );
                                        break;
                                   case POS_RESTING:
                                        SNP ( st, "resting" );
                                        break;
                                   case POS_SITTING:
                                        SNP ( st, "sitting" );
                                        break;
                                   case POS_STANDING:
                                        SNP ( st, "standing" );
                                        break;
                                   case POS_FIGHTING:
                                        SNP ( st, "fighting" );
                                        break;
                                   default:
                                        SNP ( st, "buggy" );                                                                                                                                
                                   } // End of position switch
                              }
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'q':	// QP current
                              SNP ( st, "%ld", ch->pcdata->questpoints );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'Q':	// QP Earned
                              SNP ( st, "%ld", ch->pcdata->questearned );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'r':	// race
                              SNP ( st, "%s", race_table[ch->race].name );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'R':	// Room name
                              SNP ( st, "%s", ch->in_room->name );
                              strcat ( buf, st );
                              count += 1;                              
                              break;
                         case 's':	// sex
                              SNP ( st, "%s", ch->sex == 0 ? "sexless" : ch->sex == 1 ? "male" : "female" );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'S':	// # sign
                              strcat ( buf, "#" );
                              count += 1;
                              break;
                         case 't':	// title
                              SNP ( st, "%s", ch->pcdata->title );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'T':	// Last tell from
                              if ( ch->reply == NULL )
                                   SNP ( st, "Nobody" );
                              else
                                   SNP ( st, "%s", ch->reply->name );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'u':	// trust level
                              SNP ( st, "%d", get_trust ( ch ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'v':	// current movement
                              SNP ( st, "%d", ch->move );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'V':	// max movement
                              SNP ( st, "%d", ch->max_move );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'w':	// current waitstates
                              SNP ( st, "%d", ch->wait );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'W':	// wimpy level
                              SNP ( st, "%d", ch->wimpy );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'x':      // items carried
                              SNP ( st, "%d", ch->carry_number );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'X':	// max items carried
                              SNP ( st, "%d", can_carry_n ( ch ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'y':	// weight carried
                              SNP ( st, "%d", ch->carry_weight );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'Y':	// max weight
                              SNP ( st, "%d", can_carry_w ( ch ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'z':	// save throw
                              SNP ( st, "%d", ch->saving_throw );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         default:
                              {
                                   int temp =strlen ( buf );
                                   buf[temp] = csc_str[count];
                                   buf[temp + 1] = csc_str[count + 1];
                                   buf[temp + 2] = '\0';
                                   count += 1;
                              }                              
                         } // End of # switch
                    } // End of case #
                    break;
               case '@':	// Do the @ codes
                    {
                         switch ( csc_str[count+1] )
                         {
                         case 'a':	// alignment
                              SNP ( st, "%d", ch->alignment );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'A':	// the @ symbol
                              strcat ( buf, "@" );
                              count += 1;                              
                              break;
                         case 'b':	// mob rating
                              SNP ( st, "%ld", IS_NPC(ch) ? 0 : ch->pcdata->mob_rating );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'B':	// pk rating
                              SNP ( st, "%d", ch->pcdata->battle_rating );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'c':	// con base
                              SNP ( st, "%d", ch->perm_stat[STAT_CON] );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'C':	// con current
                              SNP ( st, "%d", get_curr_stat ( ch, STAT_STR ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'd':	// dex base
                              SNP ( st, "%d", ch->perm_stat[STAT_DEX] );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'D':	// dex current
                              SNP ( st, "%d", get_curr_stat ( ch, STAT_DEX ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'e':	// encumbrance
                              SNP ( st, "%d", total_encumbrance ( ch ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'E':	// email
                              if ( ch->pcdata->email[0] == '\0' )
                                   SNP ( st, "No Email Set" );
                              else
                                   SNP ( st, "%s", ch->pcdata->email );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'i':	// int base
                              SNP ( st, "%d", ch->perm_stat[STAT_INT] );
                              strcat ( buf, st );
                              count += 1;                              
                              break;
                         case 'I':	// int current
                              SNP ( st, "%d", get_curr_stat ( ch, STAT_INT ) );
                              strcat ( buf, st );
                              count += 1;                              
                              break;
                         case 'k':	// mob wins
                              SNP ( st, "%ld", ch->pcdata->mob_wins );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'K':	// mob losses
                              SNP ( st, "%ld", ch->pcdata->mob_losses );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'l':	// logon time
                              SNP ( st, "%s", ( char * ) ctime ( &ch->logon ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'L':	// language spoken
                              SNP ( st, "%s", ch->speaking );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'm':	// mortal/demi/imm
                              SNP ( st, "%s", 
                                    IS_IMMORTAL (ch) ? "Immortal" : (ch->pcdata->mortal ? "Mortal" : "Demi-God") );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'p':	// prompt
                              SNP ( st, "%s", ch->pcdata->prompt );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'r':	// hitroll
                              SNP ( st, "%d", GET_HITROLL ( ch ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'R':	// damroll
                              SNP ( st, "%d", GET_DAMROLL ( ch ) );
                              count += 1;
                              break;
                         case 's':	// str base
                              SNP ( st, "%d", ch->perm_stat[STAT_STR] );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 'S':	// str current
                              SNP ( st, "%d", get_curr_stat ( ch, STAT_STR ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         case 't':	// immtitle
                              if ( IS_IMMORTAL ( ch ) )
                              {
                                   SNP ( st, "%s", ch->pcdata->immtitle );
                                   strcat ( buf, st );
                                   count += 1;
                              }
                              break;
                         case 'v':	// Room vnum
                              {
                                   if ( get_trust ( ch ) >= LEVEL_IMMORTAL )
                                   {
                                        SNP ( st, "%d", ch->in_room->vnum );
                                        strcat ( buf, st );
                                        count += 1;
                                        break;
                                   }
                                   else
                                   {
                                        int temp  = strlen ( buf );
                                        buf[temp] = csc_str[count];
                                        buf[temp + 1] = csc_str[count + 1];
                                        buf[temp + 2] = '\0';
                                        count += 1;
                                        break;
                                   }
                              }                              
                         case 'w':	// wis base
                              SNP ( st, "%d", ch->perm_stat[STAT_WIS] );
                              strcat ( buf, st );
                              count += 1;                              
                              break;
                         case 'W':	// wis current
                              SNP ( st, "%d", get_curr_stat ( ch, STAT_WIS ) );
                              strcat ( buf, st );
                              count += 1;
                              break;
                         default:
                              {
                                   int temp =strlen ( buf );
                                   buf[temp] = csc_str[count];
                                   buf[temp + 1] = csc_str[count + 1];
                                   buf[temp + 2] = '\0';
                                   count += 1;
                              }                              
                         } // End of @ switch
                    } // End of case @
                    break;
               default:
                    break;
               } // End of # or @ switch
          } // End of found # or @
     } // End of for loop

     return buf;
}