dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// handler.cpp - Utility functions etc
/***************************************************************************
 * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt                    *
 * >> A number of people have contributed to the Dawn codebase, with the   *
 *    majority of code written by Michael Garratt - www.dawnoftime.org     *
 * >> To use this source code, you must fully comply with all the licenses *
 *    in licenses.txt... In particular, you may not remove this copyright  *
 *    notice.                                                              *
 ***************************************************************************
 * >> Original Diku Mud copyright (c)1990, 1991 by Sebastian Hammer,       *
 *    Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, & Katja Nyboe.   *
 * >> Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael       *
 *    Chastain, Michael Quan, and Mitchell Tse.                            *
 * >> ROM 2.4 is copyright 1993-1995 Russ Taylor and has been brought to   *
 *    you by the ROM consortium: Russ Taylor(rtaylor@pacinfo.com),         *
 *    Gabrielle Taylor(gtaylor@pacinfo.com) & Brian Moore(rom@rom.efn.org) *
 * >> Oblivion 1.2 is copyright 1996 Wes Wagner                            *
 **************************************************************************/
#include <assert.h>
#include "include.h" // dawn standard includes

#include "magic.h"
#include "msp.h"
#include "ictime.h"
#include "pload.h"
#include "lockers.h"

// command procedures needed 
DECLARE_DO_FUN(do_return	);
void set_char_magic_bits(char_data * ch);
void affect_copy_to_char( char_data *ch, AFFECT_DATA *paf );
char *flag_string( const struct flag_type *flag_table, int bits);
/**************************************************************************/
/*
 * Local functions.
 */
void	affect_modify	args( ( char_data *ch, AFFECT_DATA *paf, bool fAdd ) );
void	room_update( AREA_DATA *pArea );
void	room_aff_update( ROOM_INDEX_DATA *room );

void bash_eq(char_data *ch, int chance)
{
  OBJ_DATA *obj, *obj_next;

  for(obj = ch->carrying; obj != NULL; obj = obj_next)        
        {
            obj_next = obj->next_content;
            if(obj->condition>0 && number_percent()<=chance+20 &&
              !IS_SET(obj->extra_flags ,OBJEXTRA_NO_DEGRADE))
		obj->condition-=1;
        }
  return;
}
/**************************************************************************/
// return the bit vector table relating to a given where location - Kal
const flag_type *affect_get_bitvector_table_for_where(int where)
{
	switch(where)
	{
	case WHERE_AFFECTS:	return affect_flags;	// character
	case WHERE_AFFECTS2:	return affect2_flags;	// character
	case WHERE_IMMUNE:		return imm_flags;		// character
	case WHERE_RESIST:		return res_flags;		// character
	case WHERE_VULN:		return vuln_flags;		// character
	case WHERE_OBJEXTRA:	return objextra_flags;	// object
	case WHERE_OBJEXTRA2:	return objextra2_flags;	// object
	case WHERE_WEAPON:		return weapon_flags;	// object
	case WHERE_OBJECTSPELL:return objspell_flags;	// object spell affect
	case WHERE_MODIFIER:	break;
	case WHERE_SKILLS:		break;
	default:
		bugf("affect_get_bitvector_table_for_where(): "
			"Unsupported where value of %d.", where);
		do_abort();
		break;
	}
	return NULL;
}
/**************************************************************************/
/* returns number of people on an object */
int count_users(OBJ_DATA *obj)
{
    char_data *fch;
    int count = 0;

    if(obj->in_room == NULL)
	return 0;

    for(fch = obj->in_room->people; fch; fch = fch->next_in_room){
		if(fch->on == obj){
			count++;
		}
	}

    return count;
}

/**************************************************************************/
int num_enemies(char_data *ch)
{
	char_data *fch;
 	int count=0;

	for(fch = ch->in_room->people; fch != NULL; fch = fch->next_in_room)
	 if(fch->fighting==ch)
          count+=1;

        return count;
}   

/**************************************************************************/
int weapontype (const char *name)
{
    int type;
 
    for(type = 0; weapon_table[type].name != NULL; type++)
    {
        if(LOWER(name[0]) == LOWER(weapon_table[type].name[0])
        &&  !str_prefix(name,weapon_table[type].name))
            return weapon_table[type].type;
    }
 
    return WEAPON_EXOTIC;
}

/**************************************************************************/
char *get_weapontype(OBJ_DATA *obj)
{
	if( obj->item_type == ITEM_WEAPON ){
		return flag_string( weapon_class_types, obj->value[0] );
	}

	return NULL;
}

/**************************************************************************/
int item_lookup(const char *name)
{
    int type;

    for(type = 0; item_table[type].name != NULL; type++)
    {
        if(LOWER(name[0]) == LOWER(item_table[type].name[0])
        &&  !str_prefix(name,item_table[type].name))
            return item_table[type].type;
    }
 
    return -1;
}

/**************************************************************************/
char *item_name(int item_type)
{
//	return(flag_string(item_types, item_type));
    int type;

    for(type = 0; item_table[type].name != NULL; type++)
	if(item_type == item_table[type].type)
	    return item_table[type].name;
    return "none";
}

/**************************************************************************/
char *weapon_name( int weapon_type)
{
    int type;
 
    for(type = 0; weapon_table[type].name != NULL; type++)
        if(weapon_type == weapon_table[type].type)
            return weapon_table[type].name;
    return "exotic";
}



/**************************************************************************/
/* for immunity, vulnerabiltiy, and resistant
   the 'globals' (magic and weapons) may be overriden
   three other cases -- wood, silver, and iron -- are checked in fight.c */
int check_immune(char_data *ch, int dam_type)
{
    int immune, def;
    int bit;

    immune = -1;
    def = IS_NORMAL;

    if(dam_type == DAM_NONE)
	return immune;

    if(dam_type <= 3)
    {
	if(IS_SET(ch->imm_flags,IMM_WEAPON))
	    def = IS_IMMUNE;
	else if (IS_SET(ch->res_flags,RES_WEAPON))
	    def = IS_RESISTANT;
	else if (IS_SET(ch->vuln_flags,VULN_WEAPON))
	    def = IS_VULNERABLE;
    }
    else /* magical attack */
    {	
	if(IS_SET(ch->imm_flags,IMM_MAGIC))
	    def = IS_IMMUNE;
	else if (IS_SET(ch->res_flags,RES_MAGIC))
	    def = IS_RESISTANT;
	else if (IS_SET(ch->vuln_flags,VULN_MAGIC))
	    def = IS_VULNERABLE;
    }

    /* set bits to check -- VULN etc. must ALL be the same or this will fail */
    switch(dam_type)
    {
	case(DAM_BASH):			bit = IMM_BASH;		break;
	case(DAM_PIERCE):		bit = IMM_PIERCE;	break;
	case(DAM_SLASH):		bit = IMM_SLASH;	break;
	case(DAM_FIRE):			bit = IMM_FIRE;		break;
	case(DAM_COLD):			bit = IMM_COLD;		break;
	case(DAM_LIGHTNING):	bit = IMM_LIGHTNING;break;
	case(DAM_ACID):			bit = IMM_ACID;		break;
	case(DAM_POISON):		bit = IMM_POISON;	break;
	case(DAM_NEGATIVE):		bit = IMM_NEGATIVE;	break;
	case(DAM_HOLY):			bit = IMM_HOLY;		break;
	case(DAM_ENERGY):		bit = IMM_ENERGY;	break;
	case(DAM_MENTAL):		bit = IMM_MENTAL;	break;
	case(DAM_DISEASE):		bit = IMM_DISEASE;	break;
	case(DAM_DROWNING):		bit = IMM_DROWNING;	break;
	case(DAM_LIGHT):		bit = IMM_LIGHT;	break;
	case(DAM_CHARM):		bit = IMM_CHARM;	break;
	case(DAM_SOUND):		bit = IMM_SOUND;	break;
	case(DAM_ILLUSION):		bit = IMM_ILLUSION;	break;
	default:		return def;
    }

    if(IS_SET(ch->imm_flags,bit))
	immune = IS_IMMUNE;
    else if (IS_SET(ch->res_flags,bit) && immune != IS_IMMUNE)
	immune = IS_RESISTANT;
    else if (IS_SET(ch->vuln_flags,bit))
    {
	if(immune == IS_IMMUNE)
	    immune = IS_RESISTANT;
	else if (immune == IS_RESISTANT)
	    immune = IS_NORMAL;
	else
	    immune = IS_VULNERABLE;
    }

    if(immune == -1)
	return def;
    else
      	return immune;
}

/**************************************************************************/
bool is_clan(char_data *ch)
{   
    return(ch->clan>0);
}

/**************************************************************************/
bool is_same_clan(char_data *ch, char_data *victim)
{
    return(ch->clan  && ch->clan == victim->clan );
}

/**************************************************************************/
// for returning skill information 
int get_skill(char_data *ch, int sn)
{
	return(ch->get_skill(sn));
}

/**************************************************************************/
// for returning weapon information 
int get_weapon_sn(char_data *ch)
{
    OBJ_DATA *wield;
    int sn;

    wield = get_eq_char( ch, WEAR_WIELD );
    if(wield == NULL || wield->item_type != ITEM_WEAPON)
        sn = gsn_hand_to_hand;
    else switch (wield->value[0])
    {
        default :               sn = -1;                break;
        case(WEAPON_SPEAR):     sn = gsn_spear;         break;
        case(WEAPON_SWORD):     sn = gsn_sword;         break;
        case(WEAPON_DAGGER):    sn = gsn_dagger;        break;
        case(WEAPON_STAFF):     sn = gsn_staff;         break;
        case(WEAPON_MACE):      sn = gsn_mace;          break;
        case(WEAPON_AXE):       sn = gsn_axe;           break;
        case(WEAPON_FLAIL):     sn = gsn_flail;         break;
        case(WEAPON_WHIP):      sn = gsn_whip;          break;
        case(WEAPON_POLEARM):   sn = gsn_polearm;       break;
		case(WEAPON_SICKLE):	sn = gsn_sickle;		break;
   }
   return sn;
}

/**************************************************************************/
int get_weapon_skill(char_data *ch, int sn)
{
     int skill;

     /* -1 is exotic */
    if(IS_NPC(ch))
    {
	if(sn == -1)
	    skill = 3 * ch->level;
	else if (sn == gsn_hand_to_hand)
	    skill = 40 + 2 * ch->level;
	else 
	    skill = 40 + 5 * ch->level / 2;
    }
    
    else
    {
	if(sn == -1)
	    skill = 3 * ch->level;
	else
	    skill = ch->pcdata->learned[sn];
    }

    return URANGE(0,skill,100);
} 


/**************************************************************************/
// used to de-screw characters 
void reset_char(char_data *ch)
{
    int loc,mod,stat;
    OBJ_DATA *obj;
    AFFECT_DATA *af;
    int i;
	int ac_amount;

    if(IS_NPC(ch))
        return;

	// THIS FIRST SECTION OF CODE IS ONLY RUN ON NEWBIES OR CORRUPTED PFILES
    if(    ch->pcdata->perm_hit   == 0 
        ||  ch->pcdata->perm_mana  == 0
        ||  ch->pcdata->perm_move  == 0
        ||  (ch->pcdata->last_level == 0 && ch->played==0))
    {   // do a FULL reset  - START of full reset
        for(loc = 0; loc < MAX_WEAR; loc++)
        {  // start of wear location loop 
            obj = get_eq_char(ch,loc);
            if(obj == NULL)
                continue;

			if( obj->wear_loc == WEAR_SHEATHED
			||   obj->wear_loc == WEAR_CONCEALED )
				continue;

			for(af=OBJECT_AFFECTS(obj); af; af = af->next )
            {
                if(af->level > ch->level) continue;
                mod = af->modifier;
                switch(af->location)
                {
                    case APPLY_SEX:
                        ch->sex -= mod;
                        if(ch->sex < 0 || ch->sex >2)
                            ch->sex = IS_NPC(ch) ? 0 : ch->pcdata->true_sex;
                        break;
                    case APPLY_MANA: ch->max_mana -= mod;   break;
                    case APPLY_HIT:  ch->max_hit  -= mod;   break;
                    case APPLY_MOVE: ch->max_move -= mod;   break;
					
					default: break; // do nothing for the rest of the conditions
                }
            }
        }  // end of wear location loop

        // now reset the permanent stats
        ch->pcdata->perm_hit    = ch->max_hit;
        ch->pcdata->perm_mana   = ch->max_mana;
        ch->pcdata->perm_move   = ch->max_move;
        ch->pcdata->last_level  = ch->played/3600;
        if(ch->pcdata->true_sex < 0 || ch->pcdata->true_sex > 2)
        {
            if(ch->sex > 0 && ch->sex < 3)
                ch->pcdata->true_sex    = ch->sex;
            else
                ch->pcdata->true_sex    = 0;
        }
    } // do a FULL reset  - END of full reset


    // BELOW HERE IS RUN ON EVERY CHARACTER WHEN THEY CONNECT AND LEVEL
	
	// should do invunerablities etc in here? or just above

	// calculate their object restriction groupings
	if(ch->pcdata){
		int index;
		ch->pcdata->objrestrict=0;
		for(index=0; !IS_NULLSTR(classgroup_table[index].name); index++){
			if(is_exact_name(class_table[ch->clss].name,classgroup_table[index].text_members)){
				ch->pcdata->objrestrict|= (1<<classgroup_table[index].bitindex);
			}
		}
	}

	// update their body parts and form
	ch->form	= race_table[ch->race]->form;
	ch->parts	= race_table[ch->race]->parts;

    // now restore the character to his/her true condition 
    for(stat = 0; stat < MAX_STATS; stat++)
    {
        ch->modifiers[stat]=0;
		if(GAMESETTING(GAMESET_USE_ROLEMASTER_MODIFIERS)){
			if(ch->perm_stats[stat] > 90){
				ch->modifiers[stat] = (ch->perm_stats[stat]-90)*2+20;
			}else if(ch->perm_stats[stat] > 70){
				ch->modifiers[stat] = ch->perm_stats[stat]-70;
			}else if(ch->perm_stats[stat] < 26){
				ch->modifiers[stat] = -26 + ch->perm_stats[stat]; 
			}
		}else{
			if(ch->perm_stats[stat] > 95){
				ch->modifiers[stat] = (ch->perm_stats[stat]-95)*3+45;
			}else if(ch->perm_stats[stat] > 85){
				ch->modifiers[stat] = (ch->perm_stats[stat]-85)*2+25;
			}else if(ch->perm_stats[stat] > 60){
					ch->modifiers[stat] = ch->perm_stats[stat]-60;
			}else if(ch->perm_stats[stat] < 26){
					ch->modifiers[stat] = -26 + ch->perm_stats[stat];
			}
		}
        ch->modifiers[stat] += race_table[ch->race]->stat_modifier[stat];
    }
    
    
    if(ch->pcdata->true_sex < 0 || ch->pcdata->true_sex > 2){
        ch->pcdata->true_sex = 0;  // reset true sex if it is stuffed
	}
    ch->sex     = ch->pcdata->true_sex;
    ch->max_hit     = ch->pcdata->perm_hit;
    ch->max_mana    = ch->pcdata->perm_mana;
    ch->max_move    = ch->pcdata->perm_move;
       
    for(i = 0; i < 4; i++)
    {
		if(HAS_CLASSFLAG(ch, CLASSFLAG_LEVEL_BASED_AC)){
			ch->armor[i]    = 100-4*ch->level;
		}else{
            ch->armor[i]    = 100;
		}
    }
    
    ch->hitroll     = 0;
    ch->damroll     = 0;
    ch->saving_throw    = 0;
	
	// setup their magic bits
	set_char_magic_bits(ch);

    // now start adding back the effects of objects (excluding paf->where==WHERE_OBJECTSPELL affects)
    for(loc = 0; loc < MAX_WEAR; loc++)
    {
        obj = get_eq_char(ch,loc);
        if(obj == NULL)
            continue;

		if( obj->wear_loc == WEAR_SHEATHED
		||   obj->wear_loc == WEAR_CONCEALED )
			continue;

		for(i = 0; i < 4; i++)
		{
			ac_amount = apply_ac( obj, loc, i );
			if(ch->level< obj->level)
				ac_amount = ac_amount * ch->level/ obj->level;
			ch->armor[i] -= ac_amount;
		}

		for(af = OBJECT_AFFECTS(obj); af; af = af->next)
		{
			if(af->level > ch->level ){
				continue;
			}
			if(af->where!=WHERE_OBJECTSPELL){
				mod = af->modifier;
				switch(af->location)
				{
					case APPLY_ST:  ch->modifiers[STAT_ST]  += mod; break;
					case APPLY_QU:  ch->modifiers[STAT_QU]  += mod; break;
					case APPLY_PR:  ch->modifiers[STAT_PR]  += mod; break;
					case APPLY_EM:  ch->modifiers[STAT_EM]  += mod; break;
					case APPLY_IN:  ch->modifiers[STAT_IN]  += mod; break;
					case APPLY_CO:  ch->modifiers[STAT_CO]  += mod; break;
					case APPLY_AG:  ch->modifiers[STAT_AG]  += mod; break;
					case APPLY_SD:  ch->modifiers[STAT_SD]  += mod; break;
					case APPLY_ME:  ch->modifiers[STAT_ME]  += mod; break;
					case APPLY_RE:  ch->modifiers[STAT_RE]  += mod; break;      
					case APPLY_SEX:         ch->sex         += mod; break;
					case APPLY_MANA:        ch->max_mana    += mod; break;
					case APPLY_HIT:         ch->max_hit     += mod; break;
					case APPLY_MOVE:        ch->max_move    += mod; break;
					case APPLY_AC:   
						for(i = 0; i < 4; i ++)
							ch->armor[i] += mod; 
						break;
					case APPLY_HITROLL:     ch->hitroll     += mod; break;
					case APPLY_DAMROLL:     ch->damroll     += mod; break;
					// saving throws 
					case APPLY_SAVES:         ch->saving_throw += mod; break;

					default: break; // do nothing for the rest of the conditions
				}			
			}
		}

		// IC object restriction system - Kal
		if(obj->pIndexData && ch->pcdata
			&& HAS_CONFIG(ch,CONFIG_OBJRESTRICT) 
			&& ((ch->pcdata->objrestrict& obj->pIndexData->objrestrict)>0))
		{
			OBJRESTRICT_LIST_DATA *pr;
			AFFECT_DATA aff;
			int top_prority=-1;
			for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
			{
				if(IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex))){
					top_prority=UMAX(pr->priority, top_prority);
				}
				
			}

			// IC object restriction system - Kal
			for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
			{
				if(top_prority!=pr->priority 
					||	!IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex)))
				{
					continue;
				}
				aff.where		= WHERE_RESTRICT;
				aff.type		= -1;
				aff.level		= 0;
				aff.duration	= -1;
				aff.location	= pr->affectprofile->wear_location;
				aff.modifier	= pr->affectprofile->wear_amount;
				aff.bitvector	= 0;
				// do the act text
