DalekenMUD/
DalekenMUD/data/
DalekenMUD/data/notes/
DalekenMUD/data/player/
DalekenMUD/data/system/poses/
DalekenMUD/doc/Homepage/images/
DalekenMUD/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael	 ||
    || Chastain, Michael Quan, and Mitchell Tse.			 ||
    || Original Diku Mud copyright (C) 1990, 1991			 ||
    || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt,	 ||
    || Tom Madsen, and Katja Nyboe.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  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.					 ||
    || ----------------------------------------------------------------- ||
    ||                            handler.c                              ||
    || General functions for manipulation of stuff.                      ||
 *_/<>\_________________________________________________________________/<>\_*/


#include <math.h>
#include "mud.h"
#include "event.h"

/*
 * Local functions.
 */


/*
 * Retrieve a character's trusted level for permission checking.
 */
int get_trust( CHAR_DATA *ch )
{
    if( ch->desc && ch->desc->original )
	ch = ch->desc->original;

    if( ch->trust != 0 )
	return ch->trust;

    if( IS_NPC( ch ) && ch->level >= LEVEL_HERO )
	return LEVEL_HERO - 1;
    else
	return UMAX( 1, ch->level );
}


/*
 * Retrieve a character's age.
 * (428400 = 30 secs/mud hour * 24 hours/day * 35 days/month * 17 months/year
 *  - Kahn)
 */
int get_age( CHAR_DATA *ch )
{
    if( IS_NPC( ch ) )
	return 15 + ch->level;
    if( race_table[ch->race].base_age )
	return race_table[ch->race].base_age + ( ch->played + (int)( current_time - ch->logon ) ) / 428400;
    return 0;
}


int get_stat_mods( CHAR_DATA * ch, int apply )
{
    OBJ_DATA *obj;
    AFFECT_DATA *af;
    int amnt = 0;

    for( af = ch->affected; af; af = af->next )
    {
	if( af->deleted || af->location != apply )
	    continue;
	if( af->type == gsn_continuous_effect
	    || af->type == gsn_racial_fatigue
	    || af->type == gsn_religious
	    || af->type == gsn_delayed_effect )
	    continue;
	amnt -= af->modifier;
    }

    for( obj = ch->carrying; obj; obj = obj->next_content )
    {
	if( obj->deleted || obj->wear_loc == WEAR_NONE )
	    continue;
	for( af = obj->affected; af; af = af->next )
	{
	    if( af->deleted || af->location != apply )
		continue;
	    if( af->type == gsn_continuous_effect
		|| af->type == gsn_racial_fatigue
		|| af->type == gsn_religious
		|| af->type == gsn_delayed_effect )
		continue;
	    amnt -= af->modifier;
	}

	for( af = obj->pIndexData->affected; af; af = af->next )
	{
	    if( af->deleted || af->location != apply )
		continue;
	    if( af->type == gsn_continuous_effect
		|| af->type == gsn_racial_fatigue
		|| af->type == gsn_religious
		|| af->type == gsn_delayed_effect )
		continue;
	    amnt -= af->modifier;
	}
    }

    return amnt;
}

int get_max_hit( CHAR_DATA *ch )
{
    return ch->max_hit + get_stat_mods( ch, APPLY_HIT );
}


int get_max_mana( CHAR_DATA *ch, int which )
{
    switch( which )
    {
    case MAGIC_AIR:
	return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_AIR );
    case MAGIC_EARTH:
	return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_EARTH );
    case MAGIC_FIRE:
	return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_FIRE );
    case MAGIC_SPIRIT:
	return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_SPIRIT );
    case MAGIC_WATER:
	return ch->max_mana[which] + get_stat_mods( ch, APPLY_MANA_WATER );
    }
    bug( "Getting max mana %d for %s.", which, ch->name );
    return 0;
}


int get_max_move( CHAR_DATA *ch )
{
    return ch->max_move + get_stat_mods( ch, APPLY_MOVE );
}


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

    if( IS_NPC( ch ) )
    {
	max = ( int )log( ( double )ch->level );
	if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_STR )
	    max += max / 2;
	return 15 + race_table[ch->race].str_mod + max;
    }

    if( class_table[ch->class].attr_prime == APPLY_STR )
	max += 2;
    if( get_first_class( ch ) != CLASS_NONE
	&& class_table[get_first_class( ch )].attr_prime == APPLY_STR )
	max++;
    return ch->pcdata->perm_str + UMIN( ch->pcdata->mod_str, max );
}


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

    if( IS_NPC( ch ) )
    {
	max = ( int )log( ( double )ch->level );
	if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_INT )
	    max += max / 2;
	return 15 + race_table[ch->race].int_mod + max;
    }

    if( class_table[ch->class].attr_prime == APPLY_INT )
	max += 2;
    if( get_first_class( ch ) != CLASS_NONE
	&& class_table[get_first_class( ch )].attr_prime == APPLY_INT )
	max++;
    return ch->pcdata->perm_int + UMIN( ch->pcdata->mod_int, max );
}


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

    if( IS_NPC( ch ) )
    {
	max = ( int )log( ( double )ch->level );
	if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_WIS )
	    max += max / 2;
	return 15 + race_table[ch->race].wis_mod + max;
    }

    if( class_table[ch->class].attr_prime == APPLY_WIS )
	max += 2;
    if( get_first_class( ch ) != CLASS_NONE
	&& class_table[get_first_class( ch )].attr_prime == APPLY_WIS )
	max++;
    return ch->pcdata->perm_wis + UMIN( ch->pcdata->mod_wis, max );
}


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

    if( IS_NPC( ch ) )
    {
	max = ( int )log( ( double )ch->level );
	if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_DEX )
	    max += max / 2;
	return 15 + race_table[ch->race].dex_mod + max;
    }

    if( class_table[ch->class].attr_prime == APPLY_DEX )
	max += 2;
    if( get_first_class( ch ) != CLASS_NONE
	&& class_table[get_first_class( ch )].attr_prime == APPLY_DEX )
	max++;
    return ch->pcdata->perm_dex + UMIN( ch->pcdata->mod_dex, max );
}


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

    if( IS_NPC( ch ) )
    {
	max = ( int )log( ( double )ch->level );
	if( ch->class >= 0 && class_table[ch->class].attr_prime == APPLY_CON )
	    max += max / 2;
	return 15 + race_table[ch->race].con_mod + max;
    }

    if( class_table[ch->class].attr_prime == APPLY_CON )
	max += 2;
    if( get_first_class( ch ) != CLASS_NONE
	&& class_table[get_first_class( ch )].attr_prime == APPLY_CON )
	max++;
    return ch->pcdata->perm_con + UMIN( ch->pcdata->mod_con, max );
}


/*
 * Retrieve character's current magic stat.
 */
int get_magic( CHAR_DATA *ch, int sphere )
{
    if( IS_NPC( ch ) )
	return (int)log( ch->level ) + ch->level / 50;
    return ch->pcdata->perm_magic[sphere]
	+ UMIN( ch->pcdata->mod_magic[sphere], 5 );
}


/* array summer for magic/mana stats */
int total_mana( int *arr )
{
    int i, tot = 0;
    for( i = 0; i < MAGIC_MAX; ++i )
	tot += arr[i];
    return tot;
}


int get_magic_resist( CHAR_DATA *ch )
{
    return ch->saving_throw + race_table[ch->race].magic_resist;
}

/*
 * Retrieve character's current resilience.
 */
int get_curr_resil( CHAR_DATA *ch )
{
    int resil;

    resil = race_table[ch->race].resil + ch->resil_mod;
    if( !IS_NPC( ch ) )
	resil -= ch->pcdata->condition[COND_DRUNK] / 10;
    return resil;
}


/*
 * Retrieve character's current body temperature.
 */
int get_curr_temp( CHAR_DATA *ch )
{
    return race_table[ch->race].body_temp + URANGE( -100, ch->temp_mod, 100 );
}


/*
 * How hot/cold is it in here?
 */
int get_room_temp( ROOM_INDEX_DATA *room )
{
    int temp;

    if( !room )
	return room->area->plane->weather.temperature;
    temp = room->area->ave_temp;
    if( !IS_SET( room->room_flags, ROOM_UNDERGROUND ) )
	temp += room->area->plane->weather.temperature;

    if( IS_SET( room->room_flags, ROOM_HOT ) )
	temp += 40;
    if( IS_SET( room->room_flags, ROOM_COLD ) )
	temp -= 40;

    switch( room->sector_type )
    {
    case SECT_MOUNTAIN:
	temp -= 10;
	break;
    case SECT_WATER_SWIM:
    case SECT_WATER_NOSWIM:
    case SECT_UNDERWATER:
	temp /= 2;
	break;
    case SECT_AIR:
	temp = temp * 3 / 2;
	break;
    case SECT_DESERT:
	temp += 15;
	break;
    case SECT_SPACE:
	temp -= 32;
	break;
    default:
	break;
    }

    if( IS_SET( room->room_flags, ROOM_FLOODED ) )
	temp /= 2;
    return temp + dice( 2, 3 ) - 4;
}


/*
 * Retrieve character's current hitroll
 */
int get_hitroll( CHAR_DATA *ch )
{
    int hitroll = ch->hitroll;

    if( !IS_NPC( ch ) )
    {
	hitroll += ch->level * 2 / 3;
	hitroll += power( 5, 10, get_curr_str( ch ) - 15 ) - 4;
	hitroll += get_speed( ch ) / 2;
    }

    if( IS_AFFECTED( ch, AFF_BLEEDING ) )
	hitroll = hitroll * 9 / 10;

    return hitroll;
}


/*
 * Retrieve character's current damroll
 */
int get_damroll( CHAR_DATA *ch )
{
    int damroll;

    damroll = ch->damroll;
    damroll += UMAX( -1, power( 9, 9, get_curr_str( ch ) - 15 ) - 8 );
    if( IS_NPC( ch ) )
	damroll += ch->level / 2;
    if( IS_AFFECTED( ch, AFF_BLEEDING ) )
	damroll -= 10;

    return damroll;
}


/*
 * Speed.
 * Bonus: dex (if awake).
 * Penalty: drunkeness / 10, weight carried over half maximum.
 * Opponent level affects your speed.
 */
int get_speed( CHAR_DATA *ch )
{
    int init;
    int tmp;

    init = ch->speed;
    if( IS_AWAKE( ch ) )
    {
	init -= 15;
	if( IS_NPC( ch ) )
	    init += 12 + race_table[ch->race].dex_mod + ch->level / 3;
	else
	    init += ch->pcdata->perm_dex + ch->pcdata->mod_dex;
    }
    if( ch->fighting )
	init += ch->level - ch->fighting->level;
    if( !IS_NPC( ch ) )
    {
	init -= ch->pcdata->condition[COND_DRUNK] / 10;
	tmp = ch->carry_weight * 1000 / UMAX( 1, can_carry_w( ch ) );
	tmp = URANGE( 0, tmp, 2500 );
	if( tmp > 600 )		/* over 60% of weight limit */
	    init -= ( tmp - 600 ) / 20;
    }
    return init;
}


int get_size( CHAR_DATA *ch )
{
    return race_table[ch->race].size + ch->size_mod;
}


