area_current/castle/
area_current/gahld/
clans/
player/
player/c/
/***************************************************************************
 *  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.                                                  *
 ***************************************************************************/

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include "merc.h"
#include <pthread.h>

/*
 * Local functions.
 */
void    affect_modify   args( ( CHAR_DATA *ch, AFFECT_DATA *paf, bool fAdd ) );
void  obj_set_to_char( CHAR_DATA *, OBJ_DATA * );



/*
 * Retrieve a character's trusted level for permission checking.
 */
int get_trust( CHAR_DATA *ch )
{

    if( IS_NPC(ch) && ch->pIndexData->vnum!=9900 && !IS_AFFECTED(ch,AFF_CHARM)  
        && !IS_AFFECTED(ch, AFF2_POSSESS) && ch->desc==NULL )
       return( 99 );

    if ( ch->desc != NULL && ch->desc->original != NULL )
	return (ch->desc->original->level);

    if( IS_NPC(ch) )
      return(1);

    if( ch->trust<ch->level )
      ch->trust = ch->level;
    return (ch->trust);
}



/*
 * Retrieve a character's age.
 */
int get_age( CHAR_DATA *ch )
{
    return (17 + (ch->played  / 20000));
}



/*
 * Retrieve character's current strength.
 */
int get_curr_str( CHAR_DATA *ch )
{
    int max;

    if ( IS_NPC(ch) )
	return 13;

    if ( class_table[ch->class].attr_prime == APPLY_STR )
	max = 22+ch->level/9;
    else
	max = 19+ch->level/9;

    max += race_table[ ch->race ].race_mod[0];

    return URANGE( 3, ch->pcdata->perm_str + ch->pcdata->mod_str, max );
}



/*
 * Retrieve character's current intelligence.
 */
int get_curr_int( CHAR_DATA *ch )
{
    int max;

    if ( IS_NPC(ch) )
	return 13;

    if ( class_table[ch->class].attr_prime == APPLY_INT )
	max = 22+ch->level/9;
    else
	max = 19+ch->level/9;

    max += race_table[ ch->race ].race_mod[2];

    return URANGE( 3, ch->pcdata->perm_int + ch->pcdata->mod_int, max );
}



/*
 * Retrieve character's current wisdom.
 */
int get_curr_wis( CHAR_DATA *ch )
{
    int max;

    if ( IS_NPC(ch) )
	return 13;

    if ( class_table[ch->class].attr_prime == APPLY_WIS )
	max = 22+ch->level/9;
    else
	max = 19+ch->level/9;

    max += race_table[ ch->race ].race_mod[3];

    return URANGE( 3, ch->pcdata->perm_wis + ch->pcdata->mod_wis, max );
}



/*
 * Retrieve character's current dexterity.
 */
int get_curr_dex( CHAR_DATA *ch )
{
    int max;

    if ( IS_NPC(ch) )
	return 13;

    if ( class_table[ch->class].attr_prime == APPLY_DEX )
	max = 22+ch->level/9;
    else
	max = 19+ch->level/9;

    max += race_table[ ch->race ].race_mod[1];

    return URANGE( 3, ch->pcdata->perm_dex + ch->pcdata->mod_dex, max );
}



/*
 * Retrieve character's current constitution.
 */
int get_curr_con( CHAR_DATA *ch )
{
    int max;

    if ( IS_NPC(ch) )
	return 13;

    if ( class_table[ch->class].attr_prime == APPLY_CON )
	max = 22+ch->level/9;
    else
	max = 19+ch->level/9;

    max += race_table[ ch->race ].race_mod[4];

    return URANGE( 3, ch->pcdata->perm_con + ch->pcdata->mod_con, max );
}



/*
 * Retrieve a character's carry capacity.
 */
int can_carry_n( CHAR_DATA *ch )
{
    if ( !IS_NPC(ch) && ch->level >= LEVEL_IMMORTAL )
	return 1000;

    if ( IS_NPC(ch) && IS_SET(ch->act, ACT_PET) )
	return 0;

    return MAX_WEAR + 2 * get_curr_dex( ch ) / 3+ch->level;
}



/*
 * Retrieve a character's carry capacity.
 */
int can_carry_w( CHAR_DATA *ch )
{
    if ( !IS_NPC(ch) && ch->level >= LEVEL_IMMORTAL )
	return 1000000;

    if ( IS_NPC(ch) && IS_SET(ch->act, ACT_PET) )
	return 0;

    return str_app[get_curr_str(ch)].carry;
}



/*
 * See if a string is one of the names of an object.
 */
bool is_name( const char *str, char *namelist )
{
    char name[MAX_INPUT_LENGTH];

    for ( ; ; )
    {
	namelist = one_argument( namelist, name );
	if ( *name == '\0' )
	    return FALSE;
	if ( !strcasecmp( (char *)str, name ) )
	    return TRUE;
    }
}

bool is_name_short( const char *str, char *namelist )
{
    char name[MAX_INPUT_LENGTH];
    for ( ; ; )
    {
	namelist = one_argument( namelist, name );
	name[strlen(str)]='\0';
	if ( *name == '\0' || *(name+1)=='\0')
	    return FALSE;
	if ( !strcasecmp( (char *)str, name ) )
	    return TRUE;
    }
}

bool is_object_reference( OBJ_DATA *obj, char *argument )
  {
  int number;
    if( *argument=='#'  &&
        ( *(argument+1)>='0' && *(argument+1)<='9' ) &&
        ( *(argument+2)>='0' && *(argument+2)<='9' ) &&
        ( *(argument+3)>='0' && *(argument+3)<='9' ) )
      {
      number = (*(argument+1)-'0')*100 + (*(argument+2)-'0')*10 + 
               (*(argument+3)-'0');
          if( !obj->basic || obj->item_type==ITEM_ARMOR ||
                obj->item_type==ITEM_CONTAINER || obj->item_type==ITEM_WEAPON )
	    if( number==obj->index_reference[0]%1000 )
	      return TRUE;
      }

      return( FALSE );
      }


bool is_short_of_name( const char *str, char *namelist )
{
    char name[MAX_INPUT_LENGTH];
    char str2[MAX_INPUT_LENGTH];

    for ( ; ; )
    {
        strcpy(str2,str);
	namelist = one_argument( namelist, name );
	if ( *name == '\0' || *(name+1)=='\0')
	    return FALSE;
	str2[strlen(name)]='\0';
	if ( !strcasecmp( str2, name ) )
	    return TRUE;
    }
}

bool is_short_of_name_reverse( const char *str, char *namelist )
{
    char name[MAX_INPUT_LENGTH];
    char *str2,*strEnd;

    strEnd=(char *)str+strlen(str);
    for ( ; ; )
    {
	namelist = one_argument( namelist, name );
	if ( *name == '\0' || *(name+1)=='\0')
	    return FALSE;
        str2=strEnd-strlen(name);
	if ( !strcasecmp( str2, name ) )
	    return TRUE;
    }
}

/*
 * Apply or remove an affect to a character.
 */
void affect_modify( CHAR_DATA *ch, AFFECT_DATA *paf, bool fAdd )
  {
  /*OBJ_DATA *wield;*/
  int mod;


  if( paf == NULL )
    return;

  mod = paf->modifier;


  if ( fAdd )
    {
       if( paf->bitvector < 0 )
	  SET_BIT( ch->affected2_by, 0-paf->bitvector );
       else
	  SET_BIT( ch->affected_by, paf->bitvector );
    }
  else
    {
       if( paf->bitvector < 0 )
	  REMOVE_BIT( ch->affected2_by, 0-paf->bitvector );
       else
	  REMOVE_BIT( ch->affected_by, paf->bitvector );
	  mod = 0 - mod;
    }

  if( IS_SET( paf->bitvector, AFF_HASTE )  && paf->bitvector > 0 )
    ch->speed = get_max_speed( ch );

  /*if ( IS_NPC(ch) )
	  return;
*/
  switch ( paf->location )
    {
    default:
	  bug( "Affect_modify: unknown location %d.", paf->location );
	  return;

    case APPLY_NONE:                                            break;
    case APPLY_STR:           if (!IS_NPC(ch)) ch->pcdata->mod_str       += mod; break;
    case APPLY_DEX:           if (!IS_NPC(ch)) ch->pcdata->mod_dex       += mod; break;
    case APPLY_INT:           if (!IS_NPC(ch)) ch->pcdata->mod_int       += mod; break;
    case APPLY_WIS:           if (!IS_NPC(ch)) ch->pcdata->mod_wis       += mod; break;
    case APPLY_CON:           if (!IS_NPC(ch)) ch->pcdata->mod_con       += mod; break;
    case APPLY_SEX:           ch->sex                   += mod;
	    if( ch->sex>2)
	      ch->sex=0;
	    if( ch->sex<0)
	      ch->sex=2;
		break;
    case APPLY_CLASS:                                           break;
    case APPLY_LEVEL:                                           break;
    case APPLY_AGE:                                               break;
    case APPLY_HEIGHT:                                  break;
    case APPLY_WEIGHT:                                  break;
    case APPLY_MANA:          ch->max_mana              += mod; 
			      if(ch->max_mana<1)
				{
				ch->max_mana=1;
				break;
				}
			      if(ch->max_mana<ch->mana)
				ch->mana=ch->max_mana;
			      break;
    case APPLY_HIT:           ch->max_hit               += mod;
			      if(ch->max_hit<1)
				{
				ch->max_hit=1;
				break;
				}
			      if(ch->max_hit<ch->hit)
				ch->hit=ch->max_hit;
			      break;
    case APPLY_MOVE:          ch->max_move              += mod; break;
			      if(ch->max_move<1)
				{
				ch->max_move=1;
				break;
				}
			      if(ch->max_move<ch->move)
				ch->move=ch->max_move;
			      break;
    case APPLY_GOLD:                                            break;
    case APPLY_EXP:                                               break;
    case APPLY_AC:            ch->armor                 += mod; break;
    case APPLY_HITROLL:       ch->hitroll               += mod; 
                              if( equipment_affecting && !IS_NPC( ch ))
                                ch->pcdata->eqhitroll += mod;
                              break;
    case APPLY_DAMROLL:       if (IS_NPC(ch))
			       ch->npcdata->damplus      += mod; 
		   	      else
			       ch->damroll               += mod; 
                              if( equipment_affecting && !IS_NPC( ch ))
                                ch->pcdata->eqdamroll += mod;
                              break;
    case APPLY_SAVING_PARA:   
    case APPLY_SAVING_ROD:    
    case APPLY_SAVING_PETRI:  
    case APPLY_SAVING_BREATH: 
    case APPLY_SAVING_SPELL:  ch->saving_throw          += mod;
                              if( equipment_affecting && !IS_NPC( ch ))
                                ch->pcdata->eqsaves += mod;
                              break;
    }

    /*
     * Check for weapon wielding.
     * Guard against recursion (for weapons with affects).

       This code was crashing, so let them wield heavy weapons, till drop.

  if ( ( wield = get_eq_char( ch, WEAR_WIELD ) ) != NULL
    &&   get_obj_weight(wield) > str_app[get_curr_str(ch)].wield )
    {
	  static int depth;

	  if ( depth == 0 )
	    {
	    depth++;
	    act( "You drop $p.", ch, wield, NULL, TO_CHAR );
	    act( "$n drops $p.", ch, wield, NULL, TO_ROOM );
	    obj_from_char( wield );
      wield->sac_timer=OBJ_SAC_TIME;
	    obj_to_room( wield, ch->in_room );
	    depth--;
	    }
    } */

  return;
  }



/*
 * Give an affect to a char.
 */
void affect_to_char( CHAR_DATA *ch, AFFECT_DATA *paf )
{
  AFFECT_DATA *paf_new;

  CREATE(paf_new, AFFECT_DATA, 1);
  LINK( paf_new, ch->first_affect, ch->last_affect, next, prev );

  paf_new->type       = paf->type;
  paf_new->duration   = paf->duration;
  paf_new->location   = paf->location;
  paf_new->modifier   = paf->modifier;
  paf_new->bitvector  = paf->bitvector;

  affect_modify( ch, paf_new, TRUE );
  return;
}


/*
 * Remove an affect from a char.
 */
void affect_remove( CHAR_DATA *ch, AFFECT_DATA *paf )
{
  AFFECT_DATA *tpaf;
  bool found;

  for( found=FALSE, tpaf=ch->first_affect; tpaf!=NULL; tpaf=tpaf->next)
    if( tpaf==paf )
      found=TRUE;
  if ( !found )
    {
        bug( "UNLINK ERROR Affect_remove(%s, %d): no affect.", ch->name,
                paf ? paf->type : 0 );
    return;
    }

  affect_modify( ch, paf, FALSE );
  UNLINK( paf, ch->first_affect, ch->last_affect, next, prev );
  DISPOSE( paf );
  return;
}



/*
 * Strip all affects of a given sn.
 */
void affect_strip( CHAR_DATA *ch, int sn )
{
    AFFECT_DATA *paf;
    AFFECT_DATA *paf_next;

    for ( paf = ch->first_affect; paf != NULL; paf = paf_next )
    {
	paf_next = paf->next;

             /*  Kill of charmed mobiles */
        if( paf->type == gsn_charm_person && IS_NPC( ch ) &&
            !IS_SET( ch->act, ACT_PET ) )
          SET_BIT( ch->act, ACT_WILL_DIE );

	if ( paf->type == sn )
	    affect_remove( ch, paf );
    }

    return;
}



/*
 * Return true if a char is first_affect by a spell.
 */
bool is_affected( CHAR_DATA *ch, int sn )
{
  AFFECT_DATA *paf;

  if( ch==NULL )
    return( FALSE );

  for ( paf = ch->first_affect; paf != NULL; paf = paf->next )
      if ( paf->type == sn )
          return TRUE;

  return FALSE;
}

bool is_affected_external( CHAR_DATA *ch, int sn )
{
  AFFECT_DATA *paf;

  sn = slot_lookup( sn );
  for ( paf = ch->first_affect; paf != NULL; paf = paf->next )
      if ( paf->type == sn )
          return TRUE;

  return FALSE;
}


/*
 * Add or enhance an affect.
 */