//				act(pr->affectprofile->wear_message,ch,obj,NULL,TO_CHAR);	
				// add the affect
    			affect_modify( ch, &aff, true );

				if(top_prority==-1) // -1 means only the first affectprofile is applied
					break;		
			}
		}
    }
  
    // now add back effects on the character 
    // now add back spell effects on the character
    for(af = ch->affected; af; af = af->next){
        mod = af->modifier;
        switch(af->location)
        {
			case APPLY_ST:  ch->modifiers[STAT_ST]  += mod; break;
			case APPLY_QU:  ch->modifiers[STAT_QU]  += mod; break;
			case APPLY_PR:  ch->modifiers[STAT_PR]  += mod; break;
			case APPLY_EM:  ch->modifiers[STAT_EM]  += mod; break;
			case APPLY_IN:  ch->modifiers[STAT_IN]  += mod; break;
			case APPLY_CO:  ch->modifiers[STAT_CO]  += mod; break;
			case APPLY_AG:  ch->modifiers[STAT_AG]  += mod; break;
			case APPLY_SD:  ch->modifiers[STAT_SD]  += mod; break;
			case APPLY_ME:  ch->modifiers[STAT_ME]  += mod; break;
			case APPLY_RE:  ch->modifiers[STAT_RE]  += mod; break;      
			case APPLY_SEX:         ch->sex         += mod; break;
			case APPLY_MANA:        ch->max_mana    += mod; break;
			case APPLY_HIT:         ch->max_hit     += mod; break;
			case APPLY_MOVE:        ch->max_move    += mod; break;
			case APPLY_AC:   
				for(i = 0; i < 4; i ++)
					ch->armor[i] += mod; 
				break;
			case APPLY_HITROLL:     ch->hitroll     += mod; break;
			case APPLY_DAMROLL:     ch->damroll     += mod; break;
			// saving throws 
			case APPLY_SAVES:         ch->saving_throw += mod; break;

			default: break; // do nothing for the rest of the conditions
		} 
    }
	ch->sex=URANGE(0, ch->sex, 3); // crash proof sex modifiers
}


/**************************************************************************/
// Retrieve a character's trusted level for permission checking.
// if someone is switched, their trust value is returned
int get_trust( char_data *ch )
{
    if(!ch)
    {
       log_string("BUG: in get_trust - ch was NULL");
        return 0;
    }

    if( TRUE_CH(ch)->trust)
		return TRUE_CH(ch)->trust;

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

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

    if( IS_NPC(ch) && IS_SET(ch->act, ACT_PET) ){
		return 50;
	}
    return(MAX_WEAR +  2 * (ch->modifiers[STAT_ST]+28) + ch->level/5 + 10)*10;
    
}

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

    if( IS_NPC(ch) && IS_SET(ch->act, ACT_PET) && !IS_CONTROLLED(ch))
	return 10;

    return((ch->modifiers[STAT_ST]+28) * 10 + ch->level * 5)*10;
}


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

	if(IS_NULLSTR(namelist)){
		return false;
	}

    string = str;
    // we need ALL parts of string to match part of namelist 
    for( ; ; )  // start parsing string 
    {
		str = one_argument(str,part);

		if(part[0] == '\0' )
			return true;

		// check to see if this is part of namelist 
		list = namelist;
		for( ; ; )  // start parsing namelist 
		{
			list = one_argument(list,name);
			if(name[0] == '\0')  // this name was not found 
			return false;

			if(!str_prefix(string,name))
			return true; // full pattern match

			if(!str_prefix(part,name))
			break;
		}
    }
}
/**************************************************************************/
// See if a string has one of the names infix 
bool is_name_infix( const char *str, const char *namelist )
{
    char name[MSL*2], part[MSL*2];
    const char *list, *string;

	if(IS_NULLSTR(namelist))
		return false;

    string = str;
    // we need parts of string to match part of namelist 
    for( ; ; )  // start parsing string 
    {
		str = one_argument(str,part);

		if(part[0] == '\0' )
			return true;

		// check to see if this is part of namelist 
		list = namelist;
		for( ; ; )  
		{
			list = one_argument(list,name);
			if(name[0] == '\0')  
			return false;

			if(!str_infix(string,name))
			return true; 

			if(!str_infix(part,name))
			break;
		}
    }
}

/**************************************************************************/
// return true if name str is in the name list
bool is_exact_name( const char *str, const char *namelist )
{
    char name[MSL*2];

	if(IS_NULLSTR(namelist))
		return false;

	if(!str_cmp(str, namelist)){
		return true;
	}

    for( ; ; )
    {
        namelist = one_argument( namelist, name );
        if( name[0] == '\0' )
            return false;
        if( !str_cmp( str, name ) )
            return true;
    }
}

/**************************************************************************/
// move the affects from the olc template of an object to the actual
// object - used when enchanting etc.
void affects_from_template_to_obj(OBJ_DATA *obj)
{
    if(obj->affected || obj->no_affects)
    {
		// previously got affects from template or has been disenchanted
		return;
	}

    // okay, move all the old flags into new vectors if we have to 
    AFFECT_DATA *paf, *af_new;
    for(paf = obj->pIndexData->affected; paf; paf = paf->next) // affects_from_template_to_obj
    {
		af_new = new_affect();

        af_new->next = obj->affected;
        obj->affected = af_new;

		af_new->where		= paf->where;
        af_new->type        = UMAX(0,paf->type);
        af_new->level       = paf->level;
        af_new->duration    = paf->duration;
        af_new->location    = paf->location;
        af_new->modifier    = paf->modifier;
        af_new->bitvector   = paf->bitvector;
    }
}
           
/**************************************************************************/
// Reduces the wealth on a mob if it is above the highest it could 
// possibly be rolled to.
// Notes:
//	Used to reduce a mobs money when they are in roles that a lot of players
//	give them money - e.g. bribe triggers for mobprogs, healers, money 
//	changers etc... (even though a lot of the code already does 'automated
//	banking' on some of the above.  - Kal May 1999
//  This system prevents players from subduing or killing these mobs for 
//	their money
void limit_mobile_wealth(char_data *mob)
{
	// can't be called on players
	if(!IS_NPC(mob))
		return;

	// get their mobindex
	MOB_INDEX_DATA *pMobIndex=mob->pIndexData;

	// must have a valid index data
	if(!pMobIndex)
		return;

	// get the maximum wealth value they could roll when the mob is 
	// created by create_mobile()
	long max_wealth= 3 * pMobIndex->wealth/2;

	long current_wealth= (mob->gold*100) + mob->silver;

	// if their wealth is greater than the max possible 
	// reroll it using similar code as in create_mobile()
	if(current_wealth>max_wealth){
		// limit the mobs weatlth to its wealth value
		long wealth = number_range(pMobIndex->wealth, 3 * pMobIndex->wealth/2);
		mob->gold = number_range(wealth/200,wealth/100);
		mob->silver = wealth - (mob->gold * 100);
	}
}
/**************************************************************************/
extern char *target_name;
/**************************************************************************/
// Cast a spell onto a player (by an object)
// - should only be called from affect_modify() 
static void affect_add_objectspell(char_data *ch, AFFECT_DATA *paf)
{
	// the assumption made
	assert(paf->where==WHERE_OBJECTSPELL);

	int sn=paf->type;
	if(sn<FIRST_SPELL || sn>LAST_SPELL){
		bugf("affect_add_castspell(): sn==%d, which isn't a spell sn!", sn);
		return;	
	}

	// see if they already have a spell based on that 
	// spell function at a higher level
	AFFECT_DATA *paf1;
	SPELL_FUN * spell_fun= skill_table[sn].spell_fun;
	if(!spell_fun || spell_fun==spell_null){
		bugf("affect_add_castspell(): No spell function for sn==%d '%s'.",
			sn, skill_table[sn].name);
		REMOVE_BIT(paf->bitvector,OBJSPELL_ACTIVE);
		return;
	}
    for( paf1= ch->affected; paf1; paf1= paf1->next ) {
		if(paf1->type>=FIRST_SPELL && paf1->type<=LAST_SPELL){
			if(skill_table[paf1->type].spell_fun == spell_fun 
				&& paf1!=paf && paf1->level>=paf->level)
			{
				// found an existing objectspell with same spell function 
				// at higher or equal level
				REMOVE_BIT(paf->bitvector,OBJSPELL_ACTIVE);
				return;
			}
		}
	}

	affect_parentspellfunc_strip( ch, sn);	// remove all previous spells based on spell sn
	AFFECT_DATA *stop=ch->affected;			// backup where the affects are currently
	// cast the new spell on the player
	target_name = str_dup(ch->name);
	(*skill_table[sn].spell_fun) ( sn, paf->level, ch, ch, TARGET_CHAR);	
	free_string(target_name);
	target_name=NULL;
	// affect is considered active
	SET_BIT(paf->bitvector,OBJSPELL_ACTIVE);

	// now find the affects from the spell just cast and patch the duration accordingly
	if(paf->duration!=0){
		AFFECT_DATA *paf_patch;
		for(paf_patch=ch->affected; paf_patch && paf_patch!=stop; paf_patch=paf_patch->next){
			if(paf_patch->type==sn){
				paf_patch->duration=paf->duration;

			}
		}
//			assert(paf_patch);	// this shouldn't go false unless some spell is
								// removing paf from ch->affected which shouldn't 
								// be the case since paf->where==WHERE_OBJECTSPELL
					// unless an object has been equiped twice - booting up
	}	
}
/**************************************************************************/
// Remove a spell from a player, that was cast on it by an object
// - should only be called from affect_modify()
// - should only remove the spell if it is exactly matching
//   and there are no other objects on the player which would 
//   cast the spell at the same level.  If another object is there
//   that will cast the object at a different level, remove then recast.
// - because affect_add_castspell() will only of put this spell on
//   if it was the highest level at the time, we dont have to check 
//   for spells above us.
static void affect_remove_objectspell(char_data *ch, AFFECT_DATA *paf)
{
	assert(paf->where==WHERE_OBJECTSPELL);

	// if the affect isn't active, then we dont have any spells to remove
	// that relate to this spell
	if(!IS_SET(paf->bitvector,OBJSPELL_ACTIVE)){
		return;
	}

	int sn=paf->type;
	// make sure we are dealing with a valid spell
	if(sn<FIRST_SPELL || sn>LAST_SPELL){
		bugf("affect_remove_castspell(): sn==%d, which isn't a spell sn!", sn);
		do_abort(); // abort here - how did they get it on in the first place			
	}

	// find the affects of the particular sn and remove them accordingly
	AFFECT_DATA *lpaf, *lpaf_next;
	bool show_msg =true;
	for(lpaf=ch->affected; lpaf; lpaf=lpaf_next){		
		lpaf_next=lpaf->next;
		if(lpaf->where!=WHERE_OBJECTSPELL && lpaf->type==sn){
			affect_remove(ch, lpaf);
			if(show_msg && !IS_NULLSTR(skill_table[sn].msg_off))
			{						
				ch->printf("%s\r\n",skill_table[sn].msg_off);
				show_msg = false; // only display the fade out message once
			}
		}
	}

	// check if there are any other objectspells which is to replace 
	// the spell just removed, if there are find the highest level casting one
	AFFECT_DATA *raf; // replacement affect
	AFFECT_DATA *match=NULL; // matching affect
	for(raf=ch->affected; raf; raf=raf->next){
		if( raf!=paf
			&& raf->where==WHERE_OBJECTSPELL
			&& raf->type== paf->type)
		{
			if(match){
				if(raf->level>match->level){
					match=raf; // take the higher level affect
				}
			}else{
				match=raf;
			}
		}
	}
	if(match){
		affect_modify( ch, match, true); // cast the replacement 
	}
}
/**************************************************************************/
void affect_apply_modifier( char_data *ch, AFFECT_DATA *paf, bool fAdd )						   
{
	int i;
	int amount=(fAdd?paf->modifier:-paf->modifier);
	switch( paf->location )
	{
	default:
		bugf( "affect_apply_modifier(): unknown location %d.", paf->location );
		do_abort(); // should never be in this situation
		return;
		
	case APPLY_NONE:		break;
	case APPLY_CLASS:		break;
	case APPLY_LEVEL:		break;
	case APPLY_AGE: 		break;
	case APPLY_HEIGHT:		break;
	case APPLY_WEIGHT:		break;
	case APPLY_GOLD:		break;
	case APPLY_EXP: 		break;
		
	case APPLY_ST:	 ch->modifiers[STAT_ST] += amount; break;
	case APPLY_QU:	 ch->modifiers[STAT_QU] += amount; break;
	case APPLY_PR:	 ch->modifiers[STAT_PR] += amount; break;
	case APPLY_EM:	 ch->modifiers[STAT_EM] += amount; break;
	case APPLY_IN:	 ch->modifiers[STAT_IN] += amount; break;
	case APPLY_CO:	 ch->modifiers[STAT_CO] += amount; break;
	case APPLY_AG:	 ch->modifiers[STAT_AG] += amount; break;
	case APPLY_SD:	 ch->modifiers[STAT_SD] += amount; break;
	case APPLY_ME:	 ch->modifiers[STAT_ME] += amount; break;
	case APPLY_RE:	 ch->modifiers[STAT_RE] += amount; break;		
		
	case APPLY_SEX: 		  
		if(fAdd){ 
			ch->sex+= amount;
		}else{
			// revert to default sex dont worry if they had 
			// multiple objects on them modifying their sex.
			if(IS_NPC(ch)){
				ch->sex = ch->pIndexData->sex;
				if(ch->sex == 3){ // random sex 
					ch->sex = number_range(1,2);
				}
			}else{
				ch->sex= ch->pcdata->true_sex;
			}		
		}
		ch->sex=URANGE(0, ch->sex, 3); // crash proof sex modifiers
		break;
		
	case APPLY_MANA:		
		ch->max_mana+= amount; 
		ch->mana+= amount; 
		break;
	case APPLY_HIT: 		
		ch->max_hit+= amount; 
		if(  !fAdd 
			&& (GAMESETTING3(GAMESET3_ALWAYS_NO_NEGATIVE_HP_AT_AFFECTOFF) || 
			(paf->type>=0 
			&& paf->type<MAX_SKILL
			&& IS_SET(skill_table[paf->type].flags, SKFLAGS_NO_NEGATIVE_HP_AT_AFFECTOFF))))
		{
			if(ch->hit>0){
				ch->hit+= amount; 
				ch->hit=UMAX(1,ch->hit); // no death from spell when it wears off
			}else{
				ch->hit+= amount; 
			}
		}else{
			ch->hit+= amount; 
		}
		
		break;
	case APPLY_MOVE:		
		ch->max_move+= amount; 
		ch->move+= amount; 
		break;
	case APPLY_AC:			
		for(i = 0; i < 4; i ++){
			ch->armor[i] += amount;
		}
		break;
		
	case APPLY_HITROLL: 	ch->hitroll+= amount; break;
	case APPLY_DAMROLL: 	ch->damroll+= amount; break;
		
	case APPLY_SAVES:		ch->saving_throw+= amount; break;
		
	}
}
/**************************************************************************/
// Apply or remove an affect to a character.
void affect_modify( char_data *ch, AFFECT_DATA *paf, bool fAdd )
{
    OBJ_DATA *wield= NULL;
	
    if( fAdd )
    {
		switch(paf->where)
		{
		case WHERE_OBJECTSPELL:
			affect_add_objectspell(ch,paf);
			break;		
		// if the object is set to affect skills
		case WHERE_SKILLS:
			affect_to_skill( ch, paf->type, paf->modifier );
			break;
		case WHERE_AFFECTS:
			SET_BIT(ch->affected_by, paf->bitvector);
			break;
		case WHERE_AFFECTS2:
			SET_BIT(ch->affected_by2, paf->bitvector);
			break;
		case WHERE_IMMUNE:
			SET_BIT(ch->imm_flags,paf->bitvector);
			break;
		case WHERE_RESIST:
			SET_BIT(ch->res_flags,paf->bitvector);
			break;
		case WHERE_VULN:
			SET_BIT(ch->vuln_flags,paf->bitvector);
			break;			
		}
    }else{ // removing the affect
        switch(paf->where)
        {
		case WHERE_OBJECTSPELL:
			affect_remove_objectspell(ch,paf);
			break;		
		case WHERE_SKILLS:
			affect_to_skill( ch, paf->type, -paf->modifier );
			break;
        case WHERE_AFFECTS:
            REMOVE_BIT(ch->affected_by, paf->bitvector);
            break;
        case WHERE_AFFECTS2:
            REMOVE_BIT(ch->affected_by2, paf->bitvector);
            break;
        case WHERE_IMMUNE:
            REMOVE_BIT(ch->imm_flags,paf->bitvector);
            break;
        case WHERE_RESIST:
            REMOVE_BIT(ch->res_flags,paf->bitvector);
            break;
        case WHERE_VULN:
            REMOVE_BIT(ch->vuln_flags,paf->bitvector);
            break;
        }
    }

	// perform the modifiers of the affect
	affect_apply_modifier(ch, paf, fAdd);
	
    // Check for weapon wielding.
	// Guard against recursion (for weapons with affects).
    if( !IS_NPC(ch) 
		&& ( wield = get_eq_char( ch, WEAR_WIELD ) ) != NULL
		&&   get_obj_weight(wield) > (ch->modifiers[STAT_ST]+20)*10 
		&& ch->position != POS_DEAD )
    {
		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--;
		}
    }
	update_pos(ch);
	return;
}

