/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

handler.c			Vital code related to char and object
				moving.  Other miscellaneous mud handler
				functions.

		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "comm.h"
#include "db.h"
#include "handler.h"
#include "interpreter.h"
#include "acmd.h"
#include "mudlimits.h"
#include "magic.h"
#include "affect.h"
#include "lists.h"
#include "fight.h"
#include "global.h"
#include "plshop.h"
#include "objsave.h"
#include "clicomm.h"
#include "darkenelf.h"
#include "descmenu.h"

/* external vars */
extern chdata 		*combat_list;
extern char             *wv_bits[];

/* external functions */
void	free_char(chdata *ch);
void	stop_fighting(chdata *ch);
void	remove_follower(chdata *ch);
void	clearMemory(chdata *ch);
void 	float_sink_char(chdata *ch);
void 	float_sink_object(obdata *ob, int room);
void	reload_zone(int zone);
int     unidle_reset_zone(int zone);
void	null_char_room_affects(chdata *ch);
void	remove_song_from_world(chdata *ch, sh_int song);
void 	send_aff1_mesg(chdata *ch, int j, BOOL flag);
void 	send_aff2_mesg(chdata *ch, int j, BOOL flag);
int 	apply_ac(chdata *ch, int pos);
int	can_see(chdata *ch, chdata *i);
int 	num_remorts(chdata *ch);
void     update_client_eq(dsdata *d);
void     update_client_inv(dsdata *d);
void	die_follower(chdata *ch);

char *fname(char *namelist)
{
  static char	holder[30];
  register char	*point;

  for (point = holder; isalpha(*namelist); namelist++, point++)
    *point = *namelist;

  *point = '\0';

  return(holder);
}

int isname(char *str, char *namelist)
{
  register char	*curname, *curstr;

  if (!namelist || !*namelist) 
    return FALSE;

  curname = namelist;
  for (; ; ) 
  {
    for (curstr = str; ; curstr++, curname++) 
    {
      if (!*curstr && !isalpha(*curname))
	return TRUE;
      if (!*curname)
	return FALSE;
      if (!*curstr || *curname == ' ')
	break;
      if (LOWER(*curstr) != LOWER(*curname))
	break;
    }

    // skip to next name
    for (; isalpha(*curname); curname++)
      ;
    if (!*curname)
      return FALSE;
    curname++;			/* first char of new name */
  }
}

/* move a player out of a room */
void	char_from_room(chdata *ch)
{
  chdata *temp;
  int room = ch->in_room;

  if (IN_NOWHERE(ch))
  {
    sprintf(buf, "SYSERR: NOWHERE chfromroom (%s)",GET_NAME(ch));
    log(buf);
    exit(1);
  }

  if (EQ(ch, W_HOLD) && OBJ_LIGHT(EQ(ch, W_HOLD)) && LIT(EQ(ch, W_HOLD)))
    world[room].light--;

  if (IS_AFFECTED2(ch, AFF2_LIGHT))
    world[room].light--;

  world[room].light = MAX(0, world[room].light);

  REMOVE_FROM_LIST(ch, world[room].people, next_in_room);

  if (IS_NPC(ch))
    world[room].num_present--;   /* -1 to the room for MOBS for now*/ 
  else
  if (!IS_IMMORTAL(ch))
  {
    if (GET_RACE(ch) == RACE_DRAGON)
     world[room].num_present -= 4;   /* dragon size 4 */
    if (GET_RACE(ch) == RACE_OGRE || GET_RACE(ch) == RACE_ORC)
     world[room].num_present -= 3;   /* these guys are 3 */
    if (GET_RACE(ch) == RACE_PIXIE || GET_RACE(ch) == RACE_NIXIE)
     world[room].num_present -= 1;   /* small size 1 */
    else
     world[room].num_present -= 2;   /* normal size 2 */
  }

  ch->in_room = NOWHERE;
  ch->next_in_room = NULL;
}

