1stMud/CVS/
1stMud/area/CVS/
1stMud/backup/CVS/
1stMud/bin/
1stMud/bin/CVS/
1stMud/bin/extras/
1stMud/bin/extras/CVS/
1stMud/data/CVS/
1stMud/data/i3/CVS/
1stMud/doc/1stMud/
1stMud/doc/1stMud/CVS/
1stMud/doc/CVS/
1stMud/doc/Diku/
1stMud/doc/Diku/CVS/
1stMud/doc/MPDocs/CVS/
1stMud/doc/Merc/CVS/
1stMud/doc/Rom/
1stMud/doc/Rom/CVS/
1stMud/log/CVS/
1stMud/notes/
1stMud/notes/CVS/
1stMud/player/CVS/
1stMud/player/backup/CVS/
1stMud/player/deleted/CVS/
1stMud/src/CVS/
1stMud/src/config/CVS/
1stMud/src/h/CVS/
1stMud/src/o/CVS/
1stMud/win/CVS/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements 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          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*          1stMud ROM Derivative (c) 2001-2004 by Markanth                *
*            http://www.firstmud.com/  <markanth@firstmud.com>            *
*         By using this code you have agreed to follow the term of        *
*             the 1stMud license in ../doc/1stMud/LICENSE                 *
***************************************************************************/

#include "merc.h"
#include "interp.h"
#include "recycle.h"
#include "tables.h"
#include "olc.h"

void
cleanup_char (CharData * ch)
{
  CorpseData *c, *c_next;
  int hash;
  RoomIndex *pRoom;
  bool found = false;
  Proto (void save_corpses, (void));

  if (IsNPC (ch))
    return;

  update_statlist (ch, true);
  if (is_clan (ch))
    {
      update_members (ch, true);
    }
  update_webpasses (ch, true);
  if (IsImmortal (ch))
    update_wizlist (ch, true);
  for (c = corpse_first; c; c = c_next)
    {
      c_next = c->next;
      if (c->corpse && is_name (ch->name, c->corpse->owner))
	{
	  UnLink (c, corpse, next, prev);
	  free_corpse (c);
	  found = true;
	}
    }
  if (found)
    {
      save_corpses ();
      found = false;
    }
  if (ch->pcdata->backup)
    unlink (pfilename (ch->name, PFILE_BACKUP));

  delete_home (ch);

  for (hash = 0; hash < MAX_KEY_HASH; hash++)
    {
      for (pRoom = room_index_hash[hash]; pRoom != NULL; pRoom = pRoom->next)
	{
	  if (!NullStr (pRoom->owner) && !str_cmp (pRoom->owner, ch->name))
	    {
	      replace_str (&pRoom->owner, "");
	      SetBit (pRoom->area->area_flags, AREA_CHANGED);
	    }
	}
    }
  for (hash = 0; hash < pfiles.count; hash++)
    {
      if (found)
	{
	  replace_str (&pfiles.names[hash - 1], pfiles.names[hash]);
	  free_string (pfiles.names[hash]);
	  continue;
	}

      if (!str_cmp (pfiles.names[hash], ch->name))
	{
	  found = true;
	  free_string (pfiles.names[hash]);
	}
    }
  if (found)
    {
      pfiles.count--;
    }
}


Do_Fun (do_delete)
{
  if (IsNPC (ch))
    return;

  if (ch->pcdata->confirm_delete)
    {
      if (!NullStr (argument))
	{
	  chprintln (ch, "Delete status removed.");
	  ch->pcdata->confirm_delete = false;
	  return;
	}
      else
	{
	  char strsave[MAX_INPUT_LENGTH];

	  cleanup_char (ch);
	  sprintf (strsave, pfilename (ch->name, PFILE_NORMAL));
	  if (get_trust (ch) >= mud_info.min_save_lvl)
	    {
	      char strdel[MAX_INPUT_LENGTH];

	      sprintf (strdel, DELETE_DIR "%s", pfile_filename (ch->name));
	      save_char_obj_to_filename (ch, strdel);
	    }
	  wiznet ("$N turns $Mself into line noise.", ch, NULL, 0, true, 0);
	  stop_fighting (ch, true);
	  do_function (ch, &do_quit, "");
	  unlink (strsave);
	  mud_info.stats.deletions++;
	  return;
	}
    }

  if (!NullStr (argument))
    {
      chprintln (ch, "Just type delete. No argument.");
      return;
    }

  chprintln (ch, "Type delete again to confirm this command.");
  chprintln (ch, "{f{RWARNING{x: this command is irreversible.");
  chprintln (ch, "Typing delete with an argument will undo delete status.");
  ch->pcdata->confirm_delete = true;
  wiznet ("$N is contemplating deletion.", ch, NULL, 0, false,
	  get_trust (ch));
}

Do_Fun (do_nogocial)
{
  set_on_off (ch, &ch->comm, COMM_NOGOCIAL,
	      "You no longer see socials/emotes over channels.",
	      "You now see socials/emotes over channels.");
}



Do_Fun (do_deaf)
{
  set_on_off (ch, &ch->comm, COMM_DEAF,
	      "From now on, you won't hear tells.",
	      "You can now hear tells again.");
}



Do_Fun (do_quiet)
{
  set_on_off (ch, &ch->comm, COMM_QUIET,
	      "From now on, you will only hear says and emotes.",
	      "Quiet mode removed.");
}