void affect_join( CHAR_DATA *ch, AFFECT_DATA *paf )
{
    AFFECT_DATA *paf_old;
    bool found;

    found = FALSE;
    for ( paf_old = ch->first_affect; paf_old != NULL; paf_old = paf_old->next )
    {
	if ( paf_old->type == paf->type )
	{
	    paf->duration += paf_old->duration;
	    paf->modifier += paf_old->modifier;
	    affect_remove( ch, paf_old );
	    break;
	}
    }

    affect_to_char( ch, paf );
    return;
}



/*
 * Move a char out of a room.
 */
void char_from_room( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    CHAR_DATA *tch;
    bool found;

    if ( ch->in_room == NULL )
    {
	bug( "Char_from_room: NULL.", 0 );
	return;
    }

    if( ch == ch->in_room->sanctify_char )
      {
      CHAR_DATA *fch;
      REMOVE_BIT( ch->in_room->room_flags, ROOM_SAFE );
      ch->in_room->sanctify_timer = 0;
      ch->in_room->sanctify_char = NULL;
      for( fch=ch->in_room->first_person; fch!=NULL; fch=fch->next_in_room )
        send_to_char( "The area does not look safe now.\n\r", fch );
      }

    if( aggr_ch_next == ch )
      aggr_ch_next = ch->next_in_room;
    if( violence_rch_next == ch )
      violence_rch_next = ch->next_in_room;

  if(IS_AFFECTED(ch,AFF2_CAMPING))
    {
    send_to_char( "You break camp.\n\r", ch );
    act( "$n breaks camp.", ch, NULL, NULL, TO_ROOM );
    REMOVE_BIT(ch->affected2_by,0-AFF2_CAMPING);
    }


    if ( !IS_NPC(ch) )
	--ch->in_room->area->nplayer;

    if ( ( obj = get_eq_char( ch, WEAR_LIGHT ) ) != NULL
    &&   obj->item_type == ITEM_LIGHT
    &&   obj->value[2] != 0
    &&   ch->in_room->light > 0 )
	--ch->in_room->light;

    for( found=FALSE, tch=ch->in_room->first_person; tch!=NULL;
        tch=tch->next_in_room )
      if( tch==ch )
        found=TRUE;
    /* if( found )
      bug( "Not found in room on removal, %s", ch->name ); */
    if( found )
     {
#ifdef UNLINKCHECK
     CHAR_DATA *tch;
     bool foundc;

      for( foundc=FALSE,tch=ch->in_room->first_person; tch!=NULL; 
        tch=tch->next_in_room )
        if( tch==ch )
          {
          foundc=TRUE;
          break;
          }
    if( foundc )
     UNLINK( ch, ch->in_room->first_person, ch->in_room->last_person,
                next_in_room, prev_in_room );
    /*else
      bug( "UNLINK ERROR character %s not found in room.", ch->name ); */
#else
     UNLINK( ch, ch->in_room->first_person, ch->in_room->last_person,
                next_in_room, prev_in_room );
#endif
     }

    ch->in_room      = NULL;
    ch->next_in_room = NULL;
    ch->prev_in_room = NULL;
    return;
}



/*
 * Move a char into a room.
 */
void char_to_room( CHAR_DATA *ch, ROOM_INDEX_DATA *pRoomIndex )
{
    OBJ_DATA *obj;

    if ( pRoomIndex == NULL )
    {
	bug( "Char_to_room: NULL.", 0 );
	pRoomIndex=room_index[ ROOM_VNUM_TEMPLE ];
    }

    ch->in_room         = pRoomIndex;

    LINK( ch, pRoomIndex->first_person, pRoomIndex->last_person,
              next_in_room, prev_in_room );

    if ( !IS_NPC(ch) )
	++ch->in_room->area->nplayer;

    if ( ( obj = get_eq_char( ch, WEAR_LIGHT ) ) != NULL
    &&   obj->item_type == ITEM_LIGHT
    &&   obj->value[2] != 0 )
	++ch->in_room->light;

    if( !IS_NPC( ch ) && ch->pcdata->bet_mode == 2 &&
	ch->in_room->area->low_r_vnum != ROOM_VNUM_ARENA )
	do_resign( ch, NULL );


  if( !IS_NPC( ch ) && ch->desc != NULL && ch->desc->character == ch )
    if( ch->pcdata->tactical != NULL )
     if( IS_SET( ch->act, PLR_PROMPT))
      vt100prompt( ch );

  if( !IS_NPC( ch ) && ch->desc!=NULL &&
      ch->desc->character==ch &&
      !IS_SET( ch->pcdata->player2_bits, PLR2_EXTERNAL_FILES ) &&
      IS_SET( ch->act, PLR_TERMINAL ) )
    {
    /* Send notice of file */
    char buf[200];

    if( ch->in_room->room_file != NULL )
      sprintf( buf, "%c%c%s%c", 30, 'E', ch->in_room->room_file, 29);
    else
      sprintf( buf, "%c%c", 30, 'S');
    write_to_port( ch->desc );
    write_to_buffer( ch->desc, buf, 1000000 );
    }
    
    return;
}



/*
 * Give an obj to a char.
 */
void obj_to_char( OBJ_DATA *obj, CHAR_DATA *ch )
{
  OBJ_DATA *otmp=NULL;

    if( obj->in_room != NULL )
      obj_from_room( obj );
    if( obj->in_obj != NULL )
      obj_from_obj( obj );
    if( obj->carried_by != NULL )
      obj_from_char( obj );

    if( !IS_NPC( ch ) )
      {
      obj->reset = NULL;
 	LINK( obj, ch->first_carrying, ch->last_carrying,
                next_content, prev_content );
      }
    else
      {
      if( ch->first_carrying == NULL || obj->level < ch->first_carrying->level )
        {
 	    LINK( obj, ch->first_carrying, ch->last_carrying,
                next_content, prev_content );
            obj->carried_by                 = ch;
            obj->in_room                    = NULL;
            obj->in_obj                     = NULL;

        }
      else /* If ch is a shopkeeper, add the obj using an insert sort */
 	 {
            for ( otmp = ch->first_carrying; otmp; otmp = otmp->next_content)
            {
                if ( obj->level > otmp->level )
                {
                    INSERT(obj, otmp, ch->first_carrying,
                        next_content, prev_content);
                    break;
                }
                else
                if ( obj->level == otmp->level
                &&   strcmp(obj->short_descr,otmp->short_descr) < 0 )
                {
                    INSERT(obj, otmp, ch->first_carrying,
                        next_content, prev_content);
                    break;
                }
            }
 	    if ( !otmp)
            {
                LINK(obj, ch->first_carrying, ch->last_carrying,
                    next_content, prev_content);
            }
	}
            obj->carried_by = ch;
            obj->in_room = NULL;
            obj->in_obj = NULL;
        }

    ch->carry_number    += get_obj_number( obj );
    ch->carry_weight    += get_obj_weight( obj );
    
    obj_set_to_char( ch, obj );
}

void  obj_set_to_char( CHAR_DATA *ch, OBJ_DATA *obj )
  {
  OBJ_DATA *c_obj;
  obj->carried_by = ch;

  for( c_obj = obj->first_content; c_obj != NULL; c_obj=c_obj->next_content )
    obj_set_to_char( ch, c_obj);

 if( ch!= NULL && !IS_NPC( ch ) && obj->in_obj==NULL)
  if( obj->pIndexData->obj_prog != NULL || obj->pIndexData->obj_fun!=NULL)
    {
    OBJ_WITH_PROG *with=NULL, *pwith=NULL;
        /* Let's attempt to add this at the end, to possibly fix a mem prob */
        /*   Chaos   10/24/95   */
    for( with=ch->obj_with_prog; with != NULL; with = with->next )
	pwith=with;

    CREATE(with, OBJ_WITH_PROG, 1);
    with->obj = obj;
    with->next = NULL;
    if( pwith == NULL )
     ch->obj_with_prog = with;
    else
     pwith->next = with;
    }

  /* Auto engrave stuff */
 if( ch != NULL )
  if( IS_SET(obj->pIndexData->extra_flags, ITEM_AUTO_ENGRAVE) &&
      obj->owned_by<1 && !IS_NPC(ch) && ch->level < MAX_LEVEL)
        {
        obj->owned_by = ch->pcdata->pvnum;
        obj->basic = FALSE;
        }

  return;
  }                                      

/*
 * Take an obj from its character.
 */
void obj_from_char( OBJ_DATA *obj )
{
    CHAR_DATA *ch;
    char buf[200];

  if( obj == NULL )
    return;

    if( obj->in_obj != NULL )
      {
      obj_from_obj( obj );
      return;
      }

    if( obj->in_room != NULL )
      {
      obj_from_room( obj );
      return;
      }

    if ( ( ch = obj->carried_by ) == NULL )
    {
	bug( "Obj_from_char: null ch.", 0 );
	return;
    }

    if ( obj->wear_loc != WEAR_NONE )
	unequip_char( ch, obj );
#ifdef UNLINKCHECK
    {
    bool foundo;
    OBJ_DATA *tobj;

    for(tobj=ch->first_carrying, foundo=FALSE; tobj!=NULL;
            tobj=tobj->next_content )
      if( tobj==obj )
        {
        foundo=TRUE;
        break;
        }
    if( foundo )
      UNLINK( obj, ch->first_carrying, ch->last_carrying,
              next_content, prev_content );
    else
        bug( "UNLINK ERROR object %s not carried by %s", obj->name,
              ch->name );
    }
#else
    UNLINK( obj, ch->first_carrying, ch->last_carrying, next_content, prev_content );
#endif

    obj_set_to_char( NULL, obj);
    obj->next_content    = NULL;
    ch->carry_number    -= get_obj_number( obj );
    ch->carry_weight    -= get_obj_weight( obj );


       /* Let's make the item update when removed  */
    if( obj->first_content != NULL )
      {
      OBJ_DATA *c_obj;

      for( c_obj = obj->first_content; c_obj != NULL; c_obj=c_obj->next_content )
        if( c_obj->reset != NULL )
          {
          c_obj->reset->obj = NULL;
          c_obj->reset = NULL;
          }
      }

    if( obj->reset != NULL && obj->reset->obj == obj )
      {
      obj->reset->obj = NULL;
      obj->reset = NULL;
      }  

 if( ch!=NULL && !IS_NPC( ch ))
  if( obj->pIndexData->obj_prog != NULL || obj->pIndexData->obj_fun!=NULL)
    {
    OBJ_WITH_PROG *with, *prev;
    bool found_it;
    prev = NULL;
    found_it = FALSE;
    if(ch->obj_with_prog!=NULL && ch->obj_with_prog->obj == obj )
         {
         with = ch->obj_with_prog;
         ch->obj_with_prog = with->next;
	 DISPOSE( with );
         found_it = TRUE;
         }
    else
    for( with = ch->obj_with_prog; with!=NULL; prev=with, with = with->next)
       if( with->obj == obj)
	 {
	 prev->next = with->next;
	 DISPOSE( with );
         found_it = TRUE;
	 break;
	 }
    if( !found_it )
      {
      sprintf( buf, "Found obj(%s) with prog not referenced on char(%s)",
          obj->name, ch->name );
      log_string( buf ); 
      }
    }
    obj->in_room         = NULL;
    obj->carried_by    = NULL;
    return;
}



/*
 * Find the ac value of an obj, including position effect.
 */
int apply_ac( OBJ_DATA *obj, int iWear )
{
    if ( obj->item_type != ITEM_ARMOR )
	    return 0;

    switch ( iWear )
    {
    case WEAR_BODY:
       if(IS_NPC(obj->carried_by) )
	      return 3 * obj->value[0];
	 if (obj->carried_by->pcdata->learned[gsn_armor_usage]<=0)
            {
	      return 3 * obj->value[0];
            }
        
       return(int)((((3.0*obj->carried_by->pcdata->learned[gsn_armor_usage])/
	       100)+3)*obj->value[0]);

    case WEAR_HEAD:     
       if( IS_NPC( obj->carried_by ) )
         return 2 * obj->value[0];
       if( obj->carried_by->class == 0 || obj->carried_by->class == 2 )
         return 3 * obj->value[0] / 2;
       if( obj->carried_by->class == 6 )
         return obj->value[0];
       return 2 * obj->value[0];

    case WEAR_LEGS:
       if( IS_NPC( obj->carried_by ) )
         return 2 * obj->value[0];
       if( obj->carried_by->class == 5 )
         return obj->value[0];
       return 2 * obj->value[0];

    case WEAR_FEET:     return     obj->value[0];
    case WEAR_HANDS:    return     obj->value[0];
       if( IS_NPC( obj->carried_by ) )
         return obj->value[0];
       if( obj->carried_by->class == 4 )
         return obj->value[0] / 2;
       return obj->value[0];

    case WEAR_ARMS:     return     obj->value[0];
       if( IS_NPC( obj->carried_by ) )
         return obj->value[0];
       if( obj->carried_by->class == 3 )
         return 3 * obj->value[0] / 2;
       if( obj->carried_by->class == 5 )
         return 4 * obj->value[0] / 5;
       if( obj->carried_by->class == 2 )
         return obj->value[0] / 2;
       if( obj->carried_by->class == 4 )
         return 0;
       return obj->value[0];

    case WEAR_SHIELD: 
       if( IS_NPC( obj->carried_by ) )
         return obj->value[0];
       if( obj->carried_by->class == 3 )
         return 3 * obj->value[0] / 2;
       if( obj->carried_by->class == 2 || obj->carried_by->class == 5 )
         return obj->value[0] / 2;
       if( obj->carried_by->class == 0 || obj->carried_by->class == 6 ||
           obj->carried_by->class == 4)
         return 0;
       return obj->value[0];

    case WEAR_FINGER_L: return     obj->value[0];
    case WEAR_FINGER_R: return     obj->value[0];
    case WEAR_NECK_1:   return     obj->value[0];
    case WEAR_NECK_2:   return     obj->value[0];
    case WEAR_ABOUT:    return 2 * obj->value[0];
    case WEAR_WAIST:    return     obj->value[0];
    case WEAR_WRIST_L:  return     obj->value[0];
    case WEAR_WRIST_R:  return     obj->value[0];
    case WEAR_HOLD:     return     obj->value[0];
    case WEAR_HEART:     return     obj->value[0];
    }

    return 0;
}



/*
 * Find a piece of eq on a character.
 */
OBJ_DATA *get_eq_char( CHAR_DATA *ch, int iWear )
{
    OBJ_DATA *obj;
    for ( obj = ch->first_carrying; obj != NULL; obj = obj->next_content )
    {
	if ( obj->wear_loc == iWear )
	    return obj;
    }
    return NULL;
}

  /* Determine is an object has an extra affect.    -1 is any apply type */