/**************************************************************************/
// find an effect in an affect list
AFFECT_DATA  *affect_find(AFFECT_DATA *paf, int sn)
{
    AFFECT_DATA *paf_find;
    
    for( paf_find = paf; paf_find != NULL; paf_find = paf_find->next )
    {
        if( paf_find->type == sn )
	return paf_find;
    }

    return NULL;
}

/**************************************************************************/
// fix object affects when removing one 
void affect_check(char_data *ch, int where, int vector)
{
    AFFECT_DATA *paf;
    OBJ_DATA *obj;
	
	if(vector == 0 || flag_value(to_types, flag_string(to_types, where)) == NO_FLAG){
		// flag_value(to_types, flag_string(to_types, where)) will return NO_FLAG
		// if the where location relates to a table that isn't relevant to characters
		return;
	}

	// loop thru a players current affects, reseting a bit if necessary
    for(paf = ch->affected; paf; paf = paf->next){
		if(paf->where == where && paf->bitvector == vector)
		{
			switch(where)
			{
			case WHERE_AFFECTS:
				SET_BIT(ch->affected_by, paf->bitvector);
				break;
			case WHERE_AFFECTS2:
				SET_BIT(ch->affected_by2, paf->bitvector);
				break;
			case WHERE_IMMUNE:
				SET_BIT(ch->imm_flags,paf->bitvector);
				break;
			case WHERE_RESIST:
				SET_BIT(ch->res_flags,paf->bitvector);
				break;
			case WHERE_VULN:
				SET_BIT(ch->vuln_flags,paf->bitvector);
				break;			
			}
			return;
		}
	}

	// loop thru what a player is wearing, reseting a bit if necessary
    for(obj = ch->carrying; obj; obj = obj->next_content)
    {
		if(obj->wear_loc == WEAR_NONE)
			continue;
		
		for(paf = OBJECT_AFFECTS(obj); paf; paf = paf->next){
            if(paf->where == where && paf->bitvector == vector){
                switch(where)
                {
				case WHERE_AFFECTS:
					SET_BIT(ch->affected_by, paf->bitvector);
					break;
				case WHERE_AFFECTS2:
					SET_BIT(ch->affected_by2, paf->bitvector);
					break;
				case WHERE_IMMUNE:
					SET_BIT(ch->imm_flags,paf->bitvector);
					break;
				case WHERE_RESIST:
					SET_BIT(ch->res_flags,paf->bitvector);
					break;
				case WHERE_VULN:
					SET_BIT(ch->vuln_flags,paf->bitvector);
					break;			
					
                }
                return;
            }
		}	
    }
}

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

	// do debugging stuff
	if(paf->type>=MAX_SKILL)
	{
		bug("affect_to_char(): Invalid affect!!!\r\n");
		do_abort();
	}

    paf_new = new_affect();
    *paf_new		= *paf;
    paf_new->next	= ch->affected;
    ch->affected	= paf_new;
	affect_modify( ch, paf_new, true );
    return;
}
/**************************************************************************/
// Give an affect to a char, when paf->where==WHERE_OBJECTSPELL
// Give it if they are high enough level for the affect or 
// if ignore level is set on the affect bitvector
void affect_copy_to_char( char_data *ch, AFFECT_DATA *paf, int objlevel )
{
    AFFECT_DATA *paf_new;

	// do debugging stuff
	if(paf->type>=MAX_SKILL)
	{
		bugf("affect_copy_to_char(): Invalid affect - paf->type=%d (higher than MAX_SKILL)!!!", paf->type);
		do_abort();
	}

	if(paf->where!=WHERE_OBJECTSPELL){
		bug("affect_copy_to_char(): paf->where==WHERE_OBJECTSPELL!!!\r\n");
		do_abort();
	}

	// level check 
	int castlevel=paf->level?paf->level:objlevel;
	if(!IS_SET(OBJSPELL_IGNORE_LEVEL, paf->bitvector) 
		&& castlevel>ch->level)
	{
		return;
	}

	// store the affect on the player
    paf_new = new_affect();
    *paf_new		= *paf;
	// if it is a level 0 objectspell, patch up the cast level 
	if(paf_new->where==WHERE_OBJECTSPELL && paf_new->level==0){
		paf_new->level=objlevel;
	}
    paf_new->next	= ch->affected;
    ch->affected	= paf_new;
	affect_modify( ch, paf_new, true ); // handles casting if necessary
    return;
}
/**************************************************************************/
// loop thru all the affects on the object, finding which one it is
// - called from unequip_char()
// - paf is the affect on an object, not the copy
// - once the copy on the char has been found, it is removed, if there is
//   multiple affects of the same type, the active one will be removed.
void affect_removecopy_from_char( char_data *ch, AFFECT_DATA *paf, int objlevel )
{
	assert(paf->where==WHERE_OBJECTSPELL);

	// level check 
	if(!IS_SET(OBJSPELL_IGNORE_LEVEL, paf->bitvector) 
		&& paf->level>ch->level)
	{
		return;
	}

	AFFECT_DATA *caf=NULL;
	AFFECT_DATA *match=NULL;
	// find our affect, favouring an OBJSPELL_ACTIVE if possible
	for( caf= ch->affected; caf; caf=caf->next)
	{
		if((caf->type == paf->type) &&
			(caf->where==WHERE_OBJECTSPELL))
		{
			// if it is a level 0 objectspell, patch up the cast level 
			if(paf->where==WHERE_OBJECTSPELL && paf->level==0){
				if(caf->level!=objlevel){
					continue;
				}
			}else{
				if(caf->level!=paf->level){
					continue;
				}
			}

			match=caf;
			if(IS_SET(caf->bitvector,OBJSPELL_ACTIVE)){
				break; // found the active version
			}				
		}
	}

	if(match){
		affect_remove( ch, match);
	}else{
		bug("affect_removecopy_from_char(): couldn't find any affect copy.");
		return;
	}	
}
/**************************************************************************/
// give an affect to an object
void affect_to_obj(OBJ_DATA *obj, AFFECT_DATA *paf)
{
    AFFECT_DATA *paf_new;

	affects_from_template_to_obj(obj);
	
    paf_new = new_affect();
	
    *paf_new		= *paf;
    paf_new->next	= obj->affected;
    obj->affected	= paf_new;
	
    // apply any affect vectors to the object's extra_flags 
    if(paf->bitvector){
		switch(paf->where)
		{
        case WHERE_OBJEXTRA:
			SET_BIT(obj->extra_flags,paf->bitvector);
			break;
        case WHERE_OBJEXTRA2:
			SET_BIT(obj->extra2_flags,paf->bitvector);
			break;
        case WHERE_WEAPON:
			if(obj->item_type == ITEM_WEAPON)
				SET_BIT(obj->value[4],paf->bitvector);
			break;

		default:
			break;
		}
	}
    return;
}


/**************************************************************************/
// Remove an affect from a char.
void affect_remove( char_data *ch, AFFECT_DATA *paf )
{
    int where;
    int vector;
	
    if( ch->affected == NULL )
    {
		bug("Affect_remove: no affects on ch!");
		return;
    }
	
    affect_modify( ch, paf, false );
    where = paf->where;
    vector = paf->bitvector;
	
    if( paf == ch->affected ){
		ch->affected	= paf->next;
    }else{
		AFFECT_DATA *prev;
		
		for( prev = ch->affected; prev != NULL; prev = prev->next )
		{
			if( prev->next == paf )
			{
				prev->next = paf->next;
				break;
			}
		}
		
		if( prev == NULL )
		{
			bug("Affect_remove: cannot find paf.");
			return;
		}
    }
	
    free_affect(paf);
	
	// fix up flying
	if(!IS_AFFECTED(ch, AFF_FLYING)){
		REMOVE_BIT(ch->dyn,DYN_NONMAGICAL_FLYING);
	}
    affect_check(ch,where,vector);
    return;
}

/**************************************************************************/
void affect_remove_obj( OBJ_DATA *obj, AFFECT_DATA *paf)
{
    int where, vector;
    if( obj->affected == NULL )
    {
        bug("Affect_remove_object: no affect.");
        return;
    }
	
    if(obj->carried_by != NULL && obj->wear_loc != -1)
		affect_modify( obj->carried_by, paf, false );
	
    where  = paf->where;
    vector = paf->bitvector;
	
    /* remove flags from the object if needed */
    if(paf->bitvector)
		switch( paf->where)
	{
		case WHERE_OBJEXTRA:
            REMOVE_BIT(obj->extra_flags,paf->bitvector);
            break;
		case WHERE_OBJEXTRA2:
            REMOVE_BIT(obj->extra2_flags,paf->bitvector);
            break;
        case WHERE_WEAPON:
            if(obj->item_type == ITEM_WEAPON)
                REMOVE_BIT(obj->value[4],paf->bitvector);
            break;
	}
	
    if( paf == obj->affected )
    {
        obj->affected    = paf->next;
    }
    else
    {
        AFFECT_DATA *prev;
		
        for( prev = obj->affected; prev != NULL; prev = prev->next )
        {
            if( prev->next == paf )
            {
                prev->next = paf->next;
                break;
            }
        }
		
        if( prev == NULL )
        {
            bug("Affect_remove_object: cannot find paf.");
            return;
        }
    }
	
    free_affect(paf);
	
    if(obj->carried_by != NULL && obj->wear_loc != -1)
		affect_check(obj->carried_by,where,vector);
    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->affected; paf != NULL; paf = paf_next )
    {
		paf_next = paf->next;
		if(paf->where==WHERE_OBJECTSPELL){
			continue;
		}
		if( paf->type == sn ){
		    affect_remove( ch, paf );
		}
    }

    return;
}

/**************************************************************************/
// Strip all affects from spells with the same spell function as that 
// belonging to the given sn if it is a spell, otherwise just strip the 
// affect
void affect_parentspellfunc_strip( char_data *ch, int sn )
{
    AFFECT_DATA *paf;
    AFFECT_DATA *paf_next;

	if(sn<FIRST_SPELL || sn>LAST_SPELL){
		affect_strip( ch, sn );
		return;
	}

	SPELL_FUN * spell_fun= skill_table[sn].spell_fun;

	if(!spell_fun || spell_fun==spell_null){
		affect_strip( ch, sn );
		return;
	}

    for( paf = ch->affected; paf != NULL; paf = paf_next )
    {
		paf_next = paf->next;
		if(paf->type>=FIRST_SPELL && paf->type<=LAST_SPELL){
			if(skill_table[paf->type].spell_fun == spell_fun){
				if(paf->where==WHERE_OBJECTSPELL)
				{
					REMOVE_BIT(paf->bitvector,OBJSPELL_ACTIVE);
				}else{
					affect_remove( ch, paf );
				}
			}
		}
	}
    return;
}

/**************************************************************************/
// Return the number of spells ch is affected by that are based on sn
int count_affected_by_base_spell( char_data *ch, int parentspell_sn )
{
    AFFECT_DATA *paf;
    AFFECT_DATA *paf_next;
	int count = 0;

	if(parentspell_sn <FIRST_SPELL || parentspell_sn >LAST_SPELL){
		return 0;
	}

	SPELL_FUN * spell_fun= skill_table[parentspell_sn ].spell_fun;

	if(!spell_fun || spell_fun==spell_null){
		return 0;
	}

    for( paf = ch->affected; paf != NULL; paf = paf_next )
    {
		paf_next = paf->next;
		if(paf->type>=FIRST_SPELL && paf->type<=LAST_SPELL){
			if(!(paf->where==WHERE_OBJECTSPELL))
			{
				if(skill_table[paf->type].spell_fun == spell_fun){
					count++;
				}
			}
		}
	}

    return count;
}

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

	if( IS_NULLSTR(ch->name))
		return false;

    for( paf = ch->affected; paf != NULL; paf = paf->next ) {
		if(paf->where==WHERE_OBJECTSPELL){
			continue;
		}
		if( paf->type == sn )
		    return true;
    }
    return false;
}

/**************************************************************************/
// Kal - August 99
// notes: goes thru char, if they are flying, checking if they are flying
//     due to magical causes (any spell that gives the AFF_FLY affect)
//     If it is true it removes the DYN_NONMAGICAL_FLYING bit so we dont 
//     subtract movement points from them for flying.
void affect_fly_update(char_data *ch)
{
	if( IS_AFFECTED(ch, AFF_FLYING) ){
		SET_BIT(ch->dyn,DYN_NONMAGICAL_FLYING);
		// loop thru finding any fly affects
		AFFECT_DATA *paf;
		for( paf= ch->affected; paf; paf = paf->next)
		{
			if( paf->bitvector== AFF_FLYING){
				REMOVE_BIT(ch->dyn,DYN_NONMAGICAL_FLYING);
			}
		}
	}else{
		REMOVE_BIT(ch->dyn,DYN_NONMAGICAL_FLYING);
	}
}
	
/**************************************************************************/
// 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->affected; paf_old != NULL; paf_old = paf_old->next )
    {
		if(paf->where==WHERE_OBJECTSPELL){
			continue;
		}
		if( paf_old->type == paf->type )
		{
			paf->level = (paf->level += paf_old->level) / 2;
			paf->duration += paf_old->duration;
			paf->modifier += paf_old->modifier;
			affect_remove( ch, paf_old );
			break;
		}
    }

    affect_to_char( ch, paf );
    return;
}