int get_success( CHAR_DATA *ch, int sn, int percent )
{
    int hehe;

    if( IS_AFFECTED( ch, AFF_MIND_MIST ) )
	return 0;

    if( IS_NPC( ch ) )
    {
	if( ch->class == CLASS_NONE )
	    hehe = 85;
	else if( can_prac( ch, sn ) )
	    hehe = class_table[ch->class].skill_adept;
	else
	    hehe = 0;
	if( number_percent( ) <= hehe )
	    return hehe;
	else
	    return 0;
    }
    else if( IS_SET( ch->pcdata->pc_bits, PC_BIT_RACIAL ) )
	return 80;

    if( ch->pcdata->learned[sn] < 25 )
	hehe = 0;
    else
    {
	hehe = 22 - ch->pcdata->perm_int + UMIN( 7, ch->pcdata->mod_int );
	hehe = power( 900, 7, hehe );
	hehe += skill_table[sn].usage * hehe / 650;
	hehe = number_range( 1, hehe );
    }

    if( ch->pcdata->learned[sn] * percent / 100 >= number_percent( ) )
    {
	if( hehe == 1 )
	{
	    send_to_char( "&BYou feel a faint tingling sensation.&n\n\r", ch );
	    act( "&BYou learned something about $t!",
		 ch, skill_table[sn].name, NULL, TO_CHAR );
	    ch->pcdata->learned[sn] += 1;
	}

	return ch->pcdata->learned[sn];
    }
    else if( hehe == 1 )
    {
	send_to_char( "&BYou feel a faint tingling sensation.&n\n\r", ch );
	act( "&BYou learn from your mistakes and you learn about $t!",
	     ch, skill_table[sn].name, NULL, TO_CHAR );
	ch->pcdata->learned[sn] += 1;
    }
    return 0;
}


bool can_prac( CHAR_DATA *ch, int sn )
{
    int i;

    if( IS_NPC( ch ) )
    {
	if( ( ch->class == CLASS_NONE
	      && skill_table[sn].skill_level[0] <= L_APP )
	    || ( ch->class != CLASS_NONE
		 && skill_table[sn].skill_level[ch->class] <= ch->level ) )
	    return TRUE;
	return FALSE;
    }

    if( get_trust( ch ) >= skill_table[sn].skill_level[ch->class]
	|| IS_SET( ch->pcdata->pc_bits, PC_BIT_RACIAL ) )
	return TRUE;
    for( i = 0; i < NUM_MULTI_CLASS; ++i )
    {
	if( ch->level < skill_table[sn].skill_level[i] )
	    continue;
	if( ch->pcdata->multi_class[i] == CLASS_ASPIRING
	    && ch->sublevel >= skill_table[sn].skill_level[i] )
	    return TRUE;
	if( ch->level == LEVEL_HERO
	    && ch->sublevel / 4 >= skill_table[sn].skill_level[i] )
	    return TRUE;
	if( ch->pcdata->multi_class[i] >= CLASS_ADEPT
	    && ch->level >= skill_table[sn].skill_level[i] )
	    return TRUE;
    }
    return FALSE;
}


bool can_use( CHAR_DATA *ch, int sn )
{
    if( ( !IS_NPC( ch ) && IS_SET( ch->pcdata->pc_bits, PC_BIT_RACIAL ) )
	|| ( can_prac( ch, sn )
	     && ( IS_NPC( ch ) || ch->pcdata->learned[sn] > 0 ) ) )
	return TRUE;
    return FALSE;
}


int get_first_class( CHAR_DATA *ch )
{
    int i;

    if( IS_NPC( ch ) )
	return CLASS_NONE;

    for( i = 0; i < AVAIL_CLASS; ++i )
    {
	if( ch->pcdata->multi_class[i] == CLASS_FIRST )
	    return i;
    }

    return CLASS_NONE;
}


int get_second_class( CHAR_DATA *ch )
{
    int i;

    if( IS_NPC( ch ) )
	return CLASS_NONE;

    for( i = 0; i < AVAIL_CLASS; ++i )
    {
	if( ch->pcdata->multi_class[i] == CLASS_SECOND )
	    return i;
    }

    return CLASS_NONE;
}


int get_aspire_class( CHAR_DATA *ch )
{
    int i;

    if( IS_NPC( ch ) )
	return -1;

    for( i = 0; i < AVAIL_CLASS; ++i )
    {
	if( ch->pcdata->multi_class[i] == CLASS_ASPIRING )
	    return i;
    }

    return -1;
}


/*
 * Finds lord level, used for do_who after this
 */
int get_lord_level( CHAR_DATA *ch )
{
    int i, total;

    total = 0;
    for( i = 0; i < AVAIL_CLASS; ++i )
    {
	if( ch->pcdata->multi_class[i] == CLASS_ADEPT
	    || ch->pcdata->multi_class[i] == CLASS_FIRST )
	    total++;
    }

    return total;
}


/*
 * race_tnl().
 * This function uses fine precision and a while lot of constants
 * to figure out how much a race should need to level.
 * The constants here could be jiggled a little to discretion but be
 * wary of the effect it would have on ALL tnls.
 * --Symposium
 */

#define TWIST( diff )				\
if( (diff) > 0 )				\
    d = 1.045;					\
else						\
    d = 1.028;					\
d = pow( d, (diff) );				\
tnl = d * tnl;

#define SHAKE( diff, scale )			\
d = pow( (scale), (diff) );			\
tnl = d * tnl;

int race_tnl( int race )
{
    /* This static array allows this function to only be called once. */
    static int tnls[MAX_RACE];
    int i;
    float tnl = 1003270;	/* magic number, this is the base effect */
    const int race_perm[][2] = {
	{ RACE_BREATHING, 1062 },	{ RACE_INFRAVISION, 1015 },
	{ RACE_DETECT_INVIS, 1025 },	{ RACE_DETECT_HIDDEN, 1020 },
	{ RACE_DETECT_ALIGN, 1007 },	{ RACE_PROTECTION, 1100 },
	{ RACE_SANCT, 1500 },		{ RACE_NO_WEAPON_WIELD, 885 },
	{ RACE_MUTE, 700 },		{ RACE_NO_SUN, 930 },
	{ RACE_DUAL_WIELD, 1050 },	{ RACE_DARK_SIGHT, 1042 },
	{ RACE_NO_POISON, 1060 },	{ RACE_SWIM, 1030 },
	{ 0, 0 },
    };
    /*
     * Body parts.
     * less than 1000 will be multiplied if the part isn't there
     */
    const int race_part[][2] = {
	{ BODY_PART_TORSO, 875 },	{ BODY_PART_HEAD, 990 },
	{ BODY_PART_EYES, 800 },	{ BODY_PART_NOSE, 995 },
	{ BODY_PART_EAR_L, 995 },	{ BODY_PART_EAR_R, 995 },
	{ BODY_PART_ARM_L, 960 },	{ BODY_PART_ARM_R, 960 },
	{ BODY_PART_HAND_L, 910 },	{ BODY_PART_HAND_R, 910 },
	{ BODY_PART_FINGERS_L, 910 },	{ BODY_PART_FINGERS_R, 910 },
	{ BODY_PART_LEG_L, 940 },	{ BODY_PART_LEG_R, 940 },
	{ BODY_PART_FOOT_L, 960 },	{ BODY_PART_FOOT_R, 970 },
	{ BODY_PART_TAIL, 1020 },	{ BODY_PART_HORNS, 1025 },
	{ BODY_PART_WINGS, 1040 },	{ BODY_PART_LUNGS, 1050 },
	{ BODY_PART_GILLS, 950 },
	{ 0, 0 },
    };
    const char *sk;
    char buf[MAX_INPUT_LENGTH];
    int sn, lowest;
    float d;

    if( tnls[race] != 0 )
	return tnls[race];

    sk = race_table[race].racial_skill;
    while( sk && sk[0] != '\0' )
    {
	sk = one_argument( sk, buf );
	sn = skill_lookup( buf );
	lowest = LEVEL_HERO;
	for( i = 0; i < MAX_CLASS; ++i )
	    if( skill_table[sn].skill_level[i] < lowest )
		lowest = skill_table[sn].skill_level[i];
	tnl += lowest * 400;
    }

    for( i = 0; race_perm[i][0] != 0; i++ )
    {
	if( IS_SET( race_table[race].race_abilities, race_perm[i][0] ) )
	{
	    tnl *= race_perm[i][1];
	    tnl /= 1000;
	}
    }

    for( i = 0; race_part[i][0] != 0; i++ )
    {
	if( ( race_part[i][1] < 1000
	      && !IS_SET( race_table[race].body_parts,
			  race_part[i][0] ) )
	    || ( race_part[i][1] > 1000
		 && IS_SET( race_table[race].body_parts,
			    race_part[i][0] ) ) )
	{
	    tnl *= race_part[i][1];
	    tnl /= 1000;
	}
    }

    TWIST( race_table[race].str_mod );
    TWIST( race_table[race].int_mod );
    TWIST( race_table[race].wis_mod );
    TWIST( race_table[race].dex_mod );
    TWIST( race_table[race].con_mod );

    SHAKE( race_table[race].size - 30, 1.002 );
    SHAKE( race_table[race].hp_gain, 1.01 );
    for( i = 0; i < MAGIC_MAX; ++i )
    {
	if( race_table[race].mana_gain[i] > 0 )
	{
	    SHAKE( race_table[race].mana_gain[i], 1.0025 );
	}
	else
	{
	    SHAKE( race_table[race].mana_gain[i], 1.00125 );
	}
    }
    SHAKE( race_table[race].move_gain, 1.003 );
    SHAKE( ( race_table[race].magic_resist ) / 3.1, 1.01 );
    SHAKE( ( 1000 - race_table[race].resil ) / 12.2, 1.0162 );
    tnls[race] = ((int)tnl) / 1000;
    return tnls[race];
}

#undef TWIST

int get_tnl( CHAR_DATA *ch )
{
    int i;
    int tnl = race_tnl( ch->race );

    if( IS_NPC( ch ) )
	return tnl * 100;
    if( ch->class == CLASS_BUILDER || ch->class == CLASS_ANGEL )
	return 100000;

    for( i = 0; i < 5; i++ )
    {
	switch( ch->pcdata->multi_class[i] )
	{
	case CLASS_FIRST:
	    if( get_second_class( ch ) == CLASS_NONE )
		tnl = ( tnl * 4 ) / 3;
	    break;
	case CLASS_ADEPT:
	case CLASS_SECOND:
	    tnl = ( tnl * 5 ) / 3;
	    break;
	default:
	    break;
	}
    }
    if( ch->level == LEVEL_HERO )
	tnl += tnl * ch->sublevel / 100;

    return tnl * 100;
}


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

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

    return MAX_WEAR + get_curr_dex( ch ) / 2 + get_curr_str( ch );
}


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

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

    return UMIN( power( 500, 15, get_curr_str( ch ) - 20 ), 999 );
}


/* checks of ch is the owner of obj */
bool is_owner( CHAR_DATA *ch, OBJ_DATA *obj )
{
    char buf[MAX_INPUT_LENGTH];

    if( !IS_SET( obj->extra_flags, ITEM_OWNER ) )
	return FALSE;
    sprintf( buf, " {%s}", ch->name );
    if( !str_infix( buf, obj->name ) )
	return TRUE;
    return FALSE;
}