int count_obj_affected( OBJ_DATA *obj, int iAffect, int iLocation )
  {
  AFFECT_DATA *paf;
  int cnt;

  cnt = 0;

  for ( paf = obj->first_affect; paf != NULL; paf = paf->next )
    if( paf->type == iAffect && ( iLocation==-1 || paf->location==iLocation ))
      cnt++;

  return( cnt );
  }


/*
 * Equip a char with an obj.
 */
void equip_char( CHAR_DATA *ch, OBJ_DATA *obj, int iWear )
  {
  AFFECT_DATA *paf;
  OBJ_PROG *prg;

  if ( get_eq_char( ch, iWear ) != NULL )
    {
	  bug( "Equip_char: already equipped (vnum %u).", 
        IS_NPC(ch) ? ch->pIndexData->vnum : 0  );
	  return;
    }

  if ( ( IS_OBJ_STAT(obj, ITEM_ANTI_EVIL)    && IS_EVIL(ch)    )
    ||   ( IS_OBJ_STAT(obj, ITEM_ANTI_GOOD)    && IS_GOOD(ch)    )
    ||   ( IS_OBJ_STAT(obj, ITEM_ANTI_NEUTRAL) && IS_NEUTRAL(ch) ) )
    {
	  act( "You are zapped by $p.", ch, obj, NULL, TO_CHAR );
	  act( "$n is zapped by $p.",  ch, obj, NULL, TO_ROOM );
	  return;
    }

  ch->armor     -= apply_ac( obj, iWear );
  obj->wear_loc  = iWear;

  equipment_affecting = TRUE;
  for ( paf = obj->pIndexData->first_affect; paf != NULL; paf = paf->next )
	  affect_modify( ch, paf, TRUE );
  for ( paf = obj->first_affect; paf != NULL; paf = paf->next )
	  affect_modify( ch, paf, TRUE );
  equipment_affecting = FALSE;

  if ( obj->item_type == ITEM_LIGHT
    &&   obj->value[2] != 0
    &&   ch->in_room != NULL )
	  ++ch->in_room->light;


      if( ch->obj_prog_ip==0 && obj->pIndexData->obj_prog!=NULL)
        for( prg = obj->pIndexData->obj_prog; prg!=NULL ; prg=prg->next)
          if( prg->cmd == -7 && number_percent() < prg->percentage )
            {
            open_timer( TIMER_OBJ_PROG );
            start_object_program( ch, obj, prg, "");
            close_timer( TIMER_OBJ_PROG );
            tail_chain();
            }
  return;
  }



/*
 * Unequip a char with an obj.
 */
void unequip_char( CHAR_DATA *ch, OBJ_DATA *obj )
  {
  AFFECT_DATA *paf;
  OBJ_PROG *prg;

  if ( obj->wear_loc == WEAR_NONE )
    {
	  bug( "Unequip_char: already unequipped.", 0 );
	  return;
    }

  ch->armor             += apply_ac( obj, obj->wear_loc );
  obj->wear_loc  = -1;

  equipment_affecting = TRUE;
  for ( paf = obj->pIndexData->first_affect; paf != NULL; paf = paf->next )
	  affect_modify( ch, paf, FALSE );
  for ( paf = obj->first_affect; paf != NULL; paf = paf->next )
	  affect_modify( ch, paf, FALSE );
  equipment_affecting = FALSE;

  if ( obj->item_type == ITEM_LIGHT
    &&   obj->value[2] != 0
    &&   ch->in_room != NULL
    &&   ch->in_room->light > 0 )
	  --ch->in_room->light;

      if( ch->obj_prog_ip==0 && obj->pIndexData->obj_prog!=NULL)
        for( prg = obj->pIndexData->obj_prog; prg!=NULL ; prg=prg->next)
          if( prg->cmd == -8 && number_percent() < prg->percentage )
            {
            open_timer( TIMER_OBJ_PROG );
            start_object_program( ch, obj, prg, "");
            close_timer( TIMER_OBJ_PROG );
            tail_chain();
            }
  return;
  }



/*
 * Count occurrences of an obj in a list.
 */
int count_obj_list( OBJ_INDEX_DATA *pObjIndex, OBJ_DATA *list )
{
    OBJ_DATA *obj;
    int nMatch;

    nMatch = 0;
    if(list==NULL)
      nMatch = pObjIndex->total_objects;
     /*
      for ( obj = first_object; obj != NULL; obj = obj->next )
	{
	      if ( obj->pIndexData == pObjIndex )
		nMatch++;
	}
      */
    else
      for ( obj = list; obj != NULL; obj = obj->next_content )
	{
	      if ( obj->pIndexData == pObjIndex )
		nMatch++;
	if ( obj->item_type == ITEM_CONTAINER &&
	     obj->first_content  != NULL )
	  nMatch+=count_obj_list(pObjIndex,obj->first_content);
	}
      

    return nMatch;
}

int count_mob_list( MOB_INDEX_DATA *pMobIndex, OBJ_DATA *list )
{
    CHAR_DATA *mob;
    int nMatch;

    nMatch = 0;
  if(list==NULL)
    for ( mob = first_char; mob != NULL; mob = mob->next )
	    if ( IS_NPC(mob) && mob->pIndexData == pMobIndex )
	      nMatch++;
    return nMatch;
}



/*
 * Move an obj out of a room.
 */
void obj_from_room( OBJ_DATA *obj )
{
    ROOM_INDEX_DATA *in_room;
   
    if ( ( in_room = obj->in_room ) == NULL )
    {
	bug( "obj_from_room: NULL room.", 0);
	return;
    }
#ifdef UNLINKCHECK
    {
    bool foundo;
    OBJ_DATA *tobj;

    for(tobj=in_room->first_content, foundo=FALSE; tobj!=NULL;
            tobj=tobj->next_content )
      if( tobj==obj )
        {
        foundo=TRUE;
        break;
        }
    if( foundo )
      UNLINK( obj, in_room->first_content, in_room->last_content,
        next_content, prev_content );
    else
      bug( "UNLINK ERROR object %s not found in room.", obj->name );
    }
#else
    UNLINK( obj, in_room->first_content, in_room->last_content,
        next_content, prev_content );
#endif
       /* Let's make the item update when removed  */
    if( obj->first_content != NULL )
      {
      OBJ_DATA *c_obj;

      for( c_obj = obj->first_content; c_obj != NULL; c_obj=c_obj->next_content )
        if( c_obj->reset != NULL )
          {
          c_obj->reset->obj = NULL;
          c_obj->reset = NULL;
          }
      }
    if( obj->reset != NULL && obj->reset->obj == obj )
      {
      obj->reset->obj = NULL;
      obj->reset = NULL;
      } 

    in_room->content_count--;
    obj->in_room      = NULL;
    obj->in_obj       = NULL;
    obj->carried_by   = NULL;
    obj->next_content   = NULL;
    return;
}



/*
 * Move an obj into a room.
 */
void obj_to_room( OBJ_DATA *obj, ROOM_INDEX_DATA *pRoomIndex )
{
    if( obj->in_room != NULL )
      obj_from_room( obj );
    if( obj->in_obj != NULL )
      obj_from_obj( obj );
    if( obj->carried_by != NULL )
      obj_from_char( obj );

    if( pRoomIndex==NULL || pRoomIndex->content_count >= MAX_OBJECTS_IN_ROOM )
      {
      extract_obj( obj );
      return;
      }

    LINK( obj, pRoomIndex->first_content, pRoomIndex->last_content,
               next_content, prev_content );
    obj->in_room                                = pRoomIndex;
    obj->carried_by                             = NULL;
    obj->in_obj                                 = NULL;

    pRoomIndex->content_count++;
    return;
}



/*
 * Move an object into an object.
 */
void obj_to_obj( OBJ_DATA *obj, OBJ_DATA *obj_to )
{
    CHAR_DATA *who;

    /* Think there's a null object reference crashing the game.  -Presto */
    if(obj == NULL || obj_to == NULL)
    {
      bug("Null object reference in function OBJ_TO_OBJ.");
      return;
    }

    if( obj->in_room != NULL )
      obj_from_room( obj );
    if( obj->in_obj != NULL )
      obj_from_obj( obj );
    if( obj->carried_by != NULL )
      obj_from_char( obj );

    if ( (who=obj_to->carried_by) != NULL )
        who->carry_weight += get_obj_weight(obj);

    LINK( obj, obj_to->first_content, obj_to->last_content,
        next_content, prev_content );

    obj->in_obj                          = obj_to;
    obj->in_room                         = NULL;
    obj->carried_by                      = NULL;
    return;
}

/*
 * Move an object out of an object.
 */
void obj_from_obj( OBJ_DATA *obj )
{
    OBJ_DATA *obj_from;
    char buf[MAX_STRING_LENGTH];

    if ( ( obj_from = obj->in_obj ) == NULL )
    {
	sprintf( buf, "Obj_from_obj: null obj_from  item: %s", obj->name);
	bug( buf, 0 );
	return;
    }
#ifdef UNLINKCHECK
    {
    bool foundo;
    OBJ_DATA *tobj;

    for(tobj=obj_from->first_content, foundo=FALSE; tobj!=NULL;
            tobj=tobj->next_content )
      if( tobj==obj )
        {
        foundo=TRUE;
        break;
        }
    if( foundo )
      UNLINK( obj, obj_from->first_content, obj_from->last_content,
        next_content, prev_content );
    else
      bug( "UNLINK ERROR object %s not found in object %s.",
            obj->name, obj_from->name );
    }
#else
      UNLINK( obj, obj_from->first_content, obj_from->last_content,
        next_content, prev_content );
#endif
    obj->in_obj       = NULL;
    obj->in_room      = NULL;
    obj->carried_by   = NULL;

    for ( ; obj_from; obj_from = obj_from->in_obj )
     if ( obj_from->carried_by )
      obj_from->carried_by->carry_weight -= get_obj_weight( obj );

       /* Let's make the item update when removed  */
    if( obj->first_content != NULL )
      {
      OBJ_DATA *c_obj;

      for( c_obj = obj->first_content; c_obj != NULL; c_obj=c_obj->next_content )
        if( c_obj->reset != NULL )
          {
          c_obj->reset->obj = NULL;
          c_obj->reset = NULL;
          c_obj->carried_by = NULL;
          }
      }

    if( obj->reset != NULL && obj->reset->obj == obj )
      {
      obj->reset->obj = NULL;
      obj->reset = NULL;
      } 

    return;
}



/*
 * Extract an obj from the world.
 */
void extract_obj( OBJ_DATA *obj )
{
    OBJ_DATA *obj_content;
    OBJ_DATA *obj_next;
    MOB_PACKET *packet;
    int cnt;

    if( obj==NULL )
      return;

    for ( cnt=0; cnt<2 && obj->in_room != NULL ; cnt++)
	obj_from_room( obj );

    for ( cnt=0; cnt<2 && obj->in_obj != NULL ; cnt++)
	  obj_from_obj( obj );

    for ( cnt=0; cnt<2 && obj->carried_by != NULL ; cnt++)
	  obj_from_char( obj );

    for ( obj_content = obj->first_content; obj_content; obj_content = obj_next )
    {
	obj_next = obj_content->next_content;
	extract_obj( obj_content );
    }
    
    remove_from_object_reference_hash( obj );
#ifdef UNLINKCHECK
    {
    bool foundo;
    OBJ_DATA *tobj;

    for(tobj=first_object, foundo=FALSE; tobj!=NULL;
            tobj=tobj->next )
      if( tobj==obj )
        {
        foundo=TRUE;
        break;
        }
    if( foundo )
      UNLINK( obj, first_object, last_object, next, prev );
    else
      bug( "UNLINK ERROR object %s not in first_object list.", obj->name);
    }
#else
    UNLINK( obj, first_object, last_object, next, prev );
#endif

  for( packet = mob_packet_list; packet != NULL; packet = packet->next )
    {
    if( packet->obj == obj )
      packet->obj = NULL;
    if( packet->vo == obj )
      packet->vo = NULL;
    }

    /* remove affects */
    {
        AFFECT_DATA *paf;
        AFFECT_DATA *paf_next;

        for ( paf = obj->first_affect; paf; paf = paf_next )
        {
            paf_next    = paf->next;
            DISPOSE( paf );
        }
        obj->first_affect = obj->last_affect = NULL;
    }
    /* remove extra descriptions */
    {
        EXTRA_DESCR_DATA *ed;
        EXTRA_DESCR_DATA *ed_next;

        for ( ed = obj->first_extradesc; ed; ed = ed_next )
        {
            ed_next = ed->next;
            STRFREE( ed->description );
            STRFREE( ed->keyword     );
            DISPOSE( ed );
        }
        obj->first_extradesc = obj->last_extradesc = NULL;
    }

    if( obj->reset != NULL )
      {
      obj->reset->obj = NULL;
      obj->reset = NULL;
      }  

    if( obj->poison != NULL )
      {
      DISPOSE( obj->poison );
      obj->poison = NULL ;
      }
    STRFREE( obj->name        );
    STRFREE( obj->description );
    STRFREE( obj->short_descr );
    STRFREE( obj->long_descr );
    if( obj->obj_quest != NULL )
      DISPOSE( obj->obj_quest );
    if( obj->pIndexData != NULL )
      {
      --obj->pIndexData->count;
      --obj->pIndexData->total_objects;  /* Total for resets */
      }
    total_objects--;
    DISPOSE( obj);
    return;
}
extern int top_exit;

void extract_exit ( ROOM_INDEX_DATA *room, EXIT_DATA * pexit, int door )
{
    if (pexit->keyword != NULL )
      STRFREE (pexit->keyword );
    STRFREE (pexit->description );
    DISPOSE( pexit );
    room_index[ room->vnum ]->exit[door]=NULL;
    top_exit--;
}

/*
 * Extract a char from the world.
 */