/**************************************************************************/
// returns true if the ch is actually in the list of people in a room
// used by nuke_pets() and extract_char() to detect the difference between:
// * When someones ch->in_room and/or ch->pet_in_room has been set to point 
//   to a room, but they arent actually in the room yet as they haven't 
//   gone thru nanny_read_motd() to get to the char_to_room() call.
//   This time they wont be in the room.
// * When someone drops link, and reconnects to their linkdead char, 
//   then next time they login, they drop due to idling before a
//   char_to_room() call.  (This time they will be in the room, and
//   ch->desc->connected_state will show them as something other than
//   CON_PLAYING)
bool is_character_loaded_into_room(char_data *ch, ROOM_INDEX_DATA *room)
{
	if(!room){
		return false;
	}

	char_data *rch;
    
	for( rch = room->people; rch; rch = rch->next_in_room )
    {
		if(rch==ch){
			return true;
		}
    }
	return false;
}
/**************************************************************************/
void make_corefile();
/**************************************************************************/
// Move a char out of a room.
void char_from_room( char_data *ch )
{
    OBJ_DATA *obj;

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

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

	ch->in_room->number_in_room--;

	if(ch->in_room->number_in_room<0){
		bugf("char_from_room(%s): ch->in_room->number_in_room<0!!!", ch->name);
		ch->in_room->number_in_room=0;
	}

	// They arent actually in the room, just have the ch->room pointer
	// set cause logging in then timed out
	if((ch->desc && ch->desc->connected_state!=CON_PLAYING) || !ch->desc){
		// if they arent in the game yet (haven't logged in, and this 
		// isn't a reconnect) or they are linkdead confirm
		// they are in the room
		if(!is_character_loaded_into_room(ch,ch->in_room)){
			ch->in_room		= NULL;
			ch->next_in_room= NULL;
			ch->on 			= NULL;  // sanity check! 
			return;
		};
	}

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

    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 == NULL ){
            bugf( "Char_from_room: ch not found!!! ch->name=%s, "
				"ch->in_room->vnum=%d", ch->name, ch->in_room->vnum);
		}
    }

    ch->in_room      = NULL;
    ch->next_in_room = NULL;
    ch->on 	     = NULL;  // sanity check! 
    return;
}

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

    if( pRoomIndex == NULL )
    {
		ROOM_INDEX_DATA *room;

		bugf( "Char_to_room: pRoomIndex==NULL!, get_room_index()'s last failed call was for vnum %d.", DEBUG_LAST_NON_EXISTING_REQUESTED_ROOM_VNUM);
		ch->printlnf("Bug in Char_to_room(): pRoomIndex==NULL!...`1`1"
			"Last unfound requested room vnum was %d.`1"
			"Please report this to the admin.",
			DEBUG_LAST_NON_EXISTING_REQUESTED_ROOM_VNUM);		
		if((room = get_room_index(ROOM_VNUM_OOC)) != NULL){
			ch->println("Taking you to the OOC rooms");
			char_to_room(ch,room);
		}
		return;
    }

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

    if( !IS_NPC(ch) )
    {
        if(ch->in_room->area->empty)
        {
            ch->in_room->area->empty = false;
            ch->in_room->area->age = 0;
        }
        ch->in_room->area->nplayer++;
    }

	ch->in_room->number_in_room++;

    if( ( obj = get_eq_char( ch, WEAR_LIGHT ) ) != NULL
    &&   obj->item_type == ITEM_LIGHT
    &&   obj->value[2] != 0 )
	++ch->in_room->light;
	
    if(IS_AFFECTED(ch,AFF_PLAGUE))
    {
        AFFECT_DATA *af, plague;
        char_data *vch;
        
        for( af = ch->affected; af != NULL; af = af->next )
        {
            if(af->type == gsn_plague)
                break;
        }
        
        if(af == NULL)
        {
            REMOVE_BIT(ch->affected_by,AFF_PLAGUE);
            return;
        }
        
        if(af->level == 1)
            return;
        
		plague.where		= WHERE_AFFECTS;
        plague.type 		= gsn_plague;
        plague.level 		= af->level - 1; 
        plague.duration 	= number_range(1,2 * plague.level);
        plague.location		= APPLY_ST;
        plague.modifier 	= -5;
        plague.bitvector 	= AFF_PLAGUE;
        
        for( vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room)
        {
            if(!saves_spell(plague.level - 2,vch,DAM_DISEASE) 
	    &&  !IS_IMMORTAL(vch) &&
            	!IS_AFFECTED(vch,AFF_PLAGUE) && number_bits(6) == 0)
            {
            	vch->println("You feel hot and feverish.");
            	act("$n shivers and looks very ill.",vch,NULL,NULL,TO_ROOM);
            	affect_join(vch,&plague);
            }
        }
    }

	// MSP Check
	if( !IS_NPC( ch )) {
		if( CAN_HEAR_MSP( ch ) && !IS_NULLSTR(ch->in_room->msp_sound))
		{
			msp_to_room(MSPT_ROOM, ch->in_room->msp_sound, 
					0, 
					ch,
					true,
					false);
		}
	}

	if( !IS_NPC( ch )) {
	    if( IS_SET( ch->in_room->affected_by, ROOMAFF_ALARM ))
		{
			if( !IS_NULLSTR( ch->in_room->alarm->name )
			&&	 ch->in_room->alarm != ch )
			{
				ch->in_room->alarm->println("Your alarm has been triggered!");
				REMOVE_BIT( ch->in_room->affected_by, ROOMAFF_ALARM );
				ch->in_room->alarm = NULL;
			}
		}
	}
	return;
}


/**************************************************************************/
// Give an obj to a char.
void obj_to_char( OBJ_DATA *obj, char_data *ch )
{
    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 ) == NULL )
    {
        bug("Obj_from_char: null ch.");
        return;
    }

    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 != NULL; prev = prev->next_content )
        {
            if( prev->next_content == obj )
            {
                prev->next_content = obj->next_content;
                break;
            }
        }
    
        if( prev == NULL )
        {
            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( OBJ_DATA *obj, int /*iWear*/, int type )
{
    if( obj->item_type != ITEM_ARMOR ){
		return 0;
	}

	return obj->value[type];
}

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

    if(ch == NULL)
	return NULL;

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

    return NULL;
}


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

    if( get_eq_char( ch, iWear ) != NULL )
    {        
        bug("Equip_char: already equipped");
		bugf("(on mob %d, room=%d, object=%d, worn: %s).",
            (IS_NPC(ch)?(ch->pIndexData->vnum) :0),
			ch->in_room_vnum(), obj->pIndexData->vnum, 
			flag_string(wear_location_strings_types, iWear));
		bugf("(%s, %s).", ch->short_descr, obj->short_descr);
        return;
    }

	if( !IS_IMMORTAL( ch ) && !(IS_NPC(ch) && 
			(IS_SET(ch->act,ACT_NOALIGN) || IS_SET(ch->act2,ACT2_NO_TENDENCY))  
							   )
	  ){
	    if( ( IS_OBJ_STAT(obj, OBJEXTRA_ANTI_EVIL)    && IS_EVIL(ch)    )	
	    ||   ( IS_OBJ_STAT(obj, OBJEXTRA_ANTI_GOOD)    && IS_GOOD(ch)    )
	    ||   ( IS_OBJ_STAT(obj, OBJEXTRA_ANTI_NEUTRAL) && IS_NEUTRAL(ch) )
        ||   ( IS_OBJ2_STAT(obj, OBJEXTRA2_ANTI_CHAOS)    && IS_TEND_CHAOTIC(ch) )
        ||   ( IS_OBJ2_STAT(obj, OBJEXTRA2_ANTI_LAW)    && IS_TEND_LAWFUL(ch) )
        ||   ( IS_OBJ2_STAT(obj, OBJEXTRA2_ANTI_BALANCE) && IS_TEND_NEUTRAL(ch) ) )
		{
			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 );
			obj_to_room( obj, ch->in_room );
			return;
		}
	}

    // apply the armour class
	for(i = 0; i < 4; i++)
	{
		ac_amount = apply_ac( obj, iWear, i );
		if(ch->level< obj->level)
			ac_amount = ac_amount * ch->level/ obj->level;           
		ch->armor[i] -= ac_amount;
	}

	// record on the object where it is being worn
    obj->wear_loc	 = iWear;

	// Apply the affects of the object
	// - from olc template if object doesn't have custom enchants
	for(paf=OBJECT_AFFECTS(obj); paf; paf = paf->next )
	{
		if( paf->where==WHERE_OBJECTSPELL)	{
			affect_copy_to_char( ch, paf, obj->level);
		}else{
			if(paf->level<=ch->level){
				affect_modify( ch, paf, true );
			}
		}
	}

	// check for restrictgroup based restrictions on object, 
	// if they exist apply them to the wearer	
	if(obj->pIndexData && ch->pcdata
		&& HAS_CONFIG(ch,CONFIG_OBJRESTRICT) 
		&& ((ch->pcdata->objrestrict& obj->pIndexData->objrestrict)>0))
	{
		OBJRESTRICT_LIST_DATA *pr;
		AFFECT_DATA aff;
		int top_prority=-1;
		for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
		{
			if(IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex))){
				top_prority=UMAX(pr->priority, top_prority);
			}
			
		}

		// IC object restriction system - Kal
		for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
		{
			if(top_prority!=pr->priority 
				||	!IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex)))
			{
				continue;
			}
			aff.where		= WHERE_RESTRICT;
			aff.type		= -1;
			aff.level		= 0;
			aff.duration	= -1;
			aff.location	= pr->affectprofile->wear_location;
			aff.modifier	= pr->affectprofile->wear_amount;
			// can't have positive modifiers - invert them - done now in object loading
//			if(aff.modifier>0){
//				aff.modifier=0-aff.modifier;
//			}
			aff.bitvector	= 0;
			// do the act text
			act(pr->affectprofile->wear_message,ch,obj,NULL,TO_CHAR);	
			// add the affect
    		affect_modify( ch, &aff, true );

			if(top_prority==-1) // -1 means only the first affectprofile is applied
				break;		
		}
	}

	// if the object is a light, change the room lighting
    if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0 && ch->in_room)
		++ch->in_room->light;

    return;
}


/**************************************************************************/
// Unequip a char with an obj.
void unequip_char( char_data *ch, OBJ_DATA *obj )
{
    AFFECT_DATA *paf = NULL;
    int i, ac_amount;
	
    if( obj->wear_loc == WEAR_NONE )
    {
		bug("Unequip_char: already unequipped.");
		return;
    }
	
    // remove the affects on the armour class
	for(i = 0; i < 4; i++)
	{
		ac_amount = apply_ac( obj, obj->wear_loc, i );
		if(ch->level< obj->level){
			ac_amount = ac_amount * ch->level/ obj->level;
		}
		ch->armor[i] += ac_amount;
	}
	
	// record on the object that it is no longer worn
    obj->wear_loc = WEAR_NONE;
	
	// Remove the affects of the object
	// - from olc template if object doesn't have custom enchants
	for(paf=OBJECT_AFFECTS(obj); paf; paf = paf->next )
	{
		if( paf->where==WHERE_OBJECTSPELL)
		{
	   		affect_removecopy_from_char( ch, paf, obj->level );			
		}
		else
		{
			if(paf->level<=ch->level){
				affect_modify( ch, paf, false );
				affect_check(ch,paf->where,paf->bitvector);
			}
		}
	}

	// check for restrictgroup based restrictions on object, 
	// if they exist remove them from the wearer	
	if(obj->pIndexData && ch->pcdata
		&& HAS_CONFIG(ch,CONFIG_OBJRESTRICT) 
		&& ((ch->pcdata->objrestrict& obj->pIndexData->objrestrict)>0))
	{
		OBJRESTRICT_LIST_DATA *pr;
		AFFECT_DATA aff;
//		ch->println("equip_char(): group restriction on object match, searching...");
		int top_prority=-1;
		for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
		{
			if(IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex))){
				top_prority=UMAX(pr->priority, top_prority);
			}
			
		}

		for( pr = obj->pIndexData->restrict; pr; pr = pr->next )
		{
			if(top_prority!=pr->priority 
				||	!IS_SET(ch->pcdata->objrestrict,(1<<pr->classgroup->bitindex)))
			{
				continue;
			}
//			ch->println("unequip_char(): Found... removing.");
			aff.where		= WHERE_RESTRICT;
			aff.type		= -1;
			aff.level		= 0;
			aff.duration	= -1;
			aff.location	= pr->affectprofile->wear_location;
			aff.modifier	= pr->affectprofile->wear_amount;
			aff.bitvector	= 0;
			// do the act text
			act(pr->affectprofile->remove_message,ch,obj,NULL,TO_CHAR);	
			// remove the affect
    		affect_modify( ch, &aff, false);

			if(top_prority==-1) // -1 means only the first affectprofile is applied
				break;		
		}
	}

	// if the object is a light, change the room lighting
	if( obj->item_type == ITEM_LIGHT && obj->value[2] != 0
		&& ch->in_room != NULL && ch->in_room->light > 0 ){
		--ch->in_room->light;
	}
	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 != NULL; obj = obj->next_content )
    {
		if( 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 *ch;

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

    for(ch = in_room->people; ch != NULL; ch = ch->next_in_room)
	if(ch->on == obj)
	    ch->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 == NULL )
	{
	    bug("Obj_from_room: obj not found.");
	    return;
	}
    }

    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 )
{
	if(!pRoomIndex){
		bug("obj_to_room(): NULL input pRoomIndex");
	}else{
	    obj->next_content		= pRoomIndex->contents;
		pRoomIndex->contents	= obj;
	}
    obj->in_room		= pRoomIndex;
    obj->carried_by		= NULL;
    obj->in_obj			= NULL;
    return;
}



/**************************************************************************/
// Move an object into an object.
void obj_to_obj( OBJ_DATA *obj, OBJ_DATA *obj_to )
{
    obj->next_content		= obj_to->contains;
    obj_to->contains		= obj;
    obj->in_obj			= obj_to;
    obj->in_room		= NULL;
    obj->carried_by		= NULL;
    if(obj_to->pIndexData->vnum == OBJ_VNUM_PIT)
        obj->cost = 0; 

    for( ; obj_to != NULL; obj_to = obj_to->in_obj )
    {
	if( obj_to->carried_by != NULL )
	{
	    obj_to->carried_by->carry_number += get_obj_number( obj );
	    obj_to->carried_by->carry_weight += get_obj_weight( obj )
		* WEIGHT_MULT(obj_to) / 100;
	}
    }

    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 ) == NULL )
	{
		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 == NULL )
		{
			bug("Obj_from_obj: obj not found.");
			return;
		}
	}

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

	for( ; obj_from != NULL; obj_from = obj_from->in_obj )
	{
		if( obj_from->carried_by != NULL )
		{
			obj_from->carried_by->carry_number -= get_obj_number( obj );
			obj_from->carried_by->carry_weight -= get_obj_weight( obj ) 
				* WEIGHT_MULT(obj_from) / 100;
		}
	}

	return;
}


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

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

    for( obj_content = obj->contains; obj_content; obj_content = obj_next )
    {
        obj_next = obj_content->next_content;
        extract_obj( obj_content );
    }

    if( object_list == obj )
    {
        object_list = obj->next;
    }
    else
    {
        OBJ_DATA *prev;
    
        for( prev = object_list; prev != NULL; prev = prev->next )
        {
            if( prev->next == obj )
            {
                prev->next = obj->next;
                break;
            }
        }
    
        if( prev == NULL ){
            if(obj->pIndexData){
                bugf( "Extract_obj: obj %d not found.", obj->pIndexData->vnum );
            }else{
                bug("Extract_obj: obj ??? not found. (no pIndexData on it!)");
			}
            return;
        }
    }

    if(obj->pIndexData)
        obj->pIndexData->count--;

    free_obj(obj);
    return;
}

/**************************************************************************/
void duel_logout(char_data *ch);
/**************************************************************************/
void extract_char_from_char_list(char_data *ch)
{
    if( ch == char_list ){
		char_list = ch->next;
    }else{
		char_data *prev;

		for( prev = char_list; prev != NULL; prev = prev->next )
		{
			if( prev->next == ch )
			{
			prev->next = ch->next;
			break;
			}
		}

		if( prev == NULL )
		{
			bug("extract_char_from_char_list(): char not found.");
			return;
		}
    }

}
/**************************************************************************/
// Extract a char from the world - does NOT handle any pfile saving
void extract_char( char_data *ch, bool fPull )
{
    char_data *wch;
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
	ROOM_INDEX_DATA *location;
	ROOM_INDEX_DATA *recount_in_room;
    vn_int recall_vnum;

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

	// ploaded players can't be extracted using extract_char()
	if(ch->pload && ch->pload->dont_save){
		if(ch->pload->loaded_by){
			ch->pload->loaded_by->printlnf( "extract_char(): character %s can't be extracted cause it was ploaded.", ch->name);
		}			
		logf( "extract_char(): character %s can't be extracted cause it was ploaded.", ch->name);
		return;
	}


    nuke_pets(ch);
    ch->pet = NULL; // just in case 

    if( fPull )
	{
		die_follower( ch );
	}
    
	stop_fighting( ch, true );

    for( obj = ch->carrying; obj != NULL; obj = obj_next )
    {
        obj_next = obj->next_content;
		if(obj->item_type == ITEM_TOKEN && 
			!IS_SET ( obj->value[0], TOKEN_DROPDEATH )) {
				continue;
		}
        extract_obj( obj );
    }
    
	recount_in_room=ch->in_room;
    char_from_room( ch );

	ch->mpqueue_dequeue_all();

    // Death room is set in the clan table now
    if( !fPull ) // was a player that died - therefore not removed from the game
    {
		if(GAMESETTING5(GAMESET5_DEDICATED_PKILL_STYLE_MUD)){
			recall_vnum =ROOM_PKILLPORT_DEATH_ROOM; // pkill port hack
		}else{
			recall_vnum = race_table[ch->race]->death_room;
		}
    
		if( ( location = get_room_index( recall_vnum ) ) == NULL )
		{
			ch->println("You feel totally disorientated.");

			if( ( location = get_room_index( ROOM_VNUM_OOC ) ) == NULL)
			{
				ch->printf("BUG: Cant find the main ooc room (vnum = %d)\r\n"
					"Please report this to an admin.\r\n", ROOM_VNUM_OOC);
				return;
			}
			else
			{
				if(IS_SET(location->room_flags, ROOM_OOC))
				{
					ch->println("Taking you to the main OOC room since your normal death room doesn't exist.");				
				}
				else
				{
					ch->printf("BUG: Taking you to the main ooc room (vnum = %d)\r\n"
					"This room SHOULD be an OOC room - please report this bug to an admin.\r\n", ROOM_VNUM_OOC);
				}
			}	
		}

        char_to_room(ch,location);
        return;
    }

	// extracting characters cause they are quiting
    if( IS_NPC(ch) ){
        ch->pIndexData->count--;
    }else{
		// record the characters logout time 
        laston_logout(ch); 

		// 'logout' any duels
		duel_logout(ch);
	}

    // in switched mobs
	if( ch->desc != NULL && ch->desc->original != NULL )
    {
        do_return( ch, "" );
        ch->desc = NULL;
    }

    // break off all those that reply, retell or mprog target memories
    for( wch = char_list; wch; wch = wch->next )
    {
        if( wch->reply == ch )
            wch->reply = NULL;
        if( wch->retell == ch )
            wch->retell = NULL;
        if( wch->mprog_target == ch )
            wch->mprog_target = NULL;
    }


	// take the mob/player out of the character list
	// unless they are a ploaded player or pet of a ploaded player
	if(!ch->pload){
		extract_char_from_char_list(ch);
	}

	if(!IS_NPC(ch) )
	{
		if( ch == player_list ) 
		{
			player_list = ch->next_player;
		}
        else 
        {  
            char_data *prev;
            for(prev = player_list; prev != NULL; prev = prev->next_player )
            {
  				if(prev->next_player == ch)
				{ 
					prev->next_player = ch->next_player;
					break;
				}
            }
        }


		// if they ploaded someone, extract the ploaded character
		// should really automatically unload the loaded person
		if(ch->ploaded){
			assert(ch->ploaded->pload && ch->ploaded->pload->loaded_by==ch); // if we link to them, they should link to us
			pload_extract(ch, ch->ploaded);
		}
	}


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

    free_char( ch );

	// recount the number of people in the room they just logged out of
	if(recount_in_room){
		int rcount=0;
		ch=recount_in_room->people;
		while(ch){
			rcount++;
			ch=ch->next_in_room;
		}
		recount_in_room->number_in_room=rcount;
	}
    return;
}