// give newbie eq to ch if <= level 1 and they enter newbie eq room
void    give_newbie_eq(chdata *ch)
{
  obdata *ob = NULL;
  int i;

  // torch
  ob = read_object(172, VIRTUAL);
  obj_to_char(ob, ch);

  // bag
  ob = read_object(173, VIRTUAL);
  obj_to_char(ob, ch);

  // waterskin i think
  ob = read_object(175, VIRTUAL);
  obj_to_char(ob, ch);

  // 4 breads
  for (i = 0; i < 4; i++)
  {
    ob = read_object(174, VIRTUAL);
    obj_to_char(ob, ch);
  }

  switch (GET_CLASS(ch)) {
    case CLASS_WARRIOR:
      ob = read_object(153, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(162, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(171, VIRTUAL);
      obj_to_char(ob, ch);
      break;
    case CLASS_MAGE:
    case CLASS_MADEPT:
      ob = read_object(151, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(160, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(169, VIRTUAL);
      obj_to_char(ob, ch);
      break;
    case CLASS_CLERIC:
    case CLASS_DRUID:
      ob = read_object(152, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(161, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(171, VIRTUAL);
      obj_to_char(ob, ch);
      break;
    case CLASS_THIEF:
      ob = read_object(154, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(163, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(171, VIRTUAL);
      obj_to_char(ob, ch);
      break;
    case CLASS_RANGER:
      ob = read_object(156, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(165, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(171, VIRTUAL);
      obj_to_char(ob, ch);
      break;
    case CLASS_BARD:
      ob = read_object(157, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(166, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(170, VIRTUAL);
      obj_to_char(ob, ch);
      break;
    case CLASS_SHAMAN:
      ob = read_object(155, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(164, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(171, VIRTUAL);
      obj_to_char(ob, ch);
      break;
    case CLASS_MONK:
      ob = read_object(159, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(168, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(169, VIRTUAL);
      obj_to_char(ob, ch);
      break;
    case CLASS_WARLOCK:
      ob = read_object(158, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(167, VIRTUAL);
      obj_to_char(ob, ch);
      ob = read_object(170, VIRTUAL);
      obj_to_char(ob, ch);
      break;
  };
  GET_GOLD(ch) = 500;
  send_to_char("%B%6You've been given some equipment and currency!%0\n\r",ch);
}

// if this room has a sound file... if ch has client connection... send it..
// also, after that, if mob in room has a sound.. send that
void check_room_soundsend(chdata *ch)
{
  void send_soundfile_to_client(dsdata *d, char *sound_file);
  chdata *m;

  if (INVALID_ROOM(ch->in_room))
    return;

  if (!ch->desc || !HAS_CLIENT(ch->desc))
    return;

  // first, check the room they're in...
  if (world[ch->in_room].sound_file)
  {
    send_soundfile_to_client(ch->desc, world[ch->in_room].sound_file);
    ch->desc->room_stimer = world[ch->in_room].music_timer;
    ch->desc->music_room  = world[ch->in_room].number;
  }

  // ALSO have to check if a mob in the room with them is makin noise...
  for (m=world[ch->in_room].people; m; m=m->next_in_room)
    if (IS_NPC(m) && m->npc_specials.sound_file)
    {
      send_soundfile_to_client(ch->desc, m->npc_specials.sound_file);
      ch->desc->mob_stimer = m->npc_specials.music_timer;
      ch->desc->music_room = GET_MOB_VNUM(m);
      break;
    }
}

/* place a character in a room */
void	char_to_room(chdata *ch, int room)
{
  int zone;
  void reset_roazone(int zone);
  int purge_zone(int zone);

  ch->next_in_room = world[room].people;
  world[room].people = ch;

  zone = world[room].zone;
  
  /* CHECK FOR FREENESS, IF SO LOAD ROOMS AND ZONE COMLIST!!! */
  /* even an immortal will trigger this one */
  if (REAL_ZONE(zone) && ZONE_FREED(zone))
    reload_zone(zone);

  if (IS_NPC(ch))
    world[room].num_present++;   /* add one to the room for MOBS for now*/ 
  else
  if (IS_PC(ch) && !IS_IMMORTAL(ch))
  {
    if (GET_RACE(ch) == RACE_DRAGON)
     world[room].num_present += 4;   /* dragon size 4 */
    if (GET_RACE(ch) == RACE_OGRE || GET_RACE(ch) == RACE_ORC)
     world[room].num_present += 3;   /* these guys are 3 */
    if (GET_RACE(ch) == RACE_PIXIE || GET_RACE(ch) == RACE_NIXIE)
     world[room].num_present += 1;   /* small size 1 */
    else
     world[room].num_present += 2;   /* normal size 2 */

    /* CHECK FOR IDLENESS, IF SO RESET THE ZONE!!! */
    if (REAL_ZONE(zone) && ZONE_IDLE(zone))
      unidle_reset_zone(zone);
  }
 
  ch->in_room = room;

  if ((room == real_room(1904)) && (GET_LEVEL(ch) <= 1)) 
    give_newbie_eq(ch);

  if (EQ(ch, W_HOLD) && OBJ_LIGHT(EQ(ch, W_HOLD)) && LIT(EQ(ch, W_HOLD)) )
    world[room].light++;

  if (IS_AFFECTED2(ch, AFF2_LIGHT))
    world[room].light++;

  // if light isnt waterproof, extinguish it  -roa
  if (ROOM_FLAGGED(room, UWATER) && EQ(ch, W_HOLD) && 
      LIT(EQ(ch, W_HOLD)) && !OBJ_FLAGGED(EQ(ch, W_HOLD), ITEM_WATERPROOF))
    do_extinguish(ch, "", 0, 0);

  // if they were eavesdroppin, pull them from it now... 4/17/98 -jtrhone
  if (CHAR_FLAGGED(ch, CH_EAVESDROPPING) && ch->desc && !INVALID_ROOM(ch->desc->room_snooping))
  {
    remove_room_snooper(&world[ch->desc->room_snooping], ch->desc);
    REMOVE_BIT(CHAR_FLAGS(ch), CH_EAVESDROPPING);
  }

  check_room_soundsend(ch);
}

/* give an object to a char   */
void	obj_to_char(obdata *object, chdata *ch)
{
  object->next_content = ch->carrying;
  ch->carrying = object;
  object->carried_by = ch;
  object->in_room = NOWHERE;
  IS_CARRYING_W(ch) += GET_OBJ_WEIGHT(object);
  IS_CARRYING_N(ch)++;

  if ((IS_PC(ch) && !IS_IMMORTAL(ch)) || (SPC_FLAGGED(ch, SPC_SHOPKEEP) || SPC_FLAGGED(ch, SPC_FIXER)))
    object->touched = TRUE;

  /* set flag for crash-save system */
  if (IS_PC(ch))
    SET_BIT(PLR_FLAGS(ch), PLR_CRASH);

  // update client inv list... 6/17/98 -jtrhone
  if (ch->desc && D_CHECK(ch->desc) && HAS_CLIENT(ch->desc) && PLR2_FLAGGED(ch, PLR2_CLIENTINV))
    update_client_inv(ch->desc);
}

/* take an object from a char */
void	obj_from_char(obdata *object)
{
  obdata *temp = NULL;
  chdata *ch = object->carried_by;

  REMOVE_FROM_LIST(object, ch->carrying, next_content);

  /* this object has been touched by mortals */
  if (IS_PC(ch) && !IS_IMMORTAL(ch))
    object->touched = TRUE;

  /* player gets flagged crash for next autosave */
  if (IS_PC(ch))
    SET_BIT(PLR_FLAGS(ch), PLR_CRASH);

  IS_CARRYING_W(object->carried_by) -= GET_OBJ_WEIGHT(object);
  IS_CARRYING_N(object->carried_by)--;
  object->carried_by = NULL;
  object->next_content = NULL;

  // update client inv list... 6/17/98 -jtrhone
  if (ch->desc && D_CHECK(ch->desc) && HAS_CLIENT(ch->desc) && PLR2_FLAGGED(ch, PLR2_CLIENTINV))
    update_client_inv(ch->desc);
}

// object - character restrictions, return true if any
// remort only eq update 5/29/98 -jtrhone
BOOL object_restricts(chdata *ch, obdata *obj)
{
  if ((OBJ_FLAGGED(obj, ITEM_ANTI_EVIL) && IS_EVIL(ch)) )
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("You, being evil, are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_GOOD) && IS_GOOD(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("You, being good, are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_NEUTRAL) && IS_NEUTRAL(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("You, being neutral, are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_MAGE)  && IS_NAT_MAGE(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Mages are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_CLERIC) && IS_NAT_CLERIC(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Clerics are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_WARRIOR) && IS_NAT_WARRIOR(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Warriors are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_THIEF) && IS_NAT_THIEF(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Thieves are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_SHAMAN) && IS_NAT_SHAMAN(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Shaman are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_BARD) && IS_NAT_BARD(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Bards are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_RANGER) && IS_NAT_RANGER(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Rangers are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_WARLOCK) && IS_NAT_WARLOCK(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Warlocks are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED(obj, ITEM_ANTI_MONK) && IS_NAT_MONK(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Monshai are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_HUMAN) && IS_HUMAN(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Humans are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_ELF) && IS_ELF(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Elves are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_HALF_ELF) && IS_HALF_ELF(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Half-elves are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_ORC) && IS_ORC(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Orcs are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_OGRE) && IS_OGRE(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Ogres are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_DROW) && IS_DROW(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Drow are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_DWARF) && IS_DWARF(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Dwarves are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_PIXIE) && IS_PIXIE(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Pixies are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_NIXIE) && IS_NIXIE(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Nixies are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_MADEPT) && IS_NAT_MADEPT(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Madept are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_DRUID) && IS_NAT_DRUID(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Druids are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_DRAGON) && IS_DRAGON(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Drakyn are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_ASSASSIN) && IS_ASSASSIN(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Assassins are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ANTI_ASSASSIN) && MOB2_FLAGGED(ch, MOB2_ASSASSIN))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Assassins are unable to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  if (OBJ_FLAGGED2(obj, ITEM_ASSASSIN_ONLY) && (!MOB2_FLAGGED(ch, MOB2_ASSASSIN) && !IS_ASSASSIN(ch)))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Only assassins are able to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  // let NPCs/Immortals get remort waver  6/24/98 -jtrhone
  if (OBJ_FLAGGED2(obj, ITEM_REMORT_ONLY) && IS_PC(ch) && !IS_IMMORTAL(ch) && !num_remorts(ch))
  {
    if (!IN_NOWHERE(ch)) 
    {
      act("Only remorts are able to use $p.", FALSE, ch, obj, 0, TO_CHAR);
      act("$n is unable to use $p.", FALSE, ch, obj, 0, TO_ROOM);
    } 
    else
      log("SYSERR: ch->in_room = NOWHERE when equipping char.");
    return TRUE;
  }

  // else, all is good, return ok...
  return FALSE;
}

/* RoA ... we MUST know if equip was successful! */
// updated to support wvectors  5/28/98 -jtrhone
// if anything fails, give obj to char, do not make it disappear  6/4/98 -jtrhone
BOOL	equip_char(chdata *ch, obdata *obj, int pos, BOOL mode)
{
  int	i, j;
  long oldbitv1, newbitv1;
  long oldbitv2, newbitv2;

  assert(pos >= 0 && pos < MAX_WEAR);

  if (EQ(ch, pos) || WV_FLAGGED(ch, ((1 << pos)) )) 
  {
    sprintf(buf, "SYSERR: Char already equipped: %s, %s (%d), pos: %d", GET_NAME(ch), obj->shdesc, GET_OBJ_VNUM(obj), pos);
    mudlog(buf, BRF, LEV_IMM, TRUE);
    obj_to_char(obj, ch);
    return FALSE;
  }

  if (obj->carried_by) 
  {
    sprintf(buf, "SYSERR: EQUIP: Obj(%d: %s) carried_by %s", GET_OBJ_VNUM(obj), obj->shdesc, GET_NAME(obj->carried_by));
    mudlog(buf, BRF, LEV_IMM, TRUE);
    obj_to_char(obj, ch);
    return FALSE;
  }

  if (obj->in_room != NOWHERE) 
  {
    log("SYSERR: EQUIP: Obj is in_room when equip.");
    obj_to_char(obj, ch);
    return FALSE;
  }

  if (IS_AFFECTED2(ch, AFF2_CLAWS) && (pos == W_WIELD || pos == W_HOLD))
  {
    if (!IN_NOWHERE(ch))
      act("Your claws prevent you from wielding or holding anything.", FALSE, ch, 0, 0, TO_CHAR);
    obj_to_char(obj, ch);
    return FALSE;
  }

  // object_restricts now sends tailored failure messages
  if (!IS_IMMORTAL(ch) && object_restricts(ch, obj))
  {
    obj_to_char(obj, ch);
    return FALSE;
  }

  obj->in_room = NOWHERE;
  EQ(ch, pos) = obj;

  if (ITEM_TYPE(obj) == ITEM_ARMOR)
    GET_AC(ch) -= apply_ac(ch, pos);

  oldbitv1 = AFF_FLAGS(ch);
  oldbitv2 = AFF2_FLAGS(ch);

  for (j = 0; j < MAX_OBJ_AFFECT; j++)
    affect_modify(ch, obj->affected[j].location, obj->affected[j].modifier, 
		  obj->eqaffbit, obj->eqaff2bit, TRUE);

  newbitv1 = AFF_FLAGS(ch);
  newbitv2 = AFF2_FLAGS(ch);

  for(j = 1, i = 0; i < MAX_AFFECT; i++, j *= 2)
    if(IS_SET(newbitv1, j) && !IS_SET(oldbitv1, j) && mode) 
      send_aff1_mesg(ch, j, TRUE);

  for(j = 1, i = 0; i < MAX_AFFECT; i++, j *= 2)
    if(IS_SET(newbitv2, j) && !IS_SET(oldbitv2, j) && mode) 
      send_aff2_mesg(ch, j, TRUE);

  affect_total(ch);

  // now, set wvector flags from object's wvflags and send messages 5/28/98 -jtrhone
  if (WV_FLAGS(obj))
  {
    SET_BIT(WV_FLAGS(ch), WV_FLAGS(obj));
    if (mode)
    {
      act("$p also occupies the following positions:", FALSE, ch, obj, 0, TO_CHAR);
      sprintbit(WV_FLAGS(obj), wv_bits, buf2);
      sprintf(buf, "%%B%%6%s%%0\n", buf2);
      S2C();
    }
  }

  // update client eq list... 6/17/98 -jtrhone
  if (mode && ch->desc && D_CHECK(ch->desc) && HAS_CLIENT(ch->desc) && PLR2_FLAGGED(ch, PLR2_CLIENTEQ))
    update_client_eq(ch->desc);

  return TRUE;
}

// updated to support slot revamp, 5/26/98 -jtrhone
// updated to support wvectors  5/28/98 -jtrhone
obdata *unequip_char(chdata *ch, int pos, BOOL mode)
{
  int	i, j;
  obdata *obj;
  long oldbitv1, newbitv1;
  long oldbitv2, newbitv2;

  assert(pos >= 0 && pos < MAX_WEAR);
  assert(EQ(ch, pos));

  obj = EQ(ch, pos);

  if (ITEM_TYPE(obj) == ITEM_ARMOR)
    GET_AC(ch) += apply_ac(ch, pos);

  // this supports new slot revamp 5/26/98 -jtrhone
  if (OBJ_LIGHT(obj) && LIT(obj) && mode)
    do_extinguish(ch, "", 0, 0);

  EQ(ch, pos) = NULL;

  oldbitv1 = AFF_FLAGS(ch);
  oldbitv2 = AFF2_FLAGS(ch);

  for (j = 0; j < MAX_OBJ_AFFECT; j++)
    affect_modify(ch, obj->affected[j].location, obj->affected[j].modifier, 
		  obj->eqaffbit, obj->eqaff2bit, FALSE);

  affect_total(ch);
 
  newbitv1 = AFF_FLAGS(ch);
  newbitv2 = AFF2_FLAGS(ch);

  for(j = 1, i = 0; i < MAX_AFFECT;i++, j *= 2) 
    if(!IS_SET(newbitv1, j) && IS_SET(oldbitv1, j) && mode) 
      send_aff1_mesg(ch, j, FALSE);

  for(j = 1, i = 0; i < MAX_AFFECT;i++, j *= 2) 
    if(!IS_SET(newbitv2, j) && IS_SET(oldbitv2, j) && mode) 
      send_aff2_mesg(ch, j, FALSE);

  EQ(ch, pos) = NULL;

  // now, remove wvector flags from object's wvflags and send messages 5/28/98 -jtrhone
  if (WV_FLAGS(obj))
  {
    REMOVE_BIT(WV_FLAGS(ch), WV_FLAGS(obj));
    if (mode)
    {
      act("Removing $p has freed up the following positions:", FALSE, ch, obj, 0, TO_CHAR);
      sprintbit(WV_FLAGS(obj), wv_bits, buf2);
      sprintf(buf, "%%B%%6%s%%0\n", buf2);
      S2C();
    }
  }

  // update client eq list... 6/17/98 -jtrhone
  if (mode && ch->desc && D_CHECK(ch->desc) && HAS_CLIENT(ch->desc) && PLR2_FLAGGED(ch, PLR2_CLIENTEQ))
    update_client_eq(ch->desc);

  return(obj);
}

// yank a piece of eq out of equip list, place it into inventory -roa
BOOL eq_to_inv(chdata *ch, int pos, BOOL tellem)
{
  obdata *ob;

  assert(pos >= 0 && pos < MAX_WEAR);
  ob = unequip_char(ch, pos, tellem);
  obj_to_char(ob, ch);

  return TRUE;
}

// yank a piece of inv out of inv list, wield/wear it -roa
BOOL inv_to_eq(chdata *ch, obdata *ob, int pos, BOOL tellem)
{
  assert(pos >= 0 && pos < MAX_WEAR);
  obj_from_char(ob);
  return equip_char(ch, ob, pos, tellem);
}

int	get_number(char **name)
{
  int	i;
  char	*ppos;
  char	number[MAX_INPUT_LENGTH];

  *number = '\0';
  if ((ppos = strchr(*name, '.'))) 
  {
    *ppos++ = '\0';
    strcpy(number, *name);
    strcpy(*name, ppos);

    for (i = 0; *(number + i); i++)
      if (!isdigit(*(number + i)))
	return 0;

    return(atoi(number));
  }
  return 1;
}

/* Search a given list for an object, and return a pointer to that object */
obdata *get_obj_in_list(char *name, obdata *list)
{
  obdata *i;
  int	j, number;
  char	tmpname[MAX_INPUT_LENGTH];
  char	*tmp;

  str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_obj_in_list");
  tmp = tmpname;

  if (!(number = get_number(&tmp)))
    return NULL;

  for (i = list, j = 1; i && (j <= number); i = i->next_content)
    if (isname(tmp, i->name)) 
    {
      if (j == number)
	return(i);
      j++;
    }

  return NULL;
}

/* Search a given list for an object number, and return a ptr to that obj */
obdata *get_obj_in_list_num(int num, obdata *list)
{
  obdata *i;

  for (i = list; i; i = i->next_content)
    if (i->item_number == num)
      return(i);

  return NULL;
}

/*search the entire world for an object, and return a pointer  */
obdata *get_obj(char *name)
{
  obdata *i;
  int	j, number;
  char	tmpname[MAX_INPUT_LENGTH];
  char	*tmp;

  str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_obj");
  tmp = tmpname;

  if (!(number = get_number(&tmp)))
    return NULL;

  for (i = object_list, j = 1; i && (j <= number); i = i->next)
    if (isname(tmp, i->name)) 
    {
      if (j == number)
        return(i);
      j++;
    }

  return NULL;
}

/*search the entire world for an object number, and return a pointer  */
obdata *get_obj_num(int nr)
{
  obdata *i;

  for (i = object_list; i; i = i->next)
    if (i->item_number == nr)
      return(i);

  return NULL;
}

/* search a room for a char, and return a pointer if found..  */
chdata *get_char_room(char *name, int room)
{
  chdata *i;
  int	j, number;
  char	tmpname[MAX_INPUT_LENGTH];
  char	*tmp;

  str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_char_room");
  tmp = tmpname;

  if (!(number = get_number(&tmp)))
    return NULL;

  for (i = world[room].people, j = 1; i && (j <= number); i = i->next_in_room)
    if (isname(tmp, i->player.name)) 
    {
      if (j == number)
	return(i);
      j++;
    }

  return NULL;
}

/* search all over the world for a char, and return a pointer if found */
chdata *get_char(char *name)
{
  chdata *i;
  int	j, number;
  char	tmpname[MAX_INPUT_LENGTH];
  char	*tmp;

  if (!name || !*name) return NULL;

  str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_char");
  tmp = tmpname;

  if (!(number = get_number(&tmp)))
    return NULL;

  for (i = character_list, j = 1; i && (j <= number); i = i->next)
    if (isname(tmp, i->player.name)) 
    {
      if (j == number)
	return(i);
      j++;
    }

  return NULL;
}

/* search all over the world for a char num, and return a pointer if found */
chdata *get_char_num(int nr)
{
  chdata *i;

  for (i = character_list; i; i = i->next)
    if (i->nr == nr)
      return(i);

  return NULL;
}

/* put an object in a room */
void	obj_to_room(obdata *object, int room)
{
  object->next_content 	= world[room].contents;
  world[room].contents 	= object;
  object->in_room 	= room;
  object->carried_by 	= NULL;

  if (ROOM_FLAGGED2(room, HOUSE))
    SET_BIT(ROOM_FLAGS2(room), HOUSE_CRASH);
  else
  if (ROOM_FLAGGED2(room, SAVE_ROOM))
    SET_BIT(ROOM_FLAGS2(room), ROOM_CRASH);
}

/* Take an object from a room */
void	obj_from_room(obdata *object)
{
  obdata *temp;

  if (object->in_room == NOWHERE)
  {
    sprintf(buf, "SYSERR: Obj in NOWHERE, placed in void.(%d)", GET_OBJ_VNUM(object));
    mudlog(buf, BRF, LEV_IMM, TRUE);
    obj_to_room(object, 0);
    return;
  }

  REMOVE_FROM_LIST(object, world[object->in_room].contents, next_content);

  if (ROOM_FLAGGED2(object->in_room, HOUSE))
    SET_BIT(ROOM_FLAGS2(object->in_room), HOUSE_CRASH);
  else
  if (ROOM_FLAGGED2(object->in_room, SAVE_ROOM))
    SET_BIT(ROOM_FLAGS2(object->in_room), ROOM_CRASH);

  object->in_room = NOWHERE;
  object->next_content = NULL;
}

// place an object inside another object
void	obj_to_obj(obdata *obj, obdata *obj_to)
{
  obdata *tmp_obj;

  if (!obj || !obj_to) return;

  obj->next_content = obj_to->contains;
  obj_to->contains = obj;
  obj->in_obj = obj_to;

  for (tmp_obj = obj->in_obj; tmp_obj->in_obj; tmp_obj = tmp_obj->in_obj)
    GET_OBJ_WEIGHT(tmp_obj) += GET_OBJ_WEIGHT(obj);

  /* top level object.  Subtract weight from inventory if necessary. */
  GET_OBJ_WEIGHT(tmp_obj) += GET_OBJ_WEIGHT(obj);
  if (tmp_obj->carried_by)
    IS_CARRYING_W(tmp_obj->carried_by) += GET_OBJ_WEIGHT(obj);
}

/* remove an object from an object */
void obj_from_obj(obdata *obj)
{
  obdata *temp, *obj_from;

  if (obj->in_obj == NULL) {
    log("SYSERR: (handler.c): trying to illegally extract obj from obj");
    return;
  }
  obj_from = obj->in_obj;

  REMOVE_FROM_LIST(obj, obj_from->contains, next_content);

  /* Subtract weight from containers container */
  for (temp = obj->in_obj; temp->in_obj; temp = temp->in_obj)
    GET_OBJ_WEIGHT(temp) -= GET_OBJ_WEIGHT(obj);

  /* Subtract weight from char that carries the object */
  GET_OBJ_WEIGHT(temp) -= GET_OBJ_WEIGHT(obj);
  if (temp->carried_by)
    IS_CARRYING_W(temp->carried_by) -= GET_OBJ_WEIGHT(obj);

  obj->in_obj = NULL;
  obj->next_content = NULL;
}

/* Set all carried_by to point to new owner */
void	object_list_new_owner(obdata *list, chdata *ch)
{
  if (list) 
  {
    object_list_new_owner(list->contains, ch);
    object_list_new_owner(list->next_content, ch);
    list->carried_by = ch;
  }
}

/* Extract an object from the world */
void	extract_obj(obdata *obj)
{
  obdata *temp;

  if (!INVALID_ROOM(obj->in_room))
    obj_from_room(obj);
  else if (obj->carried_by)
    obj_from_char(obj);
  else if (obj->in_obj)
    obj_from_obj(obj);

  /* leave nothing behind */
  while(obj->contains)
    extract_obj(obj->contains);
  obj->contains = NULL;

  REMOVE_FROM_LIST(obj, object_list, next);

  if (GET_OBJ_RNUM(obj) >= 0)
    obj_index[GET_OBJ_RNUM(obj)].number--;

  free_obj(obj);
}

// update object just decrements the timers on this obj and all within it/after it in list
void	update_object(obdata *obj, int use)
{
  if (!obj)
  {
    mudlog("SYSERR: NO OBJECT in update_object, handler.c", BRF, LEV_IMM, TRUE);
    return;
  }

  if (obj->timer > 0)
    obj->timer -= use;

  if (obj->contains)
    update_object(obj->contains, use);

  if (obj->next_content)
    update_object(obj->next_content, use);
}

// updated to support pure poisons and brewing and the like... 4/17/98 -jtrhone
// updated to check hold slot for light objs rather than light slot
// i.e. to support slot revamp 5/26/98 -jtrhone
// todo: should move light updates to update_obj and add inv scanning
void	update_char_objects( chdata *ch )
{
  int	i;
  BOOL waslit = FALSE;

  if (EQ(ch, W_HOLD) && OBJ_LIGHT(EQ(ch, W_HOLD)) && 
      LIGHT_TIME(EQ(ch, W_HOLD)) > 0)
  {
    LIGHT_TIME(EQ(ch, W_HOLD))--;
    waslit = TRUE;
  }

  if (EQ(ch, W_HOLD) && OBJ_LIGHT(EQ(ch, W_HOLD)) && 
      !LIGHT_TIME(EQ(ch, W_HOLD)) && waslit && !IN_NOWHERE(ch))
  {
    EQ(ch, W_HOLD)->lit = FALSE; 
    act("Your light source has burnt out!",FALSE,ch,0,0,TO_CHAR);
    if (world[ch->in_room].light > 0)
    {
      if ((world[ch->in_room].light--) > 0)
      {
	act("The area surrounding you gets slightly darker.",FALSE,ch,0,0,TO_CHAR);
	act("The area surrounding you gets slightly darker.",FALSE,ch,0,0,TO_ROOM);
      }
      else
      {
	act("The area surrounding you suddenly gets dark!",FALSE,ch,0,0,TO_CHAR);
	act("The area surrounding you suddenly gets dark!",FALSE,ch,0,0,TO_ROOM);
      }
    }
  }

  /* update all but light objects */
  // apparently, objs in EQ list time out faster
  for (i = 0; i < MAX_WEAR; i++)
    if (EQ(ch, i) && !OBJ_LIGHT(EQ(ch, i)))
      update_object(EQ(ch, i), 2);

  // than objs in inventory (why... i dunno.. )  4/17/98 -jtrhone
  if (ch->carrying)
    update_object(ch->carrying, 1);

  // now, update the poison if ch is brewing and is holding the right stuff
  // 4/17/98 -jtrhone
  if (affected_by_spell(ch, SKILL_BREW))
  {
    obdata *o;
    if (!(o = EQ(ch, W_HOLD)) || (!IS_LIQCONT(o)) || (DRINKS_LEFT(o) && DRINK_TYPE(o) != LIQ_POISON))
      affect_from_char(ch, SKILL_BREW);
    else
    if (!DRINKS_LEFT(o) || IS_PUREPOISON(o))
    {
      act("You continue to brew your poison.", FALSE,ch,0,0,TO_CHAR);
      DRINK_TYPE(o) = LIQ_POISON;
      DRINKS_LEFT(o)++;
      POISON_LEVEL(o)++;
    }
  }
}

// for NPC - extract mob from real world and free_char as well...
// for PCs - extract character from real world, leave char at menu...
void	extract_char(chdata *ch)
{
  obdata *obj;
  chdata *k, *temp;
  dsdata *d;
  int	l;
  BOOL zap = FALSE;
  void update_client_who(void);

  if (CHAR_FLAGGED(ch, CH_RECORD))
    fclose(ch->fp);

  if (IS_PC(ch))
  {
    global_forget(ch);

    if (SINGING(ch))
      remove_song_from_world(ch, SINGING(ch));   

    if (PLAYING(ch))
      remove_song_from_world(ch, PLAYING(ch));

    remove_char_plshops(ch);

    // if they have any clpids, we wanna not point to this char anymore
    // 3/1/98 -jtrhone
    null_char_clpids(ch);
  }

  if (IS_PC(ch) && !ch->desc) 
    for (d = descriptor_list; d; d = d->next)
      if (d->original == ch)
	do_return(d->character, "", 0, 0);

  if (IN_NOWHERE(ch)) 
  {
    log("SYSERR: NOWHERE extracting char. (handler.c, extract_char)");
    log(GET_NAME(ch));  /* just to see if mob or player next time */
    exit(1);
  }

  /* if player in void or nowehere, wipe the objects RoA */
  if (!ch->in_room)
    zap = TRUE;

  if (ch->followers || ch->master)
    die_follower(ch);

  if (ch->desc) 
  {
    if (ch->desc->snooping)
      ch->desc->snooping->snoop_by = 0;
    if (ch->desc->snoop_by) 
    {
      if (ch->desc->snoop_by->character)
        send_to_char("Your victim is no longer among us.\n\r", ch->desc->snoop_by->character);
      ch->desc->snoop_by->snooping = 0;
    }
    ch->desc->snooping = ch->desc->snoop_by = 0;

    // now for room snooping
    if (ch->desc->room_snooping)
      remove_room_snooper(&world[ch->desc->room_snooping], ch->desc);
  }

  if (MOB_FLAGGED(ch, MOB_PLSHOPKEEP))
  {
    save_plshopkeep_objs(ch);
    extract_objects(ch->carrying);
  }
  else
  while (ch->carrying) 
  {
    obj = ch->carrying;
    obj_from_char(obj);
    if (zap)
    {
      obj_to_room(obj, ch->in_room);
      extract_obj(obj);
    }
    else
      float_sink_object(obj, ch->in_room);
  }

  /* clear equipment_list */
  for (l = 0; l < MAX_WEAR; l++)
    if (EQ(ch, l))
    {
      obj = unequip_char(ch, l, FALSE);
      if (zap)
      {
        obj_to_room(obj, ch->in_room);
	extract_obj(obj);
      }
      else
	float_sink_object(obj, ch->in_room);
    }

  if (FIGHTING(ch))
     stop_fighting(ch);

  for (k = combat_list; k ; k = temp) 
  {
    temp = k->next_fighting;
    if (FIGHTING(k) == ch)
      stop_fighting(k);
  }

  null_char_room_affects(ch);

  /* in case they's a shaman and they be in a rite */
  DELAY_TYPE(ch) = 0;

  char_from_room(ch);

  /* pull the char from the list */
  REMOVE_FROM_LIST(ch, character_list, next);

  if (ch->desc && ch->desc->original) 
    do_return(ch, "", 0, 0);

  if (IS_NPC(ch)) 
  {
    if (GET_MOB_RNUM(ch) > -1) 
      mob_index[GET_MOB_RNUM(ch)].number--;
    clearMemory(ch); 
    free_char(ch);    /* note, ch is invalid after this call */
  }
  else
  if (ch->desc) 
  {
    /* if they hit a dt, have them press return to go to menu */
    if (CHAR_FLAGGED(ch, CH_HIT_DT))
    {
      CHAR_FLAGS(ch) = 0;
      save_char(ch, NOWHERE);
      STATE(ch->desc) = CON_DESCMENU;
      descmenu_next(ch->desc, mainmenu);
      descmenu_push_jump(ch->desc, genericreturn);
    }
    else
    {
      CHAR_FLAGS(ch) = 0;
      save_char(ch, NOWHERE);
      STATE(ch->desc) = CON_DESCMENU;
      if (VT100(ch))
      {
	resetscr(ch);
	send_to_q(VTMENU, ch->desc);
      }
      else
	send_to_q(MENU, ch->desc);
      descmenu_next(ch->desc, mainmenu);
    }

    // now, update the client who lists  6/15/98 -jtrhone
    update_client_who();
  }
}

// unlike the previous function, this one does not leave a PC at main menu, but rather
// flings him/her from the game, also does not rely on there being a descriptor
// also, it's assumed all equipment has already been removed before this point...
// usually called on PCs that have been force-saved -roa
int	eject_char(chdata *ch)
{
  chdata *k, *temp;
  dsdata *d;
  int	l;

  // if char is switched, return em now
  if (IS_PC(ch)) 
    for (d = descriptor_list; d; d = d->next)
      if (d->original == ch)
	do_return(d->character, "", 0, 0);

  if (ch->followers || ch->master)
    die_follower(ch);

  // shouldn't have eq or inventory here, but hey...
  for (l = 0; l < MAX_WEAR; l++)
    if (EQ(ch, l) || ch->carrying)
    {
      sprintf(buf, "SYSERR: Ejected char had equipment/inventory (%s).", GET_NAME(ch));
      mudlog(buf, BRF, LEV_IMM, TRUE);
      break;
    }

  // shouldn't be fighting, but just in case...
  if (FIGHTING(ch))
     stop_fighting(ch);

  for (k = combat_list; k ; k = temp) 
  {
    temp = k->next_fighting;
    if (FIGHTING(k) == ch)
      stop_fighting(k);
  }

  // remove any song affects and set room affects by this char to NULL
  if (IS_PC(ch))
  {
    if (SINGING(ch))
      remove_song_from_world(ch, SINGING(ch));   
    if (PLAYING(ch))
      remove_song_from_world(ch, PLAYING(ch));
  }

  null_char_room_affects(ch);

  /* in case they's a shaman and they be in a rite */
  DELAY_TYPE(ch) = CHAR_FLAGS(ch) = 0;

  char_from_room(ch);

  // yank character from list now
  REMOVE_FROM_LIST(ch, character_list, next);

  if (IS_PC(ch))
    save_char(ch, NOWHERE);
  else
  if (IS_NPC(ch)) 
  {
    if (GET_MOB_RNUM(ch) > -1) 
      mob_index[GET_MOB_RNUM(ch)].number--;
    clearMemory(ch); 
  }

  free_char(ch);    /* note, ch is invalid after this call */
  return CHAR_DIED;
}

/* ***********************************************************************
   Here follows high-level versions of some earlier routines, ie functions
   which incorporate the actual player-data.
   *********************************************************************** */
// Changed this to allow 0.<name> for PCs and add "me" and "self" support
// ala Circle 3.0... 03/22/98 -callahan
CharData *get_char_room_vis(CharData *ch, char *name)
{
  CharData *i;
  int j = 0, number;
  char tmpname[MAX_INPUT_LENGTH];
  char *tmp = tmpname;

  if (IN_NOWHERE(ch))
    return (NULL);

  if (!str_cmp(name, "self") || !str_cmp(name, "me"))
    return ch;

  // 0.<name> means PC with name
  str_cpy(tmp, name, MAX_INPUT_LENGTH, "get_char_room_vis");
  if (!(number = get_number(&tmp)))
    return get_player_vis(ch, tmp, TRUE);
  
  for (i = Adjacents(ch); i && j <= number; i = i->next_in_room)
    if (isname(tmp, PName(i)))
      if (can_see(ch, i))
        if (++j == number)
          return i;
 
  return NULL;
}


// Added int inroom check so 0.playername only works within a room.
// 03/22/98 -callahan
chdata *get_player_vis(chdata *ch, char *name, int inroom)
{
   chdata *i;

   for (i = character_list; i; i = i->next)
      if (IS_PC(i) && !str_cmp(i->player.name, name) && can_see(ch, i) &&
          (!inroom || (InRoom(ch) == InRoom(i))))
	 return i;

   return 0;
}

chdata *get_char_zone_vis(chdata *ch, char *name)
{
   chdata *i;
   int  j, number;
   char tmpname[MAX_INPUT_LENGTH];
   char *tmp;

   /* check location */
   if ((i = get_char_room_vis(ch, name)))
      return(i);

   str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_char_zone_vis");
   tmp = tmpname;
   if (!(number = get_number(&tmp)))
      return(0);

   for (i = character_list, j = 1; i && (j <= number); i = i->next)
     if (isname(tmp, i->player.name) && can_see(ch, i) && !INVALID_ROOM(i->in_room) &&
         world[i->in_room].zone == world[ch->in_room].zone)
     {
       if (j == number)
         return(i);
         j++;
     }

   return(0);
}

chdata *get_char_zone(int zone, char *name)
{
   chdata *i;
   int	j, number;
   char	tmpname[MAX_INPUT_LENGTH];
   char	*tmp;

   str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_char_zone");
   tmp = tmpname;
   if (!(number = get_number(&tmp)))
      return(0);

   for (i = character_list, j = 1; i && (j <= number); i = i->next)
     if (isname(tmp, i->player.name) && !INVALID_ROOM(i->in_room) &&
	 world[i->in_room].zone == zone)	 
     {
       if (j == number)
	 return(i);
	 j++;
     }

   return(0);
}

chdata *get_char_vis(chdata *ch, char *name)
{
   chdata *i;
   int	j, number;
   char	tmpname[MAX_INPUT_LENGTH];
   char	*tmp;

   /* check location */
   if ((i = get_char_room_vis(ch, name)))
      return(i);

   str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_char_vis");
   tmp = tmpname;
   if (!(number = get_number(&tmp)))
      return(0);

   for (i = character_list, j = 1; i && (j <= number); i = i->next)
     if (isname(tmp, i->player.name) && can_see(ch, i))	 
     {
       if (j == number)
	 return(i);
	 j++;
     }

   return(0);
}

obdata *get_obj_in_list_vis(chdata *ch, char *name, obdata *list)
{
   obdata *i;
   int	j, number;
   char	tmpname[MAX_INPUT_LENGTH];
   char	*tmp;

   str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_obj_in_list_vis");
   tmp = tmpname;
   if (!(number = get_number(&tmp)))
      return(0);

   for (i = list, j = 1; i && (j <= number); i = i->next_content)
      if (isname(tmp, i->name))
	 if (CAN_SEE_OBJ(ch, i)) {
	    if (j == number)
	       return(i);
	    j++;
	 }
   return(0);
}

// same as get_obj_vis, but with a zone check
obdata *get_obj_zone_vis(chdata *ch, char *name)
{
   obdata *i;
   int	j, number;
   char	tmpname[MAX_INPUT_LENGTH];
   char	*tmp;

   /* scan room */
   if ((i = get_obj_in_list_vis(ch, name, world[ch->in_room].contents)))
      return(i);

   str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_obj_zone_vis");
   tmp = tmpname;
   if (!(number = get_number(&tmp)))
      return(0);

   /* ok.. no luck yet. scan the entire obj list   */
   for (i = object_list, j = 1; i && (j <= number); i = i->next)
      if (isname(tmp, i->name))
	 if (CAN_SEE_OBJ(ch, i) && !INVALID_ROOM(i->in_room) &&
	     world[i->in_room].zone == world[ch->in_room].zone) 
	 {
	    if (j == number)
	       return(i);
	    j++;
	 }
   return(0);
}

obdata *get_object_zone(int zone, char *name)
{
   obdata *i;
   int  j, number;
   char tmpname[MAX_INPUT_LENGTH];
   char *tmp;

   str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_obj_zone");
   tmp = tmpname;
   if (!(number = get_number(&tmp)))
     return(0);

   for (i = object_list, j = 1; i && (j <= number); i = i->next)
     if (isname(tmp, i->name))
       if (!INVALID_ROOM(i->in_room) && world[i->in_room].zone == zone)
       {
         if (j == number)
           return(i);
         j++;
       }

   return NULL;
}

/*search the entire world for an object, and return a pointer  */
obdata *get_obj_vis(chdata *ch, char *name)
{
   obdata *i;
   int	j, number;
   char	tmpname[MAX_INPUT_LENGTH];
   char	*tmp;

   /* scan items carried */
   if ((i = get_obj_in_list_vis(ch, name, ch->carrying)))
      return(i);

   /* scan room */
   if ((i = get_obj_in_list_vis(ch, name, world[ch->in_room].contents)))
      return(i);

   str_cpy(tmpname, name, MAX_INPUT_LENGTH, "get_obj_vis");
   tmp = tmpname;
   if (!(number = get_number(&tmp)))
      return(0);

   /* ok.. no luck yet. scan the entire obj list   */
   for (i = object_list, j = 1; i && (j <= number); i = i->next)
      if (isname(tmp, i->name))
	 if (CAN_SEE_OBJ(ch, i)) {
	    if (j == number)
	       return(i);
	    j++;
	 }
   return(0);
}

obdata *get_object_in_equip_vis(chdata *ch, char *arg, obdata *equipment[], int *j)
{
   for ((*j) = 0; (*j) < MAX_WEAR ; (*j)++)
      if (equipment[(*j)])
	 if (CAN_SEE_OBJ(ch, equipment[(*j)]))
	    if (isname(arg, equipment[(*j)]->name))
	       return(equipment[(*j)]);

   return (0);
}

// no longer create an exdesc on money objs, now use obj ldesc  11/28/97 -jtrhone
obdata *create_money(int amount)
{
   char	buf[200];
   obdata *obj;

   if (amount <= 0) 
   {
      log("SYSERR: Try to create negative or 0 currency.");
      exit(1);
   }

   CREATE(obj, obdata, 1);
   clear_object(obj);

   if (amount == 1) 
   {
      sprintf(buf, "%s %s", currency_name, currency_name_plural);
      obj->name = str_dup(buf);

      sprintf(buf, "a %s", currency_name);
      obj->shdesc = str_dup(buf);

      sprintf(buf, "One miserable %s is lying here.", currency_name);
      obj->description = str_dup(buf);

      sprintf(buf, "One miserable %s.", currency_name);
      obj->actdesc = str_dup(buf);
   } 
   else 
   {
      sprintf(buf, "%s %s", currency_name_plural, currency_name);
      obj->name = str_dup(buf);

      if (amount <= 100) 
      {
         sprintf(buf, "a small pile of %s", currency_name_plural);
         obj->shdesc = str_dup(buf);
         sprintf(buf, "A small pile of %s is lying here.", currency_name_plural);
         obj->description = str_dup(buf);
      } else if (amount <= 1000) {
         sprintf(buf, "a pile of %s", currency_name_plural);
         obj->shdesc = str_dup(buf);
         sprintf(buf, "A pile of %s is lying here.", currency_name_plural);
         obj->description = str_dup(buf);
      } else if (amount <= 25000) {
         sprintf(buf, "a large heap of %s", currency_name_plural);
         obj->shdesc = str_dup(buf);
         sprintf(buf, "A large heap of %s is lying here.", currency_name_plural);
         obj->description = str_dup(buf);
      } else if (amount <= 500000) {
         sprintf(buf, "a huge mound of %s", currency_name_plural);
         obj->shdesc = str_dup(buf);
         sprintf(buf, "A huge mound of %s is lying here.", currency_name_plural);
         obj->description = str_dup(buf);
      } else {
         sprintf(buf, "an enormous mountain of %s", currency_name_plural);
         obj->shdesc = str_dup(buf);
         sprintf(buf, "An enormous mountain of %s is lying here.", currency_name_plural);
         obj->description = str_dup(buf);
      }

      if (amount < 10) {
	 sprintf(buf, "There are %d %s.", amount, currency_name_plural);
         obj->actdesc = str_dup(buf);

      } else if (amount < 100) {
	 sprintf(buf, "There are about %d %s.", 10 * (amount / 10), currency_name_plural);
         obj->actdesc = str_dup(buf);

      } else if (amount < 1000) {
	 sprintf(buf, "There looks to be about %d %s.", 100 * (amount / 100), currency_name_plural);
         obj->actdesc = str_dup(buf);

      } else if (amount < 100000) {
	 sprintf(buf, "You guess there are, maybe, %d %s.",
	     1000 * ((amount / 1000) + number(0, (amount / 1000))), currency_name_plural);
         obj->actdesc = str_dup(buf);

      } 
      else
      {
	 sprintf(buf, "There are a LOT of %s.", currency_name_plural);
         obj->actdesc = str_dup(buf);
      } 
   }

   obj->type_flag = ITEM_MONEY;
   OBJ_WEARS(obj) = ITEM_TAKE;
   obj->value[0] = amount;
   obj->cost = amount;
   obj->item_number = -1;

   obj->next = object_list;
   object_list = obj;

   return(obj);
}

/* Generic Find, designed to find any object/character                    */
/* Calling :                                                              */
/*  *arg     is the sting containing the string to be searched for.       */
/*           This string doesn't have to be a single word, the routine    */
/*           extracts the next word itself.                               */
/*  bitv..   All those bits that you want to "search through".            */
/*           Bit found will be result of the function                     */
/*  *ch      This is the person that is trying to "find"                  */
/*  **tar_ch Will be NULL if no character was found, otherwise points     */
/* **tar_obj Will be NULL if no object was found, otherwise points        */
/*                                                                        */
/* The routine returns a pointer to the next word in *arg (just like the  */
/* one_argument routine).                                                 */

int	generic_find(char *arg, int bitvector, chdata *ch, chdata **tar_ch, 
		     obdata **tar_obj) 
{
   static char	*ignore[] = {
      "the",
      "in",
      "on",
      "at",
      "\n"       
   };

   int	i;
   char	name[256];
   BOOL found = FALSE;

   /* Eliminate spaces and "ignore" words */
   while (*arg && !found) {

      for (; *arg == ' '; arg++)
	 ;

      for (i = 0; (name[i] = *(arg + i)) && (name[i] != ' '); i++)
	 ;
      name[i] = 0;
      arg += i;
      if (search_block(name, ignore, TRUE) > -1)
	 found = TRUE;
   }

   if (!name[0])
      return(0);

   *tar_ch  = 0;
   *tar_obj = 0;

   if (IS_SET(bitvector, FIND_CHAR_ROOM)) {      /* Find person in room */
      if ((*tar_ch = get_char_room_vis(ch, name))) {
	 return(FIND_CHAR_ROOM);
      }
   }

   if (IS_SET(bitvector, FIND_CHAR_WORLD)) {
      if ((*tar_ch = get_char_vis(ch, name))) {
	 return(FIND_CHAR_WORLD);
      }
   }

   if (IS_SET(bitvector, FIND_OBJ_EQUIP)) 
   {
      for (found = FALSE, i = 0; i < MAX_WEAR && !found; i++)
	 if (EQ(ch, i) && isname(name, EQ(ch, i)->name)) 
	 {
	    *tar_obj = EQ(ch, i);
	    found = TRUE;
	 }
      if (found)
	 return(FIND_OBJ_EQUIP);
   }

   if (IS_SET(bitvector, FIND_OBJ_INV)) {
      if ((*tar_obj = get_obj_in_list_vis(ch, name, ch->carrying))) {
	 return(FIND_OBJ_INV);
      }
   }

   if (IS_SET(bitvector, FIND_OBJ_ROOM)) {
      if ((*tar_obj = get_obj_in_list_vis(ch, name, world[ch->in_room].contents))) {
	 return(FIND_OBJ_ROOM);
      }
   }

   if (IS_SET(bitvector, FIND_OBJ_WORLD)) {
      if ((*tar_obj = get_obj_vis(ch, name))) {
	 return(FIND_OBJ_WORLD);
      }
   }

   return(0);
}

/* a function to scan for "all" or "all.x" */
int	find_all_dots(char *arg)
{
   if (!str_cmp(arg, "all"))
      return FIND_ALL;
   else if (!strncmp(arg, "all.", 4)) {
      strcpy(arg, arg+4);
      return FIND_ALLDOT;
   } else
      return FIND_INDIV;
}

// Function to manipulate multiple objects. 08/10/98 -callahan
int ManyObjs(char *arg, char *ptr)
{
  int amount = 0;
  char buff[MAX_INPUT_LENGTH];

  buff[0] = 0;

  sscanf(arg, "%d*%s", &amount, buff);

  if (buff[0] == '\0')
    return 0;

  if (amount < 1)
    return 0;

  if (amount > 20)
    amount = 20;

  while (*arg != '*')
    arg++;

  arg++;

  for (; (*ptr = *arg); arg++, ptr++);

  return amount;
}