void extract_char( CHAR_DATA *ch, bool fPull )
  {
  CHAR_DATA *wch, *old_ch;
  OBJ_DATA *obj;
  OBJ_DATA *obj_next;
  MOB_PACKET *packet;
  bool ARENA_DEATH;

  if(ch==NULL)
    {
    bug("extract_char: NULL ch!",0);
    return;
    }

  if( !IS_NPC( ch ))
    if( ch->pcdata->bet_mode == 2)
      do_resign( ch, NULL );

  if ( fPull )
    {
    die_follower( ch );
    }
  /*if (!IS_NPC(ch) && ch->pcdata->beingsaved!=NULL)
  {
   pthread_join(ch->pcdata->beingsaved, NULL);
   ch->pcdata->beingsaved = NULL;
  } */

  die_shadow(ch);
  if(ch->shadowed_by!=NULL)
    die_shadow(ch->shadowed_by);

  stop_fighting( ch, FALSE );

    if( aggr_ch_next == ch )
      aggr_ch_next = ch->next_in_room;
    if( aggr_wch_next == ch )
      aggr_wch_next = ch->next;
    if( violence_ch_next == ch )
      violence_ch_next = ch->next;
    if( violence_rch_next == ch )
      violence_rch_next = ch->next_in_room;

  for ( obj = ch->first_carrying; obj != NULL; obj = obj_next )
    {
    obj_next = obj->next_content;
    if( fPull || !IS_SET(obj->extra_flags, ITEM_INVENTORY))
      extract_obj( obj );
    }
    
  ARENA_DEATH = FALSE;
  if ( ch->in_room != NULL )
    {
    if( ch->in_room->area->low_r_vnum == ROOM_VNUM_ARENA )
      ARENA_DEATH = TRUE;
    char_from_room( ch );
    }
  ch->was_in_room = NULL;

  if( (IS_NPC(ch) && ch->npcdata->poison!=NULL) ||
      (!IS_NPC(ch) && ch->pcdata->poison!=NULL) )
    {
    POISON_DATA *npd, *pd;
    if( !IS_NPC(ch) )
      {
      pd = ch->pcdata->poison;
      ch->pcdata->poison=NULL;
      }
    else
      {
      pd = ch->npcdata->poison;
      ch->npcdata->poison=NULL;
      }

    while( pd!=NULL )
      {
      npd = pd->next;
      DISPOSE( pd );
      pd = npd;
      }
    }


  if ( !fPull )
    {
    if( ch->level <2)
      char_to_room( ch, get_room_index( ROOM_VNUM_SCHOOL) );
    else
      {
         /* This is a source coded area specific command.
            It makes the arena deaths go to the frog. - Chaos 11/22/95  */
      if( !ARENA_DEATH )
        char_to_room( ch, get_room_index( ch->pcdata->death_room) );
      else
        char_to_room( ch, get_room_index( 9755 ) );
      }
    ch->recall=ch->in_room->vnum;
    return;
    }

  if ( ch->desc != NULL && ch->desc->original != NULL )
    {
    old_ch = ch;
    /* stuff to do if this is a Homonculous switch */
    if(is_name("homonculous",ch->name))
      {
      ch->desc->original->hit-=50;
      if(ch->desc->original->hit<0)
	ch->desc->original->hit=0;
      send_to_char("You suffer from the death of your imp!\n\r",ch);
      };
    /* do regular switch stuff */
    do_return( ch, NULL );
    ch = old_ch;  /* remember to reset ch here */
    ch->desc = NULL;
    }


  for( packet = mob_packet_list; packet != NULL; packet=packet->next )
    {
    if( packet->actor == ch )
      packet->actor = NULL;
    if( packet->mob == ch )
      packet->mob = NULL;
    if( packet->rndm == ch )
      packet->rndm = NULL;
    if( packet->vo == ch )
      packet->vo = NULL;
    }

  if ( IS_NPC(ch) )
    {
    --ch->pIndexData->count;
    total_mobiles--;

    if( ch->reset != NULL && ch->reset->mob == ch )
      ch->reset->mob = NULL;  
    }
  else
    sub_player( ch );

  for ( wch = first_char; wch != NULL; wch = wch->next )
    {
    if ( wch->reply == ch )
    wch->reply = NULL;
    }
  if ( !IS_NPC(ch) && is_desc_valid(ch) )
    for ( wch = first_char; wch != NULL; wch = wch->next )
      {
      if( !IS_NPC(wch) && is_desc_valid(wch) && wch->desc->snoop_by==ch->desc )
        wch->desc->snoop_by= NULL;
      }

  if( !IS_NPC( ch) && ch->pcdata->corpse != NULL)
    {
    obj_from_room( ch->pcdata->corpse);
    extract_obj( ch->pcdata->corpse );
    }

  for ( obj = ch->first_carrying; obj != NULL; obj = obj_next )
    {
    obj_next = obj->next_content;
    extract_obj( obj );
    }
#ifdef UNLINKCHECK
     {
     CHAR_DATA *tch;
     bool foundc;

      for( foundc=FALSE,tch=first_char; tch!=NULL; tch=tch->next )
        if( tch==ch )
          {
          foundc=TRUE;
          break;
          }
    if( foundc )
      UNLINK( ch, first_char, last_char, next, prev );
    /*else
      bug( "UNLINK ERROR character %s not found in char_list.", ch->name );*/
    }
#else
    UNLINK( ch, first_char, last_char, next, prev );
#endif

  if ( ch->desc != NULL )
    ch->desc->character = NULL;
  free_char( ch );
  return;
}

/*
 * Find a char in the room.
 */
CHAR_DATA *get_char_room( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *rch;
    int number;
    int count;

    if( argument == NULL || ch->in_room == NULL )
      return NULL;

    number = number_argument( argument, arg );
    count  = 0;
    if ( !strcasecmp( arg, "self" ) )
	    return ch;
    for ( rch = ch->in_room->first_person; rch != NULL; rch = rch->next_in_room )
      {
	    if(!can_see(ch,rch) && !IS_NPC(ch))
              continue;
            if (IS_AFFECTED( rch, AFF_ETHEREAL ) )
              continue;
            if(!is_name(arg,rch->name))
	      continue;
	    if ( ++count == number )
	      return rch;
      }
    count=0;
    for ( rch = ch->in_room->first_person; rch != NULL; rch = rch->next_in_room )
      {
	    if((!can_see(ch,rch))||!is_name_short(arg,rch->name))
	      continue;
            if (IS_AFFECTED( rch, AFF_ETHEREAL ) )
              continue;
	    if ( ++count == number )
	      return rch;
      }

    return NULL;
}


/*
 * Find a char in the room, even if hidden.
 */
CHAR_DATA *get_char_room_even_hidden( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *rch;
    int number;
    int count;

    if( argument == NULL )
      return NULL;

    number = number_argument( argument, arg );
    count  = 0;
    if ( !strcasecmp( arg, "self" ) )
	    return ch;
    for ( rch = ch->in_room->first_person; rch != NULL; rch = rch->next_in_room )
      {
	    if(!can_see(ch,rch) && !IS_NPC(ch)&&!can_see_is_hidden)
              continue;
            if (IS_AFFECTED( rch, AFF_ETHEREAL ) )
              continue;
            if(!is_name(arg,rch->name))
	      continue;
	    if ( ++count == number )
	      return rch;
      }
    count=0;
    for ( rch = ch->in_room->first_person; rch != NULL; rch = rch->next_in_room )
      {
	    if((!can_see(ch,rch)&&!can_see_is_hidden)||!is_name_short(arg,rch->name))
	      continue;
            if (IS_AFFECTED( rch, AFF_ETHEREAL ) )
              continue;
	    if ( ++count == number )
	      return rch;
      }

    return NULL;
}

/*
 * Find a char (by vnum) in the room, even if hidden.
 */
CHAR_DATA *get_char_room_even_hidden_vnum( CHAR_DATA *ch, int vnum)
  {
  CHAR_DATA *rch;

  for ( rch = ch->in_room->first_person; rch != NULL; rch = rch->next_in_room )
    {
	  if((!can_see(ch,rch)&&!can_see_is_hidden)||rch->pIndexData->vnum!=vnum)
	    continue;
            if (IS_AFFECTED( rch, AFF_ETHEREAL ) )
              continue;
	  return rch;
    }

  return NULL;
  }


/*
 * Find a char in the world.
 */
CHAR_DATA *get_char_world( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *wch;
    int number;
    int count;

    if ( ( wch = get_char_room( ch, argument ) ) != NULL )
	return wch;
    if ( ( wch = get_player_world( ch, argument ) ) != NULL )
	return wch;

    number = number_argument( argument, arg );
    count  = 0;
    for ( wch = first_char; wch != NULL ; wch = wch->next )
     {
     if(!can_see(ch,wch)||!is_name(arg,wch->name))
	    continue;
            if (IS_AFFECTED( wch, AFF_ETHEREAL ) )
              continue;
	if ( ++count == number )
	    return wch;
     }
    count=0;
    for ( wch = first_char; wch != NULL ; wch = wch->next )
     {
     if(!can_see(ch,wch)||!is_name_short(arg,wch->name))
	    continue;
            if (IS_AFFECTED( wch, AFF_ETHEREAL ) )
              continue;
	if ( ++count == number )
	    return wch;
     }

    return NULL;
}

/*
 * CHAR_EXISTS
 *
 * Determine if a PC with the specified name exists.  This function will first
 * search the active player list, and then the player directories.
 *
 * Presto 2-20-99
 */
int char_exists(char *arg)
{
  char         pfile[MAX_STRING_LENGTH];
  char         pfilegz[MAX_STRING_LENGTH];
  PLAYER_GAME *fpl;
  FILE        *charfile;

  if(*arg=='\0' || *arg==' ')
    return FALSE;

  for(fpl=first_player;fpl!=NULL;fpl=fpl->next)
  {
    if(is_name(arg,fpl->ch->name))
      return TRUE;
  }

#if !defined(macintosh) && !defined(MSDOS)
  sprintf(pfile,  "%s/%c/%s",   PLAYER_DIR,tolower(arg[0]),capitalize(arg));
  sprintf(pfilegz,"%s/%c/%s.gz",PLAYER_DIR,tolower(arg[0]),capitalize(arg));
#else
  sprintf(pfile,   "%s%s",    PLAYER_DIR, capitalize(arg));
  sprintf(pfilegz, "%s%s.gz", PLAYER_DIR, capitalize(arg));
#endif

  if(!(charfile=fopen(pfile, "r")))
    if(!(charfile=fopen(pfilegz, "r")))
      return FALSE;

  fclose(charfile);
  return TRUE;
}



/*
 * Find a player in the world that you can see.
 */
CHAR_DATA *get_player_world( CHAR_DATA *ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  PLAYER_GAME *fpl;
  CHAR_DATA *wch;

  if((wch = get_char_room(ch,argument)) != NULL && !IS_NPC(wch) )
    return wch;
  argument = one_argument( argument, arg );

  for(fpl=first_player;fpl!=NULL;fpl=fpl->next)
    {
    if(!can_see(ch,fpl->ch)||!is_name(arg,fpl->ch->name))
      continue;
    return fpl->ch;
    }
  for(fpl=first_player;fpl!=NULL;fpl=fpl->next)
    {
    if(!can_see(ch,fpl->ch)||!is_name_short(arg,fpl->ch->name))
      continue;
    return fpl->ch;
    }

  return NULL;
}



/*
 * Find some object with a given index data.
 * Used by area-reset 'P' command.
 */
OBJ_DATA *get_obj_type( OBJ_INDEX_DATA *pObjIndex , ROOM_INDEX_DATA *room)
{
    OBJ_DATA *obj;

    for ( obj = room->first_content; obj != NULL; obj = obj->next_content )
	if ( obj->pIndexData == pObjIndex)
	    return obj;

    return NULL;
}


/*
 * Find an obj in a list by it's vnum.
 */
OBJ_DATA *get_obj_list_vnum( CHAR_DATA *ch, int vnum, OBJ_DATA *list )
  {
  OBJ_DATA *obj;

  for ( obj = list; obj != NULL; obj = obj->next_content )
    {
	  if ( can_see_obj( ch, obj ) && obj->pIndexData->vnum==vnum )
		  return obj;
    }

  return NULL;
  }

/*
 * Find an obj in a list.
 */
OBJ_DATA *get_obj_list( CHAR_DATA *ch, char *argument, OBJ_DATA *list )
{
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    int number;
    int count;

    /*  Adding index references  -  Chaos  4/18/99 */
    if( *argument=='#'  &&
        ( *(argument+1)>='0' && *(argument+1)<='9' ) &&
        ( *(argument+2)>='0' && *(argument+2)<='9' ) &&
        ( *(argument+3)>='0' && *(argument+3)<='9' ) )
      {
      number = (*(argument+1)-'0')*100 + (*(argument+2)-'0')*10 + 
               (*(argument+3)-'0');
      for( obj = list; obj != NULL; obj = obj->next_content )
	if( can_see_obj( ch, obj ) )
          if( !obj->basic || obj->item_type==ITEM_ARMOR ||
                obj->item_type==ITEM_CONTAINER || obj->item_type==ITEM_WEAPON )
	   if( CAN_WEAR(obj, ITEM_TAKE ) )
    if ( !IS_NPC(ch) && !IS_SET(ch->pcdata->player2_bits, PLR2_ITEM_REF ) )
	    if( number==obj->index_reference[0]%1000 )
	      return obj;

      return( NULL );
      }


      number = number_argument( argument, arg );
    count  = 0;
    for ( obj = list; obj != NULL; obj = obj->next_content )
    {
	if ( can_see_obj( ch, obj ) && is_name( arg, obj->name ) )
	{
	    if ( ++count == number )
		return obj;
	}
    }
    count=0;
    for ( obj = list; obj != NULL; obj = obj->next_content )
    {
	if ( can_see_obj( ch, obj ) && is_name_short( arg, obj->name ) )
	{
	    if ( ++count == number )
		return obj;
	}
    }

    return NULL;
}


/*
 * Find an obj in player's inventory.
 */