/**************************************************************************/
/*
 * Find a char in the room.
 * major bits rewritten by Kalahn - March 98
 */
char_data *get_char_room( char_data *ch, char *argument )
{
    char arg[MIL];
    char_data *rch;
    int number;
    int count;

    number = number_argument( argument, arg );
    if( !str_cmp( arg, "self" ) )
	    return ch;

	// support UID
	int uid=get_uid(arg);
	if(uid){
		for( rch = ch->in_room->people; rch; rch = rch->next_in_room )
		{
			if(uid==rch->uid){
				if(can_see( ch, rch )){
					return rch;
				}else{
					return NULL;
				}
			}
		}
		return NULL;
	}


    // first do a name match and exact short descript match on mobs
    count  = 0;
	for( rch = ch->in_room->people; rch != NULL; rch = rch->next_in_room )
    {
        if( !can_see( ch, rch ))
			continue;

        if(!(	is_name( arg, rch->name ) 
				|| (IS_NPC(rch) && rch!=ch && is_name( arg, strip_colour(rch->short_descr)) )
			 ))
            continue;

        if( rch==ch && HAS_AUTOSELF(ch))
            continue;
        
        if( ++count == number )
            return rch;
    }

    // now do a name and exact short descript match on everyone
	count = 0;
	for( rch = ch->in_room->people; rch != NULL; rch = rch->next_in_room )
    {
        if( !can_see( ch, rch ))
			continue;

        if(!(rch!=ch && is_name( arg, strip_colour(rch->short_descr))) )
            continue;

        if( rch==ch && HAS_AUTOSELF(ch))
            continue;
        
        if( ++count == number )
            return rch;
    }

    return NULL;
}

/**************************************************************************/
// Find a pet in the room.
char_data *get_pet_room( char_data *ch, char *argument )
{
    char arg[MIL];
    char_data *rch;
    int number;
    int count;

    number = number_argument( argument, arg );
    count  = 0;
    if( !str_cmp( arg, "self" ) )
	    return ch;

    for( rch = ch->in_room->people; rch != NULL; rch = rch->next_in_room )
    {
        if( !can_see( ch, rch ))
			continue;

		// pets only
		if( !IS_NPC(rch) || !IS_SET(rch->act, ACT_PET) )
			continue;

        if(!(	is_name( arg, rch->name ) 
				|| (rch!=ch && is_name( arg, strip_colour(rch->short_descr)) )
			 ))
            continue;
/*
        if( !can_see( ch, rch ) ||
            (!is_name( arg, rch->name ) && (!is_name( arg, strip_colour(rch->short_descr)) ) ) )
            continue;
*/
		if(!IS_NPC(rch))
			continue;

        if( rch==ch && HAS_AUTOSELF(ch))
            continue;
        
        if( ++count == number )
            return rch;
    }

    return NULL;
}


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

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

    if( !str_cmp( arg, "self" ) )
	    return ch;

	// support UID
	int uid=get_uid(arg);
	if(uid){
	    for( wch = char_list; wch ; wch = wch->next ){
			if(uid==wch->uid){
				if(wch->in_room && can_see( ch, wch )){
					return wch;
				}else{
					return NULL;
				}
			}
		}
		return NULL;
	}

    // first check for an exact name match through
    // the whole world -- for PCs only 
    for( wch = char_list; wch ; wch = wch->next ){
        if( wch->in_room == NULL || !can_see( ch, wch ) 
        ||  str_cmp(arg, wch->name) || IS_NPC(wch))
            continue;
        
        if( wch==ch && HAS_AUTOSELF(ch)) 
            continue;
         
        if( ++count == number )
            return wch;
    }


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

    count  = 0;
    for( wch = char_list; wch != NULL ; wch = wch->next )
    {
		if(!wch->in_room)
			continue;

		if(!can_see(ch,wch))
			continue;

        if(!(	is_name( arg, wch->name ) 
				|| (wch!=ch && is_name( arg, strip_colour(wch->short_descr)) )
			 ))
            continue;

		if( wch==ch && HAS_AUTOSELF(ch)) 
            continue;
        
        if( ++count == number )
            return wch;
    }

    return NULL;
}

/**************************************************************************/
// Find a char in the ic world. (ie non ooc, or olconly areas)
char_data *get_char_icworld( char_data *ch, char *argument )
{
    char arg[MIL];
    char_data *wch;
    int number;
    int count;

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

    if( !str_cmp( arg, "self" ) )
	    return ch;

	// support UID
	int uid=get_uid(arg);
	if(uid){
	    for( wch = char_list; wch ; wch = wch->next ){
			if(uid==wch->uid){
				if(wch->in_room && can_see( ch, wch ) && IS_IC(wch)){
					return wch;
				}else{
					return NULL;
				}
			}
		}
		return NULL;
	}

        /* first check for an exact name match through
     * the whole world -- for PCs only */
    for( wch = char_list; wch != NULL ; wch = wch->next )
    {
        if( wch->in_room == NULL || !can_see( ch, wch ) 
        ||  str_cmp(arg, wch->name) || IS_NPC(wch))
            continue;

		if(!IS_IC(wch))
            continue;
        
        if( wch==ch && HAS_AUTOSELF(ch)) 
            continue;
        
        if( ++count == number )
            return wch;
    }


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

    count  = 0;
    for( wch = char_list; wch != NULL ; wch = wch->next )
    {
		if(!wch->in_room)
			continue;

		if(!can_see(ch,wch))
			continue;

        if(!(	is_name( arg, wch->name ) 
				|| (wch!=ch && is_name( arg, strip_colour(wch->short_descr)) )
			 ))
            continue;

        if( wch==ch && HAS_AUTOSELF(ch)) 
            continue;

		if(!IS_IC(wch))
            continue;
        
        if( ++count == number )
            return wch;
    }

    return NULL;
}


/**************************************************************************/
/*
 * Find a whovis char in the world.
 *  - called from do_tell, do_requestooc, do_dlook etc
 *  - will never return a mob
 */
char_data *get_whovis_player_world( char_data *ch, char *argument )
{
    char arg[MIL];
    char_data *wch;
    int number;
    int count;

    number = number_argument( argument, arg );

    if( !str_cmp( arg, "self" ) )
	    return ch;

	// support UID
	int uid=get_uid(arg);
	if(uid){
		for( wch = player_list; wch; wch = wch->next_player )
		{
			if(uid==wch->uid){
				if(can_see_who(ch,wch)){
					return wch;
				}else{
					return NULL;
				}
			}
		}
		return NULL;
	}

    // first check uid combined with for an exact name match whole world - player list
    count  = 0;
    for( wch = player_list; wch; wch = wch->next_player )
    {
		// must be able to see the player on the who list
		if(!can_see_who(ch,wch))
			continue;

		if(uid){ // searching by uid 
			if(uid==wch->uid){
				return wch;
			}
		}else{
			// must have an exactly matching name
			if(str_cmp(arg, wch->name))
				continue;


			if(wch==ch	&& HAS_AUTOSELF(ch))
				continue;

			if( ++count == number )
				return wch;
		}
    }

	// next check for substring match on
	// name in same room
    count  = 0;
    for( wch = player_list; wch; wch = wch->next_player )
    {
		// must be in the same room
        if( wch->in_room != ch->in_room)
            continue;

		// must be able to see the player on the who list
		if(!can_see_who(ch,wch))
			continue;

		// must have a substring matching short or name 
		if(!is_name( arg, wch->name))
			continue;

		//autoself
        if(wch==ch	&& HAS_AUTOSELF(ch))
            continue;

        if( ++count == number )
            return wch;
    }


	// next check for substring match on 
	// player name anywhere in the game
    count  = 0;
    for( wch = player_list; wch; wch = wch->next_player )
    {
		// must be able to see the player on the who list
		if(!can_see_who(ch,wch))
			continue;

		// must have a substring matching short or name 
		if(!is_name( arg, wch->name))
			continue;

		//autoself
        if(wch==ch	&& HAS_AUTOSELF(ch))
            continue;

        if( ++count == number )
            return wch;
    }


    // next check for substring match on short 
	// description or name in same room
    count  = 0;
    for( wch = player_list; wch; wch = wch->next_player )
    {
		// must be in the same room
        if( wch->in_room != ch->in_room)
            continue;

		// must be able to see the player on the who list
		if(!can_see_who(ch,wch))
			continue;

		// must have a substring matching short or name 
		if(!is_name( arg, strip_colour(wch->short_descr))
			&& !is_name( arg, wch->name))
			continue;

		//autoself
        if(wch==ch	&& HAS_AUTOSELF(ch))
            continue;

        if( ++count == number )
            return wch;
    }


	// lastly check for substring match on short 
	// description or name anywhere in the game
    count  = 0;
    for( wch = player_list; wch; wch = wch->next_player )
    {
		// must be able to see the player on the who list
		if(!can_see_who(ch,wch))
			continue;

		// must have a substring matching short or name 
		if(!is_name( arg, strip_colour(wch->short_descr))
			&& !is_name( arg, wch->name))
			continue;

		//autoself
        if(wch==ch	&& HAS_AUTOSELF(ch))
            continue;

        if( ++count == number )
            return wch;
    }

	// very very lastly check for a ploaded player in the same room as you
	wch=pload_find_player_by_name(arg);
	if(wch){
		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->pIndexData == pObjIndex )
	    return obj;
    }

    return NULL;
}
/**************************************************************************/
// will return a particular object in a chain, based on its pObjIndex
OBJ_DATA *get_obj_in_chain_recursive_by_index(OBJ_DATA *startobj, OBJ_INDEX_DATA *pObjIndex)
{
	OBJ_DATA *result;
	for( ; startobj; startobj= startobj->next_content )
    {
		if(startobj->pIndexData==pObjIndex){
			return startobj;
		}

		if(startobj->contains){
			// search deeper if necessary
			result=get_obj_in_chain_recursive_by_index(startobj->contains, pObjIndex);
			if(result){
				return result;
			}
		}
	}
	return NULL;

}

/**************************************************************************/
/*
 * Find some object with a given index data in the room.
 * Used by area-reset 'P' command.
 * will never return an object carried by a player
 */