/*
 * Special is_name for objects that considers the unique key.
 */
bool is_obj_name( OBJ_DATA *obj, const char *str )
{
    if( str[0] == '#' )
    {
	if( LOWER( str[1] ) == 'o' )
	    return ( atoi( &str[2] ) == obj->unique_key ) ? TRUE : FALSE;
	return ( atoi( &str[1] ) == obj->unique_key ) ? TRUE : FALSE;
    }
    return is_name( str, obj->name );
}


/*
 * Special is_name for characters that considers the unique key.
 */
bool is_char_name( CHAR_DATA *ch, const char *str )
{
    if( str[0] == '#' )
    {
	if( LOWER( str[1] ) == 'o' )
	    return ( atoi( &str[2] ) == ch->unique_key ) ? TRUE : FALSE;
	return ( atoi( &str[1] ) == ch->unique_key ) ? TRUE : FALSE;
    }
    return is_name( str, ch->name );
}


/*
 * See if a string is one of the names of an object.
 * New is_name sent in by Alander.
 * Modified by Symposium to allow greater definition
 * Now only the prefix is matched and it also allows
 * multiple word matches. ie "wh gu" matches "white guard"
 */
bool is_name( const char *str, const char *namelist )
{
    const char *nextkey = str;
    char keybuf[MAX_INPUT_LENGTH];
    const char *nextpat;
    char patbuf[MAX_INPUT_LENGTH];
    bool wordmatch;

    do
    {
	nextkey = one_argument( nextkey, keybuf );
	if( keybuf[0] == '\0' )
	    return TRUE;

	wordmatch = FALSE;
	nextpat = namelist;
	do
	{
	    nextpat = one_argument( nextpat, patbuf );
	    if( !str_prefix( keybuf, patbuf ) )
		wordmatch = TRUE;
	}
	while( !wordmatch && patbuf[0] != '\0' );
    }
    while( wordmatch );
    return FALSE;
}


char *show_affect( char *outbuf, AFFECT_DATA *paf, bool extra, bool imm )
{
    char buf[MAX_INPUT_LENGTH];

    if( paf->type == gsn_perm_spell )
	sprintf( outbuf, "&gPermanent spell affect '&y%s&g'",
		 skill_table[paf->location].name );
    else if( paf->type == gsn_racial_fatigue )
	sprintf( outbuf, "&gRacial &y%s&g fatigue",
		skill_table[paf->location].name );
    else if( paf->type == gsn_religious )
	sprintf( outbuf, "&gReligious &y%s&g disfavour",
		     skill_table[paf->location].name );
    else if( paf->type == gsn_clan_power )
	sprintf( outbuf, "&gClan &y%s&g power fatigue",
		skill_table[paf->location].name );
    else if( paf->type > 0 )
    {
	strcpy( outbuf, "&gSpell:" );
	/* delayed effect code by incubus */
	if( paf->type == gsn_delayed_effect )
	    sprintf( buf, " '&y%s %s&g'", skill_table[paf->type].name,
		    skill_table[paf->location].name );
	else if( paf->type == gsn_continuous_effect )
	    sprintf( buf, " '&y%s&g' on the hour every hour",
		    skill_table[paf->location].name );
	else
	    sprintf( buf, " '&y%s&g'", skill_table[paf->type].name );
	strcat( outbuf, buf );
    }
    else
	strcpy( outbuf, "&gAffect:" );

    if( extra )
    {
	if( paf->type == gsn_racial_fatigue
	    || paf->type == gsn_religious
	    || paf->type == gsn_perm_spell )
	    ;
	else if( paf->type == gsn_delayed_effect
		 || paf->type == gsn_continuous_effect )
	{
	    sprintf( buf, " at level &y%d&g", paf->modifier );
	    strcat( outbuf, buf );
	}
	else if( paf->location == APPLY_BODY_PART )
	{
	    sprintf( buf, " modifies &cbody parts&g by &c%s&g",
		    flag_string( body_part_flags, &paf->modifier ) );
	    strcat( outbuf, buf );
	}
	else if( paf->location != APPLY_NONE && paf->modifier )
	{
	    sprintf( buf, " modifies &c%s&g by &c%d&g",
		    flag_string( apply_name_flags, &paf->location ),
		    paf->modifier );
	    strcat( outbuf, buf );
	}

	if( paf->type == gsn_perm_spell )
	    ;
	else if( paf->type == gsn_delayed_effect )
	{
	    if( paf->duration >= 0 )
		sprintf( buf, " goes off in &c%d&g hours", paf->duration );
	    else
		strcpy( buf, ", indeterminate time until effect" );
	    strcat( outbuf, buf );
	}
	else if( paf->duration >= 0 )
	{
	    sprintf( buf, " for &c%d&g hours", paf->duration );
	    strcat( outbuf, buf );
	}
	else
	{
	    strcat( outbuf, " in permanence" );
	}
    }
    if( imm && !vnull( paf->bitvector ) )
    {
	sprintf( buf, " with bits %s",
		flag_string( affect_flags, paf->bitvector ) );
	strcat( outbuf, buf );
    }
    strcat( outbuf, ".&n\n\r" );
    return outbuf;
}


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

    mod = paf->modifier;

    /* Incubus's delayed spell effect */
    if( !fAdd )
    {
	if( paf->type == gsn_delayed_effect )
	{
	    /* hit the character with the spell... */
	    ( *skill_table[paf->location].spell_fun )
		( paf->location, URANGE( 1, paf->modifier, LEVEL_HERO * 2 ),
		  ch, ch );
	    return;
	}
    }

    if( paf->type == gsn_continuous_effect
	|| paf->type == gsn_racial_fatigue
	|| paf->type == gsn_religious
	|| paf->type == gsn_delayed_effect )
	return;

    if( fAdd )
    {
	if( xIS_SET( paf->bitvector, AFF_DARKNESS ) )
	    ch->in_room->light -= 50;
	for ( i = 0; i < MAX_VECTOR; i++ )
	    SET_BIT( ch->affected_by[i], paf->bitvector[i] );
    }
    else
    {
	if( xIS_SET( paf->bitvector, AFF_DARKNESS ) )
	    ch->in_room->light += 50;

	for ( i = 0; i < MAX_VECTOR; i++ )
	    REMOVE_BIT( ch->affected_by[i], paf->bitvector[i] );
	if( paf->location != APPLY_BODY_PART )
	    mod = 0 - mod;
    }

    switch( paf->location )
    {
    default:
	bug( "Affect_modify: unknown location %d on %s.",
	     paf->location, ch->name );
	return;

    case APPLY_NONE:
    case APPLY_GOLD:
    case APPLY_EXP:
    case APPLY_CLASS:
    case APPLY_LEVEL:
    case APPLY_AGE:
    case APPLY_HEIGHT:
    case APPLY_WEIGHT:
    case APPLY_APPEARANCE:				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;					break;
    case APPLY_RACE:
	ch->race += mod;				break;
    case APPLY_HIT:
	ch->max_hit += mod;				break;
    case APPLY_MOVE:
	ch->max_move += mod;				break;
    case APPLY_AC:
	ch->armour += mod;				break;
    case APPLY_HITROLL:
	ch->hitroll += mod;				break;
    case APPLY_DAMROLL:
	ch->damroll += mod;				break;
    case APPLY_RESILIENCE:
	ch->resil_mod += mod;				break;
    case APPLY_BODY_TEMP:
	ch->temp_mod += mod;				break;
    case APPLY_BODY_PART:
	if( fAdd )
	    SET_BIT( ch->body_parts, mod );
	else
	    REMOVE_BIT( ch->body_parts, mod );
	break;
    case APPLY_SPEED:
	ch->speed += mod;				break;
    case APPLY_SIZE:
	ch->size_mod += mod;				break;
    case APPLY_AIR:
	if( !IS_NPC( ch ) )
	    ch->pcdata->mod_magic[MAGIC_AIR] += mod;
	break;
    case APPLY_EARTH:
	if( !IS_NPC( ch ) )
	    ch->pcdata->mod_magic[MAGIC_EARTH] += mod;
	break;
    case APPLY_FIRE:
	if( !IS_NPC( ch ) )
	    ch->pcdata->mod_magic[MAGIC_FIRE] += mod;
	break;
    case APPLY_SPIRIT:
	if( !IS_NPC( ch ) )
	    ch->pcdata->mod_magic[MAGIC_SPIRIT] += mod;
	break;
    case APPLY_WATER:
	if( !IS_NPC( ch ) )
	    ch->pcdata->mod_magic[MAGIC_WATER] += mod;
	break;
    case APPLY_MANA_AIR:
	ch->max_mana[MAGIC_AIR] += mod;		break;
    case APPLY_MANA_EARTH:
	ch->max_mana[MAGIC_EARTH] += mod;	break;
    case APPLY_MANA_FIRE:
	ch->max_mana[MAGIC_FIRE] += mod;	break;
    case APPLY_MANA_SPIRIT:
	ch->max_mana[MAGIC_SPIRIT] += mod;	break;
    case APPLY_MANA_WATER:
	ch->max_mana[MAGIC_WATER] += mod;	break;
    case APPLY_MAGIC_RESIST:
	ch->saving_throw += mod;			break;
    }

    if( IS_NPC( ch ) )
	return;

    /*
     * Check for PC weapon wielding.
     * Guard against recursion (for weapons with affects).
     * If more than one weapon, drop weapon 2 first, then recheck.
     * And yes, it does work.  :)  --- Thelonius ( Monk )
     */
    if( ( wield = get_eq_char( ch, WEAR_WIELD_R ) )
	|| ( wield = get_eq_char( ch, WEAR_WIELD_DOUBLE ) ) )
    {
	if( ( wield2 = get_eq_char( ch, WEAR_WIELD_L ) ) )
	{
	    if( ( ( get_obj_weight( wield ) + get_obj_weight( wield2 ) )
		  > str_app_wield( get_curr_str( ch ) ) )
		|| IS_SET( race_table[ch->race].race_abilities,
			   RACE_NO_WEAPON_WIELD ) )
	    {
		static int depth;

		if( depth == 0 )
		{
		    depth++;
		    act( "You drop $p.", ch, wield2, NULL, TO_CHAR );
		    act( "$n drops $p.", ch, wield2, NULL, TO_ROOM );
		    obj_from_char( wield2 );
		    obj_to_room( wield2, ch->in_room );
		    depth--;
		}
	    }
	}
	else if( ( get_obj_weight( wield ) > str_app_wield( get_curr_str( ch ) ) )
		 || IS_SET( race_table[ch->race].race_abilities,
			    RACE_NO_WEAPON_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 );
		obj_to_room( wield, ch->in_room );
		depth--;
	    }
	}
    }
    else if( ( wield2 = get_eq_char( ch, WEAR_WIELD_L ) )
	     && ( get_obj_weight( wield2 ) > str_app_wield( get_curr_str( ch ) )
		  || IS_SET( race_table[ch->race].race_abilities,
			     RACE_NO_WEAPON_WIELD ) ) )
    {
	static int depth;

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

    if( ( ch->affected_by[0] & ( AFF_BLEEDING | AFF_HIDE ) ) > 0 )
    {
	ch->affected_by[0] &= ( AFF_BLEEDING | AFF_HIDE );
	for( i = 1; i < MAX_VECTOR; i++ )
	    ch->affected_by[i] = 0;
    }
    else
	vzero( ch->affected_by );
    /* put all the old AFF_* bits back on */
    for( af = ch->affected; af; af = af->next )
    {
	if( af->deleted || ( af == paf && !fAdd ) )
	    continue;
	for ( i = 0; i < MAX_VECTOR; i++ )
	    SET_BIT( ch->affected_by[i], af->bitvector[i] );
    }

    for( mod = 1; mod < MAX_WEAR; mod++ )
    {
	wield = get_eq_char( ch, mod );
	if( wield )
	{
	    for( af = wield->pIndexData->affected; af; af = af->next )
	    {
		if( af->deleted || ( af == paf && !fAdd ) )
		    continue;
		for ( i = 0; i < MAX_VECTOR; i++ )
		    SET_BIT( ch->affected_by[i], af->bitvector[i] );
	    }

	    for( af = wield->affected; af; af = af->next )
	    {
		if( af->deleted || ( af == paf && !fAdd ) )
		    continue;
		for ( i = 0; i < MAX_VECTOR; i++ )
		    SET_BIT( ch->affected_by[i], af->bitvector[i] );
	    }
	}
    }
    if( !fAdd && xIS_SET( paf->bitvector, AFF_FLYING ) )
	char_fall_check( ch, 0 );
    if( fAdd && xIS_SET( paf->bitvector, AFF_HOLD ) )
	web_update( ch );

    return;
}