OBJ_DATA *get_obj_carry( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    int number;
    int count;

    /*  Adding index references  -  Chaos  4/18/99 */
    if( *argument=='#'  &&
        ( *(argument+1)>='0' && *(argument+1)<='9' ) &&
        ( *(argument+2)>='0' && *(argument+2)<='9' ) &&
        ( *(argument+3)>='0' && *(argument+3)<='9' ) )
      {
      number = (*(argument+1)-'0')*100 + (*(argument+2)-'0')*10 + 
               (*(argument+3)-'0');
      for( obj = ch->first_carrying; obj != NULL; obj = obj->next_content )
	if( can_see_obj( ch, obj ) && obj->wear_loc == WEAR_NONE)
          if( !obj->basic || obj->item_type==ITEM_ARMOR ||
                obj->item_type==ITEM_CONTAINER || obj->item_type==ITEM_WEAPON )
	   if( CAN_WEAR(obj, ITEM_TAKE ) )
    if ( !IS_NPC(ch) && !IS_SET(ch->pcdata->player2_bits, PLR2_ITEM_REF ) )
	    if( number==obj->index_reference[0]%1000 )
	      return obj;

      return( NULL );
      }

    number = number_argument( argument, arg );
    count  = 0;
    for ( obj = ch->first_carrying; obj != NULL; obj = obj->next_content )
    {
	if ( obj->wear_loc == WEAR_NONE
	&&   can_see_obj( ch, obj )
	&&   is_name( arg, obj->name ) )
	{
	    if ( ++count == number )
		return obj;
	}
    }
    count=0;
    for ( obj = ch->first_carrying; obj != NULL; obj = obj->next_content )
    {
	if ( obj->wear_loc == WEAR_NONE
	&&   can_see_obj( ch, obj )
	&&   is_name_short( arg, obj->name ) )
	{
	    if ( ++count == number )
		return obj;
	}
    }

    return NULL;
  }

/*
 * Find an obj in keeper's inventory.
 */
OBJ_DATA *get_obj_carry_keeper( CHAR_DATA *keeper, char *argument, CHAR_DATA *ch, int bargain )
  {
  char arg[MAX_INPUT_LENGTH];
  OBJ_DATA *obj,*oobj;
  int number;
  int count,cost;

    /*  Adding index references  -  Chaos  4/18/99 */
    if( *argument=='#'  &&
        ( *(argument+1)>='0' && *(argument+1)<='9' ) &&
        ( *(argument+2)>='0' && *(argument+2)<='9' ) &&
        ( *(argument+3)>='0' && *(argument+3)<='9' ) )
      {
      number = (*(argument+1)-'0')*100 + (*(argument+2)-'0')*10 + 
               (*(argument+3)-'0');
      for( obj = keeper->first_carrying; obj != NULL; obj = obj->next_content )
        {
	cost = (get_cost( keeper, obj, TRUE )*bargain)/100;
	if( cost>0 && can_see_obj( ch, obj ) )
	 if ( obj->wear_loc == WEAR_NONE && (cost<=ch->gold)
	       && (obj->level<=ch->level) )
          if( !obj->basic || obj->item_type==ITEM_ARMOR ||
                obj->item_type==ITEM_CONTAINER || obj->item_type==ITEM_WEAPON )
	   if( CAN_WEAR(obj, ITEM_TAKE ) )
    if ( !IS_NPC(ch) && !IS_SET(ch->pcdata->player2_bits, PLR2_ITEM_REF ) )
	    if( number==obj->index_reference[0]%1000 )
	      return obj;
        }
      return( NULL );
      }

  number = number_argument( argument, arg );
  count  = 0;
  for ( obj = keeper->first_carrying; obj != NULL; obj = obj->next_content )
    {
	  cost = (get_cost( keeper, obj, TRUE )*bargain)/100;
	  if ( cost <= 0 || !can_see_obj( ch, obj ) )
      continue;

	  if ( obj->wear_loc == WEAR_NONE
	&& (cost<=ch->gold)
	&& (obj->level<=ch->level)
	      && is_name_short( arg, obj->name ) )
	    {
      if(number==0)
	return obj;
      for( oobj = keeper->first_carrying; oobj!=obj; oobj = oobj->next_content )
	{
	      cost = (get_cost( keeper, oobj, TRUE )*bargain)/100;
	      if ( cost <= 0 || !can_see_obj( ch, oobj ) )
	  continue;
	if( oobj->wear_loc == WEAR_NONE && (is_name_short(arg, oobj->name)))
	  {
	  if(oobj->pIndexData->vnum == obj->pIndexData->vnum &&
	     obj->level == oobj->level)
	    {
	    oobj=NULL;
	    break;
	    }
	  }
	}
	    if ((oobj!=NULL)&& ++count == number )
		    return obj;
	    }
    }

  return NULL;
  }


/*
 * Find an obj in player's equipment.
 */
OBJ_DATA *get_obj_wear( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    int number;
    int count;

    /*  Adding index references  -  Chaos  4/18/99 */
    if( *argument=='#'  &&
        ( *(argument+1)>='0' && *(argument+1)<='9' ) &&
        ( *(argument+2)>='0' && *(argument+2)<='9' ) &&
        ( *(argument+3)>='0' && *(argument+3)<='9' ) )
      {
      number = (*(argument+1)-'0')*100 + (*(argument+2)-'0')*10 + 
               (*(argument+3)-'0');
      for( obj = ch->first_carrying; obj != NULL; obj = obj->next_content )
	if( can_see_obj( ch, obj ) && obj->wear_loc != WEAR_NONE )
          if( !obj->basic || obj->item_type==ITEM_ARMOR ||
                obj->item_type==ITEM_CONTAINER || obj->item_type==ITEM_WEAPON )
	   if( CAN_WEAR(obj, ITEM_TAKE ) )
    if ( !IS_NPC(ch) && !IS_SET(ch->pcdata->player2_bits, PLR2_ITEM_REF ) )
	    if( number==obj->index_reference[0]%1000 )
	      return obj;

      return( NULL );
      }

    number = number_argument( argument, arg );
    count  = 0;
    for ( obj = ch->first_carrying; obj != NULL; obj = obj->next_content )
    {
	if ( obj->wear_loc != WEAR_NONE
	&&   can_see_obj( ch, obj )
	&&   is_name( arg, obj->name ) )
	{
	    if ( ++count == number )
		return obj;
	}
    }
    count=0;
    for ( obj = ch->first_carrying; obj != NULL; obj = obj->next_content )
    {
	if ( obj->wear_loc != WEAR_NONE
	&&   can_see_obj( ch, obj )
	&&   is_name_short( arg, obj->name ) )
	{
	    if ( ++count == number )
		return obj;
	}
    }

    return NULL;
}

/*
 * Find an obj vnum in player's equipment.
 */
OBJ_DATA *get_obj_wear_vnum( CHAR_DATA *ch, int vnum)
  {
  OBJ_DATA *obj;

  for ( obj = ch->first_carrying; obj != NULL; obj = obj->next_content )
    {
	  if ( obj->wear_loc != WEAR_NONE
	      &&   can_see_obj( ch, obj )
	      &&   obj->pIndexData->vnum==vnum )
		  return obj;
    }

  return NULL;
  }

/*
 * Find an obj vnum in a players inventory.
 */
OBJ_DATA *get_obj_carry_vnum( CHAR_DATA *ch, int vnum)
  {
  OBJ_DATA *obj;

  for ( obj = ch->first_carrying; obj != NULL; obj = obj->next_content )
    {
	  if (obj->pIndexData->vnum==vnum)
		  return obj;
    }

  return NULL;
  }


/*
 * Find an obj in the room or in inventory.
 */
OBJ_DATA *get_obj_here( CHAR_DATA *ch, char *argument )
{
    OBJ_DATA *obj;

    obj = get_obj_list( ch, argument, ch->in_room->first_content );
    if ( obj != NULL )
	return obj;

    if ( ( obj = get_obj_carry( ch, argument ) ) != NULL )
	return obj;

    if ( ( obj = get_obj_wear( ch, argument ) ) != NULL )
	return obj;

    return NULL;
}

/*
 * Find an obj in the world.
 */
OBJ_DATA *get_obj_world( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    int number;
    int count;

    if ( ( obj = get_obj_here( ch, argument ) ) != NULL )
	return obj;

    number = number_argument( argument, arg );
    count  = 0;
    for ( obj = first_object; obj != NULL; obj = obj->next )
    {
	if ( can_see_obj( ch, obj ) && is_name( arg, obj->name ) )
	{
	    if ( ++count == number )
		return obj;
	}
    }
    count=0;
    for ( obj = first_object; obj != NULL; obj = obj->next )
    {
	if ( can_see_obj( ch, obj ) && is_name_short( arg, obj->name ) )
	{
	    if ( ++count == number )
		return obj;
	}
    }

    return NULL;
}



/*
 * Create a 'money' obj.
 */
OBJ_DATA *create_money( int amount )
{
    char buf[MAX_STRING_LENGTH];
    OBJ_DATA *obj;

    if ( amount <= 0 )
    {
	bug( "Create_money: zero or negative money %d.", amount );
	amount = 1;
    }

    if ( amount == 1 )
    {
	obj = create_object( get_obj_index( OBJ_VNUM_MONEY_ONE ), 0 );
    }
    else
    {
	obj = create_object( get_obj_index( OBJ_VNUM_MONEY_SOME ), 0 );
	sprintf( buf, obj->short_descr, amount );
	STRFREE( obj->short_descr );
	obj->short_descr        = STRALLOC( buf );
	obj->value[0]           = amount;
    }

    return obj;
}



/*
 * Return # of objects which an object counts as.
 * Thanks to Tony Chamberlain for the correct recursive code here.
 */
int get_obj_number( OBJ_DATA *obj )
  {
/* objects inside a container no longer count as numbers -Dug 12/16/93 
  int number;

    if ( obj->item_type == ITEM_CONTAINER )
	number = 0;
    else
	number = 1;

    for ( obj = obj->first_content; obj != NULL; obj = obj->next_content )
	number += get_obj_number( obj );

    return number;
 */
  return 1;
  }



/*
 * Return weight of an object, including weight of first_content.
 * But this linked list is extremely slow.
 */
int get_obj_weight( OBJ_DATA *obj )
{


  return( obj->weight + obj->content_weight );

/*
    int weight;

    weight = obj->weight;
    for ( obj = obj->first_content; obj != NULL; obj = obj->next_content )
	weight += get_obj_weight( obj );

    return weight;  */
}



/*
 * True if room is dark.
 */
bool room_is_dark( ROOM_INDEX_DATA *pRoomIndex )
{
    if ( pRoomIndex == NULL)
       return TRUE;
    if ( pRoomIndex->light > 0 )
	return FALSE;

    if ( IS_SET(pRoomIndex->room_flags, ROOM_DARK) )
	return TRUE;

    if ( pRoomIndex->sector_type == SECT_INSIDE
    ||   pRoomIndex->sector_type == SECT_CITY 
    ||   pRoomIndex->sector_type == SECT_INN )
	return FALSE;

       /* All times are same, so pick any area here */
    if ( room_index[1]->area->weather_info.sunlight == SUN_SET
    ||   room_index[1]->area->weather_info.sunlight == SUN_DARK )
	return TRUE;

    return FALSE;
}



/*
 * True if room is private.
 */
bool room_is_private( ROOM_INDEX_DATA *pRoomIndex )
{
    CHAR_DATA *rch;
    int count;

    count = 0;
    for ( rch = pRoomIndex->first_person; rch != NULL; rch = rch->next_in_room )
     {  
       if (!IS_NPC(rch))
         count++;
     }

    if ( IS_SET(pRoomIndex->room_flags, ROOM_PRIVATE)  && count >= 2 )
	return TRUE;

    if ( IS_SET(pRoomIndex->room_flags, ROOM_SOLITARY) && count >= 1 )
	return TRUE;

    return FALSE;
}



/*
 * True if char can see victim.
 */
bool can_see( CHAR_DATA *ch, CHAR_DATA *victim )
  {
  can_see_is_hidden=FALSE;

  if ( ch == victim )
	  return TRUE;

  if( IS_NPC( victim ) && IS_AFFECTED( victim, AFF_ETHEREAL ))
    return FALSE;

  if( IS_NPC( ch ) && ch->pIndexData->pShop != NULL)
    return( TRUE );
    
  if ( (!IS_NPC(ch)) && IS_SET(ch->act, PLR_HOLYLIGHT) )
	  return TRUE;

  if ( !IS_NPC(victim) && IS_SET(victim->act, PLR_WIZINVIS))
	  return FALSE;

  if ( IS_AFFECTED(ch, AFF_BLIND) )
	  return FALSE;

  if ( !IS_NPC(ch) && is_affected(ch, gsn_truesight))
          return TRUE;
 
  if ( room_is_dark( ch->in_room ) && !IS_AFFECTED(ch, AFF_INFRARED) )
     {
     if( race_table[ ch->race ].vision == 0)
	  return FALSE;
     if( race_table[ ch->race ].vision == 1 && 
	 (ch->in_room->sector_type == SECT_INSIDE ||
	  IS_SET( ch->in_room->room_flags, ROOM_INDOORS)))
	  return FALSE;
     }

  if ( IS_AFFECTED(victim, AFF_INVISIBLE)
    &&   !IS_AFFECTED(ch, AFF_DETECT_INVIS) )
	  return FALSE;

  if ( IS_AFFECTED(victim, AFF_HIDE)
    &&  !IS_AFFECTED(ch, AFF_DETECT_HIDDEN) 
    &&  (victim->fighting==NULL || victim->position!=POS_FIGHTING) )
    {
    can_see_is_hidden=TRUE;
	  return FALSE;  
    }

  if(IS_AFFECTED(victim,AFF_STEALTH) &&
     (!IS_AFFECTED(ch,AFF_DETECT_HIDDEN)) &&
     (victim->fighting==NULL || victim->position!=POS_FIGHTING) )
    {
    can_see_is_hidden=TRUE;
	  return FALSE;  
    }

  return TRUE;
  }


/*
 * True if char can hear victim.
 */
bool can_hear( CHAR_DATA *ch, CHAR_DATA *victim )
  {
  if ( ch == victim )
	  return TRUE;

  if( IS_NPC( victim ) && IS_AFFECTED( victim, AFF_ETHEREAL ))
    return FALSE;

  if( IS_NPC( ch ) && ch->pIndexData->pShop != NULL)
    return( TRUE );
    
  if ( (!IS_NPC(ch)) && IS_SET(ch->act, PLR_HOLYLIGHT) )
	  return TRUE;

  if ( !IS_NPC(victim) && IS_SET(victim->act, PLR_WIZINVIS))
	  return FALSE;

  if ( IS_AFFECTED(victim, AFF_HIDE)
    &&  (!IS_AFFECTED(ch, AFF_DETECT_HIDDEN))
    &&  (victim->fighting==NULL || victim->position!=POS_FIGHTING) )
    {
	  return FALSE;  
    }

  if(IS_AFFECTED(victim,AFF_STEALTH) &&
     (!IS_AFFECTED(ch,AFF_DETECT_HIDDEN)) &&
     (victim->fighting==NULL || victim->position!=POS_FIGHTING) )
    {
	  return FALSE;  
    }

  return TRUE;
  }