Do_Fun (do_afk)
{
  if (IsSet (ch->comm, COMM_AFK))
    {
      chprintln (ch, "AFK mode removed.");
      RemBit (ch->comm, COMM_AFK);
      if (!IsNPC (ch) && ch->pcdata->tells)
	{
	  chprintlnf (ch,
		      "You have %d tells waiting.  Type '%s' to view them.",
		      ch->pcdata->tells, cmd_name (do_replay));
	}
    }
  else
    {
      chprint (ch, "You are now in AFK mode. ");
      SetBit (ch->comm, COMM_AFK);
      if (!IsNPC (ch))
	{
	  replace_strf (&ch->pcdata->afk_msg, "[%s] %s",
			str_time (-1, GetTzone (ch), "%I:%M:%S %p"),
			GetStr (argument,
				"I'm away, please leave a message!"));
	  chprintln (ch, ch->pcdata->afk_msg);
	}
      else
	chprintln (ch, NULL);
    }
}

Do_Fun (do_replay)
{
  if (IsNPC (ch))
    {
      chprintln (ch, "You can't replay.");
      return;
    }

  if (buf_string (ch->pcdata->buffer)[0] == '\0')
    {
      chprintln (ch, "You have no tells to replay.");
      return;
    }

  sendpage (ch, buf_string (ch->pcdata->buffer));
  clear_buf (ch->pcdata->buffer);
  ch->pcdata->tells = 0;
}


Do_Fun (do_clantalk)
{
  if (!is_true_clan (ch))
    {
      chprintln (ch, "You aren't in a clan.");
      return;
    }

  public_ch (n_fun, ch, argument, gcn_clan);
}


char
is_eye (char eye)
{
  char eyes[] = { ':', ';', '=', NUL };
  int i;

  for (i = 0; eyes[i] != NUL; i++)
    if (eyes[i] == eye)
      return eyes[i];

  return NUL;
}

char
is_nose (char nose)
{
  char noses[] = { '-', '\'', 'o', '~', '^', NUL };
  int i;

  for (i = 0; noses[i] != NUL; i++)
    if (noses[i] == nose)
      return noses[i];

  return NUL;
}


Do_Fun (do_nosayverbs)
{

  set_on_off (ch, &ch->comm, COMM_NOSAYVERBS,
	      "You no longer see say verbs on channels.",
	      "You now see say verbs on channels again.");
}

const char *
say_verb (const char *word, CharData * ch, CharData * viewer, int S)
{
  size_t len = strlen (word), i, j;

#define sm(a, b, c) return S == 0 ? a : S == 1 ? b : c

  if ((viewer && IsSet (viewer->comm, COMM_NOSAYVERBS)) || len < 3)
    sm ("say", "says", "said");

  if (ch)
    {
      if (IsDrunk (ch))
	sm ("slur", "slurs", "slured");
    }

  j = i = len - 2;
  if (is_nose (word[i]))
    i = len - 3;

  if (is_eye (word[i]))
    {
      switch (word[len - 1])
	{
	case ')':
	  if (word[i] != ';')
	    sm ("smile", "smiles", "smiled");
	  else
	    sm ("leer", "leers", "leered");
	case '}':
	case ']':
	case '>':
	  sm ("grin", "grins", "grinned");
	case '(':
	case '{':
	case '[':
	case '<':
	  if (word[j] == '\'')
	    sm ("sob", "sobs", "sobbed");
	  else
	    sm ("sulk", "sulks", "sulked");
	case '|':
	case '/':
	case '\\':
	  sm ("stare", "stares", "stared");
	case 'P':
	case 'p':
	  sm ("smirk", "smirks", "smirked");
	case 'o':
	case 'O':
	  sm ("sing", "sings", "sung");
	case '$':
	  sm ("blush", "blushes", "blushed");
	case 's':
	case 'S':
	  sm ("blab", "blabs", "blabbed");
	case 'D':
	  sm ("beam", "beams", "beamed");
	case '@':
	  sm ("shout", "shouts", "shouted");
	}
    }
  else
    {
      switch (word[len - 1])
	{
	case '!':
	  if (word[len - 2] != '!')
	    sm ("exclaim", "exclaims", "exclaimed");
	  else
	    sm ("scream", "screams", "screamed");
	case '?':
	  if (word[len - 2] == '!')
	    sm ("boggle", "boggles", "boggled");
	  else if (word[len - 2] != '?')
	    sm ("ask", "asks", "asked");
	  else
	    sm ("demand", "demands", "demanded");
	case '.':
	  if (word[len - 2] == '.' && word[len - 3] == '.')
	    sm ("mutter", "mutters", "muttered");
	  break;
	}
    }

  sm ("say", "says", "said");
}