/*
 * Give an affect to a char.
 */
bool affect_to_char( CHAR_DATA *ch, AFFECT_DATA *paf, CHAR_DATA *caster )
{
    AFFECT_DATA *paf_old;
    AFFECT_DATA *paf_new;
    double per;

    paf_new = new_affect( );
    *paf_new = *paf;

    if( caster && !IS_NPC( caster )
	&& caster->pcdata->learned[gsn_magic_lore] > 105 )
    {
	paf_new->duration *= caster->pcdata->learned[gsn_magic_lore];
	paf_new->duration /= 100;
    }

    for( paf_old = ch->affected; paf_old; paf_old = paf_old->next )
    {
	if( paf_old->deleted )
	    continue;
	if( paf_old->type == paf->type
	    && paf_old->location == paf->location
	    && vequal( paf_old->bitvector, paf->bitvector ) )
	{
	    if( paf_old->duration == -1 )
		return FALSE;
	    if( paf->duration == -1 )
		break;
	    if( caster && !IS_NPC( caster )
		&& caster->pcdata->learned[gsn_magic_lore] > 5 )
	    {
		if( caster->pcdata->learned[gsn_magic_lore] < 105 )
		{
		    per = caster->pcdata->learned[gsn_magic_lore] - 5;
		    per = pow( per / 100, paf_old->duration / paf_new->duration );
		    per *= paf_new->duration;
		    paf_new->duration = paf_old->duration + (int)per;
		}
		else
		    paf_new->duration += paf_old->duration;
	    }

	    affect_remove( ch, paf_old );
	    break;
	}
    }

    paf_new->duration = UMIN( paf_new->duration, 250 );
    paf_new->deleted = FALSE;
    paf_new->next = ch->affected;
    ch->affected = paf_new;

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


/*
 * Remove an affect from a char.
 */
void affect_remove( CHAR_DATA *ch, AFFECT_DATA *paf )
{
    if( !ch->affected )
    {
	bug( "Affect_remove: no affect." );
	return;
    }

    affect_modify( ch, paf, FALSE );
    paf->deleted = TRUE;

    return;
}


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

    if( sn == gsn_awen )
    {
	affect_strip( ch, skill_lookup( "bless" ) );
	affect_strip( ch, skill_lookup( "armour" ) );
	affect_strip( ch, skill_lookup( "holy armour" ) );
	affect_strip( ch, skill_lookup( "holy aura" ) );
	affect_strip( ch, skill_lookup( "protection" ) );
    }
    else if( sn == gsn_foci )
    {
	affect_strip( ch, skill_lookup( "giant strength" ) );
	affect_strip( ch, skill_lookup( "phase shift" ) );
	affect_strip( ch, skill_lookup( "shield" ) );
	affect_strip( ch, skill_lookup( "fly" ) );
	affect_strip( ch, skill_lookup( "stone skin" ) );
	affect_strip( ch, skill_lookup( "haste" ) );
    }
    else if( sn == gsn_fortitudes )
    {
	affect_strip( ch, skill_lookup( "thought shield" ) );
	affect_strip( ch, skill_lookup( "mental barrier" ) );
	affect_strip( ch, skill_lookup( "combat mind" ) );
	affect_strip( ch, skill_lookup( "displacement" ) );
	affect_strip( ch, skill_lookup( "flesh armour" ) );
	affect_strip( ch, skill_lookup( "energy containment" ) );
    }

    for( paf = ch->affected; paf; paf = paf->next )
    {
	if( paf->deleted || paf->type != sn )
	    continue;
	affect_remove( ch, paf );
	if( !mesg && paf->type > 0 && skill_table[paf->type].msg_off )
	{
	    act( skill_table[paf->type].msg_off, ch, NULL, NULL, TO_CHAR );
	    mesg = TRUE;
	}
    }

    return;
}


/*
 *  Strip all affects matching paf.
 */
void affect_strip_special( CHAR_DATA *ch, AFFECT_DATA *af )
{
    AFFECT_DATA *paf;

    for( paf = ch->affected; paf; paf = paf->next )
    {
	if( paf->deleted )
	    continue;
	if( paf->type == af->type && paf->location == af->location )
	    affect_remove( ch, paf );
    }

    return;
}


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

    for( paf = ch->affected; paf; paf = paf->next )
    {
	if( paf->deleted )
	    continue;
	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;

    for( paf_old = ch->affected; paf_old; paf_old = paf_old->next )
    {
	if( paf_old->deleted )
	    continue;
	if( paf_old->type == paf->type
	    && paf_old->location == paf->location
	    && vequal( paf_old->bitvector, paf->bitvector ) )
	{
	    paf->duration += paf_old->duration;
	    paf->modifier += paf_old->modifier;
	    affect_remove( ch, paf_old );
	    break;
	}
    }

    affect_to_char( ch, paf, NULL );
    return;
}


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

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

    /* See note below in char_to_room */
    if( !IS_IMMORTAL( ch ) || !xIS_SET( ch->act, PLR_WIZINVIS ) )
    {
	if( !IS_NPC( ch ) )
	    --ch->in_room->area->nplayer;
	else
	    --ch->in_room->area->nmobile;

	if( IS_AFFECTED( ch, AFF_DARKNESS ) )
	    ch->in_room->light += 50;

	for( obj = ch->carrying; obj; obj = obj->next_content )
	{
	    if( obj->deleted || obj->wear_loc == WEAR_NONE )
		continue;

	    if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 )
	    {
		if( IS_SET( obj->extra_flags, ITEM_DARK ) )
		    ch->in_room->light += 30;
		else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
		    ch->in_room->light -= 20;
		else
		    ch->in_room->light -= 10;
	    }
	    else if( IS_SET( obj->extra_flags, ITEM_DARK ) )
		ch->in_room->light += 3;
	    else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
		ch->in_room->light -= 2;
	}
    }

    if( ch == ch->in_room->people )
    {
	ch->in_room->people = ch->next_in_room;
    }
    else
    {
	CHAR_DATA *prev;

	for( prev = ch->in_room->people; prev; prev = prev->next_in_room )
	{
	    if( prev->next_in_room == ch )
	    {
		prev->next_in_room = ch->next_in_room;
		break;
	    }
	}

	if( !prev )
	    bug( "Char_from_room: ch not found." );
    }

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


/*
 * Move a char into a room.
 */
void char_to_room( CHAR_DATA *ch, ROOM_INDEX_DATA *pRoomIndex )
{
    OBJ_DATA *obj;
    bool boat = FALSE;
    EXIT_DATA *pexit;

    if( !pRoomIndex )
    {
	bug( "Char_to_room: NULL." );
	return;
    }

    ch->in_room = pRoomIndex;
    ch->next_in_room = pRoomIndex->people;
    pRoomIndex->people = ch;

    /* Wizinvissed immortals leave no light trail, nor do they drown etc...
     * This is a security fix, no one should notice them for these slight
     * traces they leave.  Wizinvis code has also been fixed to compensate
     * for this.
     */
    if( IS_IMMORTAL( ch ) && xIS_SET( ch->act, PLR_WIZINVIS ) )
	return;

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

    if( IS_AFFECTED( ch, AFF_DARKNESS ) )
	ch->in_room->light -= 50;

    for( obj = ch->carrying; obj; obj = obj->next_content )
    {
	if( obj->item_type == ITEM_BOAT && !obj->deleted )
	    boat = TRUE;
	if( obj->deleted || obj->wear_loc == WEAR_NONE )
	    continue;

	if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 )
	{
	    if( IS_SET( obj->extra_flags, ITEM_DARK ) )
		ch->in_room->light -= 30;
	    else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
		ch->in_room->light += 20;
	    else
		ch->in_room->light += 10;
	}
	else if( IS_SET( obj->extra_flags, ITEM_DARK ) )
	    ch->in_room->light -= 3;
	else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
	    ch->in_room->light += 2;
    }

    /* falling and sinking */
    if( !IS_NPC( ch ) && !IS_AFFECTED( ch, AFF_FLYING )
	&& !IS_SET( ch->body_parts, BODY_PART_WINGS ) )
    {
	if( ch->in_room->sector_type == SECT_WATER_SWIM
	    || ch->in_room->sector_type == SECT_WATER_NOSWIM
	    || ch->in_room->sector_type == SECT_UNDERWATER
	    || IS_SET( ch->in_room->room_flags, ROOM_FLOODED ) )
	{
	    if( ( pexit = ch->in_room->exit[boat ? DIR_UP : DIR_DOWN] )
		&& ( !IS_SET( pexit->exit_info, EX_CLOSED )
		     || ( ( IS_SET( race_table[ch->race].race_abilities, RACE_PASSDOOR )
			    || IS_AFFECTED( ch, AFF_PASS_DOOR ) )
			  && !IS_SET( pexit->exit_info, EX_PASSPROOF ) ) )
		&& pexit->to_room )
		create_char_event( ch, evn_char_sink,
				   percent_fuzzy( 15 * PULSE_PER_SECOND, 10 ) );
	}
	else
	    char_fall_check( ch, 0 );
    }
    else if( !IS_AFFECTED( ch, AFF_BREATHING )
	     && ch->in_room->sector_type != SECT_WATER_SWIM
	     && ( ( ( ch->in_room->sector_type == SECT_UNDERWATER
		      || IS_SET( ch->in_room->room_flags, ROOM_FLOODED ) )
		    && !IS_SET( ch->body_parts, BODY_PART_GILLS ) )
		  || ( ch->in_room->sector_type != SECT_UNDERWATER
		       && !IS_SET( ch->body_parts, BODY_PART_LUNGS ) ) ) )
	create_char_event( ch, evn_char_drown,
			   number_fuzzy( 4 * PULSE_PER_SECOND ) );
    else if( ch->in_room->sector_type == SECT_SPACE
	     && !IS_AFFECTED( ch, AFF_BREATHING ) )
	create_char_event( ch, evn_char_suffocate,
			   number_fuzzy( 3 * PULSE_PER_SECOND ) );

    return;
}