/*
 * True if char can see obj.
 */
bool can_see_obj( CHAR_DATA *ch, OBJ_DATA *obj )
{
    if ( !IS_NPC(ch) && IS_SET(ch->act, PLR_HOLYLIGHT) )
	return TRUE;

    /*if ( obj->item_type == ITEM_POTION )
	return TRUE;*/

    if ( IS_AFFECTED( ch, AFF_BLIND ) )
	return FALSE;

    if ( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 )
	return TRUE;

    /* INFRARED should not be able to see items, but night vision should */
    if ( !IS_NPC(ch) && !can_see_in_room( ch, ch->in_room ))
	return FALSE;

    if ( !IS_NPC(ch) && is_affected(ch, gsn_truesight))
          return TRUE;

    if ( IS_SET(obj->extra_flags, ITEM_INVIS)
    &&   !IS_AFFECTED(ch, AFF_DETECT_INVIS) )
	return FALSE;

    return TRUE;
}



/*
 * True if char can drop obj.
 */
bool can_drop_obj( CHAR_DATA *ch, OBJ_DATA *obj )
{
    if ( !IS_NPC(ch) && ch->level >= LEVEL_IMMORTAL )
	return TRUE;
    if( !IS_SET(obj->extra_flags, ITEM_NODROP) )
	return TRUE;

    return FALSE;
}



/*
 * Return ascii name of an item type.
 */
char *item_type_name( OBJ_DATA *obj )
{
    switch ( obj->item_type )
    {
    case ITEM_LIGHT:            return "light";
    case ITEM_SCROLL:           return "scroll";
    case ITEM_WAND:             return "wand";
    case ITEM_STAFF:            return "staff";
    case ITEM_WEAPON:           return "weapon";
    case ITEM_TREASURE:         return "treasure";
    case ITEM_ARMOR:            return "armor";
    case ITEM_POTION:           return "potion";
    case ITEM_FURNITURE:        return "furniture";
    case ITEM_TRASH:            return "trash";
    case ITEM_CONTAINER:        return "container";
    case ITEM_DRINK_CON:        return "drink container";
    case ITEM_KEY:              return "key";
    case ITEM_FOOD:             return "food";
    case ITEM_MONEY:            return "money";
    case ITEM_BOAT:             return "boat";
    case ITEM_CORPSE_NPC:       return "npc corpse";
    case ITEM_CORPSE_PC:        return "pc corpse";
    case ITEM_FOUNTAIN:         return "fountain";
    case ITEM_PILL:             return "pill";
    case ITEM_AMMO:             return "ammunition";
    }

    bug( "Item_type_name: unknown type %d.", obj->item_type );
    return "(unknown)";
}



/*
 * Return ascii name of an affect location.
 */
char *affect_loc_name( int location )
{
    switch ( location )
    {
    case APPLY_NONE:            return "none";
    case APPLY_STR:             return "strength";
    case APPLY_DEX:             return "dexterity";
    case APPLY_INT:             return "intelligence";
    case APPLY_WIS:             return "wisdom";
    case APPLY_CON:             return "constitution";
    case APPLY_SEX:             return "sex";
    case APPLY_CLASS:           return "class";
    case APPLY_LEVEL:           return "level";
    case APPLY_AGE:             return "age";
    case APPLY_MANA:            return "mana";
    case APPLY_HIT:             return "hp";
    case APPLY_MOVE:            return "moves";
    case APPLY_GOLD:            return "gold";
    case APPLY_EXP:             return "experience";
    case APPLY_AC:              return "armor class";
    case APPLY_HITROLL:         return "hit roll";
    case APPLY_DAMROLL:         return "damage roll";
    case APPLY_SAVING_PARA:     return "save vs paralysis";
    case APPLY_SAVING_ROD:      return "save vs rod";
    case APPLY_SAVING_PETRI:    return "save vs petrification";
    case APPLY_SAVING_BREATH:   return "save vs breath";
    case APPLY_SAVING_SPELL:    return "save vs spell";
    }

    bug( "Affect_location_name: unknown location %d.", location );
    return "(unknown)";
}



/*
 * Return ascii name of an affect bit vector.
 */
char *affect_bit_name( int vector )
{
    static char buf[512];

    buf[0] = '\0';
  if( vector > 0 )
    {
    if ( vector & AFF_BLIND         ) strcat( buf, " blind"         );
    if ( vector & AFF_INVISIBLE     ) strcat( buf, " invisible"     );
    if ( vector & AFF_DETECT_EVIL   ) strcat( buf, " detect_evil"   );
    if ( vector & AFF_DETECT_INVIS  ) strcat( buf, " detect_invis"  );
    if ( vector & AFF_DETECT_MAGIC  ) strcat( buf, " detect_magic"  );
    if ( vector & AFF_DETECT_HIDDEN ) strcat( buf, " detect_hidden" );
    if ( vector & AFF_HOLD          ) strcat( buf, " hold"          );
    if ( vector & AFF_SANCTUARY     ) strcat( buf, " sanctuary"     );
    if ( vector & AFF_FAERIE_FIRE   ) strcat( buf, " faerie_fire"   );
    if ( vector & AFF_INFRARED      ) strcat( buf, " infrared"      );
    if ( vector & AFF_CURSE         ) strcat( buf, " curse"         );
    if ( vector & AFF_POISON        ) strcat( buf, " poison"        );
    if ( vector & AFF_PROTECT_EVIL  ) strcat( buf, " protect_evil"  );
    if ( vector & AFF_PROTECT_GOOD  ) strcat( buf, " protect_good"  );
    if ( vector & AFF_SLEEP         ) strcat( buf, " sleep"         );
    if ( vector & AFF_SNEAK         ) strcat( buf, " sneak"         );
    if ( vector & AFF_HIDE          ) strcat( buf, " hide"          );
    if ( vector & AFF_CHARM         ) strcat( buf, " charm"         );
    if ( vector & AFF_FLYING        ) strcat( buf, " flying"        );
    if ( vector & AFF_PASS_DOOR     ) strcat( buf, " pass_door"     );
    if ( vector & AFF_STEALTH       ) strcat( buf, " stealth"       );
    if ( vector & AFF_HASTE         ) strcat( buf, " haste"       );

    }
  else
    {
    vector = 0-vector;
    if ( vector & (0-AFF2_MAGE_SHIELD)     ) strcat( buf, " mage_shield"     );
    if ( vector & (0-AFF2_ENHANCED_REST)   ) strcat( buf, " enhanced rest"   );
    if ( vector & (0-AFF2_ENHANCED_HEAL)  ) strcat( buf, " enhanced heal"   );
    if ( vector & (0-AFF2_ENHANCED_REVIVE) ) strcat( buf, " enhanced revive" );
    if ( vector & (0-AFF2_CAMPING)         ) strcat( buf, " camping"         );
    if ( vector & (0-AFF2_BERSERK)         ) strcat( buf, " berserk"         );
    }

  return ( buf[0] != '\0' ) ? buf+1 : "none";
}



/*
 * Return ascii name of extra flags vector.
 */
char *extra_bit_name( int extra_flags )
{
    static char buf[512];

    buf[0] = '\0';
    if ( extra_flags & ITEM_GLOW         ) strcat( buf, " glow"         );
    if ( extra_flags & ITEM_HUM          ) strcat( buf, " hum"          );
    if ( extra_flags & ITEM_DARK         ) strcat( buf, " dark"         );
    if ( extra_flags & ITEM_LOCK         ) strcat( buf, " lock"         );
    if ( extra_flags & ITEM_EVIL         ) strcat( buf, " evil"         );
    if ( extra_flags & ITEM_INVIS        ) strcat( buf, " invis"        );
    if ( extra_flags & ITEM_MAGIC        ) strcat( buf, " magic"        );
    if ( extra_flags & ITEM_NODROP       ) strcat( buf, " nodrop"       );
    if ( extra_flags & ITEM_BLESS        ) strcat( buf, " bless"        );
    if ( extra_flags & ITEM_ANTI_GOOD    ) strcat( buf, " anti-good"    );
    if ( extra_flags & ITEM_ANTI_EVIL    ) strcat( buf, " anti-evil"    );
    if ( extra_flags & ITEM_ANTI_NEUTRAL ) strcat( buf, " anti-neutral" );
    if ( extra_flags & ITEM_NOREMOVE     ) strcat( buf, " noremove"     );
    if ( extra_flags & ITEM_INVENTORY    ) strcat( buf, " inventory"    );
    if ( extra_flags & ITEM_NOT_VALID    ) strcat( buf, " not_valid"    );
    return ( buf[0] != '\0' ) ? buf+1 : "none";
}

/*  Chaos 10/10/93
    Determines which class has highest level in a skill
    Return -1 is none.    */
int multi( CHAR_DATA *ch, int sn)
  {
  int cnt, mlv, tst;
  
  mlv=-1;
  tst=-1;
  if(IS_NPC(ch))
    return(-1);
  for(cnt=0;cnt<MAX_CLASS;cnt++)
    if( ch->mclass[cnt] >= skill_table[sn].skill_level[cnt])
      if(mlv<ch->mclass[cnt])
	{
	tst=cnt;
	mlv=ch->mclass[cnt];
	}
  return(tst);
  }

/* This is the variant of multi that selects the highest adept level.
       Chaos - 9/14/98 */
int multi_pick( CHAR_DATA *ch, int sn)
  {
  int cnt, mlv, tst;
  
  mlv=-1;
  tst=-1;
  if(IS_NPC(ch))
    return(-1);
  for(cnt=0;cnt<MAX_CLASS;cnt++)
    if( ch->mclass[cnt] >= skill_table[sn].skill_level[cnt])
      if(mlv<class_table[cnt].skill_adept)
	{
	tst=cnt;
	mlv=class_table[cnt].skill_adept;
	}
  return(tst);
  }

int get_pets( CHAR_DATA *ch)
{
  int cnt;
  CHAR_DATA *fch;

  cnt=0;
  for(fch=first_char ; fch!=NULL ; fch=fch->next )
     if( fch->master == ch)
       if( IS_AFFECTED( fch, AFF_CHARM) )
	 cnt++;
  return( cnt );
}

void char_reset(CHAR_DATA *ch)
  {/* reset ch's stats */
  OBJ_DATA *obj;
	AFFECT_DATA *paf;
  int old_max_hit, old_max_mana, old_max_move;

  ch->armor=100;
  ch->hitroll=0;
  ch->damroll=0;
  ch->saving_throw=0;
  ch->pcdata->eqhitroll=0;
  ch->pcdata->eqdamroll=0;
  ch->pcdata->eqsaves=0;
  ch->pcdata->mod_str=0;
  ch->pcdata->mod_dex=0;
  ch->pcdata->mod_wis=0;
  ch->pcdata->mod_con=0;
  ch->pcdata->mod_int=0;
  old_max_hit = ch->max_hit;
  old_max_mana = ch->max_mana;
  old_max_move = ch->max_move;
  ch->max_hit = ch->actual_max_hit;
  ch->max_mana = ch->actual_max_mana;
  ch->max_move = ch->actual_max_move;

  for(obj=ch->first_carrying;obj!=NULL;obj=obj->next_content)

    {
    if(obj->wear_loc!=WEAR_NONE)
      {
      ch->armor-=apply_ac(obj,obj->wear_loc);
      for ( paf = obj->first_affect; paf != NULL; paf = paf->next )
	{
	switch(paf->location)
	  {
          case APPLY_MANA: ch->max_mana += paf->modifier; break;
          case APPLY_HIT:  ch->max_hit += paf->modifier; break;
          case APPLY_MOVE:     ch->max_move += paf->modifier; break;
	  case APPLY_AC:       ch->armor+= paf->modifier;       break;
	  case APPLY_HITROLL:  ch->hitroll+= paf->modifier;     
                               ch->pcdata->eqhitroll += paf->modifier;  break;
	  case APPLY_DAMROLL:  ch->damroll+= paf->modifier;
                               ch->pcdata->eqdamroll += paf->modifier;  break;
	  case APPLY_STR:      ch->pcdata->mod_str+= paf->modifier;     break;
	  case APPLY_DEX:      ch->pcdata->mod_dex+= paf->modifier;     break;
	  case APPLY_WIS:      ch->pcdata->mod_wis+= paf->modifier;     break;
	  case APPLY_INT:      ch->pcdata->mod_int+= paf->modifier;     break;
	  case APPLY_CON:      ch->pcdata->mod_con+= paf->modifier;     break;
          case APPLY_SAVING_PARA:   
          case APPLY_SAVING_ROD:    
          case APPLY_SAVING_PETRI: 
          case APPLY_SAVING_BREATH:
          case APPLY_SAVING_SPELL: ch->saving_throw+= paf->modifier;
                                   ch->pcdata->eqsaves+= paf->modifier;  break;
	  default:
	    break;
	  }
	}
      for ( paf = obj->pIndexData->first_affect; paf != NULL; paf = paf->next )
	{
	switch(paf->location)
	  {
          case APPLY_MANA: ch->max_mana += paf->modifier; break;
          case APPLY_HIT:  ch->max_hit += paf->modifier; break;
          case APPLY_MOVE:     ch->max_move += paf->modifier; break;
	  case APPLY_AC:       ch->armor+= paf->modifier;       break;
	  case APPLY_HITROLL:  ch->hitroll+= paf->modifier;   
                               ch->pcdata->eqhitroll += paf->modifier;  break;
	  case APPLY_DAMROLL:  ch->damroll+= paf->modifier;
                               ch->pcdata->eqdamroll += paf->modifier;  break;
	  case APPLY_STR:      ch->pcdata->mod_str+= paf->modifier;     break;
	  case APPLY_DEX:      ch->pcdata->mod_dex+= paf->modifier;     break;
	  case APPLY_WIS:      ch->pcdata->mod_wis+= paf->modifier;     break;
	  case APPLY_INT:      ch->pcdata->mod_int+= paf->modifier;     break;
	  case APPLY_CON:      ch->pcdata->mod_con+= paf->modifier;     break;
          case APPLY_SAVING_PARA:   
          case APPLY_SAVING_ROD:    
          case APPLY_SAVING_PETRI: 
          case APPLY_SAVING_BREATH:
          case APPLY_SAVING_SPELL: ch->saving_throw+= paf->modifier;
                                   ch->pcdata->eqsaves+= paf->modifier;  break;
	  default:
	    break;
	  }
	}
      }
    }
  for ( paf = ch->first_affect; paf != NULL; paf = paf->next )
    {
    switch(paf->location)
      {
          case APPLY_MANA: ch->max_mana += paf->modifier; break;
          case APPLY_HIT:  ch->max_hit += paf->modifier; break;
          case APPLY_MOVE:     ch->max_move += paf->modifier; break;
	  case APPLY_AC:       ch->armor+= paf->modifier;       break;
	  case APPLY_HITROLL:  ch->hitroll+= paf->modifier;     break;
	  case APPLY_DAMROLL:  ch->damroll+= paf->modifier;     break;
	  case APPLY_STR:      ch->pcdata->mod_str+= paf->modifier;     break;
	  case APPLY_DEX:      ch->pcdata->mod_dex+= paf->modifier;     break;
	  case APPLY_WIS:      ch->pcdata->mod_wis+= paf->modifier;     break;
	  case APPLY_INT:      ch->pcdata->mod_int+= paf->modifier;     break;
	  case APPLY_CON:      ch->pcdata->mod_con+= paf->modifier;     break;
          case APPLY_SAVING_PARA:   ch->saving_throw+= paf->modifier; break;
          case APPLY_SAVING_ROD:    ch->saving_throw+= paf->modifier; break;
          case APPLY_SAVING_PETRI:  ch->saving_throw+= paf->modifier; break;
          case APPLY_SAVING_BREATH: ch->saving_throw+= paf->modifier; break;
          case APPLY_SAVING_SPELL:  ch->saving_throw+= paf->modifier; break;
      default:
	break;
      }
    }

    /* Fix limits    -  Chaos  1/31/97 */
			      if(ch->max_mana<1)
				ch->max_mana=1;
			      if(ch->max_mana<ch->mana)
				ch->mana=ch->max_mana;
			      if(ch->max_hit<1)
				ch->max_hit=1;
			      if(ch->max_hit<ch->hit)
				ch->hit=ch->max_hit;
			      if(ch->max_move<1)
				ch->max_move=1;
			      if(ch->max_move<ch->move)
				ch->move=ch->max_move;

  if( ch->actual_max_hit == 0 )  /* Reconstruct Actuals  - Chaos 12/22/94 */
    {
    ch->actual_max_hit = old_max_hit - ch->max_hit;
    ch->max_hit += ch->actual_max_hit;
    ch->actual_max_mana = old_max_mana - ch->max_mana;
    ch->max_mana += ch->actual_max_mana;
    ch->actual_max_move = old_max_move - ch->max_move;
    ch->max_move += ch->actual_max_move;
    }
  return;
  }