OBJ_DATA *get_obj_of_type_in_room( OBJ_INDEX_DATA *pObjIndex, ROOM_INDEX_DATA *room )
{
    OBJ_DATA *obj;

	// first search all objects on the floor
	obj=get_obj_in_chain_recursive_by_index(room->contents, pObjIndex);
	if(obj){
		return obj;
	}

	// now search thru all objects carried by mobs in the room
	for(char_data *mob=room->people; mob; mob=mob->next_in_room){
		obj=get_obj_in_chain_recursive_by_index(mob->carrying, pObjIndex);
		if(obj){
			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[MIL];
    OBJ_DATA *obj;
    int number;
    int count;

	// support UID
	int uid=get_uid(argument);
	if(uid){
		for( obj = list; obj; obj = obj->next_content ){
			if(uid==obj->uid
				&& !IS_SET( obj->extra_flags, OBJEXTRA_NO_GET_ALL )
				&& 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 ){
				if(	!IS_SET( obj->extra_flags, OBJEXTRA_NO_GET_ALL ) 
					|| is_exact_name( arg, obj->name ) )
				{
					return obj;
				}
			}
		}
    }

    return NULL;
}

/**************************************************************************/
// Find an obj in player's inventory, that looker can see
OBJ_DATA *get_obj_carry_for_looker( char_data *ch, char *argument, char_data *looker)
{
    char arg[MIL];
    OBJ_DATA *obj;
    int number;
    int count;

	// support UID
	int uid=get_uid(argument);
	if(uid){
		for( obj = ch->carrying; obj; obj = obj->next_content){
			if(uid==obj->uid 
				&& obj->wear_loc == WEAR_NONE
				&& can_see_obj( looker, 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( looker, obj ) ) 
		&&   is_name( arg, obj->name ) )
		{
			if( ++count == number )
			return obj;
		}
    }

    return NULL;
}


/**************************************************************************/
// Find an obj in player's inventory or an item they are holding
OBJ_DATA *get_obj_carry( char_data *ch, char *argument )
{
    char arg[MIL];
    OBJ_DATA *obj;
    int number;
    int count;

	// support UID
	int uid=get_uid(argument);
	if(uid){
		for( obj = ch->carrying; obj; obj = obj->next_content){
			if(uid==obj->uid 
				&& (obj->wear_loc == WEAR_NONE
					|| obj->wear_loc == WEAR_HOLD)
				&& 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
			|| obj->wear_loc == WEAR_HOLD)
		&&   (can_see_obj( ch, obj ) ) 
		&&   is_name( arg, obj->name ) )
		{
			if( ++count == number )
			return obj;
		}
    }

    return NULL;
}


/**************************************************************************/
//  Find a token in player's inventory
//  Regular get_obj_carry doesn't work with 
//	tremove since victim can't see tokens
// supports vnum and description searching
OBJ_DATA *get_obj_token( char_data *ch, char *argument )
{
	char arg[MIL];
	OBJ_DATA *obj;
	int number;
	int count;
	
	int tok_vnum=0;
	bool use_vnum=false;

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

	if(is_number(arg)){
		tok_vnum=atoi(arg);
		use_vnum=true;
	}

	for( obj = ch->carrying; obj; obj = obj->next_content ) {
		if( obj->item_type == ITEM_TOKEN)
		{
			if((use_vnum && obj->pIndexData && obj->pIndexData->vnum==tok_vnum)
				|| (!use_vnum && 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, char *argument )
{
    char arg[MIL];
    OBJ_DATA *obj;
    int number;
    int count;

	// support UID
	int uid=get_uid(argument);
	if(uid){
		for( obj = ch->carrying; obj; obj = obj->next_content){
			if(uid==obj->uid 
				&& obj->wear_loc != WEAR_NONE
				&& 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.
// supports uid
OBJ_DATA *get_obj_here( char_data *ch, char *argument )
{
	OBJ_DATA *obj;
	bool free_required=false;

	if( is_number( argument )){ // vnum support
		OBJ_INDEX_DATA *pObj;

		if(( pObj = get_obj_index( atoi( argument ))) == NULL ){
			return NULL;
		}
		argument = str_dup( pObj->name );
		free_required=true;
	}

	// locker support
	if(!str_prefix("locker",argument)){		
		int locker_num=atoi(argument+6);
		if(free_required){
			free_string(argument);
		}
		return lockers->find_locker_object(ch, true, locker_num);
	}

	// get_obj_list() supports uid
    obj = get_obj_list( ch, argument, ch->in_room->contents );
	if(obj){
		if(free_required){
			free_string(argument);
		}
		return obj;
	}

	// get_obj_list() supports uid
	obj = get_obj_carry( ch, argument );
	if(obj){
		if(free_required){
			free_string(argument);
		}
		return obj;
	}

	// get_obj_list() supports uid
	obj = get_obj_wear( ch, argument );
	if(obj){
		if(free_required){
			free_string(argument);
		}
		return obj;
	}

	// locker abbreviated support for your own locker
	if(!str_prefix("loc",argument)){				
		if(free_required){
			free_string(argument);
		}
		return lockers->find_locker_object(ch, true, 0);
	}

	if(free_required){
		free_string(argument);
	}
	return NULL;
}

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

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

	// support UID
	int uid=get_uid(argument);
	if(uid){
		for( obj = object_list; obj; obj = obj->next ){
			if(uid==obj->uid 
				&& 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;
}

/**************************************************************************/
// deduct cost from a character 
void deduct_cost(char_data *ch, int cost)
{
    int silver = 0, gold = 0;
	
    silver = UMIN(ch->silver,cost); 
	
    if(silver<cost){
		gold = ((cost - silver + 99) / 100);
		silver = cost - 100 * gold;
    }
	
    ch->gold -= gold;
    ch->silver -= silver;
	
    if(ch->gold < 0){
		bugf("deduct costs: gold %d < 0", (int)ch->gold);
		ch->gold = 0;
    }
    if(ch->silver<0){
		bugf("deduct costs: silver %d < 0", (int)ch->silver);
		ch->silver = 0;
    }
}   

/**************************************************************************/
// Create a 'money' obj.
OBJ_DATA *create_money( int gold, int silver )
{
    char buf[MSL];
    OBJ_DATA *obj;
	
    if( gold < 0 || silver < 0 || (gold == 0 && silver == 0) )
    {
		bug( "Create_money: zero or negative money.");
		gold = UMAX(1,gold);
		silver = UMAX(1,silver);
    }
	
    if(gold == 0 && silver == 1)
    {
		obj = create_object( get_obj_index( OBJ_VNUM_SILVER_ONE ));
    }
    else if (gold == 1 && silver == 0)
    {
		obj = create_object( get_obj_index( OBJ_VNUM_GOLD_ONE));
    }
    else if (silver == 0)
    {
        obj = create_object( get_obj_index( OBJ_VNUM_GOLD_SOME ));
        sprintf( buf, obj->short_descr, gold );
        free_string( obj->short_descr );
        obj->short_descr        = str_dup( buf );
        obj->value[1]           = gold;
        obj->cost               = gold;
		obj->weight		= gold/5;
    }
    else if (gold == 0)
    {
        obj = create_object( get_obj_index( OBJ_VNUM_SILVER_SOME ));
        sprintf( buf, obj->short_descr, silver );
        free_string( obj->short_descr );
        obj->short_descr        = str_dup( buf );
        obj->value[0]           = silver;
        obj->cost               = silver;
		obj->weight		= silver/20;
    }
	
    else
    {
		obj = create_object( get_obj_index( OBJ_VNUM_COINS ));
		sprintf( buf, obj->short_descr, silver, gold );
		free_string( obj->short_descr );
		obj->short_descr	= str_dup( buf );
		obj->value[0]		= silver;
		obj->value[1]		= gold;
		obj->cost		= 100 * gold + silver;
		obj->weight		= gold / 5 + silver / 20;
    }
	
    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;
 
	if(obj->item_type == ITEM_HERB){
		return 1;
	}
    if(obj->item_type == ITEM_CONTAINER || obj->item_type == ITEM_MONEY
    ||  obj->item_type == ITEM_GEM || obj->item_type == ITEM_JEWELRY
	||  obj->item_type == ITEM_TOKEN ){
        number = 0;
	}else{
        number = 10;
	}
 
    for( obj = obj->contains; obj != NULL; obj = obj->next_content )
        number += get_obj_number( obj );
 
    return number;
}


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

    weight = obj->weight;
    for( tobj = obj->contains; tobj != NULL; tobj = tobj->next_content )
	weight += get_obj_weight( tobj ) * WEIGHT_MULT(obj) / 100;

    return weight;
}

/**************************************************************************/
int get_true_weight(OBJ_DATA *obj)
{
    int weight;
 
    weight = obj->weight;
    for( obj = obj->contains; 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 )
{
	OBJ_DATA	*obj;
	
	if(!pRoomIndex) 
		return false;

	if( IS_SET(pRoomIndex->affected_by, ROOMAFF_LEONIDS ))
		return false;

	if( IS_SET(pRoomIndex->affected_by, ROOMAFF_UTTERDARK ))
		return true;
	
    if( pRoomIndex->light > 0 )
		return false;
	
	for( obj = pRoomIndex->contents; obj; obj = obj->next_content ) {
		if( obj->pIndexData->vnum == OBJ_VNUM_FIRE )
			return false;
    }
	
    if( IS_SET(pRoomIndex->room_flags, ROOM_LIGHT ))
		return false;
	
    if( IS_SET(pRoomIndex->room_flags, ROOM_DARK) )
		return true;

    if( pRoomIndex->sector_type == SECT_INSIDE
		||   pRoomIndex->sector_type == SECT_CITY )
		return false;
	
    if( weather_info[pRoomIndex->sector_type].sunlight == SUN_SET
		||   weather_info[pRoomIndex->sector_type].sunlight == SUN_DARK )
		return true;
	
    return false;
}

/**************************************************************************/
// true if player is in room invite list, or their clan is.
bool player_on_rooms_invite_list(char_data *ch, ROOM_INDEX_DATA *room)
{
	if(GAMESETTING4(GAMESET4_ROOM_INVITES_DISABLED)){
		return false;
	}

	if(IS_NULLSTR(room->owner)){
		return false;
	}
	if(IS_NULLSTR(room->invite_list)){
		return false;
	}
    if(is_exact_name(ch->name, room->invite_list)){
		return true;
	}
	if(ch->clan && is_exact_name(FORMATF("clan=%s", ch->clan->notename()), room->invite_list)){
		return true;
	}

	return false;    
}
/**************************************************************************/
bool is_room_owner(char_data *ch, ROOM_INDEX_DATA *room)
{
    if(IS_NULLSTR(room->owner)){
		return false;
	}

    if(is_exact_name(class_table[ch->clss].name,room->owner)){
		return true;
	}
     
    if(is_exact_name("immortal",room->owner) && IS_IMMORTAL(ch)){
		return true;
	}
	return is_exact_name(ch->name,room->owner);
}


/**************************************************************************/
// True if room is private excluding the owner question.
bool is_room_private_except_owner( ROOM_INDEX_DATA *pRoomIndex )
{
    char_data *rch;
    int count = 0;

    for( rch = pRoomIndex->people; rch; rch = rch->next_in_room )
	{
		if( INVIS_LEVEL(rch)< LEVEL_IMMORTAL )
			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;
    
    if( IS_SET(pRoomIndex->room_flags, ROOM_IMP_ONLY) )
	return true;

    return false;

}

/**************************************************************************/
// True if room is private.
bool room_is_private( ROOM_INDEX_DATA *pRoomIndex )
{
    if(!IS_NULLSTR(pRoomIndex->owner)){
		return true;
	}

	return is_room_private_except_owner( pRoomIndex );
}
/**************************************************************************/
// True if room is private to the character
bool is_room_private_to_char( ROOM_INDEX_DATA *pRoomIndex, char_data *ch )
{
	if(IS_ADMIN(ch) && IS_SET(TRUE_CH(ch)->act, PLR_HOLYWALK)){
		return false;
	}

	if(!IS_NULLSTR(pRoomIndex->owner)){
		if(!is_room_owner(ch, pRoomIndex)
			&& !player_on_rooms_invite_list(ch, pRoomIndex))
		{
			return true;
		}
	}

	return is_room_private_except_owner( pRoomIndex );
}

/**************************************************************************/
// visibility on a room -- for entering and exits
bool can_see_room( char_data *ch, ROOM_INDEX_DATA *pRoomIndex )
{
    if(IS_SET(pRoomIndex->room_flags, ROOM_IMP_ONLY) 
    &&  get_trust(ch) < MAX_LEVEL)
	return false;

    if(IS_SET(pRoomIndex->room_flags, ROOM_GODS_ONLY)
    &&  !IS_IMMORTAL(ch))
	return false;

    if(IS_SET(pRoomIndex->room_flags, ROOM_HEROES_ONLY)
    &&  !IS_IMMORTAL(ch))
	return false;

    if(IS_SET(pRoomIndex->room_flags,ROOM_NEWBIES_ONLY)
    &&  ch->level > 5 && !IS_IMMORTAL(ch))
	return false;

    if(!IS_IMMORTAL(ch) && pRoomIndex->clan && ch->clan != pRoomIndex->clan)
	return false;

	// unswitched_mobs can't see ooc rooms
	if(IS_UNSWITCHED_MOB(ch) 
		&& IS_SET((ch)->in_room->room_flags, ROOM_OOC))
		return false;

	// mortal players that arent building can't see olconly areas
	if(!IS_NPC(ch) && !IS_IMMORTAL(ch) && !IS_SET(ch->comm,COMM_BUILDING)
		&& IS_OLCAREA(pRoomIndex->area))
		return false;

	// mortal builders can't see ic areas
	if(!IS_NPC(ch) && !IS_IMMORTAL(ch) && IS_SET(ch->comm,COMM_BUILDING)
		&& !IS_SET((ch)->in_room->room_flags, ROOM_OOC) 
		&& !IS_SET((ch)->in_room->area->area_flags, AREA_OLCONLY)
		)	
		return false;

    return true;
}


/**************************************************************************/
// True if looker can see victim - (ch is doing the looking)
bool can_see( char_data *looker, char_data *victim )
{
    if( looker == victim )
		return true;

	// support the inroom command
	if(IS_SET(looker->dyn,DYN_IN_ROOM_ONLY) && looker->in_room!=victim->in_room)
	{
		return false;
	}

	// mortals can't see ploaded players
	if(victim->pload && !IS_IMMORTAL(looker)){
		return false;
	}

	// support for the 'mob seeall' command
	if(IS_SET(looker->dyn,DYN_MOB_SEE_ALL) 
		&& INVIS_LEVEL(victim)<=LEVEL_HERO)
	{
		return true;
	}
	
    if( get_trust(looker) < INVIS_LEVEL(victim))
	return false;

    if(IS_IMMORTAL(victim)
		&& get_trust(looker) < victim->incog_level 
		&& looker->in_room != victim->in_room)
		return false;

    if( !IS_NPC(looker) && IS_NPC(victim)
		&& IS_SET(victim->act, ACT_IS_UNSEEN)
		&& !HAS_HOLYLIGHT(looker))
		return false;

    if( IS_OOC(victim))
	    return true;

    if( HAS_HOLYLIGHT(looker))
		return true;

	if( IS_SET( victim->affected_by2, AFF2_TREEFORM ) 
		&& !IS_AFFECTED2(looker, AFF2_DETECT_TREEFORM ) )
		return false;
		
	if( IS_SET( victim->affected_by2, AFF2_VANISH )
		&& !(get_skill(looker,gsn_vanish) > 0)
		&& !IS_AFFECTED2(looker, AFF2_DETECT_VANISH))
		return false; 

    if( IS_AFFECTED(looker, AFF_BLIND) )
		return false;

    if( room_is_dark( looker->in_room ) &&
      (!IS_AFFECTED(looker, AFF_DARK_VISION) || 
       !IS_AFFECTED(looker, AFF_INFRARED)) )
		return false;

    if( IS_AFFECTED(victim, AFF_INVISIBLE)
		&&   !IS_AFFECTED(looker, AFF_DETECT_INVIS ))
		return false;

	if(!(IS_NPC(looker) && IS_AFFECTED(looker, AFF_DETECT_HIDDEN)) )
	{
		// hidden - chance of not being seen
		//   The higher 'chance' is the less chance of the looker (looker) 
		//   seeing the hiding mob/player (victim).
		if(IS_AFFECTED(victim, AFF_HIDE) 
			&& !is_same_group( looker, victim)
			&& victim->fighting == NULL)
		{
			int chance; // chance of not being seen
			OBJ_DATA  *obj;
			chance = UMAX(90,get_skill(victim,gsn_hide));
			chance += victim->level/5;
			chance -= looker->level/3;
			chance += (victim->level- looker->level)/3;
			chance -= IS_NPC(looker)?0:looker->pcdata->tired*2;
			chance -= looker->modifiers[STAT_AG];
			chance -= looker->modifiers[STAT_QU];
			chance += victim->modifiers[STAT_IN];
			chance += victim->modifiers[STAT_PR];
			chance -= get_skill(looker,gsn_hide)/10;

			if(room_is_dark( victim->in_room ) &&
			 !IS_AFFECTED(looker, AFF_DARK_VISION)){
				obj= get_eq_char( victim, WEAR_LIGHT );
				if(	obj &&	obj->item_type == ITEM_LIGHT
					&&	obj->value[2] != 0 ){
					chance -= 50; // hider has light
					// looker lookereck
					obj= get_eq_char( looker, WEAR_LIGHT );
					if(	obj &&	obj->item_type == ITEM_LIGHT
						&&	obj->value[2] != 0 ){
						chance += 15; // both have light
					}else{
						chance -= 50; // hider only has light
					}
				}else{
					chance += 10; // hider doesn't have light
					// looker lookereck
					obj= get_eq_char( looker, WEAR_LIGHT );
					if(	obj &&	obj->item_type == ITEM_LIGHT
						&&	obj->value[2] != 0 ){
						chance -= 50; // looker only has light
					}else{
						chance += 50; // neither have light
					}
				}

			}

			chance = UMAX(chance,1);

			if(IS_AFFECTED(looker, AFF_DETECT_HIDDEN)){
				chance/=4;
			}else{
				chance*=2;
			}
			// spamming look to see someone results in you having
			// less chance of seeing them
			if(looker->desc && looker->desc->repeat>3)
			{
				chance+=5;
				chance*=8;
			}else{
				chance = URANGE(5,chance,99);
			}

			if(chance-10 >number_percent())
				return false;
		}
	}
    return true;
}


/**************************************************************************/
// True if looker can see victim on the wholist.
bool can_see_who( char_data *looker, char_data *victim )
{
	if(!looker || !victim){
		bug("can_see_who - null looker or victim");
		make_corefile();
		return false;
	}

	// mobs arent on who
    if( IS_NPC(victim) ){
		return false;
	}

	// can always see yourself on who
    if( looker == victim ){
		return true;
	}

	// support for the 'mob seeall' command
	if(IS_SET(looker->dyn,DYN_MOB_SEE_ALL)){
		return true;
	}

	// mortals see imms based sololy on the status of whovis
	// whovis has no affect on characters less than LEVEL_IMMORTAL
	if(!IS_IMMORTAL(looker) && victim->level>LEVEL_HERO){
		if(IS_SET(victim->comm, COMM_WHOVIS))
			return true;
		else
			return false;
	}

	// newbies can always see newbie support on who
    if(IS_NEWBIE_SUPPORT(victim) && IS_NEWBIE(looker)){
        return true;
	}

#ifdef unix
    // mortals can't see someone for 60 seconds after 
    // they logon and haven't talked on ooc 
    if(!IS_IMMORTAL(looker) && !IS_NPC(looker) && ((current_time - 60) < victim->logon) )
    {
        if(!IS_NPC(victim) && !victim->pcdata->did_ooc && looker!=victim)
            return false;
    }
#endif

	// incog only works for imms in an IC sense
	if(IS_IMMORTAL(victim) && get_trust(looker) < victim->incog_level 
		&& looker->in_room != victim->in_room){
		return false;
	}

	// support the inroom command
	if(IS_SET(looker->dyn,DYN_IN_ROOM_ONLY) && looker->in_room!=victim->in_room){
		return false;
	}
	// trust check
    if( get_trust(looker) < INVIS_LEVEL(victim)){
		return false;
	}

	return true;
}
/**************************************************************************/
ROOM_INDEX_DATA *room_object_is_in(obj_data *obj)
{
	if(obj->in_room){
		return obj->in_room;
	}else if(obj->carried_by){			
		return obj->carried_by->in_room;
	}else if(obj->in_obj){
		return room_object_is_in(obj->in_obj);
	}
	bugf("room_object_is_in(): couldn't find the room which object '%s' was in!\r\n",
		obj->name);
	return NULL;
}
/**************************************************************************/
// True if char can see obj.
bool can_see_obj( char_data *ch, obj_data *obj )
{
	// support the inroom command
	if(IS_SET(ch->dyn,DYN_IN_ROOM_ONLY) && ch->in_room!=room_object_is_in(obj)){
		return false;
	}

    if( HAS_HOLYLIGHT(ch))
		return true;

	// support for the 'mob seeall' command
	if(IS_SET(ch->dyn,DYN_MOB_SEE_ALL)){
		return true;
	}

	if( obj->item_type == ITEM_TOKEN )
		return false;

	if( IS_SET( obj->extra2_flags, OBJEXTRA2_BURIED ))
		return false;

    if( IS_SET(obj->extra_flags,OBJEXTRA_VIS_DEATH))
		return false;


    if( IS_AFFECTED( ch, AFF_BLIND ) && obj->item_type != ITEM_POTION)
		return false;

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

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

    if( IS_OBJ_STAT(obj,OBJEXTRA_GLOW))
		return true;

    if( room_is_dark( ch->in_room ) && !IS_AFFECTED(ch, AFF_DARK_VISION) )
		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, OBJEXTRA_NODROP) )
		return true;
	
    if( !IS_NPC(ch) && ch->level >= LEVEL_IMMORTAL )
		return true;
	
    return false;
}



/**************************************************************************/
// Return ascii name of an item type.
char *item_type_name( OBJ_DATA *obj )
{	
	return flag_string(item_types, obj->item_type);
}

/**************************************************************************/
// Return ascii name of an affect location.
char *affect_loc_name( int location )
{
	return flag_string(apply_types, location);
}

/**************************************************************************/
char *full_affect_loc_name( AFFECT_DATA *paf)
{
	static char result[MIL];
    switch( paf->location )
    {
	case APPLY_NONE:
		{
			
			switch(paf->where)
			{
			case WHERE_WEAPON:
				sprintf( result,"%s", weapon_bit_name( paf->bitvector ));
				break;
			case WHERE_VULN:
				sprintf( result,"%s (vuln)", imm_bit_name( paf->bitvector ));
				break;
				
			case WHERE_RESIST:
				sprintf( result,"%s (resist)", imm_bit_name( paf->bitvector ));
				break;
				
			case WHERE_IMMUNE:
				sprintf( result,"%s (immune)", imm_bit_name( paf->bitvector ));
				break;
			case WHERE_OBJEXTRA:
				sprintf( result,"%s (object)", extra_bit_name(paf->bitvector));
				break;
			case WHERE_OBJEXTRA2:
				sprintf( result,"%s (object)", extra2_bit_name(paf->bitvector));
				break;
			case WHERE_AFFECTS:
			default:
				sprintf( result,"%s", affect_bit_name( paf->bitvector ));
				break;
			case WHERE_AFFECTS2:
				sprintf( result,"%s", affect2_bit_name( paf->bitvector ));
				break;
			}
			return result;
		}
	default:
		return flag_string(apply_types, paf->location);
    }
	
    bugf( "Affect_location_name: unknown location %d.", paf->location );
    return "(unknown)";
}

/**************************************************************************/
// return text flags of what the bits are for
char *affect_bit_name( int bits)
{
	return flag_string(affect_flags, bits);
}

/**************************************************************************/
char *affect2_bit_name( int bits)
{
	return flag_string(affect2_flags, bits);
}

/**************************************************************************/
char *extra_bit_name( int bits )
{
	return flag_string(objextra_flags, bits);
}

/**************************************************************************/
char *extra2_bit_name( int bits)
{
	return flag_string(objextra2_flags, bits);
}

/**************************************************************************/
char *act_bit_name( int bits)
{
    if(IS_SET(bits,ACT_IS_NPC))
    { 
		return flag_string(act_flags, bits);		
	}else{
		return flag_string(plr_flags, bits);		
	}
}

/**************************************************************************/
char *act2_bit_name( int bits)
{	
	return flag_string(act2_flags, bits);		
}

/**************************************************************************/
char *comm_bit_name(int bits)
{
	return flag_string(comm_flags, bits);		
}

/**************************************************************************/
char *imm_bit_name(int bits)
{
	
	return flag_string(imm_flags, bits);
}

/**************************************************************************/
char *wear_bit_name(int bits)
{
	return flag_string(wear_flags, bits);
}

/**************************************************************************/
char *form_bit_name(int bits)
{
	return flag_string(form_flags, bits);
}

/**************************************************************************/
char *part_bit_name(int bits)
{
	return flag_string(part_flags, bits);
}
/**************************************************************************/
char *weapon_bit_name(int bits)
{
	return flag_string(weapon_flags, bits);
}
/**************************************************************************/
char *cont_bit_name( int cont_flags)
{
    static char buf[512];

    buf[0] = '\0';

    if(cont_flags & CONT_CLOSEABLE	) strcat(buf, " closable");
    if(cont_flags & CONT_PICKPROOF	) strcat(buf, " pickproof");
    if(cont_flags & CONT_CLOSED	) strcat(buf, " closed");
    if(cont_flags & CONT_LOCKED	) strcat(buf, " locked");

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

/**************************************************************************/
char *off_bit_name(int bits)
{
	return flag_string(off_flags, bits);
}
/**************************************************************************/
char *room_flags_bit_name(int bits)
{
	return flag_string(room_flags, bits);
}    
/**************************************************************************/
/*
 * Short description function - by Kalahn, displays the word YOU if 
 *   ch==looker
 */
char * YOU_PERS( char_data *ch, char_data *looker)
{
	if(!ch || !looker){
		return "";
	}
	if(ch==looker){
		return("you");
	}
	
	return(PERS(ch, looker));
}
/**************************************************************************/
/*
 * Long description for mobs, short for players function
 */
char * LONGPERS( char_data *ch, char_data *looker)
{
	static char buf[MSL];
	char *descript="";
	
	// use long of mobs, short of pcs
	if(IS_NPC(ch)){
		descript=ch->long_descr;
	}else{
		descript=ch->short_descr;
	}

	strcpy(buf, mxp_create_tag(looker, mxp_tag_for_mob(looker, ch), capitalize(descript)));

	if(!IS_UNSWITCHED_MOB(looker))
	{
		if(IS_CONTROLLED(ch) && IS_SET(looker->act, PLR_HOLYNAME))
		{ // add the little controling name
			strcat( buf, FORMATF(" [%s]", TRUE_CH(ch)->name)) ;
		}
	}

	if(!IS_UNSWITCHED_MOB(looker) &&
		HAS_DESC(looker) && IS_SET(TRUE_CH(looker)->act, PLR_HOLYVNUM))
	{
		char buf2[MSL];
		if(IS_NPC(ch) && ch->pIndexData)
		{
			if(ch->pIndexData->mprogs)
			{
				sprintf(buf2," `#`=H*%d,%d*`&", ch->pIndexData->vnum, ch->level);
			}
			else
			{
				sprintf(buf2," `#`=h[%d,%d]`&", ch->pIndexData->vnum, ch->level);
			}
			strcat(buf,buf2);
		}
	}

	return(buf);
}

/**************************************************************************/
// char * char_position_text(char_data *ch )
// returns the correct position text for a characters position.
// eg " is <postion name> in <object_short>.", 
//    " is <postion name> on <object_short>.", 
//    " is <postion name> at <object_short>.", 
//    " is <postion name> here." 
char * char_position_text( char_data *ch)
{
	static char buf[MSL];
	sh_int pos = ch->position;

	if(ch->on != NULL)
	{			
		if(IS_SET(ch->on->value[2],SLEEP_AT))
		{
			sprintf(buf," is %s at %s.", position_table[pos].name
				,ch->on->short_descr);
		}
		else
		{
			if(IS_SET(ch->on->value[2],SLEEP_ON))
			{
				sprintf(buf," is %s on %s.", position_table[pos].name
					,ch->on->short_descr);
			}
			else
			{
				sprintf(buf," is %s in %s.", position_table[pos].name
					,ch->on->short_descr);
			}
		}
	}
	else
	{
		sprintf(buf," is %s here.",  position_table[pos].name);
	}

	return(buf);
}

/**************************************************************************/
// returns the characters after a / from an area filename
char * area_fname (AREA_DATA *pArea)
{
    static char buffer[128]; /* short filename */
    char  *slash;  /* used for finding a slash */

    if(!pArea)
    {
        bug("Area handed to area_fname was null");
        return("");
    }

    strncpy(buffer, pArea->file_name, 128); /* copy the filename */
    slash = strchr (buffer, '/'); /* find the slash (area/midgaard.are) */
    if(slash) /* if there was one move one after it*/
        slash++;      
    else 
        slash = buffer; /*otherwise set to the first word */

    return(slash);
}

/**************************************************************************/
char *shortdate(time_t *tm) // kalahn - sept 97 
{
	static int i;
    static char result[3][30];
	// rotate buffers
	++i= i%3;
    result[i][0] = '\0';

	if(!tm){
		tm=&current_time;
	}

    char *tbuf = ctime( tm );
    tbuf[str_len(tbuf)-6] = '\0';

    strcpy(result[i], tbuf);

    return(result[i]);
}

/**************************************************************************/
char *shorttime(time_t *tm) // kalahn - nov 97 
{
	static int i;
    static char result[3][30];
	// rotate buffers
	++i= i%3;
    result[i][0] = '\0';

	if(!tm){
		tm=&current_time;
	}

    char *tbuf = ctime( tm);
    tbuf[str_len(tbuf)-6] = '\0';

    strcpy(result[i], (char *)&tbuf[11]);

    return(result[i]);
}
/**************************************************************************/
// Append a string to a file.
void append_string_to_file( const char *file, const char *str, bool newline )
{
    FILE *fp;

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

	if(fpAppend2FilReserve==NULL && runlevel==RUNLEVEL_SHUTING_DOWN)
		return;


	if(fpAppend2FilReserve){
		fclose(fpAppend2FilReserve);
	}

    if( ( fp = fopen( file, "a" ) ) == NULL ){
		bugf("append_string_to_file(): fopen '%s' for append - error %d (%s)",
			file, errno, strerror( errno));
    }else{
		if(newline){
			fprintf( fp, "%s\n", str );
		}else{
			fprintf( fp, "%s", str );
		}
        fclose( fp );
    }
    fpAppend2FilReserve = fopen( ANULL_FILE, "r" );
}

/**************************************************************************/
// Append a date and time coded string to a file.
void append_datetimestring_to_file( const char *file, const char *str )
{
    char buf[MSL];

    sprintf(buf, "%s: ", shortdate(NULL));

    append_string_to_file( file, buf, false);
	append_string_to_file( file, str, true);
}
/**************************************************************************/
// Append a time coded string to a file.
void append_timestring_to_file( const char *file, const char *str )
{
    char buf[MSL];

    sprintf(buf, "%s: ", shorttime(NULL));
    append_string_to_file( file, buf, false);
	append_string_to_file( file, str, true);	
}
/**************************************************************************/
// appends to file short time code, room, players name
void append_logentry_to_file( char_data *ch, char *file, char *str )
{
    char logbuf[MSL];

    sprintf(logbuf, "[%5d] %s: %s",
       ch->in_room ? ch->in_room->vnum : 0, TRUE_CH(ch)->name, str );

    append_datetimestring_to_file(file, logbuf);
}
/**************************************************************************/
/*
 * appends to a players log the string with a short time code on it
 * and a [O] if they are in an ooc room
 */
void append_playerlog( char_data *ch, char *str )
{
    char logfname[MSL];
    char buf[MSL];
	char buf_vnum[MSL];
	char name[MIL];

	// make lowercase and first word of the name
	one_argument( TRUE_CH(ch)->name, name);

	if(IS_IMMORTAL(ch)){
		sprintf(logfname, "%s%s.log", IMMLOG_DIR, name);
	}else{
		sprintf(logfname, "%s%s.log", PLAYER_LOGS_DIR, name);
	}

	sprintf(buf_vnum, "<%5d>", (ch->in_room? ch->in_room->vnum:0));

    if(IS_OOC(ch))
        sprintf(buf,"%s [O] %s", buf_vnum, str);
    else
        sprintf(buf,"%s %s", buf_vnum, str);

    append_datetimestring_to_file(logfname, buf);
}

/**************************************************************************/
/*
 * appends to the string with a short time code on it
 * and a [O] if they are in an ooc room 
 * [N] for newbie support person
 */
void append_newbie_support_log( char_data *ch, char *str )
{
    char buf[MSL];

    
	sprintf(buf,"%3s%3s %-13s:%s", 
		IS_OOC(ch)?"[O]":"",
		IS_NEWBIE_SUPPORT(ch)?"[N]":"",
		ch->name, str);

    append_datetimestring_to_file(NEWBIE_SUPPORT_LOG_FILE, buf);
}

/**************************************************************************/
/*
 * appends to file short date time code, room, players name
 */
void append_datetime_ch_to_file( char_data *ch, const char *file, const char *str )
{
    char logbuf[MSL];

    sprintf(logbuf, "%-13s<%2d>- %s", ch->name, ch->level, str );

    append_datetimestring_to_file(file, logbuf);
}

/**************************************************************************/
void rand_to_char(char_data *ch, char *str1, char *str2, char *str3, char *str4, char *str5) 
{
	char *output="";
    switch(dice(1,5))
    {
	case 1 : output=str1; break;
	case 2 : output=str2; break;
	case 3 : output=str3; break;
	case 4 : output=str4; break;
	case 5 : output=str5; break;
	default:bug("BUG: rand_to_char: number out of range.");
		do_abort(); // this should never be possible		
		break;
    }
	ch->print(output);
}

/**************************************************************************/
// Returns the visible length of a string after colour parsing
// written by Kalahn - march 98
// note: `1 and `} returns a -1 (new line codes)
int c_str_len(const char *s)
{
	int len = 0; // visible string length (without colour codes)

	for( ; *s; s++ )
	{
		if( *s == '`' ) // skip the colour code
		{
			s++;		// move to the colour character
			switch(*s)
			{
				default:
					break;

				case '=': // custom colour
					s++; // skip the '=' moving to the colour char...
						 // we dont bother counting it since we assume 
						 // it is always a colour
					break;

				case '-': // ~
				case '`': // ` 
					len++;
					break;

				case 'N': // N - game name
					len+=str_len(game_settings->gamename);
					break;

				case '1': // newline returns a -1 
				case '}': // newline returns a -1 
					return -1;
					break;
			}
			continue;
		}	
		len++;	// normal character count that length
	}
	return len;
}

/**************************************************************************/
// used to detect arguments sent by players to defraud systems
// like sending ``1 over ooc to make a new line and then putting
// new text to send to everyone.
bool check_defrauding_argument(char_data *ch, char *argument ){
	if(IS_NULLSTR(argument)){
		return false;
	}

	if(c_str_len(argument)==-1){
		ch->println("Messages containing colour codes which create new lines will `RNOT`x be sent.");
		return true;
	}
	return false;
}

/**************************************************************************/
// Returns true if str has a ` colour code in it
// written by Kalahn - march 98
bool has_colour(const char *s)
{
	for( ; *s; s++ )
	{
		if( *s == '`' ) // find a colour code
			return true;
	}
	return false;
}

/**************************************************************************/
// Returns true if str has a space character in it
// written by Kalahn - march 98
bool has_space(const char *s)
{
	for( ; *s; s++ )
	{
		if( *s == ' ' ) // find a space
			return true;
	}
	return false;
}
/**************************************************************************/
// Returns true if str has any whitespace character in it
// written by Kalahn - march 98
bool has_whitespace(const char *s)
{
	for( ; *s; s++ )
	{
		if( is_space(*s)){ // find a whitespace
			return true;
		}
	}
	return false;
}

/**************************************************************************/
// Returns width amount of characters not counting colour codes for width
// Kalahn - Mar 98
char * str_width(const char *s, int width)
{
	static int i;
    static char buf[5][MSL];
	++i%=5;

	char *point;
	int len = 0; // visible string length (without colour codes)

	point=buf[i]; // start at the start of the input string s
	point--;

	for( ; *s && len<width; s++ )
	{	
		*++point= *s;   // copy the character
		if( *s == '`' ) // if we have a colour code copy the next char
		{
			s++;		// move to the colour character
			*++point= *s; // copy the colour character
			if(*s== '`'){ // count only the length of `` codes
				len++;
			}
			if(*s== '='){  // custom colour code... move to custom code
				s++;		// move to the custom colour code character
				*++point= *s; // copy the custom colour code character
			}
			continue;
		}	
		len++;	// normal character count that length
	}
	
	// if we need some blank spaces at the end of the string
	while(len<width)
	{
		*++point= ' ';
		len++;	
	}

	*++point = '\0'; // terminate the string
	return buf[i];
}

/**************************************************************************/
void center_to_char(char *argument, char_data *ch, int columns)
{
	int spaces;
	columns = (columns < 2) ? 80 : columns;
	spaces=(columns-c_str_len(argument))/2;

	ch->printf("%*c%s",spaces,' ',argument);
	return;
}
/**************************************************************************/
void centerf_to_char (char_data *ch,int cols, char *fmt, ...)
{
    char buf[MSL];
	va_list args;
	va_start(args, fmt);
	vsnprintf(buf, MSL, fmt, args);
	va_end(args);
	
	center_to_char(buf, ch, cols);
}

/**************************************************************************/
void bugf (char * fmt, ...)
{
    char buf[MSL*3];
	va_list args;
	va_start(args, fmt);
	vsnprintf(buf, MSL*3, fmt, args);
	va_end(args);
	bug(buf);
}

/**************************************************************************/
char *mprog_type_to_name( int type );
/**************************************************************************/
// Kal - Feb 00
// Used to log a buggy prog, and disable it at the same time... 
// till the next mpreset
void mpbuggy_prog(MPROG_LIST *trigger, char * fmt, ...)
{
    char buf[HSL];
	char *pBuf;

	assert(trigger->prog->disabled==false); // shouldn't have been attempting to run
	trigger->prog->disabled=true;

	//	add_buf(output,"{xMPBUG {G#mobprog{x, {Ymobvnum{x, {Binroom{x, {rline{x, {Ccall_level{x:\r\n");
	sprintf(buf,"`S#############\r\n"
		"             Disabling mobprog `G%d`x,`Y%d`x,`B%d`x,`r%d`x,`C%d`x:\r\n" 
		"             `Y",
		callstack_pvnum[call_level-1],
		callstack_mvnum[call_level-1],
		callstack_rvnum[call_level-1],
		callstack_line[call_level-1],
		call_level-1);

	// format and concate the error message text
	pBuf=&buf[str_len(buf)];
	va_list args;
	va_start(args, fmt);
	vsnprintf(pBuf, MSL, fmt, args);
	va_end(args);

	// put extra debugging info from the trigger
	pBuf=&buf[str_len(buf)];
	sprintf(pBuf,	"\r\n`x             Trigger type '%s' phrase '%s'\r\n",		
		mprog_type_to_name(trigger->trig_type),
		trigger->trig_phrase);

	strcat(pBuf,"             Call stack info:\r\n");

	// display the call stack
	char catbuf[MSL];
	for(int i=0; i<call_level;i++)
	{
		if(!callstack_aborted[i] || (i==call_level-1)){
			sprintf(catbuf, "             MOBprog %d on mob %d (in room %d), line %d\r\n", 
				callstack_pvnum[i], 
				callstack_mvnum[i], 
				callstack_rvnum[i],
				callstack_line[i]);
			strcat(pBuf,catbuf);
		}
	}
	strcat(pBuf,"`S#############\r\n\r\n");
	append_timestring_to_file(MPBUG_FILE, buf);
	trigger->prog->disabled_text=str_dup(buf); // record what is buggy with it
}
/**************************************************************************/
extern char *mq_mpbug_details;
extern bool mq_running_queued_command;
/**************************************************************************/
// mobprog bug log system
void mpbug(char * fmt, ...)
{
    char buf[HSL];
	char *pBuf;
//	add_buf(output,"{xMPBUG {G#mobprog{x, {Ymobvnum{x, {Binroom{x, {rline{x, {Ccall_level{x:\r\n");

	if(mq_running_queued_command && !IS_NULLSTR(mq_mpbug_details)){
		strcpy(buf, mq_mpbug_details);
	}else{
		sprintf(buf,"`G%d`x,`Y%d`x,`B%d`x,`r%d`x,`C%d`x:", 
			callstack_pvnum[call_level-1],
			callstack_mvnum[call_level-1],
			callstack_rvnum[call_level-1],
			callstack_line[call_level-1],
			call_level-1);
	}

	pBuf=&buf[str_len(buf)];
	va_list args;
	va_start(args, fmt);
	vsnprintf(pBuf, MSL, fmt, args);
	va_end(args);

	append_timestring_to_file(MPBUG_FILE, buf);
}

/**************************************************************************/
void logf(char * fmt, ...)
{
    char buf[HSL];
	va_list args;
	va_start(args, fmt);
	vsnprintf(buf, HSL-MIL, fmt, args);
	va_end(args);

	log_string(buf);
}
/**************************************************************************/
void broadcast(char_data *except, char * fmt, ...)
{
    char buf[MSL];
	char_data *wch;
	
	va_list args;
	va_start(args, fmt);
	vsnprintf(buf, MSL, fmt, args);
	va_end(args);

	for( wch = player_list; wch; wch = wch->next_player )
    {
		if(wch!=except){	
			ACTIVE_CH(wch)->print(buf);
		}
	}
}
/**************************************************************************/
void pkill_broadcast(char * fmt, ...)
{
    char buf[MSL], buf2[MSL];
	char_data *wch;
	
	va_list args;
	va_start(args, fmt);
	vsnprintf(buf, MSL, fmt, args);
	va_end(args);

	sprintf(buf2,"`RPKILL PORT BROADCAST:`W %s\r\n`x", buf);

	for( wch = player_list; wch; wch = wch->next_player )
    {
		wch->print(buf2);
	}

}
/**************************************************************************/
char * makef_titlebar(char *fmt, ...)
{
	char buf [MSL];
	char line[MSL];
	char line2[MSL];
	static char returnbuf[MSL];
	int spaces;

	// format all the text into buf
	va_list args;
	va_start(args, fmt);
	vsnprintf(buf, MSL, fmt, args);
	va_end(args);


	if(c_str_len(buf)<1 || (c_str_len(buf)==1 && (buf[0]=='-' || buf[0]=='=') ))
	{
		return "`#`=t-====================================="
			"======================================-`&\r\n";
	}

	if(c_str_len(buf)>78)
	{
		sprintf(returnbuf,"%s\r\n",buf);
		return returnbuf;
	}


	spaces= (74-c_str_len(buf))/2;
	for(int j=0;j<spaces; j++)
	{
		line[j]='=';
	}
	line[spaces]='\0';

	if(spaces%2==0){
		sprintf(returnbuf,"`#`=t-%s`# `=T%s `&%s-`&\r\n",line, buf, line);
	}else{
		strcpy(line2, line);
		line2[spaces-1]='\0';
		sprintf(returnbuf,"`#`=t-%s`# `=T%s `&%s-`&\r\n",line, buf, line2);
	}
	return returnbuf;

}
/**************************************************************************/
/* get the 'short' name of an area (e.g. MIDGAARD, MIRROR etc. */
/* assumes that the filename saved in the AREA_DATA struct is something like midgaard.are */
char * area_name (AREA_DATA *pArea)
{
	static char buffer[64]; /* short filename */
	char  *period;

	assert(pArea != NULL);
	
	strncpy(buffer, pArea->file_name, 64); /* copy the filename */	
	period = strchr (buffer, '.'); /* find the period (midgaard.are) */
	if(period) /* if there was one */
		*period = '\0'; /* terminate the string there (midgaard) */
		
	return buffer;	
}
/**************************************************************************/
char *percent_colour_codebar(void){
	return("`c0-3`x,`g4-11`x,`m12-18`x,`r19-26`x,`y27-33`x,`S34-41`x,"
	"`w42-48`x,`W49-56`x,`Y57-63`x,`R64-71`x,`M72-78`x,"
	"`G79-86`x,`C87-93`x,`B94+`x\r\n");

}
/**************************************************************************/
char *percent_colour_code(sh_int val){
    static char col[5][MIL];
	static sh_int i;
	float tval;
	sh_int newval;
	// rotate buffers
	++i= i%5;

	tval=(float)val;
	tval*=2;
	tval/=15;
	tval+=0.5;
	newval=(sh_int)tval;

    switch(newval)
    {
        case 0:
            strcpy(col[i], "`c");
            break;
        case 1:
            strcpy(col[i], "`g");
            break;
        case 2:
            strcpy(col[i], "`m");
            break;
        case 3:
            strcpy(col[i], "`r");
            break;
        case 4:
            strcpy(col[i], "`y");
            break;
        case 5:
            strcpy(col[i], "`S");
            break;
        case 6:
            strcpy(col[i], "`w");
            break;
        case 7:
            strcpy(col[i], "`W");
            break;
        case 8:
            strcpy(col[i], "`Y");
            break;
        case 9:
            strcpy(col[i], "`R");
            break;
        case 10:
            strcpy(col[i], "`M");
            break;
        case 11:
            strcpy(col[i], "`G");
            break;
        case 12:
            strcpy(col[i], "`C");
            break;
        case 13:
        case 14:
            strcpy(col[i], "`B");
            break;
    }
	return col[i];
}
/**************************************************************************/
// returns the underscored lowercase word
char * underscore_word(char *word)
{
	static char rbuf[3][MIL];
	static int index;
	char * pStr;

	++index%=3; // rotate return buffer

	sprintf(rbuf[index], "%s", word);

	pStr=&rbuf[index][0];

	// convert spaces into _ characters
	// and upper to lowercase
	do{
		if(*pStr==' '){
			*pStr='_';
		}else{
			*pStr=tolower(*pStr);
		}
	}while (*(++pStr));

	return rbuf[index];
}

/**************************************************************************/
char * lowercase(const char *str){
	static int i;
    static char buf[5][512];
	int pos;
	// rotate buffers
	++i= i%5;
	
    for( pos = 0; str[pos] != '\0'; pos++ )
		buf[i][pos] = LOWER(str[pos]);
    buf[i][pos] = '\0';

	return buf[i]; 
}
/**************************************************************************/
char * uppercase(const char *str){
	static int i;
    static char buf[5][512];
	int pos;
	// rotate buffers
	++i= i%5;
	
    for( pos = 0; str[pos] != '\0'; pos++ )
		buf[i][pos] = UPPER(str[pos]);
    buf[i][pos] = '\0';

	return buf[i]; 
}
/**************************************************************************/
void affect_to_room( ROOM_INDEX_DATA *room, AFFECT_DATA *paf )
{
    AFFECT_DATA *paf_new;
	
    paf_new = new_affect();
	
    *paf_new		= *paf;
    paf_new->next	= room->affected;
    room->affected	= paf_new;
	
    if( paf->where == WHERE_AFFECTS )
		SET_BIT(room->affected_by,paf->bitvector);
	
    return;
}
/**************************************************************************/
void affect_remove_room( ROOM_INDEX_DATA *room, AFFECT_DATA *paf )
{
    int where, vector;
    if( room->affected == NULL )
    {
        bug("Affect_remove_room: no affect.");
        return;
    }
	
    where = paf->where;
    vector = paf->bitvector;
	
    if(paf->bitvector)
		switch( paf->where)
	{
        case WHERE_AFFECTS:
            REMOVE_BIT(room->affected_by,paf->bitvector);
            break;
	}
	
    if( paf == room->affected )
    {    
        room->affected    = paf->next;
    }
    else
    {
        AFFECT_DATA *prev;
		
        for( prev = room->affected; prev != NULL; prev = prev->next )
        {
            if( prev->next == paf )
            {
                prev->next = paf->next;
                break;
            }
        }
		
        if( prev == NULL )
        {
            bug("Affect_remove_room: cannot find paf.");
            return;
        }
    }
	
    free_affect(paf);
	return;
	
}
/**************************************************************************/
void affect_to_skill( char_data *ch, int sn, int amount )
{
	if( IS_NPC( ch ) || sn < 0 ) return;

	if( TRUE_CH(ch)->pcdata->learned[sn] > 0 )
		TRUE_CH(ch)->pcdata->learned[sn] += amount;
    return;
}
/**************************************************************************/
// counts the number of times a character is in the buffer
int count_char(const char *buffer, char character)
{
	int result=0;
	
	if(buffer==NULL){
		return 0;
	}

	for(; *buffer; buffer++)
	{
		if(*buffer==character)
			result++;
	}

	return result;
}
/**************************************************************************/
// used when the moblog flag is on
void process_moblog(char_data *ch, const char *txt)
{
	if(!ch){
		return;
	}
	if( !IS_VALID(ch)){
		return;
	}
	if( !IS_NPC(ch) ){
		return;
	}

	if(!IS_NULLSTR(txt)
	&& IS_SET(ch->act,ACT_MOBLOG) )
	{
		char logbuf[MSL];
		// prevent buffer overruns
		char working[MSL];
		strncpy(working, txt, MSL-30);
		int len=UMIN(str_len(txt),MSL-40);		
		working[len]='\0';

		// ditch the \r codes
		strcpy(working, fix_string( working));

		// trim off the tail \n code if there
		if(working[str_len(working)-1]=='\n'){
			working[str_len(working)-1]='\0';
		}

		sprintf(logbuf, "[%5d] '%s' in room %d '%s'", 
			(ch->pIndexData?ch->pIndexData->vnum:0),
			ch->name,
			(ch->in_room? ch->in_room->vnum:0),
			working);
		append_timestring_to_file( MOBLOG_LOGFILE, logbuf);
	};
}
/**************************************************************************/
// reports a wiznet message about multilogging
// handles restoring of colour coding
// header is displayed before custom text
// default colour of custom text is RED
void multilog_alertf(char_data *ch, char * fmt, ...)
{
	char *pbuf;	
    char buf[HSL];
	if(!IS_NULLSTR(ch->remote_ip_copy)){
		sprintf(buf,"`#`R***MULTILOG ALERT - `x%s`R:",
			ch->remote_ip_copy);
	}else{
		strcpy(buf,"`#`R***MULTILOG ALERT: ");
	}

	pbuf=&buf[str_len(buf)];

	va_list args;
	va_start(args, fmt);
	vsnprintf(pbuf, MSL, fmt, args);
	va_end(args);
	strcat(buf,"`&");
	append_datetimestring_to_file(MULTILOG_FILE, buf);	
	wiznet(buf,NULL,NULL,WIZ_SITES,0,0);	
}
/**************************************************************************/
char *to_affect_string( AFFECT_DATA *paf, int objects_level )
{
	static char buf[MSL*2];
	buf[0]='\0';
		
	switch(paf->where)
	{
	case WHERE_AFFECTS:
		sprintf( buf, "Adds %s affect.",
			affect_bit_name(paf->bitvector));
		break;
	case WHERE_AFFECTS2:
		sprintf( buf, "Adds %s affect2.",
			affect2_bit_name(paf->bitvector));
		break;
	case WHERE_OBJEXTRA:
		sprintf( buf, "Adds object extra flag(s) %s.",
			extra_bit_name(paf->bitvector));
		break;
	case WHERE_OBJEXTRA2:
		sprintf( buf, "Adds object extra2 flag(s) %s.",
			extra2_bit_name(paf->bitvector));
		break;
	case WHERE_IMMUNE:
		sprintf( buf, "Adds immunity to %s.",
			imm_bit_name(paf->bitvector));
		break;
	case WHERE_RESIST:
		sprintf( buf, "Adds resistance to %s.",
			imm_bit_name(paf->bitvector));
		break;
	case WHERE_VULN:
		sprintf( buf, "Adds vulnerability to %s.",
			imm_bit_name(paf->bitvector));
		break;
	case WHERE_MODIFIER:
		sprintf( buf, "Adds %s modifier of %d.", 
			flag_string(apply_types, paf->location), paf->modifier);
		break;
	
	case WHERE_WEAPON:
		sprintf( buf, "Adds weapon flag(s) %s .", flag_string(weapon_flags, paf->modifier));
		break;

	case WHERE_SKILLS:
		sprintf( buf, "Adds %d%% to %s.",
			paf->modifier, skill_table[paf->type].name );
		break;
	case WHERE_OBJECTSPELL:
		if(paf->level)
		{
			sprintf( buf, "Casts '%s' at level %3d for a duration of %3d.",
				skill_table[paf->type].name, paf->level, paf->duration);
		}
		else
		{
			sprintf( buf, "Casts '%s' at objects level (%d) for a duration of %d.",
				skill_table[paf->type].name, objects_level, paf->duration);
		}
		break;
	default:
		sprintf( buf, "Unknown bit %d: %d", paf->where,paf->bitvector);
		break;
	}
	
	return buf;
}
/**************************************************************************/
// reports and logs bounds checking messages
void boundsbug(char * fmt, ...)
{
    char buf[HSL];

	char *pbuf;	
	strcpy(buf,"`#`R***BOUNDS BUG ALERT: ");
	pbuf=&buf[str_len(buf)];

	va_list args;
	va_start(args, fmt);
	vsnprintf(pbuf, MSL, fmt, args);
	va_end(args);

	append_datetimestring_to_file(BOUNDSBUG_FILE, buf);	
	bugf(buf);

	do_abort();
}
/**************************************************************************/
char *preference_word(PREFERENCE_TYPE pt)
{
	switch (pt){
		case PREF_OFF:			return "off";
		case PREF_AUTOSENSE:	return "autosense";
		case PREF_ON:			return "on";
		default:
			bugf("Unknown preference type %d", pt);
			do_abort();
	}
	return "";

}
/**************************************************************************/
// Kal - Apr 01
char *FORMATF(const char *formatbuf, ...)
{
	static int i;
    static char buf[10][MSL*3];
	++i%=10;

	va_list args;
	va_start(args, formatbuf);
	vsnprintf(buf[i], MSL*3, formatbuf, args);
	va_end(args);

	return buf[i];
}
/**************************************************************************/
// Kal - Jun 01 - unique id system
int get_next_uid()
{
	static int last=1000;
	return ++last;
}
/**************************************************************************/
// get_uid() - unique id system, returns 0 if it isn't a UID text reference
int get_uid(const char *text)
{
	const char *p=text;

	// skip leading whitespace
	while(is_space(*p)){
		p++;
	}

	if(*p=='#' && is_number(p+1)){ // it is a UID
		return atoi(p+1);
	}
	return 0;
}
/**************************************************************************/
// Kal - convert a 24 hour clock into 12 clock
char *convert24hourto12hour(int hour)
{
	// multibuffered
	static int i;
    static char buf[5][512];
	++i%=5;
    buf[i][0] = '\0';
	
	hour%=24; // avoid idiot results
	if(hour>11){ // 12 -> 23
		sprintf(buf[i], "%2dpm", ((hour-1)%12)+1);		
	}else{
		sprintf(buf[i], "%2dam", ((hour-12)%12)+12);
	}
	return buf[i];
}
/**************************************************************************/
// Kal - Sept 02
void println_delayed_to_room(int seconds, room_index_data *room, char_data *all_but_this_person, const char *text)
{
	char_data *p;

	// check the room is sane
	if(!room){
		bugf("delayed_printf_to_room(), given a NULL room!");
		return;
	}
	
	for( p=room->people; p; p=p->next_in_room ){
		if(p==all_but_this_person){
			continue;
		}
		p->println(seconds, text);
	}
}
/**************************************************************************/
// return "" if ch doesn't have autodamage enabled, otherwise return 
// the autodamage text required.
// Kal - Aug 03
char *autodamtext(char_data *ch, int damage_amount)
{
	static char result[MIL];

	if(GAMESETTING2(GAMESET2_NO_AUTODAMAGE_COMMAND) 
		|| !HAS_CONFIG2(ch, CONFIG2_AUTODAMAGE)){
		return "";
	}

	sprintf(result, " [%d]", damage_amount);

	return result;
}
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/