void char_fall_check( CHAR_DATA *ch, int fallen )
{
    EXIT_DATA *pexit;
    EVENT *e;
    ROOM_INDEX_DATA *room = ch->in_room;
    int dam;

    if( xIS_SET( ch->affected_by, AFF_FLYING )
	|| IS_SET( ch->body_parts, BODY_PART_WINGS ) )
	return;

    pexit = room->exit[DIR_DOWN];
    if( IS_SET( room->room_flags, ROOM_FALL ) && pexit
	&& ( !IS_SET( pexit->exit_info, EX_CLOSED )
	     || ( ( IS_SET( race_table[ch->race].race_abilities, RACE_PASSDOOR )
		    || IS_AFFECTED( ch, AFF_PASS_DOOR ) )
		  && !IS_SET( pexit->exit_info, EX_PASSPROOF ) ) ) )
    {
	e = create_char_event( ch, evn_char_fall,
			      UMAX( 1, 5 - fallen ) );
	e->data[0] = UMIN( 25, fallen + 1 );	/* Terminal Velocity of 25 */
	return;
    }

    if( fallen <= 0 )
	return;

    dam = get_size( ch ) / 7 + 10;
    dam += ch->carry_weight / 10;
    dam *= fallen;

    if( room->sector_type == SECT_WATER_SWIM
	|| room->sector_type == SECT_WATER_NOSWIM
	|| room->sector_type == SECT_UNDERWATER
	|| IS_SET( room->room_flags, ROOM_FLOODED ) )
    {
	act( "$n land$% in the water with a big splash!",
	     ch, NULL, NULL, TO_ALL );
	dam = dam / 2 - 25;
    }
    else if( ( fallen = get_success( ch, gsn_catfall, 100 ) ) )
    {
	dam -= get_curr_dex( ch );
	dam = dam * 50 / UMAX( 50, fallen );
	send_to_char( "You land lightly, rolling to absorb the impact.\n\r", ch );
	act( "$n hits the ground and rolls over softening the landing.",
	     ch, NULL, NULL, TO_ROOM );
    }
    else
    {
	send_to_char( "You crash to the ground.\n\r", ch );
	act( "$n crashes into the ground with a huge THUMP!",
	     ch, NULL, NULL, TO_ROOM );
    }

    if( dam > 0 )
	damage( ch, ch, dam, skill_lookup( "earth bind" ), WEAR_NONE );
}



/*
 * The only problem with this function is that it can change the order of
 * items in a character's inventory, luckily it isn't needed all that often.
 */
void weight_change_object( OBJ_DATA *obj, int weight )
{
    CHAR_DATA *tmp_ch;
    OBJ_DATA *tmp_obj;

    if( obj->in_room )
    {
	obj->weight += weight;
    }
    else if( ( tmp_ch = obj->carried_by ) )
    {
	obj_from_char( obj );
	obj->weight += weight;
	obj_to_char( obj, tmp_ch );
    }
    else if( ( tmp_obj = obj->in_obj ) )
    {
	obj_from_obj( obj );
	obj->weight += weight;
	obj_to_obj( obj, tmp_obj );
    }
    else
    {
	bug( "Unknown attempt to subtract weight from an object." );
    }
}


/*
 * Give an obj to a char.
 */
void obj_to_char( OBJ_DATA *obj, CHAR_DATA *ch )
{
    /* owner flagged items without an owner fuse themselves to
       the first person that picks them up. */
    if( ( IS_SET( obj->extra_flags, ITEM_OWNER )
	  || ( get_trust( ch ) > L_HER && get_trust( ch ) < L_APP ) ) )
    {
	char *p;
	char buf[ MAX_INPUT_LENGTH ];

	if( !( p = strchr( obj->name, '{' ) ) || !strchr( p, '}' ) )
	{
	    act( "$p fuses firmly to your hand as you touch it.",
		 ch, obj, NULL, TO_CHAR );
	    sprintf( buf, "%s {%s}", obj->name, ch->name );
	    free_string( obj->name );
	    obj->name = str_dup( buf );
	    SET_BIT( obj->extra_flags, ITEM_OWNER );
	}
    }

    obj->next_content = ch->carrying;
    ch->carrying = obj;
    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 );
}


/*
 * Take an obj from its character.
 */
void obj_from_char( OBJ_DATA *obj )
{
    CHAR_DATA *ch;

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

    if( obj->item_type == ITEM_WEAPON && ch->level < L_JUN
	&& IS_SET( obj->extra_flags, ITEM_CHARGED ) )
    {
	act( "$p loses it's shine.", ch, obj, NULL, TO_CHAR );
	act( "$p loses it's shine.", ch, obj, NULL, TO_ROOM );
	REMOVE_BIT( obj->extra_flags, ITEM_CHARGED );
    }

    if( obj->wear_loc != WEAR_NONE )
	unequip_char( ch, obj );

    if( ch->carrying == obj )
    {
	ch->carrying = obj->next_content;
    }
    else
    {
	OBJ_DATA *prev;

	for( prev = ch->carrying; prev; prev = prev->next_content )
	{
	    if( prev->next_content == obj )
	    {
		prev->next_content = obj->next_content;
		break;
	    }
	}

	if( !prev )
	    bug( "Obj_from_char: obj not in list." );
    }


    obj->carried_by = NULL;
    obj->next_content = NULL;
    ch->carry_number--;		/* -= get_obj_number( obj ); */
    ch->carry_weight -= get_obj_weight( obj );
    return;
}


/*
 * Find the ac value of an obj, including position effect.
 */
int apply_ac( CHAR_DATA *ch, OBJ_DATA *obj, int iWear )
{
    int num = 0;

    if( obj->item_type != ITEM_ARMOUR )
	return 0;

    switch( iWear )
    {
    case WEAR_BODY:
	num = 3 * obj->value[0];
	break;

    case WEAR_HEAD:	case WEAR_LEGS:
    case WEAR_SHOULDERS:
	num = 2 * obj->value[0];
	break;

    case WEAR_HORNS:	case WEAR_EAR_L:
    case WEAR_EAR_R:	case WEAR_FACE:
    case WEAR_NOSE:	case WEAR_NECK_1:
    case WEAR_NECK_2:	case WEAR_FLOAT_L:
    case WEAR_FLOAT_R:	case WEAR_WINGS:
    case WEAR_ARMS:	case WEAR_WRIST_L:
    case WEAR_WRIST_R:	case WEAR_HANDS:
    case WEAR_FINGER_L:	case WEAR_FINGER_R:
    case WEAR_HOLD_R:	case WEAR_HOLD_L:
    case WEAR_SHIELD:	case WEAR_WAIST:
    case WEAR_FEET:	case WEAR_JUGGLED:
	num = obj->value[0];
	break;
    }

    if( !IS_NPC( ch ) && obj->required_skill > 0 )
    {
	num *= UMIN( ch->pcdata->learned[obj->required_skill], 100 );
	num /= 100;
    }

    return num;
}


/*
 * Find a piece of eq on a character.
 */
OBJ_DATA *get_eq_char( CHAR_DATA *ch, int iWear )
{
    OBJ_DATA *obj;

    for( obj = ch->carrying; obj; obj = obj->next_content )
    {
	if( obj->deleted )
	    continue;
	if( obj->wear_loc == iWear )
	    return obj;
    }

    return NULL;
}


OBJ_DATA *get_held( CHAR_DATA *ch, int type, bool first )
{
    OBJ_DATA *obj;
    obj = get_eq_char( ch, WEAR_HOLD_L );
    if( !obj || obj->item_type != type )
    {
	if( first )
	    obj = get_eq_char( ch, WEAR_HOLD_R );
    }
    if( !obj || obj->item_type != type )
	return NULL;
    return obj;
}


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

    if( iWear != WEAR_JUGGLED && get_eq_char( ch, iWear ) )
    {
	bug( "Equip_char: %s already equipped at %d.",
		   ch->name, iWear );
	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 ) ) )
    {
	/*
	 * Thanks to Morgenes for the bug fix here!
	 */
	act( "You are zapped by $p and drop it.", ch, obj, NULL, TO_CHAR );
	act( "$n is zapped by $p and drops it.", ch, obj, NULL, TO_ROOM );
	obj_from_char( obj );
	if( ch->in_room )
	    obj_to_room( obj, ch->in_room );
	else
	    obj_to_room( obj, get_room_index( ROOM_VNUM_TEMPLE ) );
	return;
    }

    if( !IS_NPC( ch ) && IS_OBJ_STAT( obj, ITEM_OWNER )
	&& !is_owner( ch, obj ) )
    {
	act( "$p is torn from your body with extreme force.",
	     ch, obj, NULL, TO_CHAR );
	act( "$p is forcefully torn from $n's body.", ch, obj, NULL, TO_ROOM );
	obj_from_char( obj );
	if( ch->in_room )
	    obj_to_room( obj, ch->in_room );
	else
	    obj_to_room( obj, get_room_index( ROOM_VNUM_TEMPLE ) );
	return;
    }

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

    for( paf = obj->pIndexData->affected; paf; paf = paf->next )
    {
	if( paf->type == gsn_perm_spell )
	{
	    if( paf->location < 0 || paf->location >= MAX_SKILL )
	    {
		bug( "Unknown perm spell location %d on vnum %d.",
			   paf->location, obj->pIndexData->vnum );
	    }
	    else if( !is_affected( ch, paf->location ) )
	    {
		AFFECT_DATA *caf;
		(*skill_table[paf->location].spell_fun)
		    ( paf->location, obj->level, ch, ch );
		for( caf = ch->affected; caf; caf = caf->next )
		    if( caf->type == paf->location )
			caf->duration = -1;
	    }
	}
	else if( paf->type > 0
		 && !IS_SET( skill_table[paf->type].skill_type,
			     SKILL_TYPE_ENCHANTMENT ) )
	{
	    affect_strip_special( ch, paf );
	    affect_to_char( ch, paf, NULL );
	}
	else
	    affect_modify( ch, paf, TRUE );
    }

    for( paf = obj->affected; paf; paf = paf->next )
    {
	if( paf->type == gsn_perm_spell )
	{
	    if( paf->location < 0 || paf->location >= MAX_SKILL )
	    {
		bug( "Unknown perm spell location %d on obj [%d].",
			   paf->location, obj->unique_key );
	    }
	    else if( !is_affected( ch, paf->location ) )
	    {
		AFFECT_DATA *caf;
		(*skill_table[paf->location].spell_fun)
		    ( paf->location, obj->level, ch, ch );
		for( caf = ch->affected; caf; caf = caf->next )
		    if( caf->type == paf->location )
			caf->duration = -1;
	    }
	}
	else if( paf->type > 0
		 && !IS_SET( skill_table[paf->type].skill_type,
			     SKILL_TYPE_ENCHANTMENT ) )
	{
	    affect_strip_special( ch, paf );
	    affect_to_char( ch, paf, NULL );
	}
	else
	    affect_modify( ch, paf, TRUE );
    }

    if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 )
    {
	if( IS_SET( obj->extra_flags, ITEM_DARK ) )
	    ch->in_room->light -= 30;
	else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
	    ch->in_room->light += 20;
	else
	    ch->in_room->light += 10;
    }
    else if( IS_SET( obj->extra_flags, ITEM_DARK ) )
	ch->in_room->light -= 3;
    else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
	ch->in_room->light += 2;

    if( ch->desc && !IS_SET( ch->desc->interpreter->flags, INTERPRETER_NOMESSAGE )
	&& obj->item_type != ITEM_FOOD && obj->item_type != ITEM_LIMB
	&& obj->action && obj->action[0] != '\0' )
	send_to_char( obj->action, ch );

    return;
}


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

    if( obj->wear_loc == WEAR_NONE )
    {
	bug( "Unequip_char: %s already unequipped with %d.",
		   ch->name, obj->pIndexData->vnum );
	return;
    }

    /* if juggling these should be replaced by one that is being juggled */
    if( obj->wear_loc == WEAR_WIELD_R || obj->wear_loc == WEAR_WIELD_L )
    {
	OBJ_DATA *juggled;

	if( ( juggled = get_eq_char( ch, WEAR_JUGGLED ) ) )
	    juggled->wear_loc = obj->wear_loc;
    }

    ch->armour += apply_ac( ch, obj, obj->wear_loc );
    obj->wear_loc = WEAR_NONE;

    for( paf = obj->pIndexData->affected; paf; paf = paf->next )
    {
	if( paf->type == gsn_perm_spell )
	    affect_strip( ch, paf->location );
	else if( paf->type > 0
		 && !IS_SET( skill_table[paf->type].skill_type,
			     SKILL_TYPE_ENCHANTMENT ) )
	    affect_strip_special( ch, paf );
	else
	    affect_modify( ch, paf, FALSE );
    }

    for( paf = obj->affected; paf; paf = paf->next )
    {
	if( paf->type == gsn_perm_spell )
	    affect_strip( ch, paf->location );
	else if( paf->type > 0
		 && !IS_SET( skill_table[paf->type].skill_type,
			     SKILL_TYPE_ENCHANTMENT ) )
	    affect_strip_special( ch, paf );
	else
	    affect_modify( ch, paf, FALSE );
    }

    if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 )
    {
	if( IS_SET( obj->extra_flags, ITEM_DARK ) )
	    ch->in_room->light += 30;
	else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
	    ch->in_room->light -= 20;
	else
	    ch->in_room->light -= 10;
    }
    else if( IS_SET( obj->extra_flags, ITEM_DARK ) )
	ch->in_room->light += 3;
    else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
	ch->in_room->light -= 2;

    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;
    for( obj = list; obj; obj = obj->next_content )
    {
	if( !obj->deleted && obj->pIndexData == pObjIndex )
	    nMatch++;
    }

    return nMatch;
}