Do_Fun (do_say)
{
  if (NullStr (argument))
    {
      chprintln (ch, "Say what?");
      return;
    }

  act (CTAG (_SAY1) "$n $t '" CTAG (_SAY2) "$T" CTAG (_SAY1)
       "'{x", ch, say_verb (argument, ch, NULL, 1), argument, TO_ROOM);
  act (CTAG (_SAY1) "You $t '" CTAG (_SAY2) "$T" CTAG (_SAY1)
       "'{x", ch, say_verb (argument, ch, NULL, 0), argument, TO_CHAR);

  if (!IsNPC (ch))
    {
      CharData *mob, *mob_next;
      ObjData *obj, *obj_next;

      for (mob = ch->in_room->person_first; mob != NULL; mob = mob_next)
	{
	  mob_next = mob->next_in_room;
	  if (IsNPC (mob) && HasTriggerMob (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_first; obj; obj = obj_next)
	    {
	      obj_next = obj->next_content;
	      if (HasTriggerObj (obj, TRIG_SPEECH))
		p_act_trigger (argument, NULL, obj, NULL, ch, NULL,
			       NULL, TRIG_SPEECH);
	    }
	}
      for (obj = ch->in_room->content_first; obj; obj = obj_next)
	{
	  obj_next = obj->next_content;
	  if (HasTriggerObj (obj, TRIG_SPEECH))
	    p_act_trigger (argument, NULL, obj, NULL, ch, NULL, NULL,
			   TRIG_SPEECH);
	}

      if (HasTriggerRoom (ch->in_room, TRIG_SPEECH))
	p_act_trigger (argument, NULL, NULL, ch->in_room, ch, NULL,
		       NULL, TRIG_SPEECH);

    }

  return;
}

Do_Fun (do_sooc)
{
  if (NullStr (argument))
    {
      chprintln (ch, "Say what Out of Character?");
      return;
    }

  act ("$n says out of character '$T'", ch, NULL, argument, TO_ROOM);
  act ("You say out of character '$T'", ch, NULL, argument, TO_CHAR);
  return;
}

Do_Fun (do_whisper)
{
  char arg[MIL];
  CharData *victim;

  argument = first_arg (argument, arg, false);

  if (NullStr (arg))
    {
      chprintln (ch, "Whisper to who?");
      return;
    }

  if (NullStr (argument))
    {
      chprintln (ch, "Whisper what?");
      return;
    }

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

  act ("$n whispers to you '{M$t{x'", ch, argument, victim, TO_VICT);
  act ("You whisper to $N '{M$t{x'", ch, argument, victim, TO_CHAR);
  act ("{M$n{M whispers something to {M$N{M.{x", ch, NULL, victim,
       TO_NOTVICT);
  return;
}


Do_Fun (do_think)
{
  if (NullStr (argument))
    {
      chprintln (ch, "You don't knwo what to think.");
      return;
    }


  act ("{C$n{C . o O ( $T{C ){x", ch, NULL, argument, TO_ROOM);
  act ("{CYou . o O ( $T{C ){x", ch, NULL, argument, TO_CHAR);
  return;
}

Do_Fun (do_shout)
{
  Descriptor *d;

  if (NullStr (argument))
    {
      set_on_off (ch, &ch->comm, COMM_SHOUTSOFF,
		  "You can hear shouts again.",
		  "You will no longer hear shouts.");
      return;
    }

  if (IsSet (ch->comm, COMM_NOSHOUT))
    {
      chprintln (ch, "You can't shout.");
      return;
    }

  RemBit (ch->comm, COMM_SHOUTSOFF);

  WaitState (ch, 12);

  act (CTAG (_SHOUT1) "You shout '" CTAG (_SHOUT2) "$T"
       CTAG (_SHOUT1) "'{x", ch, NULL, swearcheck (argument, ch), TO_CHAR);
  for (d = descriptor_first; d != NULL; d = d->next)
    {
      CharData *victim;

      victim = d->original ? d->original : d->character;

      if (d->connected == CON_PLAYING && d->character != ch &&
	  !IsSet (victim->comm, COMM_SHOUTSOFF) &&
	  !is_ignoring (victim, ch->name, IGNORE_CHANNELS) &&
	  !IsSet (victim->comm, COMM_QUIET))
	{
	  act (CTAG (_SHOUT1) "$n shouts '" CTAG (_SHOUT2) "$t"
	       CTAG (_SHOUT1) "'{x", ch, swearcheck (argument, d->character),
	       d->character, TO_VICT);
	}
    }

  return;
}

#define TELLMSG1    CTAG(_TELLS1) "You tell %s '" CTAG(_TELLS2) "%s" \
    	    	    	CTAG(_TELLS1) "'{x"

#define TELLMSG2    CTAG(_TELLS1) MXPTAG("Tell '%s'") "%s" MXPTAG("/Tell") \
    	    	    	" tells you '" CTAG(_TELLS2) "%s" CTAG(_TELLS1) "'{x"

Do_Fun (do_tell)
{
  char arg[MAX_INPUT_LENGTH];
  CharData *victim;

  if (IsSet (ch->comm, COMM_NOTELL) || IsSet (ch->comm, COMM_DEAF))
    {
      chprintln (ch, "Your message didn't get through.");
      return;
    }

  if (IsSet (ch->comm, COMM_QUIET))
    {
      chprintln (ch, "You must turn off quiet mode first.");
      return;
    }

  if (IsSet (ch->comm, COMM_DEAF))
    {
      chprintln (ch, "You must turn off deaf mode first.");
      return;
    }

  argument = one_argument (argument, arg);

  if (NullStr (arg) || NullStr (argument))
    {
      chprintln (ch, "Tell whom what?");
      return;
    }


  if ((victim = get_char_world (ch, arg)) == NULL ||
      (IsNPC (victim) && victim->in_room != ch->in_room))
    {
      chprintln (ch, "They aren't here.");
      return;
    }

  if (ch == victim)
    {
      chprintln (ch, "You tell yourself to get a life.");
      return;
    }

  if (is_ignoring (victim, ch->name, IGNORE_TELLS))
    {
      act_new ("$N doesn't seem to be listening to you.", ch, NULL,
	       victim, TO_CHAR, POS_DEAD);
      return;
    }

  if (!(IsImmortal (ch) && ch->level > LEVEL_IMMORTAL) && !IsAwake (victim))
    {
      act ("$E can't hear you.", ch, 0, victim, TO_CHAR);
      return;
    }

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

  if (victim->desc == NULL && !IsNPC (victim))
    {
      act ("$N seems to have misplaced $S link...try again later.", ch,
	   NULL, victim, TO_CHAR);
      bprintlnf (victim->pcdata->buffer, TELLMSG2, argument,
		 Pers (ch, victim), argument);
      victim->pcdata->tells++;
      return;
    }


  if (IsSet (victim->comm, COMM_AFK))
    {
      if (IsNPC (victim))
	{
	  act ("$E is AFK, and not receiving tells.", ch, NULL,
	       victim, TO_CHAR);
	  return;
	}

      act
	("$E is AFK ($t), but your tell will go through when $E returns.",
	 ch, victim->pcdata->afk_msg, victim, TO_CHAR);
      bprintlnf (victim->pcdata->buffer, TELLMSG2, argument,
		 Pers (ch, victim), argument);
      victim->pcdata->tells++;
      return;
    }

  chprintlnf (ch, TELLMSG1, Pers (victim, ch), argument);
  chprintlnf (victim, TELLMSG2, argument, Pers (ch, victim), argument);

  victim->reply = ch;

  if (!IsNPC (ch) && IsNPC (victim) && HasTriggerMob (victim, TRIG_SPEECH))
    p_act_trigger (argument, victim, NULL, NULL, ch, NULL, NULL, TRIG_SPEECH);

  return;
}

Do_Fun (do_reply)
{
  CharData *victim;

  if (IsSet (ch->comm, COMM_NOTELL))
    {
      chprintln (ch, "Your message didn't get through.");
      return;
    }

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

  if (ch == victim)
    {
      chprintln (ch, "You tell yourself to get a life.");
      return;
    }

  if (is_ignoring (victim, ch->name, IGNORE_TELLS))
    {
      act_new ("$N doesn't seem to be listening to you.", ch, NULL,
	       victim, TO_CHAR, POS_DEAD);
      return;
    }

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

  if ((IsSet (victim->comm, COMM_QUIET) ||
       IsSet (victim->comm, COMM_DEAF)) && !IsImmortal (ch) &&
      !IsImmortal (victim))
    {
      act_new ("$E is not receiving tells.", ch, 0, victim, TO_CHAR,
	       POS_DEAD);
      return;
    }

  if (!IsImmortal (victim) && !IsAwake (ch))
    {
      chprintln (ch, "In your dreams, or what?");
      return;
    }

  if (victim->desc == NULL && !IsNPC (victim))
    {
      act ("$N seems to have misplaced $S link...try again later.", ch,
	   NULL, victim, TO_CHAR);
      bprintlnf (victim->pcdata->buffer, TELLMSG2, argument,
		 Pers (ch, victim), argument);
      victim->pcdata->tells++;
      return;
    }

  if (IsSet (victim->comm, COMM_AFK))
    {
      if (IsNPC (victim))
	{
	  act_new ("$E is AFK, and not receiving tells.", ch, NULL,
		   victim, TO_CHAR, POS_DEAD);
	  return;
	}

      act_new
	("$E is AFK ($t), but your tell will go through when $E returns.",
	 ch, victim->pcdata->afk_msg, victim, TO_CHAR, POS_DEAD);
      bprintlnf (victim->pcdata->buffer, TELLMSG2, argument,
		 Pers (ch, victim), argument);
      victim->pcdata->tells++;
      return;
    }

  chprintlnf (ch, TELLMSG1, Pers (victim, ch), argument);
  chprintlnf (victim, TELLMSG2, argument, Pers (ch, victim), argument);
  victim->reply = ch;

  return;
}

Do_Fun (do_yell)
{
  Descriptor *d;

  if (IsSet (ch->comm, COMM_NOSHOUT))
    {
      chprintln (ch, "You can't yell.");
      return;
    }

  if (NullStr (argument))
    {
      chprintln (ch, "Yell what?");
      return;
    }

  act ("You yell '$t'", ch, swearcheck (argument, ch), NULL, TO_CHAR);
  for (d = descriptor_first; d != NULL; d = d->next)
    {
      if (d->connected == CON_PLAYING && d->character != ch &&
	  d->character->in_room != NULL &&
	  d->character->in_room->area == ch->in_room->area &&
	  !is_ignoring (d->character, ch->name, IGNORE_CHANNELS) &&
	  !IsSet (d->character->comm, COMM_QUIET))
	{
	  act ("$n yells '$t'", ch, swearcheck (argument, d->character),
	       d->character, TO_VICT);
	}
    }

  return;
}

Do_Fun (do_emote)
{
  if (!IsNPC (ch) && IsSet (ch->comm, COMM_NOEMOTE))
    {
      chprintln (ch, "You can't show your emotions.");
      return;
    }

  if (NullStr (argument))
    {
      chprintln (ch, "Emote what?");
      return;
    }

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

Do_Fun (do_pmote)
{
  CharData *vch;
  const char *letter, *name;
  char last[MAX_INPUT_LENGTH], temp[MAX_STRING_LENGTH];
  unsigned int matches = 0;

  if (!IsNPC (ch) && IsSet (ch->comm, COMM_NOEMOTE))
    {
      chprintln (ch, "You can't show your emotions.");
      return;
    }

  if (NullStr (argument))
    {
      chprintln (ch, "Emote what?");
      return;
    }

  act ("$n $t", ch, argument, NULL, TO_CHAR);

  for (vch = ch->in_room->person_first; vch != NULL; vch = vch->next_in_room)
    {
      if (vch->desc == NULL || vch == ch)
	continue;

      if ((letter = strstr (argument, vch->name)) == NULL)
	{
	  MOBtrigger = false;
	  act ("$N $t", vch, argument, ch, TO_CHAR);
	  MOBtrigger = true;
	  continue;
	}

      strcpy (temp, argument);
      temp[strlen (argument) - strlen (letter)] = '\0';
      last[0] = '\0';
      name = vch->name;

      for (; *letter != '\0'; letter++)
	{
	  if (*letter == '\'' && matches == strlen (vch->name))
	    {
	      strcat (temp, "r");
	      continue;
	    }

	  if (*letter == 's' && matches == strlen (vch->name))
	    {
	      matches = 0;
	      continue;
	    }

	  if (matches == strlen (vch->name))
	    {
	      matches = 0;
	    }

	  if (*letter == *name)
	    {
	      matches++;
	      name++;
	      if (matches == strlen (vch->name))
		{
		  strcat (temp, "you");
		  last[0] = '\0';
		  name = vch->name;
		  continue;
		}
	      strncat (last, letter, 1);
	      continue;
	    }

	  matches = 0;
	  strcat (temp, last);
	  strncat (temp, letter, 1);
	  last[0] = '\0';
	  name = vch->name;
	}

      MOBtrigger = false;
      act ("$N $t", vch, temp, ch, TO_CHAR);
      MOBtrigger = true;
    }

  return;
}

Do_Fun (do_bug)
{
  char buf[MSL];

  sprintf (buf, "[%ld] %s: %s", ch->in_room->vnum, ch->name, argument);

  append_file (BUG_FILE, buf, true);

  chprintln (ch, "Bug logged.");
  return;
}

Do_Fun (do_typo)
{
  char buf[MSL];

  sprintf (buf, "[%ld] %s: %s", ch->in_room->vnum, ch->name, argument);

  append_file (TYPO_FILE, buf, true);
  chprintln (ch, "Typo logged.");
  return;
}

Do_Fun (do_rent)
{
  chprintln (ch, "There is no rent here.  Just save and quit.");
  return;
}

Do_Fun (do_quit)
{
  Descriptor *d, *d_next;
  int id;
  char *const quit_message[] = {
    "Alas, all good things must come to an end.",
    "{RYou have been KILLED!!!{x",
    "What???? You're not addicted yet?????",
    "Hope to see you soon!"
  };

  if (IsNPC (ch))
    return;

  if (ch->position == POS_FIGHTING)
    {
      chprintln (ch, "No way! You are fighting.");
      return;
    }

  if (ch->position < POS_STUNNED)
    {
      chprintln (ch, "You're not DEAD yet.");
      return;
    }
  if (has_auction (ch))
    {
      chprintln
	(ch,
	 "{YThe Auctioneer will not let you leave while you have an auction running.{x");
      return;
    }

  chprintln (ch,
	     quit_message[sizeof (quit_message) / sizeof (quit_message[0]) -
			  1]);
  announce (ch, INFO_LOGOUT, "$n has left the realms.");
  act ("$n has left the game.", ch, NULL, NULL, TO_ROOM);
  logf ("%s has quit.", ch->name);
  wiznet ("$N rejoins the real world.", ch, NULL, WIZ_LOGINS, false,
	  get_trust (ch));


  save_char_obj (ch);


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

  extract_quest (ch);
  extract_war (ch);
  extract_arena (ch);

  id = ch->id;
  d = ch->desc;
  extract_char (ch, true);
  if (d != NULL)
    close_socket (d);


  for (d = descriptor_first; d != NULL; d = d_next)
    {
      CharData *tch;

      d_next = d->next;
      tch = d->original ? d->original : d->character;
      if (tch && tch->id == id)
	{
	  extract_char (tch, true);
	  close_socket (d);
	}
    }

  return;
}

Do_Fun (do_save)
{
  if (IsNPC (ch))
    return;

  if (get_trust (ch) < mud_info.min_save_lvl)
    {
      chprintlnf (ch,
		  "You must be level %d before you can save your character.",
		  mud_info.min_save_lvl);
      return;
    }
  save_char_obj (ch);
  update_statlist (ch, false);
  if (is_clan (ch))
    update_members (ch, false);
  if (IsImmortal (ch))
    update_wizlist (ch, ch->level);
  chprintlnf (ch, "Saving. Remember that %s has automatic saving.",
	      mud_info.name);
  WaitState (ch, PULSE_VIOLENCE / 2);
  return;
}

Do_Fun (do_backup)
{
  if (IsNPC (ch))
    return;

  if (!str_cmp (argument, "check"))
    {
      if (ch->pcdata->played - ch->pcdata->backup >= HOUR)
	{
	  if (ch->pcdata->backup == 0)
	    {
	      chprintln
		(ch, "{RThere is currently no backup for your character.{x");
	    }
	  else if (ch->pcdata->played - ch->pcdata->backup >= (HOUR * 24))
	    {
	      chprintlnf
		(ch, "{RYou have not backed up for {W"
		 "%ld{R hours of gameplay.{x",
		 (ch->pcdata->played - ch->pcdata->backup) / HOUR);
	    }
	  else
	    {
	      chprintlnf
		(ch, "{RYou have not backed up for {W"
		 "%ld{R hour%s of gameplay.{x",
		 (ch->pcdata->played - ch->pcdata->backup) / HOUR,
		 (ch->pcdata->played - ch->pcdata->backup) / HOUR ==
		 1 ? "" : "s");
	    }

	}
      return;
    }
  if (get_trust (ch) < mud_info.min_save_lvl * 3)
    {
      chprintlnf (ch,
		  "You must be level %d before you can backup your character.",
		  mud_info.min_save_lvl * 3);
      return;
    }
  backup_char_obj (ch);
  update_statlist (ch, false);
  if (is_clan (ch))
    update_members (ch, false);
  chprintlnf (ch, "%s has been saved to a backup.", ch->name);
  WaitState (ch, PULSE_VIOLENCE);
  return;
}

Do_Fun (do_follow)
{

  char arg[MAX_INPUT_LENGTH];
  CharData *victim;

  one_argument (argument, arg);

  if (NullStr (arg))
    {
      chprintln (ch, "Follow whom?");
      return;
    }

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

  if (IsAffected (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)
	{
	  chprintln (ch, "You already follow yourself.");
	  return;
	}
      stop_follower (ch);
      return;
    }

  if (!IsNPC (victim) && IsSet (victim->act, PLR_NOFOLLOW)
      && !IsImmortal (ch))
    {
      act ("$N doesn't seem to want any followers.", ch, NULL, victim,
	   TO_CHAR);
      return;
    }

  RemBit (ch->act, PLR_NOFOLLOW);

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

  add_follower (ch, victim);
  return;
}

Do_Fun (do_ditch)
{
  char arg[MIL];
  CharData *victim;

  one_argument (argument, arg);
  if (NullStr (arg))
    {
      chprintln (ch, "Ditch whom?");
      return;
    }
  if ((victim = get_char_room (ch, NULL, arg)) == NULL)
    {
      chprintln (ch, "They aren't here.");
      return;
    }
  if (IsAffected (ch, AFF_CHARM) && ch->master != NULL)
    {
      act ("But you'd rather follow $N!", ch, NULL, ch->master, TO_CHAR);
      return;
    }
  if (victim == ch)
    {
      chprintln (ch, "You try to ditch yourself.... unsuccessfuly.");
      return;
    }
  if (victim->master != ch)
    {
      chprintln (ch, "They aren't following you.");
      return;
    }

  stop_follower (victim);
  return;
}

void
add_follower (CharData * ch, CharData * master)
{
  if (ch->master != NULL)
    {
      bug ("Add_follower: non-null master.");
      return;
    }

  ch->master = master;
  ch->leader = NULL;

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

void
stop_follower (CharData * ch)
{
  if (ch->master == NULL)
    {
      bug ("Stop_follower: null master.");
      return;
    }

  if (IsAffected (ch, AFF_CHARM))
    {
      RemBit (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;
  ch->leader = NULL;
  return;
}


void
nuke_pets (CharData * ch)
{
  CharData *pet;

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

  return;
}

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

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

  ch->leader = NULL;

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

  return;
}

Do_Fun (do_order)
{
  char buf[MAX_STRING_LENGTH];
  char arg[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
  CharData *victim;
  CharData *och;
  CharData *och_next;
  bool found;
  bool fAll;

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

  if (!str_cmp (arg2, "delete") || !str_cmp (arg2, "mob"))
    {
      chprintln (ch, "That will NOT be done.");
      return;
    }

  if (NullStr (arg) || NullStr (argument))
    {
      chprintln (ch, "Order whom to do what?");
      return;
    }

  if (IsAffected (ch, AFF_CHARM))
    {
      chprintln (ch, "You feel like taking, not giving, orders.");
      return;
    }

  if (!str_cmp (arg, "all"))
    {
      fAll = true;
      victim = NULL;
    }
  else
    {
      fAll = false;
      if ((victim = get_char_room (ch, NULL, arg)) == NULL)
	{
	  chprintln (ch, "They aren't here.");
	  return;
	}

      if (victim == ch)
	{
	  chprintln (ch, "Aye aye, right away!");
	  return;
	}

      if (!IsAffected (victim, AFF_CHARM) || victim->master != ch ||
	  (IsImmortal (victim) && victim->trust >= ch->trust))
	{
	  chprintln (ch, "Do it yourself!");
	  return;
	}
    }

  found = false;
  for (och = ch->in_room->person_first; och != NULL; och = och_next)
    {
      och_next = och->next_in_room;

      if (IsAffected (och, AFF_CHARM) && och->master == ch &&
	  (fAll || och == victim))
	{
	  found = true;
	  sprintf (buf, "$n orders you to '%s'.", argument);
	  act (buf, ch, NULL, och, TO_VICT);
	  interpret (och, argument);
	}
    }

  if (found)
    {
      WaitState (ch, PULSE_VIOLENCE);
      chprintln (ch, "Ok.");
    }
  else
    chprintln (ch, "You have no followers here.");
  return;
}

Do_Fun (do_group)
{
  char arg[MAX_INPUT_LENGTH];
  CharData *victim;

  one_argument (argument, arg);

  if (NullStr (arg))
    {
      CharData *gch;
      CharData *leader;

      leader = (ch->leader != NULL) ? ch->leader : ch;
      chprintlnf (ch, "%s's group:", Pers (leader, ch));

      for (gch = char_first; gch != NULL; gch = gch->next)
	{
	  if (is_same_group (gch, ch))
	    {
	      chprintlnf (ch,
			  "[%2d %s] %-16s %4ld/%4ld hp %4ld/%4ld mana %4ld/%4ld mv %5d xp",
			  gch->level,
			  IsNPC (gch) ? "Mob" : class_who (gch),
			  capitalize (Pers (gch, ch)), gch->hit,
			  gch->max_hit, gch->mana, gch->max_mana,
			  gch->move, gch->max_move, gch->exp);
	    }
	}
      chprintlnf (ch, "Type '%s where' to view group member locations.",
		  n_fun);
      return;
    }
  else if (!str_cmp (arg, "where"))
    {
      CharData *gch, *leader;

      leader = (ch->leader != NULL) ? ch->leader : ch;
      chprintlnf (ch, "{W%s's group:{x", Pers (leader, ch));
      for (gch = char_first; gch != NULL; gch = gch->next)
	{
	  if (is_same_group (gch, ch))
	    {
	      chprintlnf (ch, "{W%s is in %s the general area of %s.{x",
			  Pers (gch, ch), gch->in_room->name,
			  gch->in_room->area->name);
	    }
	}
      return;
    }
  if ((victim = get_char_room (ch, NULL, arg)) == NULL)
    {
      chprintln (ch, "They aren't here.");
      return;
    }

  if (ch->master != NULL || (ch->leader != NULL && ch->leader != ch))
    {
      chprintln (ch, "But you are following someone else!");
      return;
    }

  if (victim->master != ch && ch != victim)
    {
      act_new ("$N isn't following you.", ch, NULL, victim, TO_CHAR,
	       POS_SLEEPING);
      return;
    }

  if (IsAffected (victim, AFF_CHARM))
    {
      chprintln (ch, "You can't remove charmed mobs from your group.");
      return;
    }

  if (IsAffected (ch, AFF_CHARM))
    {
      act_new ("You like your master too much to leave $m!", ch, NULL,
	       victim, TO_VICT, POS_SLEEPING);
      return;
    }

  if (is_same_group (victim, ch) && ch != victim)
    {
      victim->leader = NULL;
      act_new ("$n removes $N from $s group.", ch, NULL, victim,
	       TO_NOTVICT, POS_RESTING);
      act_new ("$n removes you from $s group.", ch, NULL, victim,
	       TO_VICT, POS_SLEEPING);
      act_new ("You remove $N from your group.", ch, NULL, victim,
	       TO_CHAR, POS_SLEEPING);
      return;
    }

  victim->leader = ch;
  act_new ("$N joins $n's group.", ch, NULL, victim, TO_NOTVICT, POS_RESTING);
  act_new ("You join $n's group.", ch, NULL, victim, TO_VICT, POS_SLEEPING);
  act_new ("$N joins your group.", ch, NULL, victim, TO_CHAR, POS_SLEEPING);
  return;
}


Do_Fun (do_split)
{
  char buf[MAX_STRING_LENGTH];
  char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];
  CharData *gch;
  int members;
  money_t amount_gold = 0, amount_silver = 0;
  money_t share_gold, share_silver;
  money_t extra_gold, extra_silver;

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

  if (NullStr (arg1))
    {
      chprintln (ch, "Split how much?");
      return;
    }

  amount_silver = atoi (arg1);

  if (!NullStr (arg2))
    amount_gold = atoi (arg2);

  if (amount_gold < 0 || amount_silver < 0)
    {
      chprintln (ch, "Your group wouldn't like that.");
      return;
    }

  if (amount_gold == 0 && amount_silver == 0)
    {
      chprintln (ch, "You hand out zero coins, but no one notices.");
      return;
    }

  if (ch->gold < amount_gold || ch->silver < amount_silver)
    {
      chprintln (ch, "You don't have that much to split.");
      return;
    }

  members = 0;
  for (gch = ch->in_room->person_first; gch != NULL; gch = gch->next_in_room)
    {
      if (is_same_group (gch, ch) && !IsAffected (gch, AFF_CHARM))
	members++;
    }

  if (members < 2)
    {
      chprintln (ch, "Just keep it all.");
      return;
    }

  share_silver = amount_silver / members;
  extra_silver = amount_silver % members;

  share_gold = amount_gold / members;
  extra_gold = amount_gold % members;

  if (share_gold == 0 && share_silver == 0)
    {
      chprintln (ch, "Don't even bother, cheapskate.");
      return;
    }

  ch->silver -= amount_silver;
  ch->silver += share_silver + extra_silver;
  ch->gold -= amount_gold;
  ch->gold += share_gold + extra_gold;

  if (share_silver > 0)
    {
      chprintlnf (ch,
		  "You split %ld silver coins. Your share is %ld silver.",
		  amount_silver, share_silver + extra_silver);
    }

  if (share_gold > 0)
    {
      chprintlnf (ch,
		  "You split %ld gold coins. Your share is %ld gold.",
		  amount_gold, share_gold + extra_gold);
    }

  if (share_gold == 0)
    {
      sprintf (buf,
	       "$n splits %ld silver coins. Your share is %ld silver.",
	       amount_silver, share_silver);
    }
  else if (share_silver == 0)
    {
      sprintf (buf, "$n splits %ld gold coins. Your share is %ld gold.",
	       amount_gold, share_gold);
    }
  else
    {
      sprintf (buf,
	       "$n splits %ld silver and %ld gold coins, giving you %ld silver and %ld gold.",
	       amount_silver, amount_gold, share_silver, share_gold);
    }

  for (gch = ch->in_room->person_first; gch != NULL; gch = gch->next_in_room)
    {
      if (gch != ch && is_same_group (gch, ch)
	  && !IsAffected (gch, AFF_CHARM))
	{
	  act (buf, ch, NULL, gch, TO_VICT);
	  gch->gold += share_gold;
	  gch->silver += share_silver;
	}
    }

  return;
}

Do_Fun (do_gtell)
{
  CharData *gch;

  if (NullStr (argument))
    {
      chprintln (ch, "Tell your group what?");
      return;
    }

  if (IsSet (ch->comm, COMM_NOTELL))
    {
      chprintln (ch, "Your message didn't get through!");
      return;
    }

  for (gch = char_first; gch != NULL; gch = gch->next)
    {
      if (is_same_group (gch, ch))
	act_new (CTAG (_GTELL1) "$n tells the group '"
		 CTAG (_GTELL2) "$t" CTAG (_GTELL1) "'{x", ch,
		 argument, gch, TO_VICT, POS_SLEEPING);
    }

  return;
}


bool
is_same_group (CharData * ach, CharData * bch)
{
  if (ach == NULL || bch == NULL)
    return false;

  if (ach->leader != NULL)
    ach = ach->leader;
  if (bch->leader != NULL)
    bch = bch->leader;
  return ach == bch;
}

flag_t
ignore_bit (flag_t info)
{
  switch (info)
    {
    case INFO_LEVEL:
      return IGNORE_LEVELS;
    default:
      return IGNORE_ANNOUNCE;
    }
}

void
announce (CharData * ch, flag_t bit, const char *message, ...)
{
  Descriptor *d;
  char buf[MSL], buf2[MSL];
  char *iType;
  bool Private = false;
  va_list args;

  if (NullStr (message))
    return;

  va_start (args, message);
  vsnprintf (buf2, sizeof (buf2), message, args);
  va_end (args);

  if (IsSet (bit, INFO_PRIVATE))
    {
      if (ch)
	Private = true;
      RemBit (bit, INFO_PRIVATE);
    }

  if (bit == INFO_NOTE)
    iType = "{GBOARD";
  else if (bit == INFO_WAR)
    iType = "{CWar";
  else if (bit == INFO_GQUEST)
    iType = "{MGquest";
  else if (bit == INFO_AUCTION)
    iType = "{YAUCTION";
  else if (bit == INFO_ARENA)
    iType = "{BARENA";
  else
    iType = "{RINFO";

  sprintf (buf, "%s{W:{x %s{x", iType, buf2);

  if (!Private)
    {
      for (d = descriptor_first; d; d = d->next)
	{
	  CharData *och;

	  if ((och = d->character) == NULL)
	    continue;
	  if (d->connected != CON_PLAYING)
	    continue;
	  if (bit == INFO_NOTE && ch &&
	      !is_note_to (och, ch->pcdata->in_progress))
	    continue;
	  if (IsSet (och->info_settings, bit) && bit != INFO_ALL)
	    continue;

	  if (ch == NULL)
	    chprintln (och, buf);
	  else if (!is_ignoring (och, ch->name, ignore_bit (bit)))
	    act_new (buf, ch, NULL, och, TO_VICT, POS_DEAD);
	}
    }
  else
    {
      if (!ch)
	{
	  bug ("NULL ch in private announce");
	  return;
	}
      chprintln (ch, buf);
    }
  return;
}

Do_Fun (do_info)
{
  int i;
  char arg[MIL];
  flag_t iValue;

  argument = one_argument (argument, arg);

  if (NullStr (arg) || !str_cmp (arg, "list"))
    {
      chprintln (ch, "{RInformation settings:");
      chprintln (ch, "{r---------------------{x");
      for (i = 0; info_flags[i].name != NULL; i++)
	{
	  chprintlnf (ch, "%-13.25s [%s]",
		      info_flags[i].name,
		      IsSet (ch->info_settings,
			     info_flags[i].bit) ? "{WOFF{x" : " {RON{x");
	}
      return;
    }

  if (!str_cmp (arg, "all"))
    {
      bool fOn;

      if (!str_cmp (argument, "on"))
	fOn = true;
      else if (!str_cmp (argument, "off"))
	fOn = false;
      else
	{
	  chprintln (ch, "Turn all info channels on or off?");
	  return;
	}

      for (iValue = 0; info_flags[iValue].name != NULL; iValue++)
	{
	  if (fOn)
	    SetBit (ch->info_settings, info_flags[iValue].bit);
	  else
	    RemBit (ch->info_settings, info_flags[iValue].bit);
	}

      chprintlnf (ch, "All info channels turned %s.",
		  fOn ? "{Ron{x" : "{Roff{x");
      return;
    }

  if ((iValue = flag_value (info_flags, arg)) != NO_FLAG)
    {
      ToggleBit (ch->info_settings, iValue);
      chprintln (ch, "Info channel set.");
      return;
    }
  chprintln (ch, "Which option??");
  return;
}

Do_Fun (do_announce)
{
  announce (NULL, INFO_ALL, argument);
}