int mob_armor(CHAR_DATA *ch)
  {/* get mob's armor w/o basic AC */
  OBJ_DATA *obj;
	AFFECT_DATA *paf;
  int AC;

  AC=0;
  for(obj=ch->first_carrying;obj!=NULL;obj=obj->next_content)
    {
	  if(obj->wear_loc!=WEAR_NONE)
      {
      AC+=apply_ac(obj,obj->wear_loc);
      for ( paf = obj->first_affect; paf != NULL; paf = paf->next )
	{
	switch(paf->location)
	  {
	  case APPLY_AC:            AC                          += paf->modifier;       break;
	  default:
	    break;
	  }
	}
      for ( paf = obj->pIndexData->first_affect; paf != NULL; paf = paf->next )
	{
	switch(paf->location)
	  {
	  case APPLY_AC:            AC                          += paf->modifier;       break;
	  default:
	    break;
	  }
	}
      }
    }
  for ( paf = ch->first_affect; paf != NULL; paf = paf->next )
    {
    switch(paf->location)
      {
      case APPLY_AC:            AC                      += paf->modifier;       break;
      default:
	break;
      }
    }
  if( ch->level <5)
    return(0);
  if( AC> ch->level)
    AC=ch->level;
  return( AC );
  }

CHAR_DATA * scan_mob( RESET_DATA *pReset)
  {
  CHAR_DATA *mob;
  
  if( pReset==NULL)
    return(NULL);
  for(mob=first_char; mob!=NULL; mob=mob->next)
    if(IS_NPC(mob) &&  mob->reset==pReset)
       return(mob);
  return(NULL);
  }

bool scan_obj( RESET_DATA *pReset)
  {
  OBJ_DATA *obj;
  
  for(obj=first_object; obj!=NULL; obj=obj->next)
    if(obj->reset==pReset)
       return(TRUE);
  return(FALSE);
  }

char *pick_one( char *in1, char *in2, char *in3, char *in4)
  {
  int pick;
  pick=number_range( 1, 4);
  if(pick==1)
    return( in1);
  if(pick==2)
    return( in2);
  if(pick==3)
    return( in3);
  return( in4);
  }

bool can_see_in_room( CHAR_DATA *ch, ROOM_INDEX_DATA *room)
  {

  if(IS_NPC( ch))
    return(!room_is_dark( room ));

  if(!room_is_dark( room ))
    return( TRUE );
  if( race_table[ch->race].vision==0)
    return( FALSE );
  if( race_table[ch->race].vision==1 &&
     (room->sector_type == SECT_INSIDE ||
      IS_SET( room->room_flags, ROOM_INDOORS)))
	  return(FALSE);
  return( TRUE );   /* for vision==2 */
  }


sh_int obj_level_estimate(OBJ_INDEX_DATA *objIndex)
  {
  AFFECT_DATA *aff;
  int level;
  int value[4];

  level=1;
  for(aff=objIndex->first_affect;aff!=NULL;aff=aff->next)
    {
      switch(aff->location)
	{
	case APPLY_STR:
	  if(aff->modifier > 0)
	    level+=(aff->modifier*(3*aff->modifier))/2;
	  else
	    level-=(aff->modifier*(aff->modifier));
	  break;
	case APPLY_DEX:
	  if(aff->modifier > 0)
	    level+=(aff->modifier*(2*aff->modifier))/3;
	  else
	    level-=(aff->modifier*(aff->modifier))/3;
	  break;
	case APPLY_INT:
	  if(aff->modifier > 0)
	    level+=(aff->modifier*(2*aff->modifier))/3;
	  else
	    level-=(aff->modifier*(aff->modifier))/4;
	  break;
	case APPLY_WIS:
	  if(aff->modifier > 0)
	    level+=(aff->modifier*(4*aff->modifier))/3;
	  else
	    level-=(aff->modifier*(2*aff->modifier))/3;
	  break;
	case APPLY_CON:
	  if(aff->modifier > 0)
	    level+=(aff->modifier*(4*aff->modifier))/3;
	  else
	    level-=(aff->modifier*(2*aff->modifier))/3;
	  break;
	case APPLY_MANA:
	  if(aff->modifier > 0)
	    level+=aff->modifier/3;
	  else
	    level+=aff->modifier/5;
	  break;
	case APPLY_HIT:
	  if(aff->modifier > 0)
	    level+=aff->modifier/2;
	  else
	    level+=aff->modifier/4;
	  break;
	case APPLY_MOVE:
	  if(aff->modifier > 0)
	    level+=aff->modifier/3;
	  else
	    level+=aff->modifier/5;
	  break;
	case APPLY_AC:
	  if(aff->modifier < 0)
	    level+=(aff->modifier*(aff->modifier))/3;
	  else
	    level-=(aff->modifier*(aff->modifier))/6;
	  break;
	case APPLY_HITROLL:
	  if(aff->modifier > 0)
	    level+=(aff->modifier*(aff->modifier))*2/3;
	  else
	    level-=(aff->modifier*(aff->modifier))/6;
	  break;
	case APPLY_DAMROLL:
	  if(aff->modifier > 0)
	    level+=(aff->modifier*(aff->modifier));
	  else
	    level-=(aff->modifier*(aff->modifier))/3;
	  break;
	case APPLY_SAVING_BREATH:
	  if(aff->modifier < 0)
	    level+=(aff->modifier*(aff->modifier))/7;
	  else
	    level-=(aff->modifier*(aff->modifier))/9;
	  break;
	case APPLY_SAVING_SPELL:
	  if(aff->modifier < 0)
	    level+=(aff->modifier*(aff->modifier))/6;
	  else
	    level-=(aff->modifier*(aff->modifier))/8;
	  break;
	case APPLY_NONE:
	case APPLY_SEX:
	default:
	  break;
	}
    }

  value[0]=objIndex->value[0];
  value[1]=objIndex->value[1];
  value[2]=objIndex->value[2];
  value[3]=objIndex->value[3];
  switch(objIndex->item_type)
    {
    case ITEM_LIGHT:
      if(value[2] < 0)
	{
          if(level < 25)
	    level = 25;
        }
      else
	if( level+value[2]/2400 > 3*level/2)
	  level = level+value[2]/2400;
	else
      	  level=3*level/2;
      break;
    case ITEM_SCROLL:
      if(value[1]>0)
	level+=3*value[0]/5;
      if(value[2]>0)
	level+=3*value[0]/5;
      if(value[3]>0)
	level+=3*value[0]/5;
      break;
    case ITEM_POTION:
      if(value[1]>0)
	level+=3*value[0]/5;
      if(value[2]>0)
	level+=3*value[0]/5;
      if(value[3]>0)
	level+=3*value[0]/5;
      break;
    case ITEM_PILL:
      if(value[1]>0)
	level+=2*value[0]/3;
      if(value[2]>0)
	level+=2*value[0]/3;
      if(value[3]>0)
	level+=2*value[0]/3;
      break;
    case ITEM_WAND:
      level+=2*value[0]/3 + 2*value[0]*(value[1])/24;
      break;
    case ITEM_STAFF:
      level+=3*value[0]/5 + 3*value[0]*(value[1])/30;
      break;
    case ITEM_WEAPON:
      level+=((5*(value[1]*(1+(value[2]-1)/2.0))/2)-10);
      break;
    case ITEM_ARMOR:
      if( value[0] < 0 )
	level-=value[0]*(value[0])/4+1;
      else
	level+=value[0]*(value[0])/4+1;
      break;
    case ITEM_AMMO:
      level+=value[1]*value[3]*value[2]/20;
      break;
    case ITEM_TREASURE:
    case ITEM_FURNITURE:
    case ITEM_TRASH:
    case ITEM_CONTAINER:
      level+=value[0]/15;
      break;
    case ITEM_DRINK_CON:
    case ITEM_KEY:
    case ITEM_FOOD:
    case ITEM_MONEY:
    case ITEM_BOAT:
    case ITEM_FOUNTAIN:
    default:
      break;
    }

  if((objIndex->extra_flags & ITEM_INVIS) != 0)
    level+=(level/20);
  if((objIndex->extra_flags & ITEM_NODROP) != 0)
    level-=(level/15);
  if((objIndex->extra_flags & ITEM_ANTI_GOOD) != 0)
    level-=(level/20);
  if((objIndex->extra_flags & ITEM_ANTI_EVIL) != 0)
    level-=(level/20);
  if((objIndex->extra_flags & ITEM_ANTI_NEUTRAL) != 0)
    level-=(level/20);
  if((objIndex->extra_flags & ITEM_NOREMOVE) != 0)
    level-=(level/15);
  if((objIndex->extra_flags & ITEM_INVENTORY) != 0)
    level+=(level/10);
  if((objIndex->extra_flags & ITEM_AUTO_ENGRAVE) != 0)
    level-=(level/15);

  if(level>0)
    return level;
  else
    return 1;
  }


sh_int obj_cnt[32768];
void invalidate_obj_copies(OBJ_DATA *list,int level)
  {
  OBJ_DATA *obj;
  int i;

  if(level==0)
    for(i=0;i<32768;i++)
      obj_cnt[i]=0;

  for(obj=list;obj!=NULL;obj=obj->next_content)
    {
    if(obj_cnt[obj->pIndexData->vnum]>=obj->pIndexData->max_objs)
      {
      if(obj->wear_loc==WEAR_NONE)
	{
	 if (obj->carried_by != NULL)
	   log_printf("invalidate_obj_copies deleting %s carried by %s.", obj->name, obj->carried_by->name);
	 else
	   log_printf("invalidate_obj_copies deleting %s.", obj->name );
         SET_BIT(obj->extra_flags,ITEM_NOT_VALID);
	}
      }
    else
      obj_cnt[obj->pIndexData->vnum]++;
    if (obj->item_type == ITEM_CONTAINER)
      if(obj->first_content!=NULL)
        invalidate_obj_copies(obj->first_content,level+1);
    }
  return;
  }

bool blocking(CHAR_DATA *victim,CHAR_DATA *ch)
  {
  /* can't block NPC's */
  if(IS_NPC(ch) || IS_NPC(victim))
    return FALSE;

  /* can't block gods or police */
  if(ch->level >= LEVEL_IMMORTAL || which_god(ch)==GOD_POLICE)
    return FALSE;

  /* You can't talk to someone who is disconnected */
  /* nor can someone who is disconnected talk to you */
  if(ch->desc==NULL || victim->desc==NULL)
    return TRUE;
 
  /* victim isn't blocking anyone */
  if(victim->pcdata->block_list==NULL)
    return FALSE;

  /* victim is blocking ch by name */
  if(is_name(ch->name,victim->pcdata->block_list))
    return TRUE;

  /* ch has no host entry, so you can't block his host */
  if(ch->desc->host==NULL)
    return FALSE;

  /* named sites need to be shortened in reverse of numbered sites */
  if(isalpha((int)ch->desc->host[0]))
    return is_short_of_name_reverse(ch->desc->host,victim->pcdata->block_list);
  else
    return is_short_of_name(ch->desc->host,victim->pcdata->block_list);
  }

bool is_in_room( CHAR_DATA *ch, ROOM_INDEX_DATA *room)
  {
  CHAR_DATA *fch;

  for( fch = room->first_person; fch != NULL; fch = fch->next_in_room )
    if( ch == fch )
      return( TRUE );
  return( FALSE );
  }

bool in_camp(CHAR_DATA *ch)
  {
  CHAR_DATA *gch;
  if( ch->in_room == NULL )
    return( FALSE );
  for(gch=ch->in_room->first_person;gch!=NULL;gch=gch->next_in_room)
    if(is_same_group(ch,gch) && IS_AFFECTED(gch,AFF2_CAMPING))
      return TRUE;
  return FALSE;
  }