/*
 * Move an obj out of a room.
 */
void obj_from_room( OBJ_DATA *obj )
{
    ROOM_INDEX_DATA *in_room;
    CHAR_DATA *rch;

    if( !( in_room = obj->in_room ) )
    {
	bug( "obj_from_room: NULL." );
	return;
    }

    for( rch = in_room->people; rch; rch = rch->next_in_room )
	if( rch->on == obj )
	    rch->on = NULL;

    if( obj == in_room->contents )
    {
	in_room->contents = obj->next_content;
    }
    else
    {
	OBJ_DATA *prev;

	for( prev = in_room->contents; prev; prev = prev->next_content )
	{
	    if( prev->next_content == obj )
	    {
		prev->next_content = obj->next_content;
		break;
	    }

	}

	if( !prev )
	{
	    bug( "Obj_from_room: obj not found." );
	    return;
	}
    }

    if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 )
    {
	if( IS_SET( obj->extra_flags, ITEM_DARK ) )
	    obj->in_room->light += 14;
	else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
	    obj->in_room->light -= 10;
	else
	    obj->in_room->light -= 6;
    }

    strip_events( &obj->events, evn_imp_grab );

    obj->in_room = NULL;
    obj->next_content = NULL;
    return;
}


/*
 * Move an obj into a room.
 */
void obj_to_room( OBJ_DATA *obj, ROOM_INDEX_DATA *pRoomIndex )
{
    obj->next_content = pRoomIndex->contents;
    pRoomIndex->contents = obj;
    obj->in_room = pRoomIndex;
    obj->carried_by = NULL;
    obj->in_obj = NULL;
    set_imp_timer( obj );
    if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 )
    {
	if( IS_SET( obj->extra_flags, ITEM_DARK ) )
	    pRoomIndex->light -= 14;
	else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
	    pRoomIndex->light += 10;
	else
	    pRoomIndex->light += 6;
    }
    else if( obj->item_type == ITEM_EXPLOSIVE )
	create_obj_event( obj, evn_explode,
			  percent_fuzzy( 10 * PULSE_PER_SECOND, 5 ) );
    obj_fall_check( obj, 0 );

    return;
}


void obj_fall_check( OBJ_DATA *obj, int fallen )
{
    EXIT_DATA *pexit;
    EVENT *e;
    CHAR_DATA *rch;

    pexit = obj->in_room->exit[DIR_DOWN];

    if( IS_SET( obj->wear_flags, ITEM_TAKE )
	&& IS_SET( obj->in_room->room_flags, ROOM_FALL )
	&& pexit && pexit->to_room
	&& !IS_SET( pexit->exit_info, EX_CLOSED ) )
    {
	e = create_obj_event( obj, evn_obj_fall,
			      UMAX( 1, 5 - fallen ) );
	e->data[0] = fallen + 1;
    }
    else if( fallen > 0 )
    {
	if( ( rch = obj->in_room->people ) != NULL )
	    act( "$p slams into the ground.", rch, obj, NULL, TO_ALL );
	mod_item_condition( NULL, obj, 1 + fallen / 5 );
    }
}


/*
 * Move an object into an object.
 */
void obj_to_obj( OBJ_DATA *obj, OBJ_DATA *obj_to )
{
    if( obj_to->deleted )
    {
	bug( "Obj_to_obj:  Obj_to already deleted" );
	return;
    }

    obj->next_content = obj_to->contains;
    obj_to->contains = obj;
    obj->in_obj = obj_to;
    obj->in_room = NULL;
    obj->carried_by = NULL;

    for( ; obj_to; obj_to = obj_to->in_obj )
    {
	if( obj_to->deleted )
	    continue;
	if( obj_to->item_type == ITEM_CONTAINER
	    && IS_SET( obj_to->value[1], CONT_WEIGHTLESS ) )
	    break;
	if( obj_to->carried_by )
	    obj_to->carried_by->carry_weight += get_obj_weight( obj );
    }

    return;
}


/*
 * Move an object out of an object.
 */
void obj_from_obj( OBJ_DATA *obj )
{
    OBJ_DATA *obj_from;

    if( !( obj_from = obj->in_obj ) )
    {
	bug( "Obj_from_obj: null obj_from." );
	return;
    }

    if( obj == obj_from->contains )
    {
	obj_from->contains = obj->next_content;
    }
    else
    {
	OBJ_DATA *prev;

	for( prev = obj_from->contains; prev; prev = prev->next_content )
	{
	    if( prev->next_content == obj )
	    {
		prev->next_content = obj->next_content;
		break;
	    }
	}

	if( !prev )
	{
	    bug( "Obj_from_obj: obj not found." );
	    return;
	}
    }

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

    for( ; obj_from; obj_from = obj_from->in_obj )
    {
	if( obj_from->deleted )
	    continue;
	if( obj_from->item_type == ITEM_CONTAINER
	    && IS_SET( obj_from->value[1], CONT_WEIGHTLESS ) )
	    break;
	if( obj_from->carried_by )
	    obj_from->carried_by->carry_weight -= get_obj_weight( obj );
    }

    return;
}


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

    if( obj->deleted )
    {
	bug( "Extract_obj:  Obj already deleted" );
	return;
    }

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

    for( obj_content = obj->contains; obj_content; obj_content = obj_next )
    {
	obj_next = obj_content->next_content;
	if( obj_content->deleted )
	    continue;

	extract_obj( obj_content );
    }

    while( obj->events )
    {
	EVENT *tmp;

	tmp = obj->events;
	obj->events = tmp->next_local;
	event_remove_global( tmp );
	free_event( tmp );
    }

    obj->deleted = TRUE;

    delete_obj = TRUE;
    return;
}


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

    if( !ch->in_room )
    {
	bug( "Extract_char: NULL." );
	return;
    }

    if( IS_NPC( ch ) && ch->pIndexData->vnum == MOB_VNUM_SUPERMOB )
    {
	progbug(
	    ch, "Extracting a supermob, putting it back where it should be." );
	char_from_room( ch );
	char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) );
	clean_char( ch );
	ch->hit = ch->max_hit;
	return;
    }

    if( ch->fighting )
	stop_fighting( ch, TRUE );

    if( fPull )
    {
	char *name;

	if( IS_NPC( ch ) )
	    name = ch->short_descr;
	else
	    name = ch->name;

	die_follower( ch, name );

	/* Get rid of weapons _first_
	   - from Erwin Andreasen <erwin@pip.dknet.dk> */
	{
	    OBJ_DATA *obj, *obj2;

	    obj = get_eq_char( ch, WEAR_WIELD_R );
	    obj2 = get_eq_char( ch, WEAR_WIELD_L );
	    if( !obj )
		obj = get_eq_char( ch, WEAR_WIELD_DOUBLE );

	    if( obj )
		extract_obj( obj );

	    /* Now kill obj2 if it exists no matter if on body or floor */
	    if( obj2 )
		extract_obj( obj2 );
	}

	for( obj = ch->carrying; obj; obj = obj_next )
	{
	    obj_next = obj->next_content;
	    if( obj->deleted )
		continue;
	    extract_obj( obj );
	}

	while( ch->events )
	{
	    EVENT *tmp;

	    tmp = ch->events;
	    ch->events = tmp->next_local;
	    event_remove_global( tmp );
	    free_event( tmp );
	}
    }

    char_from_room( ch );

    if( !fPull )
    {
	ROOM_INDEX_DATA *location;

	if( !( location = get_room_index( ROOM_VNUM_TEMPLE ) ) )
	{
	    bug( "Temple does not exist!" );
	    char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) );
	}
	else
	    char_to_room( ch, location );
	return;
    }

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

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

    for( wch = char_list; wch; wch = wch->next )
    {
	if( wch->reply == ch )
	    wch->reply = NULL;
    }

    ch->deleted = TRUE;

    if( ch->desc )
	ch->desc->character = NULL;

    delete_char = TRUE;
    return;
}


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

    if( argument[0] == '#' )
    {
	int uid;

	if( LOWER( argument[1] ) == 'c' )
	    uid = atoi( &argument[2] );
	else
	    uid = atoi( &argument[1] );
	if( uid <= 0 )
	    return NULL;
	for( rch = ch->in_room->people; rch; rch = rch->next_in_room )
	{
	    if( uid == rch->unique_key && can_see( ch, rch ) )
		return rch;
	}
	return NULL;
    }

    number = number_argument( argument, arg );
    count = 0;

    if( number == 0 || !str_cmp( arg, "self" ) )
	return ch;
    for( rch = ch->in_room->people; rch; rch = rch->next_in_room )
    {
	if( !can_see( ch, rch ) || !is_name( arg, rch->name ) )
	    continue;
	if( ++count == number )
	    return rch;
    }

    return NULL;
}



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

    if( ( wch = get_char_room( ch, argument ) ) )
	return wch;

    if( argument[0] == '#' )
    {
	int uid;

	if( LOWER( argument[1] ) == 'c' )
	    uid = atoi( &argument[2] );
	else
	    uid = atoi( &argument[1] );
	if( uid <= 0 )
	    return NULL;
	for( wch = char_list; wch; wch = wch->next )
	{
	    if( uid == wch->unique_key && can_see( ch, wch ) )
		return wch;
	}
	return NULL;
    }

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

    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 )
{
    OBJ_DATA *obj;

    for( obj = object_list; obj; obj = obj->next )
    {
	if( obj->deleted )
	    continue;

	if( obj->pIndexData == pObjIndex )
	    return obj;
    }

    return NULL;
}


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

    if( argument[0] == '#' )
    {
	int uid;

	if( LOWER( argument[1] ) == 'o' )
	    uid = atoi( &argument[2] );
	else
	    uid = atoi( &argument[1] );
	if( uid <= 0 )
	    return NULL;
	for( obj = list; obj; obj = obj->next_content )
	{
	    if( uid == obj->unique_key && can_see_obj( ch, obj ) )
		return obj;
	}
	return NULL;
    }

    number = number_argument( argument, arg );
    count = 0;
    for( obj = list; obj; obj = obj->next_content )
    {
	if( can_see_obj( ch, obj ) && is_name( 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, const char *argument )
{
    OBJ_DATA *obj;
    char arg[MAX_INPUT_LENGTH];
    int number;
    int count;

    if( argument[0] == '#' )
    {
	int uid;

	if( LOWER( argument[1] ) == 'o' )
	    uid = atoi( &argument[2] );
	else
	    uid = atoi( &argument[1] );
	if( uid <= 0 )
	    return NULL;
	for( obj = ch->carrying; obj; obj = obj->next_content )
	{
	    if( obj->wear_loc == WEAR_NONE
		&& uid == obj->unique_key && can_see_obj( ch, obj ) )
		return obj;
	}
	return NULL;
    }

    number = number_argument( argument, arg );
    count = 0;
    for( obj = ch->carrying; obj; 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;
	}
    }

    return NULL;
}


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

    if( argument[0] == '#' )
    {
	int uid;

	if( LOWER( argument[1] ) == 'o' )
	    uid = atoi( &argument[2] );
	else
	    uid = atoi( &argument[1] );
	if( uid <= 0 )
	    return NULL;
	for( obj = ch->carrying; obj; obj = obj->next_content )
	{
	    if( obj->wear_loc != WEAR_NONE
		&& uid == obj->unique_key && can_see_obj( ch, obj ) )
		return obj;
	}
	return NULL;
    }

    number = number_argument( argument, arg );
    count = 0;
    for( obj = ch->carrying; obj; 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;
	}
    }

    return NULL;
}


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

    obj = get_obj_list( ch, argument, ch->in_room->contents );
    if( obj )
	return obj;

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

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

    return NULL;
}


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

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

    if( argument[0] == '#' )
    {
	int uid;

	if( LOWER( argument[1] ) == 'o' )
	    uid = atoi( &argument[2] );
	else
	    uid = atoi( &argument[1] );
	if( uid <= 0 )
	    return NULL;
	for( obj = object_list; obj; obj = obj->next )
	{
	    if( uid == obj->unique_key && can_see_obj( ch, obj ) )
		return obj;
	}
	return NULL;
    }

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

    }

    return NULL;
}


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

    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 );
	if( amount <= 10 )		p = "few";
	else if( amount <= 50 )		p = "small pile of";
	else if( amount <= 100 )	p = "pile of";
	else if( amount <= 1000 )	p = "large pile of";
	else if( amount <= 10000 )	p = "bunch of";
	else if( amount <= 50000 )	p = "whopping mound of";
	else if ( amount <= 10000000 )	p = "mountain of";
	else				p = "pitiful few million";
	sprintf( buf, "a %s gold coins", p );
	free_string( obj->short_descr );
	obj->short_descr = str_dup( buf );
	buf[0] = 'A';
	strcat( buf, " sits gleaming on the ground." );
	free_string( obj->description );
	obj->description = str_dup( 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 )
{
    int number;

    number = 0;
    if( obj->item_type == ITEM_CONTAINER )
	for( obj = obj->contains; obj; obj = obj->next_content )
	{
	    if( obj->deleted )
		continue;
	    number += get_obj_number( obj );
	}
    else
	number = 1;

    return number;
}


/*
 * Return weight of an object, including weight of contents.
 */
int get_obj_weight( OBJ_DATA *obj )
{
    int weight;

    weight = obj->weight;
    if( obj->item_type == ITEM_CONTAINER
	&& IS_SET( obj->value[1], CONT_WEIGHTLESS ) )
	return weight;
    for( obj = obj->contains; obj; obj = obj->next_content )
    {
	if( obj->deleted )
	    continue;
	weight += get_obj_weight( obj );
    }

    return weight;
}


/*
 * Return the number of people that are using the furniture to rest on.
 */
int count_users( OBJ_DATA *obj )
{
    CHAR_DATA *rch;
    int users = 0;

    if( !obj->in_room )
	return 0;

    for( rch = obj->in_room->people; rch; rch = rch->next_in_room )
	if( !rch->deleted && rch->on == obj )
	    users++;

    return users;
}


/*
 * True if room is dark.
 */
bool room_is_dark( ROOM_INDEX_DATA *pRoomIndex )
{
    if( IS_SET( pRoomIndex->room_flags, ROOM_INDOORS ) )
	return FALSE;
    if( get_room_light( pRoomIndex ) < 0 )
	return TRUE;
    return FALSE;
}


int get_room_light( ROOM_INDEX_DATA *pRoomIndex )
{
    int count;

    count = pRoomIndex->light;

    if( IS_SET( pRoomIndex->room_flags, ROOM_DARK ) )
	count -= 40;
    /* weather wont effect underground */
    if( IS_SET( pRoomIndex->room_flags, ROOM_UNDERGROUND ) )
	return count - 15;

    if( pRoomIndex->sector_type == SECT_INSIDE
	|| pRoomIndex->sector_type == SECT_CITY )
	count += 15;

    if( pRoomIndex->area->plane->weather.sunlight == SUN_SET )
	count -= 10;
    else if( pRoomIndex->area->plane->weather.sunlight == SUN_DARK )
    {
	/* Full moon on the 4th of the month */
	if( pRoomIndex->area->plane->weather.sky == SKY_CLOUDLESS )
	    count += abs( ( pRoomIndex->area->plane->time.day + 30 ) % 34 - 15 );
	count -= 36;
    }
    else if( pRoomIndex->area->plane->weather.sunlight == SUN_RISE )
	count += 8;
    else if( pRoomIndex->area->plane->weather.sunlight == SUN_LIGHT )
	count += 32;

    return count;
}


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

    count = 0;
    for( rch = pRoomIndex->people; rch; rch = rch->next_in_room )
    {
	if( rch->deleted )
	    continue;

	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 )
{
    int light;

    if( victim->deleted )
	return FALSE;

    if( ch == victim )
	return TRUE;

    /* All mobiles cannot see wizinvised immorts */
    if( IS_NPC( ch )
	&& !IS_NPC( victim )
	&& ( xIS_SET( victim->act, PLR_WIZINVIS )
	    || ( victim->position == POS_MEDITATING
		&& number_percent( ) < victim->level - ch->level + 95 ) ) )
	return FALSE;

    if( !IS_NPC( victim )
	&& xIS_SET( victim->act, PLR_WIZINVIS )
	&& get_trust( ch ) < get_trust( victim ) )
	return FALSE;

    /* Holylight and mobiles running programs, overrides almost all */
    if( ( !IS_NPC( ch ) && xIS_SET( ch->act, PLR_HOLYLIGHT ) )
	|| ( IS_NPC( ch ) && xIS_SET( ch->act, ACT_MUDPROG ) ) )
	return TRUE;

    if( IS_AFFECTED( ch, AFF_BLIND )
	|| !IS_SET( ch->body_parts, BODY_PART_EYES ) )
	return FALSE;

    if( room_is_dark( ch->in_room )
	&& !IS_SET( race_table[ch->race].race_abilities, RACE_INFRAVISION )
	&& !IS_SET( race_table[ch->race].race_abilities, RACE_DARK_SIGHT )
	&& !IS_AFFECTED( ch, AFF_INFRARED ) )
	return FALSE;

    if( victim->position == POS_DEAD )
	return TRUE;

    if( IS_NPC( victim ) && xIS_SET( victim->act, ACT_BURIED ) )
	return FALSE;

    if( IS_AFFECTED( victim, AFF_INVISIBLE )
	&& ( ( !IS_SET( race_table[ch->race].race_abilities, RACE_DETECT_INVIS )
	       && !IS_AFFECTED( ch, AFF_DETECT_INVIS ) )
	     || victim->level - 3 > ch->level ) )
	return FALSE;

    if( victim->fighting )
	return TRUE;

    if( IS_AFFECTED( victim, AFF_HIDE ) )
    {
	light = get_room_light( ch->in_room );
	if( IS_AFFECTED( ch, AFF_DETECT_HIDDEN ) )
	    light += 35;
	else if( IS_SET( race_table[ch->race].race_abilities,
			RACE_DETECT_HIDDEN ) )
	    light += 25;
	if( light < dice( 3, 6 ) )
	    return FALSE;
    }

    return TRUE;
}


/*
 * True if char can see obj.
 */