char domain_string[256];
char *domain_of(char *site)
  {
  char *p,*d;
  int pCnt;

  pCnt=0;
  if(isalpha((int)*site))
    {
    for(p=site+strlen(site);p>=site;p--)
      {
      if(*p=='.')
        {
        if((++pCnt)==2)
          return (p+1);
        continue;
        }
      }
    }
  else
    {
    d=domain_string;
    for(p=site;*p!='\0';p++)
      {
      *(d++)=*p;
      if(*p=='.')
        {
        if((++pCnt)==2)
          {
          *(d-1)='\0';
          return domain_string;
          }
        continue;
        }
      }
    }
  return site;
  }

char *ansi_translate_text( CHAR_DATA *ch, char *text_in )
  {
  char *pti, *pto, *pt2;
  int mode, val1, val2, val3;

  pti = text_in;
  pto = ansi_translate_buffer;
  mode = 0;
  val1 = 0;
  val2 = 0;
  val3 = 0;

  pt2 = get_color_string( ch, COLOR_TEXT, VT102_DIM ) ;
  for( ; *pt2!='\0'; pt2++, pto++)
    *pto = *pt2;

  while( *pti != '\0' )
    {
    switch( mode )
      {
      case 0:    /* No escape sequence current */
        if( *pti == '{' )
          mode = 1;
        else
          {
          *pto = *pti;
          pto++;
          }
        pti++;
        break;

      case 1:
        if( *pti < '0' || *pti > '7' )    /* Invalid escape */
          {
          *pto = '{';
          pto ++;
          *pto = *pti;
          pto ++;
          pti ++;
          mode = 0;
          break;
          }
        val1 = *pti - '0';
        mode = 2;
        pti++;
        break;

      case 2:
        if( *pti < '0' || *pti > '7' )    /* Invalid escape */
          {
          *pto = '{';
          pto ++;
          *pto = val1 + '0';
          pto++;
          *pto = *pti;
          pto ++;
          pti ++;
          mode = 0;
          break;
          }
        val2 = *pti - '0';
        mode = 3;
        pti++;
        break;
    
      case 3:
        if( *pti < '0' || *pti > '7' )    /* Invalid escape */
          {
          *pto = '{';
          pto ++;
          *pto = val1 + '0';
          pto++;
          *pto = val2 + '0';
          pto++;
          *pto = *pti;
          pto ++;
          pti ++;
          mode = 0;
          break;
          }
        val3 = *pti - '0';
        mode = 4;
        pti++;
        break;
    
      case 4:
        if( *pti != '}' )    /* Invalid escape */
          {
          *pto = '{';
          pto ++;
          *pto = val1 + '0';
          pto++;
          *pto = val2 + '0';
          pto++;
          *pto = val3 + '0';
          pto++;
          *pto = *pti;
          pto ++;
          pti ++;
          mode = 0;
          break;
          }
        mode = 0;
        pti++;

        if( val1 == 3 )
          {
          pt2 = get_color_string( ch, COLOR_TEXT, VT102_DIM ) ;
          for( ; *pt2!='\0'; pt2++, pto++)
            *pto = *pt2;
          }
       else
        if( val1 != 2 && val1 !=6 )
          {
          pt2 = get_color_diff( ch, -1, -1, 1-val1, val2, val3, val1 ) ;
          for( ; *pt2!='\0'; pt2++, pto++)
            *pto = *pt2;
          }

        break;
      }

    }

  if( mode > 0 )
    {
    *pto = '{';
    pto++;
    }
  if( mode > 1 )
    {
    *pto = val1 + '0';
    pto++;
    }
  if( mode > 2 )
    {
    *pto = val2 + '0';
    pto++;
    }
  if( mode > 0 )
    {
    *pto = val3 + '0';
    pto++;
    }

  *pto = '\0';

  return( ansi_translate_buffer );
  }
    

char *get_name( CHAR_DATA *ch )
{
  char buf[MAX_INPUT_LENGTH];

  *get_name_buffer='\0'; 
  *buf='\0'; 

  if( !IS_NPC( ch ) )
    {

    if( ch->name == NULL )
      {
      *get_name_buffer='\0'; 
      return( get_name_buffer );
      }

    if( which_god( ch )==GOD_POLICE  && ch->level<98 )
      {
      if( ch->sex == 2 )
        strcpy( get_name_buffer, "Sheriff " );
      else
        strcpy( get_name_buffer, "Sheriff " );
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      strcat( get_name_buffer, buf);
      return( get_name_buffer );
      }

        /* Angi's Title - Chaos 10/17/97 */
    if( ch->pcdata->pvnum == 56092 && !strcasecmp(ch->name,"lovely"))
      {
      strcpy( get_name_buffer, "Queen " );
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      strcat( get_name_buffer, buf);
      return( get_name_buffer );
      }

    /* Presto's Title - Presto 6-24-98 */
    if( ch->pcdata->pvnum == 108 && !strcasecmp(ch->name,"presto"))
      {
      strcpy( get_name_buffer, "High Wizard " );
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      strcat( get_name_buffer, buf);
      return( get_name_buffer );
      }
     
    if(!strcasecmp(ch->name,"emperor"))
      {
      strcpy( get_name_buffer, "Elder Wizard " );
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      strcat( get_name_buffer, buf);
      return( get_name_buffer );
       }  
   
      if(!strcasecmp(ch->name,"grog"))
      {
      strcpy( get_name_buffer, "Holy Knight " );
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      strcat( get_name_buffer, buf);
      return( get_name_buffer );
      }  */

    if( ch->level > 98 )
      {
      if( ch->sex == 2 )
        strcpy( get_name_buffer, "Lady " );
      else
        strcpy( get_name_buffer, "Knight " );
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      strcat( get_name_buffer, buf);
      return( get_name_buffer);
      }
    else
    if( ch->level > 97 )
      {
      if( ch->sex == 2 )
        strcpy( get_name_buffer, "Duchess " );
      else
        strcpy( get_name_buffer, "Duke " );
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      strcat( get_name_buffer, buf);
      return( get_name_buffer);
      }
    else
    if( ch->level > 96 )
      {
      if( ch->sex == 2 )
        strcpy( get_name_buffer, "Countess " );
      else
        strcpy( get_name_buffer, "Count " );
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      strcat( get_name_buffer, buf);
      return( get_name_buffer);
      }
    else
    if( ch->level > 95 )
      {
      if( ch->sex == 2 )
        strcpy( get_name_buffer, "Lady " );
      else
        strcpy( get_name_buffer, "Knight " );
      }
/*    Most players didn't like this idea  - Chaos removed 8/17/98
      Subtitles for followers - Chaste 6/20/98
    else if (which_god(ch)==GOD_CHAOS ||
             which_god(ch)==GOD_ORDER ||
             which_god(ch)==GOD_DEMISE )
    {
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      if (which_god(ch)==GOD_ORDER)
       strcat(buf, " the Lawful");
      if (which_god(ch)==GOD_CHAOS)
       strcat(buf, " the Chaotic");
      if (which_god(ch)==GOD_DEMISE)
       strcat(buf, " the Deceitful");
      strcat( get_name_buffer, buf);
      return( get_name_buffer );
    }  */
    else if( ch->level > 90 )
     {
     if( ch->mclass[ch->class]==ch->level )
       switch( ch->class )
         {
         case CLASS_ROGUE: 
               if( ch->sex == 2 )
		strcpy( get_name_buffer, "Temptress " );
               else
                strcpy( get_name_buffer, "Cutthroat " ); break;
         case CLASS_ILLUSIONIST: 
               if( ch->sex == 2 )
                strcpy( get_name_buffer, "Sorceress " );
               else
		strcpy( get_name_buffer, "Sorcerer " ); break;
         case CLASS_ELEMENTALIST: 
               if( ch->sex == 2 )
                strcpy( get_name_buffer, "Druidess " );
               else
		strcpy( get_name_buffer, "Druid " ); break;
         case CLASS_RANGER: 
               if( ch->sex == 2 )
                strcpy( get_name_buffer, "Swordmistress " );
               else
		strcpy( get_name_buffer, "Swordmaster " ); break;
         case CLASS_NECROMANCER: 
               if( ch->sex == 2 )
                strcpy( get_name_buffer, "Witch " );
               else
		strcpy( get_name_buffer, "Warlock " ); break;
         case CLASS_MONK: 
               if( ch->sex == 2 )
                strcpy( get_name_buffer, "Sister " );
               else
		strcpy( get_name_buffer, "Brother " ); break;
         case CLASS_ASSASSIN: 
               if( ch->sex == 2 )
                strcpy( get_name_buffer, "Blackwidow " );
               else
		strcpy( get_name_buffer, "Widowmaker " ); break;
         }
     else
       if( ch->sex != SEX_FEMALE )
        strcpy( get_name_buffer, "Sir " );
       else
        strcpy( get_name_buffer, "Maiden " );

     }
    else
    if( ch->level > 89 )
     {
     if( ch->sex != SEX_FEMALE )
      strcpy( get_name_buffer, "Squire " );
     else
      strcpy( get_name_buffer, "Miss " );
     }
      strcpy( buf, ch->name );
      *buf = UPPER( *buf );
      strcat( get_name_buffer, buf);
      return( get_name_buffer );
    }

  strcpy( get_name_buffer, ch->short_descr );
  return( get_name_buffer );
}


int GET_HITROLL( CHAR_DATA *ch )
{
  int val, tmp, max;

  val =(ch->hitroll+str_app[get_curr_str(ch)].tohit);

  if( IS_NPC(ch) )
    return( val );

  tmp = ch->pcdata->eqhitroll;
  max = (ch->level*120)/95 + 15;

  if( tmp>max )
    val -= ( tmp-max );

  return( val );
}

int GET_DAMROLL( CHAR_DATA *ch )
{
  int val, tmp, max;

  val = (ch->damroll+str_app[get_curr_str(ch)].todam) ;

  if( IS_NPC(ch) )
    return( val );

  tmp = ch->pcdata->eqdamroll;
  max = (ch->level*120)/95 + 10;

  if( tmp>max )
    val -= ( tmp-max );

  return( val );
}

int GET_SAVING_THROW( CHAR_DATA *ch )
{
  int val, tmp, max;

  val = ch->saving_throw;

  if( IS_NPC(ch) )
    return( val );

  tmp = ch->pcdata->eqsaves;
  max = (ch->level*45)/95 + 10;

  if( tmp>max )
    val -= ( tmp-max );

  return( val );
}

bool can_use_exit(CHAR_DATA *ch, ROOM_INDEX_DATA *room, int dir)
{
 if (ch==NULL || room == NULL || room->exit[dir] == NULL || dir<0 || dir>5) return FALSE;

 if (IS_SET(room->exit[dir]->exit_info,EX_RIP) &&
             !pvnum_in_group(ch,room->exit[dir]->pvnum))
    return FALSE;
 else if (IS_SET(room->exit[dir]->exit_info, EX_BACKDOOR) &&
             !pvnum_in_group(ch,room->exit[dir]->pvnum))
    return FALSE;
 else if (IS_SET(room->exit[dir]->exit_info, EX_CLAN_BACKDOOR) &&
	     !IS_NPC(ch) && ((ch->pcdata->clan != NULL &&
             (get_clan_from_vnum(room->exit[dir]->pvnum)!= ch->pcdata->clan) )
	     || ch->pcdata->clan == NULL ))
    return FALSE;
 else
    return TRUE;
}

 
/*                                                             
 * 1/3/99 -Martin
 * Checks if str is a name in namelist supporting multiple keywords
 */
bool nifty_is_name( char *str, char *namelist )
{
    char name[MAX_INPUT_LENGTH];

    if ( !str || str[0] == '\0' )
      return FALSE;

    for ( ; ; )
    {
        str = one_argument( str, name );
        if ( name[0] == '\0' )
            return TRUE;
        if ( !is_name( name, namelist ) )
            return FALSE;
    }
}

char *str_replace( char *in_str, char *orig_txt, char *new_txt)
  {
  char *pti, *pto, *ptt, *ptb;
  char pi, pb;
  bool foundit;

  if( in_str==NULL || orig_txt==NULL || new_txt==NULL )
    return( NULL );

  if( *in_str=='\0' )
    {
    *str_replace_txt = '\0';
    return( str_replace_txt );
    }

  if( *orig_txt=='\0' )
    {
    strcpy( str_replace_txt, in_str );
    return( str_replace_txt );
    }

  for( pti=in_str, pto=str_replace_txt; *pti!='\0'; pti++, pto++)
    {
    pi=0;
    pb=0;
    foundit=TRUE;
    for(ptt=pti,ptb=orig_txt;foundit && *ptt!='\0' && *ptb!='\0';)
      {
      pi = *ptt;
      pb = *ptb;
      if( pi>='a' && pi<='z' )
        pi -= ('a'-'A');
      if( pb>='a' && pb<='z' )
        pb -= ('a'-'A');
      if( pi!=pb )
        foundit=FALSE;
      ptb++;
      ptt++;
      if( *ptb=='\0' )
        foundit=FALSE;
      }
    if( foundit )  /* Found it */
      {
      for( ptb=new_txt; *ptb!='\0'; )
        {
        *pto = *ptb;
        ptb++;
        if( *ptb!='\0' )
          pto++;
        }
      pti = ptt-1;
      }
    else
      *pto = *pti;
    }
  *pto='\0';
  return( str_replace_txt );
  }

bool str_contains( char *in_str, char *orig_txt)
  {
  char *pti, *ptt, *ptb;
  char pi, pb;

  if( in_str==NULL || orig_txt==NULL )
    return( FALSE );

  if( *in_str=='\0' )
    {
    return( FALSE );
    }

  if( *orig_txt=='\0' )
    {
    return( FALSE );
    }

  for( pti=in_str; *pti!='\0'; pti++)
    {
    pi=0;
    pb=0;
    for(ptt=pti, ptb=orig_txt; pb==pi && *ptt!='\0' && *ptb!='\0'; ptb++, ptt++)
      {
      pi = *ptt;
      pb = *ptb;
      if( pi>='a' && pi<='z' )
        pi -= ('a'-'A');
      if( pb>='a' && pb<='z' )
        pb -= ('a'-'A');
      }
    if( *ptb=='\0' )  /* Found it */
      return( TRUE );
    }
  return( FALSE );
  }