bool can_see_obj( CHAR_DATA *ch, OBJ_DATA *obj )
{
    if( obj->deleted )
	return FALSE;

    /* Holylight and mobiles running programs, overrides almost all */
    if( ( !IS_NPC( ch ) && xIS_SET( ch->act, PLR_HOLYLIGHT ) )
	|| ( IS_NPC( ch ) && xIS_SET( ch->act, ACT_MUDPROG ) ) )
	return TRUE;

    if( IS_AFFECTED( ch, AFF_BLIND )
	|| !IS_SET( ch->body_parts, BODY_PART_EYES ) )
	return FALSE;

    if( IS_SET( obj->extra_flags, ITEM_BURIED ) )
	return FALSE;

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

    if( room_is_dark( ch->in_room )
	&& !IS_SET( race_table[ch->race].race_abilities, RACE_INFRAVISION )
	&& !IS_SET( race_table[ch->race].race_abilities, RACE_DARK_SIGHT )
	&& !IS_AFFECTED( ch, AFF_INFRARED ) )
	return FALSE;

    if( IS_SET( obj->extra_flags, ITEM_INVIS )
	&& !IS_SET( race_table[ch->race].race_abilities, RACE_DETECT_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_SET( obj->extra_flags, ITEM_NODROP ) )
	return TRUE;

    if( !IS_NPC( ch ) && ch->level >= L_APP )
	return TRUE;

    return FALSE;
}


CHAR_DATA *get_char( CHAR_DATA *ch )
{
    if( !ch->pcdata )
	return ch->desc->original;
    else
	return ch;
}


bool longstring( CHAR_DATA *ch, const char *argument )
{
    if( strlen( argument ) > 60 )
    {
	send_to_char( "No more than 60 characters in this field.\n\r", ch );
	return TRUE;
    }
    return FALSE;
}


bool authorized( CHAR_DATA *ch, const char *skllnm )
{
    char buf[MAX_STRING_LENGTH];

    if( IS_NPC( ch ) || str_infix( skllnm, ch->pcdata->immskll ) )
    {
	sprintf( buf, "Sorry, you are not authorized to use %s.\n\r", skllnm );
	send_to_char( buf, ch );
	return FALSE;
    }

    return TRUE;
}


void end_of_game( void )
{
    DESCRIPTOR_DATA *d;
    DESCRIPTOR_DATA *d_next;

    db_dump( );
    for( d = descriptor_list; d; d = d_next )
    {
	d_next = d->next;
	if( !IS_SET( d->interpreter->flags, INTERPRETER_SAFE ) )
	{
	    if( d->character->position == POS_FIGHTING )
		interpret( d->character, "save" );
	    else
		interpret( d->character, "quit" );
	}
	else
	    close_socket( d );
    }
#if defined( HAVE_ISPELL )
    ispell_done();
#endif

    return;
}


int race_lookup( const char *race )
{
    int index;

    for( index = 0; index < MAX_RACE; index++ )
	if( !str_prefix( race, race_table[index].name ) )
	    return index;

    return -1;
}


int race_full_lookup( const char *race )
{
    int index;

    for( index = 0; index < MAX_RACE; index++ )
	if( !str_cmp( race, race_table[index].name ) )
	    return index;

    return NO_FLAG;
}


int affect_lookup( const char *affectname )
{
    int index;

    for( index = 0; index < MAX_SKILL; index++ )
	if( !str_cmp( affectname, skill_table[index].name ) )
	    return index;

    return -1;
}


int liquid_lookup( const char *name )
{
    int i;

    for( i = 0; liq_table[i].liq_name != NULL; i++ )
	if( !str_cmp( liq_table[i].liq_name, name ) )
	    return i;
    return 0;
}


PLANE_DATA *plane_lookup( const char *name )
{
    PLANE_DATA *pl;

    for( pl = plane_list; pl; pl = pl->next )
    {
	if( !str_prefix( name, pl->name ) )
	    break;
    }
    return pl;
}


int power( int base, int percentage, int diff )
{
    double jig;

    jig = ( 100.0 + (double)percentage ) / 100.0;
    jig = pow( jig, diff );
    jig = jig * base;
    return (int)jig;
}


int str_app_wield( int str )
{
    return power( 15, 10, str - 15 );
}


int get_ac( CHAR_DATA *ch )
{
    int ac, dex;

    ac = ch->armour;

    if( IS_NPC( ch ) || !IS_AWAKE( ch )
	|| IS_AFFECTED( ch, AFF_HOLD ) )
	return ac;

    if( ( dex = get_curr_dex( ch ) ) < 6 )
	return ac + ( 6 - dex ) * 5;
    if( dex <= 10 )
	return ac;
    if( ch->class == CLASS_MARTIAL_ARTIST
	|| get_first_class( ch ) == CLASS_MARTIAL_ARTIST
	|| get_second_class( ch ) == CLASS_MARTIAL_ARTIST )
	return ( ac + power( -30, 12, dex - 18 ) );
    return power( -30, 10, dex - 20 ) + 11 + ac;
}


int total_mana_cost( CHAR_DATA *ch, int sn )
{
    int tot = 0, sph;
    for( sph = 0; sph < MAGIC_MAX + 1; ++sph )
	tot += mana_cost( ch, sn, sph );
    return tot;
}

/*
 * New mana cost function which is, by necessity, more complicated than the
 * macro previously supplied.
 * Finds the means by which the character can say they have known the spell
 * for longer then uses that number.		--Symposium.
 */
int mana_cost( CHAR_DATA *ch, int sn, int sphere )
{
    int i;
    int level = 0;

    if( ( IS_NPC( ch ) && ch->class < 0 ) || ch->level >= LEVEL_IMMORTAL
	|| ( !IS_NPC( ch ) && IS_SET( ch->pcdata->pc_bits, PC_BIT_RACIAL ) ) )
	return 0;
    if( !IS_NPC( ch ) )
	for( i = 0; ch->level < skill_table[sn].skill_level[ch->class]
	     && i < NUM_MULTI_CLASS && level < 4; ++i )
	{
	    if( ch->pcdata->multi_class[i] == CLASS_ASPIRING
		&& level < ch->sublevel -
		skill_table[sn].skill_level[i] )
		level = ch->sublevel - skill_table[sn].skill_level[i];

	    if( ch->pcdata->multi_class[i] >= CLASS_ADEPT
		&& level < ch->level - skill_table[sn].skill_level[i] )
		level = ch->level - skill_table[sn].skill_level[i];
	}

    level = UMAX( level, ch->level - skill_table[sn].skill_level[ch->class] );

    return UMAX( skill_table[sn].min_mana[sphere],
		( 6 * skill_table[sn].min_mana[sphere] ) / ( 2 + level ) );
}


void mod_item_condition( CHAR_DATA *ch, OBJ_DATA *obj, int amount )
{
    if( ch == NULL && obj->in_room )
	ch = obj->in_room->people;
    if( IS_SET( obj->extra_flags, ITEM_BLESS ) )
	obj->condition -= UMIN( number_range( 1, obj->pIndexData->material ),
				number_range( 1, obj->pIndexData->material ) ) * amount;
    else
	obj->condition -= number_range( 1, obj->pIndexData->material ) * amount;
    if( obj->condition <= 0
	|| ( IS_SET( obj->extra_flags, ITEM_FRAGILE )
	     && number_bits( 2 ) == 0 ) )
    {
	if( ch )
	    act( "&r$p is totally ruined.&n", ch, obj, NULL, TO_ALL );
	extract_obj( obj );
	return;
    }

    if( ch )
	act( "&r$p is damaged!&n", ch, obj, NULL, TO_ALL );

    if( number_bits( 7 ) == 0 )
    {
	if( ch )
	    act( "&r$p becomes more fragile!", ch, obj, NULL, TO_ALL );
	SET_BIT( obj->extra_flags, ITEM_FRAGILE );
    }

    if( xIS_SET( obj->pIndexData->progtypes, DAMAGE_PROG ) )
	oprog_percent_check( ch, obj, NULL, DAMAGE_PROG );
    return;
}


char *int_to_str_special( int num )
{
    static char buf[25];
    const char multiples[] = "KMBT";
    int i, max;
    int places, points;

    i = 3 + 4 * strlen( multiples );
    max = i + 1;

    places = points = 0;
    while( i > 1 )
    {
	buf[i--] = '0' + abs( num ) % 10;
	places++;
	num /= 10;
	if( places % 3 == 0 && abs( num ) >= 5 )
	{
	    points++;
	    buf[i--] = '.';
	}
    }

    ++i;
    while( buf[i] == '0' || buf[i] == '.' )
	++i;
    if( buf[i] == '\0' )
	return strcpy( buf, "0" );

    max = UMIN( max, i + 4 );
    if( points > 0 )
    {
	if( buf[max - 1] == '.' )
	    max--;
	buf[max++] = multiples[points - 1];
    }

    buf[max] = '\0';
    if( num < 0 )
	buf[--i] = '-';

    return &buf[i];
}


int atoi_special( const char *str )
{
    const char *multiples = "kKmMbBtT";
    int multiply;
    char *p;
    char *q;
    int i;

    if( is_number( str ) )
	return atoi( str );
    q = strchr( str, '\0' );
    if( !q || --q < str )
	return -1;
    while( isspace( *q ) && q > str )
	q--;
    p = strchr( multiples, *q );
    if( !p || q == str || !isdigit( *(q-1) ) )
	return -1;
    multiply = ( p - multiples ) / 2;
    multiply = pow( 1000, 1 + multiply );
    *q = '\0';

    if( !( p = strchr( str, '.' ) ) )
    {
	if( is_number( str ) )
	    return multiply * atoi( str );
    }

    else
    {
	int j;

	*p++ = '\0';
	if( !is_number( str ) || !is_number( p ) )
	    return -1;

	i = atoi( p );
	j = (int)log10( ( double )i ) + 1;
	i = multiply * i / pow( 10, j );
	i += atoi( str ) * multiply;
	return i;
    }

    return -1;
}


/*
 * Clear all the character's stats, wipe the slate clean.
 */
void clean_char( CHAR_DATA *ch )
{
    int i;

    ch->armour		= 100;
    ch->hit		= UMAX( 1, ch->hit );
    for( i = 0; i < MAGIC_MAX; ++i )
	ch->mana[i]	= UMAX( 1, ch->mana[i] );
    ch->move		= UMAX( 1, ch->move );
    ch->hitroll		= 0;
    ch->damroll		= 0;
    ch->saving_throw	= 0;
    ch->resil_mod	= 0;
    ch->temp_mod	= 0;
    ch->speed		= 0;
    ch->on		= NULL;
    if( !IS_NPC( ch ) )
    {
	ch->pcdata->mod_str = 0;
	ch->pcdata->mod_int = 0;
	ch->pcdata->mod_wis = 0;
	ch->pcdata->mod_dex = 0;
	ch->pcdata->mod_con = 0;
	for( i = 0; i < MAGIC_MAX; ++i )
	    ch->pcdata->mod_magic[i] = 0;
    }

    return;
}


/* Vector manipulation */
int *vzero( int *vector )
{
    register int i;

    for( i = 0; i < MAX_VECTOR; i++ )
	vector[i] = 0;

    return vector;
}


int *vcopy( int *dest, const int *src )
{
    register int i;

    for( i = 0; i < MAX_VECTOR; i++ )
	dest[i] = src[i];

    return dest;
}


int *vset( int *dest, const int bit )
{
    vzero( dest );
    xSET_BIT( dest, bit );
    return dest;
}


bool vequal( const int *a, const int *b )
{
    register int i;
    for( i = 0; i < MAX_VECTOR; i++ )
	if( a[i] != b[i] )
	    return FALSE;
    return TRUE;
}


bool vnull( const int *a )
{
    register int i;
    for( i = 0; i < MAX_VECTOR; i++ )
	if( a[i] )
	    return FALSE;
    return TRUE;
}


/*
 * Economy section, this is to make areas have some limit to the gold
 * that they have.
 * By Thoric of SMAUG
 *
 * Used in db.c when resetting a mob into an area                   -Thoric
 * Makes sure mob doesn't get more than 10% of that area's gold,
 * and reduces area economy by the amount of gold given to the mob
 */
void economize_mobgold( CHAR_DATA *mob )
{
    AREA_DATA *tarea;
    int scaler;

    /* make sure it isn't way too much */
    mob->gold = UMIN( mob->gold, mob->level * mob->level * 400 );
    if( !mob->in_room )
	return;
    tarea = mob->in_room->area;

    if( tarea->ave && tarea->nmobile > 5 )
    {
	scaler = tarea->economy / tarea->ave / tarea->ave
	    / tarea->nmobile / 3 + 1;
	if( number_bits( 5 ) < UMIN( 5, scaler ) )
	    mob->gold = number_range( mob->gold, mob->gold * 4 );
    }
    mob->gold = URANGE( 2, mob->gold, tarea->economy / 10 );
    if( mob->gold )
	tarea->economy -= mob->gold;
}