Mud20/accounts/
Mud20/accounts/c/
Mud20/accounts/f/
Mud20/accounts/k/
Mud20/accounts/s/
Mud20/accounts/t/
Mud20/area_current/
Mud20/area_current/newareas/
Mud20/bin/
Mud20/clans/
Mud20/gods/
Mud20/old-sources/
Mud20/player/
Mud20/player/a/del/
Mud20/player/b/
Mud20/player/b/bak/
Mud20/player/b/del/
Mud20/player/f/
Mud20/player/f/bak/
Mud20/player/f/del/
Mud20/player/k/
Mud20/player/k/bak/
Mud20/player/k/del/
Mud20/player/k/dmp/
Mud20/player/m/
Mud20/player/m/bak/
Mud20/player/o/
Mud20/player/o/bak/
Mud20/player/p/
Mud20/player/s/
Mud20/player/s/bak/
Mud20/player/s/del/
Mud20/player/t/
Mud20/player/t/del/
Mud20/player/v/
Mud20/public_html/
Mud20/races/
Mud20/skilltables/
__MACOSX/Mud20/accounts/
__MACOSX/Mud20/accounts/c/
__MACOSX/Mud20/accounts/f/
__MACOSX/Mud20/accounts/k/
__MACOSX/Mud20/accounts/s/
__MACOSX/Mud20/area_current/
__MACOSX/Mud20/area_current/core_areas/
__MACOSX/Mud20/area_current/helps/
__MACOSX/Mud20/area_current/newareas/
__MACOSX/Mud20/backups/
__MACOSX/Mud20/bin/
__MACOSX/Mud20/clans/
__MACOSX/Mud20/gods/
__MACOSX/Mud20/log/
__MACOSX/Mud20/old-sources/
__MACOSX/Mud20/player/
__MACOSX/Mud20/player/a/del/
__MACOSX/Mud20/player/b/
__MACOSX/Mud20/player/b/bak/
__MACOSX/Mud20/player/f/
__MACOSX/Mud20/player/f/bak/
__MACOSX/Mud20/player/f/del/
__MACOSX/Mud20/player/k/
__MACOSX/Mud20/player/k/bak/
__MACOSX/Mud20/player/k/del/
__MACOSX/Mud20/player/k/dmp/
__MACOSX/Mud20/player/m/
__MACOSX/Mud20/player/m/bak/
__MACOSX/Mud20/player/o/
__MACOSX/Mud20/player/o/bak/
__MACOSX/Mud20/player/p/
__MACOSX/Mud20/player/s/
__MACOSX/Mud20/player/s/bak/
__MACOSX/Mud20/player/t/del/
__MACOSX/Mud20/player/v/
__MACOSX/Mud20/public_html/
__MACOSX/Mud20/races/
__MACOSX/Mud20/skilltables/
/***************************************************************************
 * Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming   *
 * License by Wizards of the Coast. All comments referring to D20, OGL,    *
 * and SRD refer to the System Reference Document for the Open Gaming      *
 * system. Any inclusion of these derivatives must include credit to the   *
 * Mud20 system, the full and complete Open Gaming LIcense, and credit to  *
 * the respective authors. See ../doc/srd.txt for more information.        *
 *                                                                         *
 * Emud  2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem.   *
 *                                                                         *
 * MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey                *
 *                                                                         *
 * Merc  2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael      *
 * Chastain, Michael Quan, and Mitchell Tse.                               *
 *                                                                         *
 * Original Diku Mud copyright (C) 1990 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeld, Tom Madsen, and Katje Nyboe.     *
 ***************************************************************************/

/***************************************************************************
 * handler.c: Things that affect rooms, objects and characters					   *
 ***************************************************************************/

#include "mud.h"
#include <math.h>
/*
	Local functions.
*/

void obj_set_to_char( CHAR_DATA *, OBJ_DATA * );

int crash_protect = 0;
int fall_distance = 0;

#define NUM_FAKE_NAME 20

static char * const fake_name [] =
{
	"no one in particular",
	"a white rabbit",
	"a fearie dragon",
	"a house plant",
	"a twinkie",
	"your mother",
	"your alter ego",
	"a buffer overflow",
	"a voice in your head",
	"the wind",
	"Elminster",
	"your father",
	"a lollipop",
	"your index finger",
	"a large worm",
	"the ground",
	"your alter ego",
	"a fortune cookie",
	"your shadow",
	"the world"
};


/*
	Retrieve a character's trusted level for permission checking.
*/
int get_trust( CHAR_DATA *ch )
{
	push_call("get_trust(%p)",ch);

	if (IS_NPC(ch))
	{
		if (ch->desc)
		{
			if (ch->pIndexData->vnum < 100)
			{
				pop_call();
				return -3;
			}
			else
			{
				pop_call();
				return -8;
			}
		}
		if (IS_AFFECTED(ch, AFF_DOMINATE) || IS_AFFECTED(ch, AFF2_POSSESS))
		{
			pop_call();
			return -8;
		}
		if (IS_ACT(ch, ACT_PET|ACT_FAMILIAR|ACT_COMPANION))
		{
			pop_call();
			return -9;
		}
		pop_call();
		return MAX_LEVEL-1;
	}
	if (IS_GOD(ch) && IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return UMAX(ch->level, LEVEL_IMMORTAL);
	}
	pop_call();
	return ch->level;
}


/*
 * simple check for valid skill, if it has no
 * skill name, it's not a valid sn - Kregor
 */
bool valid_skill( int sn )
{
	return (sn >= 0 && is_string(skill_table[sn].name));
}

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

	push_call("affect_modify(%p,%p,%p)",ch,paf,fAdd);

	if (paf->location > MAX_APPLY)
	{
		bug("affect_modify(%s): unknown apply location!", ch->name);
		dump_stack();
		pop_call();
		return;
	}
	
	if (fAdd)
	{
		if (paf->bitvector)
		{
			switch (paf->bittype)
			{
				case AFFECT_TO_NONE:
					paf->bittype = AFFECT_TO_CHAR;
					log_printf("affect_modify: bad bittype");
					dump_stack();
				case AFFECT_TO_CHAR:
					if (paf->bitvector < 0)
					{
						SET_BIT(ch->affected2_by, 0 - paf->bitvector);
					}
					else
					{
						SET_BIT(ch->affected_by, paf->bitvector);
					}
					break;
				case AFFECT_TO_OBJ:
					break;
				default:
					log_printf("affect_modify: unknown bittype %d", paf->bittype);
					dump_stack();
					break;
			}
		}
		mod = paf->modifier;
		ch->affected_sn[paf->type]++; // stops loops for is_affected checks - Kregor
	}
	else
	{
		if (paf->bitvector)
		{
			switch (paf->bittype)
			{
				case AFFECT_TO_NONE:
					log_printf("affect_modify: bad bittype");
					paf->bittype = AFFECT_TO_CHAR;
					dump_stack();
				case AFFECT_TO_CHAR:
					if (paf->bitvector < 0)
					{
						REMOVE_BIT(ch->affected2_by, 0 - paf->bitvector);
					}
					else
					{
						REMOVE_BIT(ch->affected_by, paf->bitvector);
					}
					break;
				case AFFECT_TO_OBJ:
					break;
				default:
					log_printf("affect_modify: unknown bittype", paf->bittype);
					dump_stack();
					break;
			}
		}
		mod = 0 - paf->modifier;
		--ch->affected_sn[paf->type]; // stops loops for is_affected checks - Kregor
	}

	/* handles hp and move increases from applies - Kregor */
	hpDiff = get_max_hit(ch);
	mvDiff = get_max_move(ch);

	/* caches the loops used to calc overlapping applies */
	ch->apply[paf->location] = calc_apply(ch, paf->location);

	hpDiff = get_max_hit(ch) - hpDiff;
	mvDiff = get_max_move(ch) - mvDiff;

	if (ch->in_room)
		ch->in_room->light = calc_room_light(ch, NULL, ch->in_room, TRUE);

	/* caches the immunities from applies to avoid looping during play */
	apply_immunity_energy(ch);
	apply_immunity_sdesc(ch);

	switch (paf->location)
	{
		default:
			break;
		case APPLY_HIT:
			ch->hit					+= mod;
		case APPLY_MOVE:
			ch->move				+= mod;
		case APPLY_CON:
			ch->move += mvDiff;
			ch->hit += hpDiff;
			break;
		case APPLY_NONE:
			break;
		case APPLY_RACE:
			ch->move += mvDiff;
			break;
		case APPLY_SEX:
			ch->sex					+= mod;
			break;
	}
	// need to update position for affects that change it - Kregor
	update_pos(ch,-1);
	
	pop_call();
	return;
}


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

	push_call("affect_obj_modify(%p,%p,%p)",obj,paf,fAdd);

	if (fAdd)
	{
		if (paf->bitvector)
		{
			switch (paf->bittype)
			{
				case AFFECT_TO_NONE:
					log_printf("affect_obj_modify: bad bittype");
					dump_stack();
					break;
				case AFFECT_TO_CHAR:
					break;
				case AFFECT_TO_OBJ:
					SET_BIT(obj->extra_flags, paf->bitvector);
					break;
				default:
					log_printf("affect_obj_modify: unknown bittype %d", paf->bittype);
					dump_stack();
					break;
			}
		}
		mod = paf->modifier;
		obj->affected_sn[paf->type]++; // stops loops for is_affected checks - Kregor
	}
	else
	{
		if (paf->bitvector)
		{
			switch (paf->bittype)
			{
				case AFFECT_TO_NONE:
					log_printf("affect_obj_modify: bad bittype");
					dump_stack();
					break;
				case AFFECT_TO_CHAR:
					break;
				case AFFECT_TO_OBJ:
					REMOVE_BIT(obj->extra_flags, paf->bitvector);
					break;
				default:
					log_printf("affect_obj_modify: unknown bittype", paf->bittype);
					dump_stack();
					break;
			}
		}
		mod = 0 - paf->modifier;
		--obj->affected_sn[paf->type]; // stops loops for is_affected checks - Kregor
	}

	switch (paf->location)
	{
		default:
			break;
		case APPLY_WEAPON_FLAG:
			if (obj->item_type != ITEM_WEAPON)
				break;
			if (fAdd)
				SET_BIT(obj->value[1], paf->modifier);
			else
				REMOVE_BIT(obj->value[1], paf->modifier);
			break;
		case APPLY_OBJVAL_0:
			if (fAdd)
				obj->value[0] += paf->modifier;
			else
				obj->value[0] -= paf->modifier;
			break;
		case APPLY_OBJVAL_1:
			if (fAdd)
				obj->value[1] += paf->modifier;
			else
				obj->value[1] -= paf->modifier;
			break;
		case APPLY_OBJVAL_2:
			if (fAdd)
				obj->value[2] += paf->modifier;
			else
				obj->value[2] -= paf->modifier;
			break;
		case APPLY_OBJVAL_3:
			if (fAdd)
				obj->value[3] += paf->modifier;
			else
				obj->value[3] -= paf->modifier;
			break;
		case APPLY_OBJVAL_4:
			if (fAdd)
				obj->value[4] += paf->modifier;
			else
				obj->value[4] -= paf->modifier;
			break;
		case APPLY_OBJVAL_5:
			if (fAdd)
				obj->value[5] += paf->modifier;
			else
				obj->value[5] -= paf->modifier;
			break;
		case APPLY_OBJVAL_6:
			if (fAdd)
				obj->value[6] += paf->modifier;
			else
				obj->value[6] -= paf->modifier;
			break;
		case APPLY_OBJVAL_7:
			if (fAdd)
				obj->value[7] += paf->modifier;
			else
				obj->value[7] -= paf->modifier;
			break;
		case APPLY_MATERIAL:
			if (fAdd)
				obj->material += paf->modifier;
			else
				obj->material -= paf->modifier;
			break;
	}
	if (paf->type == gsn_harden)
	{
		if (fAdd)
			obj->hardness += paf->modifier;
		else
			obj->hardness -= paf->modifier;
	}
	if (IS_SET(paf->bitvector, ITEM_GLOW|ITEM_BURNING))
	{
		if (obj->in_room)
		{
			obj->in_room->light = calc_room_light(NULL, obj, obj->in_room, TRUE);
		}
		else if ((ch = obj->carried_by) != NULL && ch->in_room)
		{
			ch->in_room->light = calc_room_light(ch, NULL, ch->in_room, TRUE);
		}
	}
	if (paf->location != APPLY_NONE)
		obj->apply[paf->location] = get_obj_apply(obj, paf->location);
	pop_call();
	return;
}


/*
	Set just an affect bitvector on a character with no affect_data struct - Kregor.
*/
void SET_AFFECT( CHAR_DATA *ch, lg_int bitv )
{
	push_call("SET_AFFECT(%p,%p)",ch, bitv);

	if (bitv < 0)
		SET_BIT(ch->affected2_by, 0-bitv);
	else
		SET_BIT(ch->affected2_by, bitv);

	pop_call();
	return;
}


/*
	And remove it - Kregor.
*/
void REMOVE_AFFECT( CHAR_DATA *ch, lg_int bitv )
{
	push_call("REMOVE_AFFECT(%p,%p)",ch, bitv);

	if (bitv < 0)
		REMOVE_BIT(ch->affected2_by, 0-bitv);
	else
		REMOVE_BIT(ch->affected2_by, bitv);

	pop_call();
	return;
}


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

	push_call("affect_to_char(%p,%p,%p)",ch,victim,paf);

	ALLOCMEM(paf_new, AFFECT_DATA, 1);

	paf_new->type       = paf->type;
	paf_new->duration   = paf->duration;
	paf_new->location   = paf->location;
	paf_new->modifier   = paf->modifier;
	paf_new->bittype    = paf->bittype;
	paf_new->bitvector  = paf->bitvector;
	paf_new->level      = paf->level;
	if (ch == NULL)
		paf_new->caster     = STRALLOC("None");
	else if (!IS_NPC(ch))
		paf_new->caster			= STRALLOC(ch->name);
	else
		paf_new->caster     = STRALLOC(format("m%d", ch->pIndexData->vnum));

	/* added for extended and persistent spell feats from SRD - Kregor */
	if (ch != NULL)
	{
		if (is_spell(paf->type))
		{
			if (IS_SET(ch->cast_feats, METAMAGIC_EXTEND)
			|| (IS_SET(skill_table[paf->type].spell_desc, SDESC_POLYMORPH) && get_bloodline(ch) == BLOODLINE_ABERRANT))
				paf_new->duration *= 2;
			if (IS_SET(skill_table[paf->type].spell_desc, SDESC_POLYMORPH) && bloodline_capstone(ch, BLOODLINE_ABERRANT))
				paf_new->duration *= 2;
			if (is_spell(paf->type) && IS_SET(ch->cast_feats, METAMAGIC_PERSIST))
				paf_new->duration = 2400;
		}
		if (paf->type == gsn_wildshape && learned(ch, gsn_extend_wildshape))
			paf_new->duration *= 2;
		if (is_spell(paf->type) && IS_SET(skill_table[paf->type].spell_desc, SDESC_CHARM) && domain_apotheosis(ch, DOMAIN_CHARM))
			paf_new->duration *= 2;
		if (is_spell(paf->type) && skill_table[paf->type].spell_school == SCHOOL_ILLUSION && domain_apotheosis(ch, DOMAIN_ILLUSION))
			paf_new->duration *= 2;
		if (is_spell(paf->type) && skill_table[paf->type].target == TAR_CHAR_DEFENSIVE
		&& IS_SET(skill_table[paf->type].flags, SF_GROUP) && domain_apotheosis(ch, DOMAIN_ILLUSION)
		&& (skill_table[paf->type].spell_school == SCHOOL_ABJURATION || skill_table[paf->type].spell_school == SCHOOL_ENCHANTMENT))
			paf_new->duration *= 2;
	}

	LINK( paf_new, victim->first_affect, victim->last_affect, next, prev );

	affect_modify(victim, paf_new, TRUE);

	pop_call();
	return;
}

/*
	Give an affect to an object.
*/
void affect_to_obj( CHAR_DATA *ch, OBJ_DATA *obj, AFFECT_DATA *paf )
{
	AFFECT_DATA *paf_new;

	push_call("affect_to_obj(%p,%p,%p)",ch,obj,paf);

	ALLOCMEM(paf_new, AFFECT_DATA, 1);

	paf_new->type       = paf->type;
	paf_new->duration   = paf->duration;
	paf_new->location   = paf->location;
	paf_new->modifier   = paf->modifier;
	paf_new->bittype		= paf->bittype;
	paf_new->bitvector  = paf->bitvector;
	paf_new->level      = paf->level;
	if (ch == NULL)
		paf_new->caster     = STRALLOC("None");
	else if (!IS_NPC(ch))
		paf_new->caster			= STRALLOC(ch->name);
	else
		paf_new->caster     = STRALLOC(format("m%d", ch->pIndexData->vnum));

	/* added for extended and persistent spell feats from SRD - Kregor */
	if (ch != NULL)
	{
		if (is_spell(paf->type) && IS_SET(ch->cast_feats, METAMAGIC_EXTEND))
			paf_new->duration *= 2;
		if (is_spell(paf->type) && IS_SET(ch->cast_feats, METAMAGIC_PERSIST))
			paf_new->duration = 2400;
	}

	LINK( paf_new, obj->first_affect, obj->last_affect, next, prev );

	affect_obj_modify(obj, paf_new, TRUE);

	if (IS_WORN(obj) && obj->carried_by)
		affect_modify(obj->carried_by, paf, TRUE);

	pop_call();
	return;
}


/*
 * Add or enhance an affect.
 * Extensively revised, now combines modifiers if duration is fixed,
 * otherwise updates duration to new duration.
 */
void affect_join( CHAR_DATA *caster, CHAR_DATA *ch, AFFECT_DATA *paf )
{
	AFFECT_DATA *paf_old;

	push_call("affect_join(%p,%p,%p)",caster,ch,paf);

	if (caster != NULL)
	{
		if (is_spell(paf->type) && IS_SET(caster->cast_feats, METAMAGIC_PERSIST))
			paf->duration = 2400;
		else if (is_spell(paf->type) && IS_SET(caster->cast_feats, METAMAGIC_EXTEND))
			paf->duration *= 2;
		if (is_spell(paf->type) && IS_SET(skill_table[paf->type].spell_desc, SDESC_CHARM) && domain_apotheosis(caster, DOMAIN_CHARM))
			paf->duration *= 2;
		if (is_spell(paf->type) && skill_table[paf->type].spell_school == SCHOOL_ILLUSION && domain_apotheosis(ch, DOMAIN_ILLUSION))
			paf->duration *= 2;
		if (is_spell(paf->type) && skill_table[paf->type].target == TAR_CHAR_DEFENSIVE
		&& IS_SET(skill_table[paf->type].flags, SF_GROUP) && domain_apotheosis(ch, DOMAIN_ILLUSION)
		&& (skill_table[paf->type].spell_school == SCHOOL_ABJURATION || skill_table[paf->type].spell_school == SCHOOL_ENCHANTMENT))
			paf->duration *= 2;
	}

	for (paf_old = ch->first_affect ; paf_old ; paf_old = paf_old->next)
	{
		if (paf_old->type == paf->type && paf_old->location == paf->location)
		{
			// -2 duration = permanency, ignore new affect
			if (paf_old->duration == -2)
			{
				continue;
			}
			affect_modify(ch, paf_old, FALSE);

			if (paf->duration == -1 || paf_old->duration == -1)
			{
				paf_old->duration = -1;
				paf_old->modifier += paf->modifier;
			}
			else
			{
				if (paf_old->duration < paf->duration)
					paf_old->duration = paf->duration;

				if (paf_old->modifier < paf->modifier)
					paf_old->modifier = paf->modifier;
			}
			if (paf_old->level < paf->level)
				paf_old->level = paf->level;
				
			if (caster == NULL)
				paf_old->caster     = STRALLOC("None");
			else if (!IS_NPC(caster))
				paf_old->caster			= STRALLOC(caster->name);
			else
				paf_old->caster     = STRALLOC(format("m%d", caster->pIndexData->vnum));

			affect_modify(ch, paf_old, TRUE);

			pop_call();
			return;
		}
	}
	affect_to_char( caster, ch, paf );
	pop_call();
	return;
}

/*
 * Room affects new home with other affect functions
 * revamped to add more affect data - Kregor
 */
void set_room_timer(CHAR_DATA *ch, ROOM_TIMER_DATA *rtd)
{
	ROOM_TIMER_DATA *rtd_new;
	int sector;

	push_call("set_room_timer(%p,%p)",ch,rtd);

	ALLOCMEM(rtd_new, ROOM_TIMER_DATA, 1);

	rtd_new->vnum				= rtd->vnum;
	rtd_new->type				= rtd->type;
	rtd_new->location		= rtd->location;
	rtd_new->modifier		= rtd->modifier;
	rtd_new->duration		= rtd->duration;
	rtd_new->bitvector	= rtd->bitvector;
	rtd_new->level			= rtd->level;
	if (ch == NULL)
		rtd_new->caster			= STRALLOC("None");
	else if (IS_NPC(ch))
		rtd_new->caster			= STRALLOC(format("m%d",ch->pIndexData->vnum));
	else
		rtd_new->caster			= STRALLOC(ch->name);
	
	/* added for extended and persistent spell feats from SRD - Kregor */
	if (ch != NULL)
	{
		if (is_spell(rtd->type) && IS_SET(ch->cast_feats, METAMAGIC_EXTEND))
			rtd_new->duration *= 2;
		if (is_spell(rtd->type) && IS_SET(ch->cast_feats, METAMAGIC_PERSIST))
			rtd_new->duration = 2400;
	}

	if (rtd->bitvector > 0)
		SET_BIT(room_index[rtd_new->vnum]->room_flags, rtd_new->bitvector);

	switch (rtd_new->location)
	{
		case APPLY_ROOM_LIGHT:
			room_index[rtd_new->vnum]->light += rtd_new->modifier;
			break;
		case APPLY_ROOM_SECTOR:
			//calcs actual modifier based on existing sector type - Kregor
			sector = room_index[rtd_new->vnum]->sector_type;
			rtd_new->modifier = rtd_new->modifier - sector;
			room_index[rtd_new->vnum]->sector_type += rtd_new->modifier;
			break;
	}

	LINK(rtd_new, mud->f_room_timer, mud->l_room_timer, next, prev);

	pop_call();
	return;
}


bool del_room_timer(int vnum, int type)
{
	ROOM_TIMER_DATA *rtd;

	push_call("del_room_timer(%p,%p)",vnum,type);

	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd->next)
	{
		if (rtd->vnum == vnum && type == -1)
		{
			rtd->vnum = ROOM_VNUM_JUNK;
			continue;
		}

		if (rtd->vnum == vnum && rtd->type == type)
		{
			break;
		}
	}

	if (rtd)
	{
		if (rtd->bitvector > 0)
			REMOVE_BIT(room_index[rtd->vnum]->room_flags, rtd->bitvector);
		
		switch (rtd->location)
		{
			case APPLY_ROOM_LIGHT:
				room_index[rtd->vnum]->light -= rtd->modifier;
				break;
			case APPLY_ROOM_SECTOR:
				room_index[rtd->vnum]->sector_type -= rtd->modifier;
				break;
		}

		STRFREE(rtd->caster);

		UNLINK(rtd, mud->f_room_timer, mud->l_room_timer, next, prev);

		FREEMEM(rtd);

		pop_call();
		return TRUE;
	}

	pop_call();
	return FALSE;
}

/*
	Remove an affect from an obj.
*/
void affect_from_obj( OBJ_DATA *obj, AFFECT_DATA *paf )
{
	push_call("affect_from_obj(%p,%p)",obj,paf);

	affect_obj_modify(obj, paf, FALSE);

	UNLINK(paf, obj->first_affect, obj->last_affect, next, prev);

	if (IS_WORN(obj) && obj->carried_by)
		affect_modify(obj->carried_by, paf, FALSE);

	FREEMEM(paf);

	pop_call();
	return;
}

/*
	Remove an affect from a char.
*/
void affect_from_char( CHAR_DATA *ch, AFFECT_DATA *paf )
{
	push_call("affect_from_char(%p,%p)",ch,paf);

	if ((paf->prev == NULL && paf != ch->first_affect)
	||  (paf->next == NULL && paf != ch->last_affect))
	{
		log_printf("UNLINK ERROR affect_from_char: affect not found on %s", ch->name);
		dump_stack();
		pop_call();
		return;
	}

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

	if (ch->absorption[paf->location] > 0)
		ch->absorption[paf->location] = 0;

	FREEMEM( paf );

	pop_call();
	return;
}


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

	push_call("affect_strip(%p,%p)",ch,sn);

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

		if (paf->type != sn)
			continue;

		//add so when dominates run out, the control is broken.
		if (paf->bitvector == AFF_DOMINATE && ch->master)
		{
			stop_follower(ch);
		}
		affect_from_char( ch, paf );
	}
	pop_call();
	return;
}

/*
	Strip all affects of a given spell descriptor.
*/
void affect_strip_desc( CHAR_DATA *ch, lg_int desc )
{
	AFFECT_DATA *paf, *paf_next;

	push_call("affect_strip_desc(%p,%p)",ch,desc);

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

		if (IS_SET(skill_table[paf->type].spell_desc, desc))
		{
			//add so when dominates run out, the control is broken.
			if (paf->bitvector == AFF_DOMINATE && ch->master)
			{
				stop_follower(ch);
			}
			affect_from_char( ch, paf );
		}
	}
	pop_call();
	return;
}

/*
	Strip all affects of a given bitvector.
*/
void AFFECT_STRIP( CHAR_DATA *ch, lg_int bitv )
{
	AFFECT_DATA *paf, *paf_next;

	push_call("AFFECT_STRIP(%p,%p)",ch,bitv);

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

		if (IS_SET(paf->bitvector, bitv))
		{
			affect_from_char( ch, paf );
		}
	}

	//also strip out hard set bitvectors
	if (bitv < 0)
		REMOVE_BIT(ch->affected2_by, 0-bitv);
	else
		REMOVE_BIT(ch->affected_by, bitv);

	pop_call();
	return;
}

void affect_obj_strip( OBJ_DATA *obj, int sn )
{
	AFFECT_DATA *paf, *paf_next;

	push_call("affect_obj_strip(%p,%p)",obj,sn);

	for (paf = obj->first_affect ; paf ; paf = paf_next)
	{
		paf_next = paf->next;

		if (paf->type == sn)
		{
			affect_from_obj( obj, paf );
		}
	}
	pop_call();
	return;
}

void affect_room_strip( ROOM_INDEX_DATA *room, int sn )
{
	ROOM_TIMER_DATA *rtd, *rtd_next;

	push_call("affect_room_strip(%p,%p)",room,sn);

	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next)
	{
		rtd_next = rtd->next;
		
		if (rtd->vnum == room->vnum && rtd->type == sn)
		{
			if (skill_table[rtd->type].msg_off)
			{
				send_to_room(format("%s\n\r", skill_table[rtd->type].msg_off), room);
			}
			del_room_timer(rtd->vnum, rtd->type);
		}
	}
	pop_call();
	return;
}

/* strip room effects by spell descriptor */
void affect_room_strip_desc( ROOM_INDEX_DATA *room, lg_int desc )
{
	ROOM_TIMER_DATA *rtd, *rtd_next;

	push_call("affect_room_strip(%p,%p)",room,desc);

	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next)
	{
		rtd_next = rtd->next;
		
		if (rtd->vnum == room->vnum && IS_SET(skill_table[rtd->type].spell_desc, desc))
		{
			if (skill_table[rtd->type].msg_off)
			{
				send_to_room(format("%s\n\r", skill_table[rtd->type].msg_off), room);
			}
			del_room_timer(rtd->vnum, rtd->type);
		}
	}
	pop_call();
	return;
}

/*
	Return true if afected by a given sn.
	Revamped these to use a stored array,
	rather than looping thru the affect data
	each time it was called - Kregor
*/
bool is_affected( CHAR_DATA *ch, int sn )
{
	push_call("is_affected(%p,%p)",ch,sn);

	pop_call();
	return UMAX(0, ch->affected_sn[sn]);
}

bool is_obj_affected( OBJ_DATA *obj, int sn )
{
	push_call("is_obj_affected(%p,%p)",obj,sn);

	pop_call();
	return UMAX(0, obj->affected_sn[sn]);
}

bool is_room_affected( ROOM_INDEX_DATA *room, int sn )
{
	ROOM_TIMER_DATA *rtd, *rtd_next;

	push_call("is_room_affected(%p,%p)",room,sn);

	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next)
	{
		rtd_next = rtd->next;
		
		if (rtd->vnum == room->vnum && rtd->type == sn)
		{
			pop_call();
			return TRUE;
		}
	}
	pop_call();
	return FALSE;
}

ROOM_TIMER_DATA *get_room_affect( ROOM_INDEX_DATA *room, lg_int flags )
{
	ROOM_TIMER_DATA *rtd, *rtd_next;

	push_call("get_room_affect(%p,%p)",room,flags);

	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next)
	{
		rtd_next = rtd->next;
		
		if (rtd->vnum == room->vnum && IS_SET(rtd->bitvector, flags))
		{
			pop_call();
			return rtd;
		}
	}
	pop_call();
	return NULL;
}

ROOM_TIMER_DATA *get_room_affect_vnum( int vnum, lg_int flags )
{
	ROOM_TIMER_DATA *rtd, *rtd_next;

	push_call("get_room_affect(%p,%p)",vnum,flags);

	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next)
	{
		rtd_next = rtd->next;
		
		if (rtd->vnum == vnum && IS_SET(rtd->bitvector, flags))
		{
			pop_call();
			return rtd;
		}
	}
	pop_call();
	return NULL;
}

/********************************************************
 * Compute the total bonus (penalty) for a specific apply
 * All bonuses of a given type to a given location
 * overlap, not stack, per SRD rules
 * Kregor 3/16/07
 */
int calc_apply(CHAR_DATA * ch, int apply)	
{
	OBJ_DATA *obj;
	AFFECT_DATA *paf;
	int mod = 0;
	int modplus = 0;
	int modminus = 0;
	int racebonus = 0;
	
	push_call("calc_apply(%p,%p)",ch,apply);

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (IS_WORN(obj))
		{
			for (paf = obj->first_affect ; paf ; paf = paf->next)
			{
				if (paf->location == apply)
				{
					if ((mod = paf->modifier) >= 0)
					{
						if (apply == APPLY_DODGE) //dodge bonuses stack, all others overlap
							modplus += mod;
						else
							modplus = UMAX(modplus, mod);
						continue;
					}
					else
					{
						modminus += mod; //penalties always stack
						continue;
					}
				}
			}
		}
	}

	for (paf = ch->first_affect ; paf ; paf = paf->next)
	{
		if (paf->location == apply)
		{
			if ((mod = paf->modifier) >= 0)
			{
				if (apply == APPLY_DODGE) //dodge bonuses stack, all others overlap
					modplus += mod;
				else
					modplus = UMAX(modplus, mod);
				continue;
			}
			else
			{
				modminus += mod;
				continue;
			}
		}
	}
	
	//MOST racial applies stack, except for spell resist
	//broke off spell resist to its own function, skip here - Kregor
	if (apply != APPLY_SPELL_RES)
	{
		racebonus = race_table[get_race(ch)].apply[apply];
	}
	
	pop_call();
	return (modplus + modminus + racebonus);
}


/*
 * get total apply for conditional modifiers - Kregor
 */
int get_apply(CHAR_DATA * ch, int apply)	
{
	int mod;
	
	push_call("get_apply(%p,%p)",ch,apply);

	mod = ch->apply[apply];
	
	//add in DR from class abilities, which do NOT stack - Kregor
	if (apply == APPLY_DR_NONE)
	{
		if (class_level(ch, CLASS_BARBARIAN) >= 7)
			mod = UMAX(mod, ROUNDUP((class_level(ch, CLASS_BARBARIAN) - 7) / 3));
		if ((mod = multi_class_level(ch, gsn_armor_mastery)) > 0 && armor_type_worn(ch) != ARMOR_NONE)
			mod = UMAX(mod, mod / 4);
		if (class_level(ch, CLASS_DWARVEN_DEFENDER))
			mod = UMAX(mod, class_level(ch, CLASS_DWARVEN_DEFENDER) / 2);
		if (domain_apotheosis(ch, DOMAIN_EARTH) || bloodline_capstone(ch, BLOODLINE_EARTH))
			mod = UMAX(mod, 5);
	}
	// add modifiers due to class abilities and feats - Kregor
	if (apply == APPLY_DR_EVIL)
	{
		if (domain_apotheosis(ch, DOMAIN_GOOD) || learned(ch, gsn_aura_of_righteousness))
			mod = UMAX(5, mod);
	}
	if (apply == APPLY_DR_GOOD)
	{
		if (domain_apotheosis(ch, DOMAIN_EVIL) || learned(ch, gsn_aura_of_profanity))
			mod = UMAX(5, mod);
	}
	if (apply == APPLY_DR_CHAOS)
	{
		if (domain_apotheosis(ch, DOMAIN_LAW) || learned(ch, gsn_perfect_self))
			mod = UMAX(5, mod);
	}
	if (apply == APPLY_DR_MAGIC)
	{
		if (learned(ch, gsn_shadow_infusion))
			mod = UMAX(5, mod);
	}
	if (apply == APPLY_DR_COLD || apply == APPLY_DR_ELECTRIC)
	{
		if (domain_apotheosis(ch, DOMAIN_WEATHER) || learned(ch, gsn_shadow_resistance))
			mod = UMAX(5, mod);
	}
	if (apply == APPLY_DR_ELECTRIC)
	{
		if (domain_apotheosis(ch, DOMAIN_AIR))
			mod = UMAX(10, mod);
	}
	if (apply == APPLY_DR_FIRE)
	{
		if (domain_apotheosis(ch, DOMAIN_WEATHER))
			mod = UMAX(5, mod);
		if (domain_apotheosis(ch, DOMAIN_SUN) || domain_apotheosis(ch, DOMAIN_FIRE))
			mod = UMAX(10, mod);
	}
	if (apply == APPLY_DARKVISION)
	{
		if (class_level(ch, CLASS_SHADOW_ADEPT))
			mod = UMAX(60, mod);
	}			
	if (apply == APPLY_DISGUISE)
	{
		if (class_ability(ch, gsn_thousand_faces))
			mod = UMAX(10, mod);
	}			
	if (apply == APPLY_REGENERATION)
	{
		if (domain_apotheosis(ch, DOMAIN_PLANT)
		&& ch->in_room
		&& (ch->in_room->sector_type == SECT_FIELD
		|| ch->in_room->sector_type == SECT_FOREST
		|| ch->in_room->sector_type == SECT_HILLS
		|| ch->in_room->sector_type == SECT_SWAMP))
			mod = URANGE(mod, mod + 1, 1);
	}			
	if (apply == APPLY_FAST_HEALING)
	{
		if (domain_apotheosis(ch, DOMAIN_MOON)
		&& IS_OUTSIDE(ch) && mud->sunlight == SUN_DARK)
			mod = URANGE(mod, mod + 5, 5);
		if (domain_apotheosis(ch, DOMAIN_MOON)
		&& IS_OUTSIDE(ch) && mud->sunlight == SUN_SET)
			mod = URANGE(mod, mod + 2, 2);
	}			
	if (apply == APPLY_FORTIFICATION)
	{
		if (domain_apotheosis(ch, DOMAIN_DEATH))
			mod = URANGE(mod, mod + 25, 25);
	}
	if (apply == APPLY_NATURAL_AC)
	{
		if (get_bloodline(ch) == BLOODLINE_DRACONIC)
			mod = class_level(ch, CLASS_SORCERER) / 5 + 1;
	}
	pop_call();
	return (mod);
}

/*
 * Combines two apply types into one bonus
 * for when two types don't stack (like APPLY_WILL and APPLY_SAVES)
 * if one is positive and one negative, both apply
 * Kregor 3/16/07
 */
int combine_apply(CHAR_DATA * ch, int apply1, int apply2)	
{
	int mod, mod1, mod2;
	
	push_call("combine_apply(%p,%p,%p)",ch,apply1,apply2);
	
	mod = mod1 = mod2 = 0;
	
	mod1 = get_apply(ch, apply1);
	mod2 = get_apply(ch, apply2);

	if (mod1 >= 0 && mod2 >= 0)
	{
		if (mod1 >= mod2)
			mod = mod1;
		else
			mod = mod2;
	}
	else if (mod1 < 0 && mod2 >= 0)
	{
		mod = mod2 - mod1;
	}
	else if (mod1 >= 0 && mod2 < 0)
	{
		mod = mod1 - mod2;
	}
		
	pop_call();
	return mod;
}

/* calc the bonus (penalty) to an object */
int get_obj_apply( OBJ_DATA *wield, int apply )
{
	AFFECT_DATA *paf;
	int mod = 0;
	int modplus = 0;
	int modminus = 0;
	
	push_call("get_obj_apply(%p)",wield);
	
	for (paf = wield->first_affect ; paf ; paf = paf->next)
	{
		if (paf->location == apply)
		{
			if ((mod = paf->modifier) >= 0)
			{
				modplus = UMAX(modplus, mod);
				continue;
			}
			else
			{
				modminus -= mod;
				continue;
			}
		}
	}
	pop_call();
	return (modplus + modminus);
}


/*
 * Retrieve the highest level affect on obj or char
 */
int get_highest_obj_aff_level( OBJ_DATA *obj )
{
	AFFECT_DATA *paf, *paf_next;
	int mlv = 0;
	
	push_call("get_highest_obj_aff_level(%p)",obj);
	
	if (obj->first_affect != NULL)
	{
		for ( paf = obj->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;
			
			if (!is_spell(paf->type))
				continue;
		
			if (paf->level > mlv)
				mlv = paf->level;
		}
	}
	
	switch (obj->item_type)
	{
		case ITEM_SCROLL:
		case ITEM_POTION:
		case ITEM_WAND:
		case ITEM_STAFF:
			mlv = obj->value[0];
			break;
		default:
			break;
	}
	pop_call();
	return mlv;
}
					

int get_highest_ch_aff_level( CHAR_DATA *ch )
{
	AFFECT_DATA *paf, *paf_next;
	int mlv = 0;
	
	push_call("get_highest_ch_aff_level(%p)",ch);
	
	if (ch->first_affect == NULL)
	{
		pop_call();
		return 0;
	}
	
	for ( mlv = 0, paf = ch->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;
		
		if (!is_spell(paf->type))
			continue;
		
		if (paf->level > mlv)
			mlv = paf->level;
	}
	pop_call();
	return mlv;
}
					
	
/*
 * Retrieve the school of highest level affect on obj or char
 */
int get_highest_obj_aff_school( OBJ_DATA *obj )
{
	AFFECT_DATA *paf, *paf_next;
	int mlv = 0, school;
	
	push_call("get_highest_obj_aff_school(%p)",obj);
	
	if (obj->first_affect != NULL)
	{
		for ( paf = obj->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;
			
			if (!is_spell(paf->type))
				continue;
			
			if (paf->level > mlv)
			{
				mlv = paf->level;
				school = skill_table[paf->type].spell_school;
			}
		}
	}
	
	switch (obj->item_type)
	{
		case ITEM_SCROLL:
		case ITEM_POTION:
			school = skill_table[obj->value[1]].spell_school;
			break;
		case ITEM_WAND:
		case ITEM_STAFF:
			school = skill_table[obj->value[3]].spell_school;
			break;
	}
	pop_call();
	return school;
}
					

int get_highest_ch_aff_school( CHAR_DATA *ch )
{
	AFFECT_DATA *paf, *paf_next;
	int mlv, school;
	
	push_call("get_highest_ch_aff_school(%p)",ch);
	
	if (ch->first_affect == NULL)
	{
		pop_call();
		return 0;
	}
	
	for ( mlv = 0, paf = ch->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;
		
		if (!is_spell(paf->type))
			continue;
		
		if (paf->level > mlv)
			mlv = paf->level;
			school = skill_table[paf->type].spell_school;
	}
	pop_call();
	return school;
}
					

/*
 * dynamically compute the value of an item - Kregor
 */
int obj_affect_cost( AFFECT_DATA *paf )
{
	int mod, cost;

	push_call("obj_affect_cost()");
	
	mod = cost = 0;

	if (paf->bittype == AFFECT_TO_CHAR)
	{
		switch (paf->bitvector)
		{
			case AFF_INVISIBLE:
			case AFF_DETECT_INVIS:
				cost += 1200000;
				break;
			case AFF_FREEDOM:
			case AFF_SEE_DARKNESS:
				cost += 5600000;
				break;
			case AFF_IMMUNE_SPELL:
				cost += 11200000;
				break;
			case AFF_SANCTUARY:
			case AFF_DETECT_HIDDEN:
			case AFF_UNDERSTAND:
			case AFF_DETECT_TRAPS:
				cost += 600000;
				break;
			case AFF_TRUESIGHT:
				cost += 9000000;
				break;
			case AFF2_ETHEREAL:
			case AFF2_ASTRAL:
				cost += 18200000;
				break;
			case AFF_TONGUES:
			case AFF_HASTE:
				cost += 3000000;
				break;
			case AFF_NONDETECTION:
			case AFF_GASEOUS:
			case AFF_FLYING:
			case AFF_WATER_BREATH:
			case AFF_WATER_WALK:
				cost += 3000000;
				break;
		}
	}
	if (paf->location == APPLY_NONE)
	{
		pop_call();
		return cost;
	}

	if ((mod = paf->modifier) <= 0)
	{
		pop_call();
		return cost;
	}

	switch (paf->location)
	{
		default:
			break;

		case APPLY_STR:
		case APPLY_DEX:
		case APPLY_INT:
		case APPLY_WIS:
		case APPLY_CON:
		case APPLY_CHA:
		case APPLY_MANA:
		case APPLY_ARMOR:
			cost += mod * mod * 100000;
			break;
		case APPLY_ENHANCE_AC:
			cost += mod * mod * 100000;
			break;
		case APPLY_HITROLL:
		case APPLY_DAMROLL:
			cost += mod * mod * 100000;
			break;
		case APPLY_FORTIFICATION:
			cost += (mod/20) * (mod/20) * 100000;
			break;
		case APPLY_DEFLECT:
			cost += mod * mod * 200000;
			break;
		case APPLY_CONCEALMENT:
			cost += mod * 100000;
			break;
		case APPLY_SHIELD:
		case APPLY_DODGE:
		case APPLY_INS_AC:
		case APPLY_NATURAL_AC:
			cost += mod * mod * 250000;
			break;
		case APPLY_SPELL_SLOTS_1:
			cost += mod * mod * 100000;
			break;
		case APPLY_SPELL_SLOTS_2:
			cost += 2 * mod * mod * 100000;
			break;
		case APPLY_SPELL_SLOTS_3:
			cost += 3 * mod * mod * 100000;
			break;
		case APPLY_SPELL_SLOTS_4:
			cost += 4 * mod * mod * 100000;
			break;
		case APPLY_SPELL_SLOTS_5:
			cost += 5 * mod * mod * 100000;
			break;
		case APPLY_SAVING_FORT:
		case APPLY_SAVING_REFL:
		case APPLY_SAVING_WILL:
		case APPLY_SAVES:
		case APPLY_RES_SAVES:
		case APPLY_SAVE_ACID:
		case APPLY_SAVE_COLD:
		case APPLY_SAVE_ELECTRIC:
		case APPLY_SAVE_FIRE:
		case APPLY_SAVE_SONIC:
		case APPLY_SAVE_AIR:
		case APPLY_SAVE_CHAOTIC:
		case APPLY_SAVE_DARKNESS:
		case APPLY_SAVE_DEATH:
		case APPLY_SAVE_DISEASE:
		case APPLY_SAVE_EARTH:
		case APPLY_SAVE_EVIL:
		case APPLY_SAVE_FEAR:
		case APPLY_SAVE_FORCE:
		case APPLY_SAVE_GOOD:
		case APPLY_SAVE_ILLUSION:
		case APPLY_SAVE_LAWFUL:
		case APPLY_SAVE_LIGHT:
		case APPLY_SAVE_MAGIC:
		case APPLY_SAVE_MIND:
		case APPLY_SAVE_NEGATIVE:
		case APPLY_SAVE_PARALYSIS:
		case APPLY_SAVE_PETRI:
		case APPLY_SAVE_POISON:
		case APPLY_SAVE_POLYMORPH:
		case APPLY_SAVE_HEALING:
		case APPLY_SAVE_SLEEP:
		case APPLY_SAVE_WATER:
			cost += mod * mod * 100000;
			break;
		case APPLY_RES_GOOD:
		case APPLY_RES_EVIL:
		case APPLY_RES_LAW:
		case APPLY_RES_CHAOS:
		case APPLY_RES_SPELL:
		case APPLY_COMP_TOHIT:
		case APPLY_COMP_DAMG:
		case APPLY_COMP_SAVES:
		case APPLY_INS_SAVES:
		case APPLY_LUCK_SAVES:
		case APPLY_MOR_SAVES:
		case APPLY_COMP_FORT:
		case APPLY_COMP_REFL:
		case APPLY_COMP_WILL:
		case APPLY_INS_FORT:
		case APPLY_INS_REFL:
		case APPLY_INS_WILL:
		case APPLY_LUCK_FORT:
		case APPLY_LUCK_REFL:
		case APPLY_LUCK_WILL:
		case APPLY_MOR_FORT:
		case APPLY_MOR_REFL:
		case APPLY_MOR_WILL:
		case APPLY_INS_TOHIT:
		case APPLY_LUCK_TOHIT:
		case APPLY_LUCK_DAMG:
		case APPLY_MOR_TOHIT:
		case APPLY_MOR_DAMG:
			cost += mod * mod * 200000;
			break;
		case APPLY_LUCK_SKILL:
		case APPLY_MOR_SKILL:
		case APPLY_INS_SKILL:
			cost += mod * mod * 20000;
			break;
		case APPLY_COMP_SKILL:
		case APPLY_APPRAISE:
		case APPLY_BLUFF:
		case APPLY_CLIMB:
		case APPLY_CONCENTRATE:
		case APPLY_DECIPHER:
		case APPLY_DIPLOMACY:
		case APPLY_DISABLE:
		case APPLY_DISGUISE:
		case APPLY_ESCAPE:
		case APPLY_FIRST_AID:
		case APPLY_GATHER_INFO:
		case APPLY_HANDLE_ANIM:
		case APPLY_INTIMIDATE:
		case APPLY_JUMP:
		case APPLY_LISTEN:
		case APPLY_MOUNT:
		case APPLY_OPEN_LOCK:
		case APPLY_PERFORM:
		case APPLY_SEARCH:
		case APPLY_STEALTH:
		case APPLY_SENSE_MOT:
		case APPLY_SLEIGHT:
		case APPLY_SPELLCRAFT:
		case APPLY_SIGHT:
		case APPLY_SURVIVAL:
		case APPLY_SWIM:
		case APPLY_TUMBLE:
		case APPLY_USE_MAGIC:
		case APPLY_CRAFT_ALCHEMY:
		case APPLY_CRAFT_ARMOR:
		case APPLY_CRAFT_BOWS:
		case APPLY_CRAFT_COOKING:
		case APPLY_CRAFT_FLETCHING:
		case APPLY_CRAFT_JEWELRY:
		case APPLY_CRAFT_LEATHER:
		case APPLY_CRAFT_MINING:
		case APPLY_CRAFT_POISON:
		case APPLY_CRAFT_TAILOR:
		case APPLY_CRAFT_TRAPS:
		case APPLY_CRAFT_WEAPONS:
		case APPLY_DR_ACID:
		case APPLY_DR_COLD:
		case APPLY_DR_ELECTRIC:
		case APPLY_DR_FIRE:
		case APPLY_DR_SONIC:
		case APPLY_DR_BASH:
		case APPLY_DR_PIERCE:
		case APPLY_DR_SLASH:
		case APPLY_DR_MAGIC:
		case APPLY_DR_GOOD:
		case APPLY_DR_EVIL:
		case APPLY_DR_LAW:
		case APPLY_DR_CHAOS:
		case APPLY_DR_IRON:
		case APPLY_DR_SILVER:
		case APPLY_DR_ADAMANTINE:
			cost += mod * mod * 10000;
			break;
		case APPLY_DR_NONE:
			cost += mod * mod * 20000;
			break;
		case APPLY_SPELL_RES:
			cost += 100000 * UMIN(mod, 13);
			if (mod > 13)
				cost += (mod - 13) * 1000000;
			break;
		case APPLY_IMM_ACID:
		case APPLY_IMM_COLD:
		case APPLY_IMM_ELECTRIC:
		case APPLY_IMM_FIRE:
		case APPLY_IMM_SONIC:
		case APPLY_IMM_AIR:
		case APPLY_IMM_CHAOTIC:
		case APPLY_IMM_DARKNESS:
		case APPLY_IMM_DEATH:
		case APPLY_IMM_DISEASE:
		case APPLY_IMM_EARTH:
		case APPLY_IMM_EVIL:
		case APPLY_IMM_FEAR:
		case APPLY_IMM_FORCE:
		case APPLY_IMM_GOOD:
		case APPLY_IMM_ILLUSION:
		case APPLY_IMM_LAWFUL:
		case APPLY_IMM_LIGHT:
		case APPLY_IMM_MAGIC:
		case APPLY_IMM_MIND:
		case APPLY_IMM_NEGATIVE:
		case APPLY_IMM_PARALYSIS:
		case APPLY_IMM_PETRI:
		case APPLY_IMM_POISON:
		case APPLY_IMM_POLYMORPH:
		case APPLY_IMM_HEALING:
		case APPLY_IMM_SLEEP:
		case APPLY_IMM_WATER:
			cost += 10000000;
			break;
		case APPLY_FAST_HEALING:
			cost += mod * mod * 5000000;
			break;
		case APPLY_REGENERATION:
			cost += mod * mod * 10000000;
			break;
	}
	pop_call();
	return cost;
}


/*
 * dynamically compute the value of an item - Kregor
 */
int obj_cost( OBJ_DATA *obj)
{
	AFFECT_DATA *paf;
	int cost = -1;
	int spell_lvl;
	int cnt;
	
	push_call("obj_cost(%p)",obj);
	
	switch (obj->item_type)
	{
		case ITEM_SYMBOL:
			cost = 2500;
			cost += obj->value[3] * obj->value[3] * 100000;
			break;
		case ITEM_WEAPON:
			cost = weapon_table[obj->value[0]].cost;
			break;
		case ITEM_AMMO:
			cost = 10;
			break;
		case ITEM_ARMOR:
			cost = armor_table[obj->value[0]].cost;
			break;
		case ITEM_TOOLS:
			cost = tool_table[obj->value[0]].cost;
			break;
		case ITEM_POTION:
			spell_lvl = get_spell_circle(NULL, obj->value[1]);
			cost = 5000 * obj->value[0] * (spell_lvl == 0 ? 0.5 : spell_lvl);
			break;
		case ITEM_SCROLL:
			spell_lvl = get_spell_circle(NULL, obj->value[1]);
			cost = 2500 * obj->value[0] * (spell_lvl == 0 ? 0.5 : spell_lvl);
			break;
		case ITEM_WAND:
			spell_lvl = get_spell_circle(NULL, obj->value[3]);
			cost = 1500 * obj->value[0] * obj->value[1] * (spell_lvl == 0 ? 0.5 : spell_lvl);
			break;
		case ITEM_STAFF:
			spell_lvl = get_spell_circle(NULL, obj->value[3]);
			cost = 1500 * obj->value[0] * obj->value[1] * (spell_lvl == 0 ? 0.5 : spell_lvl);
			if (is_spell(obj->value[4]))
			{
				spell_lvl = get_spell_circle(NULL, obj->value[4]);
				cost += 1125 * obj->value[0] * obj->value[1] * (spell_lvl == 0 ? 0.5 : spell_lvl);
			}
			if (is_spell(obj->value[5]))
			{
				spell_lvl = get_spell_circle(NULL, obj->value[5]);
				cost += 750 * obj->value[0] * obj->value[1] * (spell_lvl == 0 ? 0.5 : spell_lvl);
			}
			if (is_spell(obj->value[6]))
			{
				spell_lvl = get_spell_circle(NULL, obj->value[6]);
				cost += 750 * obj->value[0] * obj->value[1] * (spell_lvl == 0 ? 0.5 : spell_lvl);
			}
			break;
	}
					
	if (obj->item_type == ITEM_ARMOR)
	{
		if (IS_SET(obj->wear_flags, CAN_WEAR_BODY))
		{
			cost *= 3;
		}
		else if (IS_SET(obj->wear_flags, CAN_WEAR_HEAD)
		|| IS_SET(obj->wear_flags, CAN_WEAR_ARMS)
		|| IS_SET(obj->wear_flags, CAN_WEAR_LEGS))
		{
			cost *= 2;
		}
		else if (IS_SET(obj->wear_flags, CAN_WEAR_FEET)
		|| IS_SET(obj->wear_flags, CAN_WEAR_HANDS))
		{
			cost /= 2;
		}
		for (cnt = 0 ; cnt < ARMORFLAG_MAX ; cnt++)
		{
			if (IS_SHIFT(obj->value[1], cnt))
			{
				cnt += armflag_cost_mod[cnt];
			}
		}
	}

	if (obj->item_type == ITEM_WEAPON || obj->item_type == ITEM_AMMO)
	{
		for (cnt = 0 ; cnt < WFLAG_MAX ; cnt++)
		{
			if (IS_SHIFT(obj->value[1], cnt))
			{
				cnt += wflag_cost_mod[cnt];
			}
		}
	}

	if (IS_SET(obj->extra_flags, ITEM_MASTERWORK))
	{
		switch (obj->item_type)
		{
			case ITEM_WEAPON:
				cost += 30000;
				break;
			case ITEM_AMMO:
				cost += 600;
				break;
			case ITEM_ARMOR:
				cost += 15000;
				break;
			case ITEM_TOOLS:
				cost *= (obj->value[2] * 5);
				break;
		}
	}

	if (obj->item_type == ITEM_TREASURE)
	{
		if (IS_SET(obj->value[0], TFLAG_SPELL_STORING))
		{
			cost += 10000000;
		}
		if (IS_SET(obj->value[0], TFLAG_SPELL_RECAST))
		{
			cost += 1800000;
		}
		if (IS_SET(obj->value[0], TFLAG_SPELL_CHARGES))
		{
			cost += 900000;
		}
		if (IS_SET(obj->value[0], TFLAG_ABSORPTION))
		{
			cost += 4000000;
		}
		if (IS_SET(obj->value[0], TFLAG_COUNTERSPELL))
		{
			cost += 400000;
		}
	}

	for (paf = obj->first_affect ; paf != NULL ; paf = paf->next)
	{
		if (paf->duration >= 0) // so spell affects don't add to cost
			continue;
		
		cost += obj_affect_cost(paf);
	}
	pop_call();
	return cost;
}


/*
 * Determine whether an object is burning - Kregor
 */
bool IS_BURNING( OBJ_DATA * obj ) 
{
	push_call("IS_BURNING(%p)",obj);
	
	if (IS_WEAPON(obj))
	{
		if (IS_SET(obj->value[1], WFLAG_FLAMING) || IS_SET(obj->value[1], WFLAG_FLAMING_BURST))
		{
			pop_call();
			return TRUE;
		}
	}
	if (IS_SET(obj->extra_flags, ITEM_BURNING))
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}


/*
 * NEW Disease data functions - Kregor
 */
/*
	Lookup a disease by name.
*/
int lookup_disease( char *name )
{
	int dis;

	push_call("lookup_disease(%p)",name);

	for (dis = 0 ; dis < MAX_DISEASE ; dis++)
	{
		if (tolower(name[0]) == disease_table[dis].name[0])
		{
			if (!strcmp(name, disease_table[dis].name))
			{
				pop_call();
				return dis;
			}
		}
	}
	pop_call();
	return -1;
}

/*
	Infect char with disease.
*/
void infect_char( CHAR_DATA *ch, CHAR_DATA *victim, int disease )
{
	DISEASE_DATA *dis, *dis_new;
	int bonus;

	push_call("infect_char(%p,%p,%p)",ch,victim,disease);

	// disease does not stack, nor add to DC if already infected.
	for (dis = ch->first_disease ; dis ; dis = dis->next)
	{
		if (dis->type == disease)
		{
			pop_call();
			return;
		}
	}

	ALLOCMEM(dis_new, DISEASE_DATA, 1);

	dis_new->type   	    = disease;
	dis_new->incubation   = number_range(1, disease_table[disease].incubation);
	dis_new->saves			  = 0;

	// calc actual DC of char inflicted disease based on level and stat
	if (ch != NULL)
	{
		if (get_curr_con(ch) < 0)
			bonus = stat_bonus(TRUE, ch, APPLY_CHA);
		else
			bonus = stat_bonus(TRUE, ch, APPLY_CON);
		
		dis_new->dc					= ch->level / 2 + 10 + bonus;
	}

	LINK( dis_new, victim->first_disease, victim->last_disease, next, prev );

	pop_call();
	return;
}

/*
	Infect obj with disease.
*/
void infect_obj( CHAR_DATA *ch, OBJ_DATA *obj, int disease )
{
	DISEASE_DATA *dis, *dis_new;
	int bonus;

	push_call("infect_obj(%p,%p,%p)",ch,obj,disease);

	// disease does not stack, nor add to DC if already infected.
	for (dis = obj->first_disease ; dis ; dis = dis->next)
	{
		if (dis->type == disease)
		{
			pop_call();
			return;
		}
	}

	ALLOCMEM(dis_new, DISEASE_DATA, 1);

	dis_new->type   	    = disease;
	dis_new->incubation   = number_range(1, disease_table[disease].incubation);
	dis_new->saves			  = 0;

	// calc actual DC of char inflicted disease based on level and stat
	if (ch != NULL)
	{
		if (get_curr_con(ch) < 0)
			bonus = stat_bonus(TRUE, ch, APPLY_CHA);
		else
			bonus = stat_bonus(TRUE, ch, APPLY_CON);
		
		dis_new->dc					= ch->level / 2 + 10 + bonus;
	}

	LINK( dis_new, obj->first_disease, obj->last_disease, next, prev );

	pop_call();
	return;
}


/*
	Infect room with disease.
*/
void infect_room( CHAR_DATA *ch, ROOM_INDEX_DATA *room, int disease )
{
	DISEASE_DATA *dis, *dis_new;
	int bonus;

	push_call("infect_room(%p,%p,%p)",ch,room,disease);

	// disease does not stack, nor add to DC if already infected.
	for (dis = room->first_disease ; dis ; dis = dis->next)
	{
		if (dis->type == disease)
		{
			pop_call();
			return;
		}
	}

	ALLOCMEM(dis_new, DISEASE_DATA, 1);

	dis_new->type   	    = disease;
	dis_new->incubation   = number_range(1, disease_table[disease].incubation);
	dis_new->saves			  = 0;
	
	// calc actual DC of char inflicted disease based on level and stat
	if (ch != NULL)
	{
		if (get_curr_con(ch) < 0)
			bonus = stat_bonus(TRUE, ch, APPLY_CHA);
		else
			bonus = stat_bonus(TRUE, ch, APPLY_CON);
		
		dis_new->dc					= ch->level / 2 + 10 + bonus;
	}

	LINK( dis_new, room->first_disease, room->last_disease, next, prev );

	pop_call();
	return;
}


/*
	Remove a disease from a char.
*/
void disease_from_char( CHAR_DATA *ch, DISEASE_DATA *dis )
{
	push_call("disease_from_char(%p,%p)",ch,dis);

	if ((dis->prev == NULL && dis != ch->first_disease)
	||  (dis->next == NULL && dis != ch->last_disease))
	{
		log_printf("UNLINK ERROR disease_from_char: disease not found on %s", ch->name);
		dump_stack();
		pop_call();
		return;
	}

	UNLINK(dis, ch->first_disease, ch->last_disease, next, prev);
	FREEMEM( dis );

	pop_call();
	return;
}

/*
	Remove a disease from an obj.
*/
void disease_from_obj( OBJ_DATA *obj, DISEASE_DATA *dis )
{
	push_call("disease_from_obj(%p,%p)",obj,dis);

	UNLINK(dis, obj->first_disease, obj->last_disease, next, prev);
	FREEMEM(dis);

	pop_call();
	return;
}

/*
	Remove a disease from a room.
*/
void disease_from_room( ROOM_INDEX_DATA *room, DISEASE_DATA *dis )
{
	push_call("disease_from_room(%p,%p)",room,dis);

	UNLINK(dis, room->first_disease, room->last_disease, next, prev);
	FREEMEM(dis);

	pop_call();
	return;
}


/*
	Lookup a poison by name.
*/
int lookup_poison( char *name )
{
	int poison;

	push_call("lookup_poison(%p)",name);

	for (poison = 0 ; poison < MAX_POISON ; poison++)
	{
		if (tolower(name[0]) == poison_table[poison].name[0])
		{
			if (!strcmp(name, poison_table[poison].name))
			{
				pop_call();
				return poison;
			}
		}
	}
	pop_call();
	return -1;
}

/*
 * apply poison to object - Kregor
 */
void poison_to_obj(CHAR_DATA *ch, OBJ_DATA *obj, POISON_DATA *pd)
{
	POISON_DATA *pd_new;

	push_call("poison_to_obj(%p,%p,%p)",ch,obj,pd);

	if (obj->poison)
	{
		pop_call();
		return;
	}

	ALLOCMEM( pd_new, POISON_DATA, 1 );

	pd_new->type 							= pd->type;
	pd_new->dc 								= pd->dc;
	pd_new->constant_duration = pd->constant_duration;
	if (ch != NULL && !IS_NPC(ch))
		pd_new->poisoner 				= ch->pcdata->pvnum;
	else 
		pd_new->poisoner 				= -1;
	pd_new->owner							= pd->owner;

	obj->poison = pd_new;

	pop_call();
	return;
}


/*
 * apply poison from obj to char
 */
void obj_affect_poison(OBJ_DATA *obj, CHAR_DATA *victim)
{
	POISON_DATA *pd;
	CHAR_DATA *poisoner;

	push_call("obj_affect_poison(%p,%p)",obj,victim);

	if ((pd = obj->poison) == NULL)
	{
		pop_call();
		return;
	}
	
	if ((poisoner = get_char_pvnum(pd->poisoner)) == NULL)
	{
		poisoner = victim;
	}

	poison_to_char(poisoner, victim, pd);

	// ingestibles don't go away after poisoning
	switch (obj->item_type)
	{
		default:
			obj->poison = NULL;
			FREEMEM(pd);
			break;
		case ITEM_FOOD:
		case ITEM_DRINK_CON:
		case ITEM_FOUNTAIN:
			break;
	}
	pop_call();
	return;
}


/*
 * apply poison to char - Kregor
 */
void poison_to_char(CHAR_DATA *ch, CHAR_DATA *victim, POISON_DATA *pd)
{
	POISON_DATA *pd_old, *pd_new;
	AFFECT_DATA af;
	int location;

	push_call("poison_to_char(%p,%p,%p)",ch,victim,pd);

	/* loop thru old poisons, if type matches, only do initial dmg, not infection and increase DC */
	for (pd_old = victim->poison ; pd_old != NULL ; pd_old = pd_old->next)
	{
		if (pd_old->type == pd->type)
		{
			if (!fort_save(victim, NULL, pd->dc ? pd->dc : poison_table[pd->type].dc, gsn_poison_attack))
			{
				if ((location = poison_table[pd->type].inst_loc) > APPLY_NONE)
				{
					act( "{128}You don't feel so well...", victim, NULL, NULL, TO_CHAR);
					act( "{128}$n doesn't look so well.", victim, NULL, NULL, TO_ROOM);
		
					if (pd->type == POISON_SLEEP_POISON)
					{
						if (IS_AWAKE(victim) && !is_immune(victim, SDESC_SLEEP))
						{
							act("You feel woozy and collapse.", victim, NULL, NULL, TO_CHAR);
							act("$n collapses into a deep sleep.", victim, NULL, NULL, TO_ROOM);
							victim->position = POS_SLEEPING;
						}
					}
					if (location == APPLY_HIT)
					{
						int roll = dice(poison_table[pd->type].inst_nodice, poison_table[pd->type].inst_sizedice);
						damage(ch, victim, roll, gsn_poison_attack, NULL);
					}
					else
					{
						af.type      = gsn_ability_damage;
						af.duration  = -1;
						af.location  = location;
						af.modifier  = 0 - dice(poison_table[pd->type].inst_nodice, poison_table[pd->type].inst_sizedice);
						af.bittype   = AFFECT_TO_NONE;
						af.bitvector = AFF_NONE;
						af.level     = pd->dc ? pd->dc : poison_table[pd->type].dc;
						affect_join( ch, victim, &af );
					}
				}
				// DC increases for each exposure
				pd_old->dc = UMAX(pd->dc, pd_old->dc + 2);
				update_pos(victim,-1);
			}
			pop_call();
			return;
		}
	}
		
	ALLOCMEM( pd_new, POISON_DATA, 1 );

	pd_new->type 							= pd->type;
	pd_new->constant_duration = pd->constant_duration;
	if (ch != NULL && !IS_NPC(ch))
		pd_new->poisoner 				= ch->pcdata->pvnum;
	else 
		pd_new->poisoner 				= -1;
	if (pd->dc)
		pd_new->dc							= pd->dc;
	if (victim->poison != NULL)
		pd_new->next = victim->poison;
	victim->poison = pd_new;

	/* instant damage happens here */
	if (!fort_save(victim, NULL, pd->dc ? pd->dc : poison_table[pd->type].dc, gsn_poison_attack))
	{
		if ((location = poison_table[pd->type].inst_loc) > APPLY_NONE)
		{
			act( "{128}You don't feel so well...", victim, NULL, NULL, TO_CHAR);
			act( "{128}$n doesn't look so well.", victim, NULL, NULL, TO_ROOM);

			if (location == APPLY_HIT)
			{
				int roll = dice(poison_table[pd->type].inst_nodice, poison_table[pd->type].inst_sizedice);
				damage(ch, victim, roll, gsn_poison_attack, NULL);
			}
			else
			{
				af.type      = gsn_ability_damage;
				af.duration  = -1;
				af.location  = location;
				af.modifier  = 0 - dice(poison_table[pd->type].inst_nodice, poison_table[pd->type].inst_sizedice);
				af.bittype   = AFFECT_TO_NONE;
				af.bitvector = AFF_NONE;
				af.level     = pd->dc ? pd->dc : poison_table[pd->type].dc;
				affect_join( ch, victim, &af );
			}
		}
		update_pos(victim,-1);
	}
	pop_call();
	return;
}


/*
 * Move a char out of a room.
 */

void char_from_room( CHAR_DATA *ch )
{
	CHAR_DATA *rch, *rch_next;

	push_call("char_from_room(%p)",ch);

	if (ch->in_room == NULL)
	{
		log_printf( "char_from_room: ch->in_room == NULL");
		dump_stack();
		pop_call();
		return;
	}

	/*
		Takes care of fingered and half reconnected chars - Scandum
	*/

	if ((ch->prev_in_room == NULL && ch != ch->in_room->first_person)
	||  (ch->next_in_room == NULL && ch != ch->in_room->last_person))
	{
		pop_call();
		return;
	}

	/* strip poses */
	if (!IS_NPC(ch) && is_string(ch->pcdata->pose))
	{
		STRFREE (ch->pcdata->pose);
		ch->pcdata->pose = STRALLOC("");
		send_to_char( "Your pose has been removed.\n\r", ch );
	}
	
	if (mud->update_rch == ch)
	{
		mud->update_rch = ch->next_in_room;
	}

	if (IS_AFFECTED(ch, AFF2_CAMPING))
	{
		REMOVE_AFFECT(ch, AFF2_CAMPING);
	}

	if (ch->furniture)
	{
		user_from_furniture(ch);
	}

	if (ch->hitched)
	{
		user_from_cart(ch);
	}

	if (ch->mounting)
	{
		ch->mounting = NULL;
	}

	if (is_affected(ch, gsn_defensive_stance))
	{
		act("You leave your defensive stance.", ch, NULL, NULL, TO_CHAR);
		affect_strip(ch, gsn_defensive_stance);
		if (!learned(ch, gsn_tireless_defense))
		{
			int cost = get_max_move(ch) / 2;
			move_loss(ch, NULL, cost);
		}
	}

	/*
	 * This little bit of code allows for only one spot check
	 * against a hidden char per person in the room,
	 * instead of spamming spot checks every game loop - Kregor
	 */
	for (rch = ch->in_room->first_person ; rch ; rch = rch_next)
	{
		rch_next = rch->next_in_room;
		
		if (!IS_NPC(ch))
		{
			if (IS_NPC(rch))
			{
				ch->pcdata->found_vnum[rch->pIndexData->vnum] = 0;
			}
			else
			{
				ch->pcdata->found_pvnum[rch->pcdata->pvnum] = 0;
			}
		}
		
		if (!IS_NPC(rch))
		{
			if (IS_NPC(ch))
			{
				rch->pcdata->found_vnum[ch->pIndexData->vnum] = 0;
			}
			else
			{
				rch->pcdata->found_pvnum[ch->pcdata->pvnum] = 0;
			}
		}
	}

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

	ch->in_room->light = calc_room_light(ch, NULL, ch->in_room, FALSE);

	UNLINK(ch, ch->in_room->first_person, ch->in_room->last_person, next_in_room, prev_in_room);

	ch->in_room = NULL;

	pop_call();
	return;
}

/*
	Move a char into a room.
*/

void char_to_room( CHAR_DATA *ch, int vnum, bool forreal )
{
	ROOM_INDEX_DATA *pRoomIndex;
	ROOM_INDEX_DATA *to_room;
	EXIT_DATA *pExit;
	CHAR_DATA *rch, *rch_next;

	push_call("char_to_room(%p,%p)",ch,vnum);

	if ((pRoomIndex = get_room_index(vnum)) == NULL)
	{
		log_printf("char_to_room: room_index %d == NULL", vnum);
		dump_stack();
		pRoomIndex = room_index[ROOM_VNUM_TEMPLE];
	}

	if (ch->in_room)
	{
		char_from_room(ch);
	}

	ch->in_room = pRoomIndex;

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

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

	ch->in_room->light = calc_room_light(ch, NULL, ch->in_room, TRUE);

	if (forreal)
	{
		if ((pExit = pRoomIndex->exit[DIR_DOWN]) != NULL && !IS_FLYING(ch))
		{
			if ((IS_SET(pExit->exit_info, EX_CLIMB) && IS_FALLING(ch))
			|| pRoomIndex->sector_type == SECT_AIR
			|| IS_SET(pRoomIndex->room_flags, ROOM_NOFLOOR))
			{
				if ((to_room = room_index[pExit->to_room]) == NULL)
				{
					log_build_printf(ch->in_room->vnum, "char_to_room: room falls to NULL room!");
					pop_call();
					return;
				}
	
				/* char may catch their fall against a surface with dc + 20 climb check */
				if (IS_SET(pExit->exit_info, EX_CLIMB) && climb_check(ch, NULL, climb_roll(ch), pExit->climb_dc+20) > 0)
				{
					act( "$n catches $s fall.", ch, NULL, NULL, TO_ROOM);
					act( "You catch your fall.", ch, NULL, NULL, TO_CHAR);
					REMOVE_AFFECT(ch, AFF2_FALLING);
				}
				else if (CAN_FLY(ch))
				{
					do_fly(ch, "");
				}
				else
				{				
					if (!IS_FALLING(ch))
					{
						SET_AFFECT(ch, AFF2_FALLING);
					}
						
					// feather fall is an immediate action casting, so make it reactive - Kregor
					if (!IS_AFFECTED(ch, AFF_FEATHER_FALL))
					{
						if (prepared(ch, gsn_feather_fall) != -1 || spontaneous_cast(ch, gsn_feather_fall) != -1 || race_skill(ch, gsn_feather_fall))
						{
							// yes, this bypasses the mana check, big deal its one mana point!
							((*skill_table[gsn_feather_fall].spell_fun) ( gsn_feather_fall, get_caster_level(ch, gsn_feather_fall), ch, ch, 0 ));
						}
					}
					crash_protect++;
					if (pExit->fall_dist)
						fall_distance += pExit->fall_dist;
					else
						fall_distance += 20;
					
					if (crash_protect >= 10)
					{
						pop_call();
						return;
					}
					for (rch = room_index[pExit->to_room]->first_person ; rch != NULL ; rch = rch_next)
					{
						rch_next = rch->next_in_room;
	
						act( "$t falls in from above.", ch, PERS(ch, rch), rch, TO_VICT);
					}
					if (fall_distance)
						act( "$n falls downward.", ch, NULL, NULL, TO_ROOM);
					else
						act( "$n tumbles downward as $e enters.", ch, NULL, NULL, TO_ROOM);
			
					char_from_room( ch );
					char_to_room( ch, pExit->to_room, TRUE);	
				}
				do_look(ch, "");
			}
		}
		/* repurposed dispel magic code for NO_MAGIC rooms - Kregor */	
		if (IS_SET(ch->in_room->room_flags, ROOM_NO_MAGIC))
		{
			AFFECT_DATA *paf, *paf_next;
			OBJ_DATA *obj;
			int paf_type;
			for ( paf_type = 0, paf = ch->first_affect; paf != NULL; paf = paf_next )
			{
				paf_next = paf->next;
	
				if (!is_spell(paf->type) && !IS_SET(skill_table[paf->type].flags, SF_SUPERNATURAL|SF_SPELL_LIKE))
				{
					continue;
				}
				if ((paf_type == 0 || paf_type != paf->type) && paf->duration >= 0)
				{
					if (skill_table[paf->type].msg_off)
					{
						act( skill_table[paf->type].msg_off, ch, NULL, NULL, TO_CHAR);
					}
					if (skill_table[paf->type].msg_off_room)
					{
						act( skill_table[paf->type].msg_off_room, ch, NULL, NULL, TO_ROOM);
					}
				}
				paf_type = paf->type;
				affect_from_char(ch, paf);
			}
			for (obj = ch->first_carrying; obj != NULL ; obj = obj->next_content)
			{
				for ( paf = obj->first_affect; paf != NULL; paf = paf_next )
				{
					paf_next = paf->next;
	
					if (!is_spell(paf->type))
					{
						continue;
					}
					if ((paf_type == 0 || paf_type != paf->type) && paf->duration >= 0)
					{
						if (skill_table[paf->type].msg_obj_off)
						{
							act( skill_table[paf->type].msg_obj_off, ch, obj, NULL, TO_CHAR);
							if (ch->in_room)
								act( skill_table[paf->type].msg_obj_off, ch, obj, NULL, TO_ROOM);
						}
					}
					paf_type = paf->type;
					affect_from_obj(obj, paf);
				}
			}
			if (paf_type != 0)
			{
				send_to_char( "Your enchantments are stripped from you as you enter.\n\r", ch );
			}
		}

		/*
		 * This little bit of code allows for only one spot check
		 * against a hidden char per person in the room,
		 * instead of spamming spot checks every game loop - Kregor
		 */
		for (rch = ch->in_room->first_person ; rch ; rch = rch_next)
		{
			rch_next = rch->next_in_room;
			
			if (!IS_NPC(ch))
			{
				if (IS_NPC(rch))
				{
					ch->pcdata->found_vnum[rch->pIndexData->vnum] = 0;
				}
				else
				{
					ch->pcdata->found_pvnum[rch->pcdata->pvnum] = 0;
				}
			}
			
			if (!IS_NPC(rch))
			{
				if (IS_NPC(ch))
				{
					rch->pcdata->found_vnum[ch->pIndexData->vnum] = 0;
				}
				else
				{
					rch->pcdata->found_pvnum[ch->pcdata->pvnum] = 0;
				}
			}
		}
	}
		
	if (fall_distance)
	{
		wiz_printf("char_to_room: falling char %s falls %d feet.", ch->name, fall_distance);

		int numdice = 0;

		if (learned(ch, gsn_slow_fall))
		{
			fall_distance = UMAX(0, fall_distance - (multi_class_level(ch, gsn_slow_fall) * 10 / 2));
		}
		int tumble = tumble_roll(ch);

		if (tumble_check(ch, NULL, tumble, 60))
			fall_distance = UMAX(0, fall_distance - 40);
		else if (tumble_check(ch, NULL, tumble, 45))
			fall_distance = UMAX(0, fall_distance - 30);
		else if (tumble_check(ch, NULL, tumble, 30))
			fall_distance = UMAX(0, fall_distance - 20);
		else if (tumble_check(ch, NULL, tumble, 15))
			fall_distance = UMAX(0, fall_distance - 10);
		numdice = fall_distance / 10;

		wiz_printf("char_to_room: falling char %s takes %d dice damage.", ch->name, numdice);

		if (numdice > 0)
		{
			if (IS_AFFECTED(ch, AFF_FEATHER_FALL))
			{
				act( "You land lightly upon he ground.", ch, NULL, NULL, TO_CHAR);
				act( "$n lands lightly upon the ground.", ch, NULL, NULL, TO_ROOM);
			}
			else
			{
				act( "{118}You fall to the ground with a THUD!", ch, NULL, NULL, TO_CHAR);
				act( "$n {118}hits the ground with a THUD!", ch, NULL, NULL, TO_ROOM);
				damage(ch, ch, dice(numdice, 6), TYPE_NOFIGHT, NULL);
			}
		}
	}

	REMOVE_AFFECT(ch, AFF2_FALLING);
	crash_protect = 0;
	fall_distance = 0;

	if (is_desc_valid(ch))
	{
		if (CH(ch->desc)->pcdata->vt100 == 1)
		{
			vt100prompt( ch );
		}
	}

	pop_call();
	return;
}

/*
 * Function sets char falling with char_to_room function - Kregor
 */
void set_falling( CHAR_DATA *ch, int dist )
{
	int reach;

	push_call("set_falling(%p,%p)",ch,dist);
	
	reach = (get_height(ch) + 12) / 12;

	SET_AFFECT(ch, AFF2_FALLING);

	if (dist < reach)
		fall_distance = 0;
	else
		fall_distance = UMAX(0, dist - reach);
	
	pop_call();
	return;
}

/*
	Give an obj to a char.
*/

void obj_to_char( OBJ_DATA *obj, CHAR_DATA *ch )
{
	OBJ_DATA *otmp;

	push_call("obj_to_char(%p,%p)",obj,ch);

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

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

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

	if (!IS_NPC(ch) || ch->pIndexData->pShop == NULL || ch->first_carrying == NULL)
	{
		LINK(obj, ch->first_carrying, ch->last_carrying, next_content, prev_content);
	}
	else	/* Sort by cost for shop keepers */
	{
		for (otmp = ch->last_carrying ; otmp ; otmp = otmp->prev_content)
		{
			if (obj->cost >= otmp->cost)
			{
				INSERT_RIGHT(obj, otmp, ch->last_carrying, next_content, prev_content);
				break;
			}
		}
		if (otmp == NULL)
		{
			INSERT_LEFT(obj, ch->first_carrying, ch->first_carrying, next_content, prev_content);
		}
	}
	obj->carried_by		= ch;
	obj->sac_timer		= 0;
	ch->carry_weight	= get_carry_w(ch);
	ch->carry_number++;

	if (!IS_NPC(ch))
	{
		obj_set_to_char( ch, obj );
	}
	pop_call();
	return;
}


/*
 Auto engrave stuff
*/
void obj_set_to_char( CHAR_DATA *ch, OBJ_DATA *obj )
{
	push_call("obj_set_to_char(%p,%p)",ch,obj);

	if (IS_SET(obj->pIndexData->extra_flags, ITEM_AUTO_ENGRAVE)
	&&  !obj->owned_by && ch->level < MAX_LEVEL)
	{
		obj->owned_by = ch->pcdata->pvnum;
	}
	pop_call();
	return;
}

/*
	Take an obj from its character.
*/

void obj_from_char( OBJ_DATA *obj )
{
	CHAR_DATA *ch;

	push_call("obj_from_char(%p)",obj);

	if (obj->in_obj)
	{
		log_printf("obj_from_char: obj->in_obj");
		obj_from_obj(obj);
		pop_call();
		return;
	}

	if (obj->in_room)
	{
		log_printf("obj_from_char: obj->in_room");
		obj_from_room( obj );
		pop_call();
		return;
	}

	if ((ch = obj->carried_by) == NULL)
	{
		log_printf("obj_from_char: obj->carried_by == NULL");
		pop_call();
		return;
	}

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

	if ((obj->prev_content == NULL && obj != ch->first_carrying)
	||  (obj->next_content == NULL && obj != ch->last_carrying))
	{
		log_printf("UNLINK ERROR object %s not carried by %s", obj->name, ch->name);
		dump_stack();
	}
	else
	{
		UNLINK( obj, ch->first_carrying, ch->last_carrying, next_content, prev_content );
	}

	ch->carry_number--;
	ch->carry_weight = get_carry_w(ch);

	if (obj->reset)
	{
		if (obj->first_content)
		{
			OBJ_DATA *c_obj;

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

		if (obj->reset->obj != obj)
		{
			log_printf("obj_from_char: obj->reset->obj != obj");
		}
		else
		{
			obj->reset->obj = NULL;
			obj->reset = NULL;
		}
	}

	obj->carried_by = NULL;

	pop_call();
	return;
}


/*
	Find a piece of eq on a character.
	Added layer condition. Only counts WEAR_ARMOR location - Kregor
*/
OBJ_DATA *get_eq_char( CHAR_DATA *ch, int iWear )
{
	OBJ_DATA *obj;

	push_call("get_eq_char(%p,%p)",ch,iWear);

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (get_layer(obj) != LAYER_ARMOR)
			continue;

		if (WEAR_LOC(obj, iWear))
		{
			pop_call();
			return obj;
		}
	}
	pop_call();
	return NULL;
}

/*
 * Is the character wearing metal?
 * Use for druids and the heat/chill metal spells - Kregor
 */
bool wears_metal( CHAR_DATA *ch, bool fWeapon )
{
	OBJ_DATA *obj;

	push_call("wears_metal(%p,%p)",ch,fWeapon);

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (!IS_WORN(obj))
			continue;

		if (IS_OBJ_TYPE(obj, ITEM_ARMOR) || (fWeapon && IS_OBJ_TYPE(obj, ITEM_WEAPON)))
		{
			if (obj->material && material_table[obj->material].parent == MATERIAL_TYPE_METAL)
			{
				pop_call();
				return TRUE;
			}
		}
	}
	pop_call();
	return FALSE;
}

/*
 * how many users on furniture
 */
int count_users( OBJ_DATA * obj )
{
	CHAR_DATA *fch;
	int count = 0;

	push_call("count_users(%p)",obj);

	if (obj->in_room == NULL)
	{
		log_printf("count_users: obj->in_room == NULL");
		pop_call();
		return -1;
	}

	for (fch = obj->in_room->first_person ; fch ; fch = fch->next_in_room)
	{
		if (fch->furniture == obj)
		{
			count++;
		}
	}
	pop_call();
	return count;
}


void check_zap( CHAR_DATA *ch, bool fDisplay )
{
	OBJ_DATA *obj;
	int alignment = 0;

	push_call("check_zap(%p,%p)",ch,fDisplay);

	if (!IS_NEUTRAL(ch))
	{
		SET_BIT(alignment, ITEM_NEUTRAL);
	}
	if (IS_GOOD(ch))
	{
		SET_BIT(alignment, ITEM_EVIL);
	}
	if (IS_EVIL(ch))
	{
		SET_BIT(alignment, ITEM_GOOD);
	}
	if (IS_LAWFUL(ch))
	{
		SET_BIT(alignment, ITEM_CHAOTIC);
	}
	if (IS_CHAOTIC(ch))
	{
		SET_BIT(alignment, ITEM_LAWFUL);
	}
	if (!IS_UNCONCERNED(ch))
	{
		SET_BIT(alignment, ITEM_UNCONCERNED);
	}

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (IS_WORN(obj) && IS_SET(obj->extra_flags, alignment))
		{
			if (fDisplay)
			{
				act( "You are zapped by $p.", ch, obj, NULL, TO_CHAR);
				act( "$n is zapped by $p.",   ch, obj, NULL, TO_ROOM);
			}
			unequip_char(ch, obj, FALSE);
		}
	}
	pop_call();
	return;
}

/*
	Equip a char with an obj.
*/

void equip_char( CHAR_DATA *ch, OBJ_DATA *obj, int iWear )
{
	AFFECT_DATA *paf;

	push_call("equip_char(%p,%p,%p)",ch,obj,iWear);

	if (obj->wear_loc > 0)
	{
		log_printf("equip_char: obj->wear_loc != WEAR_NONE");
		dump_stack();
	}

	if (iWear < 0 || iWear >= MAX_WEAR)
	{
		log_printf("Equip: obj: %d iWear: %d", obj->pIndexData->vnum, iWear);
		pop_call();
		return;
	}

	if (IS_SET(obj->extra_flags, ITEM_CONCEALED))
	{
		REMOVE_BIT(obj->extra_flags, ITEM_CONCEALED);
		act ("You produce $p from its concealment.", ch, obj, NULL, TO_CHAR);
		act ("$n produces $p from its concealment.", ch, obj, NULL, TO_ROOM);
	}

	if (!IS_NPC(ch))
	{
		if ((IS_OBJ_STAT(obj, ITEM_GOOD)    && IS_EVIL(ch))
		||  (IS_OBJ_STAT(obj, ITEM_EVIL)    && IS_GOOD(ch))
		||  (IS_OBJ_STAT(obj, ITEM_CHAOTIC)  && IS_LAWFUL(ch))
		||  (IS_OBJ_STAT(obj, ITEM_LAWFUL) && IS_CHAOTIC(ch))
		||  (IS_OBJ_STAT(obj, ITEM_UNCONCERNED) && !IS_UNCONCERNED(ch))
		||  (IS_OBJ_STAT(obj, ITEM_NEUTRAL) && !IS_NEUTRAL(ch)))
		{
			act( "You are zapped by $p.", ch, obj, NULL, TO_CHAR );
			act( "$n is zapped by $p.",  ch, obj, NULL, TO_ROOM );
			pop_call();
			return;
		}
	}

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

	obj->wear_loc = iWear;
	
	ch->armor = apply_ac(ch);
	ch->armor_type = apply_armor_type(ch);
	apply_dex_max(ch);
	
	for (paf = obj->first_affect ; paf ; paf = paf->next)
	{
		affect_modify(ch, paf, TRUE);
	}
	
	if (IS_OBJ_TYPE(obj, ITEM_LIGHT))
	{
		if (obj->value[3] && !IS_BURNING(obj))
		{
			SET_BIT(obj->extra_flags, ITEM_BURNING);
			act ("You ignite $p.", ch, obj, NULL, TO_CHAR);
			act ("$n ignites $p.", ch, obj, NULL, TO_ROOM);
		}
	}

	if (ch->in_room)
		ch->in_room->light = calc_room_light(ch, NULL, ch->in_room, TRUE);

	if (!IS_NPC(ch))
	{
		oprog_wear_trigger(ch, obj);
	}
	pop_call();
	return;
}

/*
	Unequip a char with an obj.
*/

void unequip_char( CHAR_DATA *ch, OBJ_DATA *obj, bool fDisplay )
{
	AFFECT_DATA *paf, *paf_next;

	push_call("unequip_char(%p,%p)",ch,obj);

	if (obj->wear_loc == WEAR_NONE)
	{
		log_printf("unequip_char: obj->wear_loc == WEAR_NONE");
		dump_stack();
		pop_call();
		return;
	}

	if (IS_SET(obj->extra_flags, ITEM_CONCEALED))
	{
		if (fDisplay)
		{
			act ("You produce $p from its concealment.", ch, obj, NULL, TO_CHAR);
			act ("$n produces $p from its concealment.", ch, obj, NULL, TO_ROOM);
		}
		REMOVE_BIT(obj->extra_flags, ITEM_CONCEALED);
	}

	if (obj->item_type == ITEM_WEAPON)
		ch->reloaded = FALSE;

	if (ARMOR_FLAG(obj, ARMORFLAG_HOODED))
	{
		if (IS_AFFECTED(ch, AFF_HOODED))
		{
			if (fDisplay)
			{
				act( "You remove your hood along with $p.", ch, obj, NULL, TO_CHAR);
				act( "$n removes $s hood along with $p.", ch, obj, NULL, TO_ROOM);
			}
			REMOVE_BIT(ch->affected_by, AFF_HOODED);
		}
	}

	obj->wear_loc = WEAR_NONE;

	if (IS_OBJ_TYPE(obj, ITEM_LIGHT))
	{
		if (obj->value[3] && IS_BURNING(obj))
		{
			REMOVE_BIT(obj->extra_flags, ITEM_BURNING);
		}
	}

	if (ch->in_room)
		ch->in_room->light = calc_room_light(ch, NULL, ch->in_room, TRUE);

	ch->armor = apply_ac(ch);
	ch->armor_type = apply_armor_type(ch);
	apply_dex_max(ch);
	
	for (paf = obj->first_affect ; paf ; paf = paf->next )
	{
		affect_modify(ch, paf, FALSE);
	}

	/* strip personal only object affects from obj */
	for (paf = obj->first_affect ; paf ; paf = paf_next)
	{
		paf_next = paf->next;

		if (skill_table[paf->type].target == TAR_OBJ_WIELD)
		{
			if (fDisplay && skill_table[paf->type].msg_obj_off)
			{
				act( skill_table[paf->type].msg_obj_off, ch, obj, NULL, TO_CHAR);
				if (ch->in_room)
					act( skill_table[paf->type].msg_obj_off, ch, obj, NULL, TO_ROOM);
			}
			affect_from_obj(obj, paf);
		}
	}
	if (!IS_NPC(ch) && fDisplay)
	{
		oprog_remove_trigger(ch, obj);
	}
	pop_call();
	return;
}


/*
	Move an obj out of a room.
*/

void obj_from_room( OBJ_DATA *obj )
{
	CHAR_DATA *rch;

	push_call("obj_from_room(%p)",obj);

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

	for (rch = obj->in_room->first_person ; rch ; rch = rch->next_in_room)
	{
		if (rch->furniture == obj)
		{
			user_from_furniture(rch);
		}
		if (rch->hitched == obj)
		{
			user_from_cart(rch);
		}
	}

	if ((obj->prev_content == NULL && obj != obj->in_room->first_content)
	||  (obj->next_content == NULL && obj != obj->in_room->last_content))
	{
		log_printf("UNLINK ERROR obj_from_room: object %s not found.", obj->name);
		dump_stack();
	}
	else
	{
		UNLINK(obj, obj->in_room->first_content, obj->in_room->last_content, next_content, prev_content);
	}


	if (obj->reset != NULL)
	{
		if (obj->first_content != NULL)
		{
			OBJ_DATA *c_obj;

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

		if (obj->reset->obj != obj)
		{
			log_printf("obj_from_room: obj->reset->obj != obj");
		}
		else
		{
			obj->reset->obj = NULL;
			obj->reset = NULL;
		}
	}
	obj->in_room->content_count--;

	obj->in_room->light = calc_room_light(NULL, obj, obj->in_room, FALSE);

	obj->in_room = NULL;

	pop_call();
	return;
}

/*
	Move an obj into a room.
	Falling object support added from gph2076@crosswinds.net
*/
void obj_to_room( OBJ_DATA *obj, int vnum)
{
	ROOM_INDEX_DATA *pRoomIndex;
	EXIT_DATA *pExit;

	push_call("obj_to_room(%p,%p)",obj, vnum);

	if (obj->in_room)
	{
		obj_from_room(obj);
	}

	if (obj->in_obj)
	{
		obj_from_obj(obj);
	}

	if (obj->carried_by)
	{
		obj_from_char(obj);
	}

	if ((pRoomIndex = get_room_index(vnum)) == NULL)
	{
		log_printf("obj_to_room: index %d == NULL", vnum);
		pop_call();
		return;
	}

	if (pRoomIndex != room_index[ROOM_VNUM_JUNK])
	{
		if (pRoomIndex->content_count >= MAX_OBJECTS_IN_ROOM)
		{
			junk_obj(obj);
			pop_call();
			return;
		}
	}
	LINK(obj, pRoomIndex->first_content, pRoomIndex->last_content, next_content, prev_content);

	obj->in_room = pRoomIndex;

	obj->in_room->light = calc_room_light(NULL, obj, obj->in_room, TRUE);

	pRoomIndex->content_count++;

	if ((pExit = pRoomIndex->exit[DIR_DOWN]) != NULL)
	{
		if (room_index[pExit->to_room] == NULL)
		{
			log_build_printf(obj->in_room->vnum, "obj_to_room: room falls to NULL room!");
			pop_call();
			return;
		}
		if (pRoomIndex->sector_type == SECT_AIR
		|| IS_SET(pExit->exit_info, EX_CLIMB)
		|| IS_SET(obj->in_room->room_flags, ROOM_NOFLOOR))
		{
			if (obj->reset)
			{
				pop_call();
				return;
			}
			crash_protect++;
	
			if (crash_protect >= 10)
			{
				pop_call();
				return;
			}
			
			act( "$p falls in from above.", room_index[pExit->to_room]->first_person, obj, NULL, TO_ROOM);
			act( "$p falls in from above.", room_index[pExit->to_room]->first_person, obj, NULL, TO_CHAR);
			act( "$t falls downward.", pRoomIndex->first_person, obj->short_descr, NULL, TO_ROOM);
			act( "$t falls downward.", pRoomIndex->first_person, obj->short_descr, NULL, TO_CHAR);
	
			obj_from_room( obj );
			obj_to_room( obj, pExit->to_room);	
		}
		crash_protect = 0;
	}	
	pop_call();
	return;
}

/*
	Move an object into an object.
*/

void obj_to_obj( OBJ_DATA *obj, OBJ_DATA *obj_to )
{
	push_call("obj_to_obj(%p,%p)",obj,obj_to);

	if (obj->in_room)
	{
		obj_from_room(obj);
	}

	if (obj->in_obj)
	{
		obj_from_obj(obj);
	}

	if (obj->carried_by)
	{
		obj_from_char(obj);
	}

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

	obj_to->content_weight += get_obj_weight(obj);
	
	obj->in_obj = obj_to;

	if (obj_to->carried_by != NULL)
	{
		obj_to->carried_by->carry_weight = get_carry_w(obj_to->carried_by);
	}

	pop_call();
	return;
}

/*
	Move an object out of an object.
*/

void obj_from_obj( OBJ_DATA *obj )
{
	push_call("obj_from_obj(%p)",obj);

	if (obj->in_obj == NULL)
	{
		log_printf("obj_from_obj: obj->in_obj == null");
		dump_stack();
		pop_call();
		return;
	}

	if ((obj->prev_content == NULL && obj != obj->in_obj->first_content)
	||  (obj->next_content == NULL && obj != obj->in_obj->last_content))
	{
		log_printf("UNLINK ERROR obj_from_obj: object %s not found", obj->name);
		dump_stack();
	}
	else
	{
		UNLINK(obj, obj->in_obj->first_content, obj->in_obj->last_content, next_content, prev_content);
	}

	obj->in_obj->content_weight -= get_obj_weight(obj);

	if (obj->in_obj->carried_by)
	{
		obj->in_obj->carried_by->carry_weight = get_carry_w(obj->in_obj->carried_by);
	}

	obj->in_obj = NULL;

	if (obj->reset != NULL)
	{
		if (obj->reset->obj != obj)
		{
			log_printf("obj_from_obj: obj->reset->obj != obj");
		}
		else
		{
			obj->reset->obj = NULL;
			obj->reset = NULL;
		}
	}
	pop_call();
	return;
}


/*
 * make char use a furniture obj.
 */
void user_to_furniture( CHAR_DATA *ch, OBJ_DATA *obj )
{
	push_call("user_to_furniture(%p,%p)",ch,obj);

	if (obj == NULL)
	{
		pop_call();
		return;
	}
	if (ch->furniture)
	{
		user_from_furniture(ch);
	}

	obj->content_weight += ch->weight * 10 + ch->carry_weight;
	
	/* make use progs trigger from using furniture, make it interesting ;) */
	oprog_use_trigger(ch, obj);

	ch->furniture = obj;
	
	pop_call();
	return;
}

/*
 * remove char from furniture obj.
 */
void user_from_furniture( CHAR_DATA *ch )
{
	push_call("user_from_furniture(%p)",ch);

	if (ch->furniture == NULL)
	{
		pop_call();
		return;
	}

	ch->furniture->content_weight -= ch->weight * 10 + ch->carry_weight;

	ch->furniture = NULL;

	pop_call();
	return;
}


/*
	Extract an obj from the world.
*/

void extract_obj( OBJ_DATA *obj )
{
	CHAR_DATA *rch;

	push_call("extract_obj(%p)",obj);

	if (obj->in_room)
	{
		for (rch = obj->in_room->first_person ; rch ; rch = rch->next_in_room)
		{
			if (rch->furniture == obj)
			{
				user_from_furniture(rch);
			}
			if (rch->hitched == obj)
			{
				user_from_cart(rch);
			}
		}
	}

	if (obj->owned_by
	&& (rch = get_char_pvnum(obj->owned_by)) != NULL
	&&  rch->pcdata->corpse == obj && rch->in_room)
	{
		rch->pcdata->corpse = find_char_corpse (rch, TRUE);
		save_char_obj(rch, NORMAL_SAVE);
	}

	if (obj->owned_by
	&& (rch = get_char_pvnum(obj->owned_by)) != NULL
	&&  rch->pcdata->cart == obj && rch->in_room)
	{
		rch->pcdata->cart = NULL;
		save_char_obj(rch, NORMAL_SAVE);
	}

	if (obj->in_room)
	{
		obj_from_room(obj);
	}

	if (obj->in_obj)
	{
		obj_from_obj(obj);
	}

	if (obj->carried_by)
	{
		obj_from_char(obj);
	}

	if (obj->next_content || obj->prev_content)
	{
		log_printf("extract_obj: object has next or prev content");
	}

	while (obj->first_content)
	{
		extract_obj(obj->first_content);
	}

	if (mud->update_obj == obj)
	{
		mud->update_obj = obj->next;
	}

	rem_obj_ref_hash(obj);

	if ((obj->prev == NULL && obj != mud->f_obj)
	||  (obj->next == NULL && obj != mud->l_obj))
	{
		log_printf("UNLINK ERROR object %s not in mud object list.", obj->name);
		dump_stack();
	}
	else
	{
		UNLINK(obj, mud->f_obj, mud->l_obj, next, prev);
		UNLINK(obj, obj->pIndexData->first_instance, obj->pIndexData->last_instance, next_instance, prev_instance);
	}

	{
		AFFECT_DATA *paf;

		while ((paf = obj->first_affect) != NULL)
		{
			UNLINK(paf, obj->first_affect, obj->last_affect, next, prev);
			FREEMEM(paf);
		}
	}

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

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

	STRFREE( obj->name        );
	STRFREE( obj->description );
	STRFREE( obj->short_descr );
	STRFREE( obj->long_descr );

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

	if (obj->pIndexData != NULL)
	{
		--mud->total_obj;
		--obj->pIndexData->total_objects;
	}

	FREEMEM( obj );

	pop_call();
	return;
}

void junk_obj( OBJ_DATA *obj )
{
	JUNK_DATA *junk;

	push_call("junk_obj(%p)",obj);

	if (obj->in_room && obj->in_room->vnum == ROOM_VNUM_JUNK && IS_SET(obj->extra_flags, ITEM_NOT_VALID))
	{
		pop_call();
		return;
	}
	SET_BIT(obj->extra_flags, ITEM_NOT_VALID);
	obj_to_room(obj, ROOM_VNUM_JUNK);

	ALLOCMEM(junk, JUNK_DATA, 1);
	junk->obj = obj;
	LINK(junk, mud->f_junk, mud->l_junk, next, prev);

	pop_call();
	return;
}


void junk_mob( CHAR_DATA *ch )
{
	JUNK_DATA *junk;

	push_call("junk_mob(%p)",ch);

	if (!IS_NPC(ch))
	{
		log_printf("junk_mob: trying to junk PC");
		dump_stack();

		pop_call();
		return;
	}

	if (IS_SET(ch->act, ACT_WILL_DIE) && ch->in_room->vnum == ROOM_VNUM_JUNK)
	{
		pop_call();
		return;
	}

	SET_BIT(ch->act, ACT_WILL_DIE);

	char_to_room(ch, ROOM_VNUM_JUNK, TRUE);

	ALLOCMEM(junk, JUNK_DATA, 1);
	junk->mob = ch;
	LINK(junk, mud->f_junk, mud->l_junk, next, prev);

	pop_call();
	return;
}

/*
	Extract a char from the world.
*/

void extract_char( CHAR_DATA *ch )
{
	PLAYER_GAME *gpl;
	ACCOUNT_DATA *acct;

	push_call("extract_char(%p)",ch);
	
	if (ch == NULL || ch->name == NULL)
	{
		log_printf("found nullified data in extract_char.");
		dump_stack();
		pop_call();
		return;
	}

	if (ch == supermob)
	{
		log_printf("extract_char - trying to extract SuperMob!");
		dump_stack();
		pop_call();
		return;
	}
	
	if (ch->desc && ch->desc->original)
	{
		do_return(ch, NULL);
	}

	die_follower(ch);

	if (ch->tracked_by)
	{
		stop_tracking(ch->tracked_by);
	}
	if (ch->grappling)
	{
		stop_grapple(ch->grappling);
	}
	if (ch->grappled_by)
	{
		stop_grapple(ch->grappled_by);
	}

	if (!IS_NPC(ch))
	{
		if (ch->pcdata->tracking)
		{
			stop_tracking(ch);
		}
		if (ch->pcdata->shadowing)
		{
			stop_shadow(ch);
		}
		if (ch->pcdata->shadowed_by)
		{
			stop_shadow(ch->pcdata->shadowed_by);
		}
		check_bad_desc(ch);
	}

	if (in_combat(ch))
		char_from_combat(ch);

	if (mud->update_rch == ch)
	{
		mud->update_rch = ch->next_in_room;
	}

	if (mud->update_wch == ch)
	{
		mud->update_wch = ch->next;
	}

	if (mud->update_ich == ch)
	{
		mud->update_ich = ch->next_instance;
	}

	if (ch->in_room)
	{
		char_from_room( ch );
	}

	if (IS_NPC(ch))
	{
		--ch->pIndexData->total_mobiles;
		--mud->total_mob;

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

	}

	if (!IS_NPC(ch) && is_desc_valid(ch))
	{
		for (gpl = mud->f_player ; gpl ; gpl = gpl->next)
		{
			if (is_desc_valid(gpl->ch) && gpl->ch->desc->snoop_by == ch->desc)
			{
				gpl->ch->desc->snoop_by = NULL;
			}
		}
	}

	// clear CH entry from account - Kregor
	if (!IS_NPC(ch))
	{
		for (acct = mud->f_acct ; acct ; acct = acct->next)
		{
			if (acct->ch && acct->ch == ch)
			{
				acct->ch = NULL;
				break;
			}
		}
	}

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

	if (!IS_NPC(ch) && ch->pcdata->cart)
	{
		extract_obj(ch->pcdata->cart);
	}

	if ((ch->prev == NULL && ch != mud->f_char)
	||  (ch->next == NULL && ch != mud->l_char))
	{
		if (IS_NPC(ch))
		{
			log_printf( "UNLINK ERROR character %s not found in char_list.", ch->name);
			dump_stack();
		}
	}
	else
	{
		UNLINK(ch, mud->f_char, mud->l_char, next, prev);
	}

	if (!IS_NPC(ch))
	{
		sub_player(ch);
	}
	else
	{
		UNLINK(ch, ch->pIndexData->first_instance, ch->pIndexData->last_instance, next_instance, prev_instance);
	}
	
	free_char( ch );

	pop_call();
	return;
}


CHAR_DATA *get_char_room( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *rch;
	int number;
	int count;

	push_call("get_char_room(%p,%p)",ch,argument);

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

	if (!is_string(argument))
	{
		pop_call();
		return NULL;
	}
	
	if (!strcasecmp(arg, "self"))
	{
		pop_call();
		return ch;
	}

	for (rch = ch->in_room->first_person ; rch != NULL ; rch = rch->next_in_room)
	{
		if (IS_NPC(rch) && !is_name(arg, rch->name))
		{
			continue;
		}
		if (!IS_NPC(rch) && !is_name(arg, PERS(rch,ch)))
		{
			continue;
		}
		if (!can_see(ch, rch) && (!IS_NPC(ch) || !IS_IMMORTAL(ch)))
		{
			continue;
		}
		if (++count != number)
		{
			continue;
		}
		pop_call();
		return rch;
	}
	count = 0;

	for (rch = ch->in_room->first_person ; rch != NULL ; rch = rch->next_in_room)
	{
		if (IS_NPC(rch) && !is_multi_name_list_short(arg, rch->name))
		{
			continue;
		}
		if (!IS_NPC(rch) && !is_multi_name_list_short(arg, PERS(rch,ch)))
		{
			continue;
		}
		if (!can_see(ch, rch) || ++count != number)
		{
			continue;
		}
		pop_call();
		return rch;
	}
	pop_call();
	return NULL;
}


CHAR_DATA *get_char_list( CHAR_DATA *ch, char *argument, CHAR_DATA *list )
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *rch;
	int number;
	int count;

	push_call("get_char_room(%p,%p)",ch,argument);

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

	if (!is_string(argument))
	{
		pop_call();
		return NULL;
	}
	
	if (!strcasecmp(arg, "self"))
	{
		pop_call();
		return ch;
	}

	for (rch = list ; rch ; rch = rch->next_in_room)
	{
		if (IS_NPC(rch) && !is_name(arg, rch->name))
		{
			continue;
		}
		if (!IS_NPC(rch) && !is_name(arg, PERS(rch,ch)))
		{
			continue;
		}
		if (!can_see(ch, rch) && (!IS_NPC(ch) || !IS_IMMORTAL(ch)))
		{
			continue;
		}
		if (++count != number)
		{
			continue;
		}
		pop_call();
		return rch;
	}
	count = 0;

	for (rch = ch->in_room->first_person ; rch != NULL ; rch = rch->next_in_room)
	{
		if (IS_NPC(rch) && !is_multi_name_list_short(arg, rch->name))
		{
			continue;
		}
		if (!IS_NPC(rch) && !is_multi_name_list_short(arg, PERS(rch,ch)))
		{
			continue;
		}
		if (!can_see(ch, rch) || ++count != number)
		{
			continue;
		}
		pop_call();
		return rch;
	}
	pop_call();
	return NULL;
}


CHAR_DATA *get_char_area( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *ach;
	int number, count, vnum;

	push_call("get_char_area(%p,%p)",ch,argument);

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

	for (vnum = ch->in_room->area->low_r_vnum ; vnum <= ch->in_room->area->hi_r_vnum ; vnum++)
	{
		if (room_index[vnum] == NULL)
		{
			continue;
		}

		for (ach = room_index[vnum]->first_person ; ach ; ach = ach->next_in_room)
		{
			if (IS_NPC(ach) && !is_name(arg, ach->name))
			{
				continue;
			}
			if (!IS_NPC(ach) && !is_name(arg, PERS(ach,ch)))
			{
				continue;
			}
			if (!can_see(ch, ach) && (!IS_NPC(ch) || !IS_IMMORTAL(ch)))
			{
				continue;
			}
			if (++count != number)
			{
				continue;
			}
			pop_call();
			return ach;
		}
	}

	count = 0;

	for (vnum = ch->in_room->area->low_r_vnum ; vnum <= ch->in_room->area->hi_r_vnum ; vnum++)
	{
		if (room_index[vnum] == NULL)
		{
			continue;
		}

		for (ach = room_index[vnum]->first_person ; ach ; ach = ach->next_in_room)
		{
			if (IS_NPC(ach) && !is_multi_name_list_short(arg, ach->name))
			{
				continue;
			}
			if (!IS_NPC(ach) && !is_multi_name_list_short(arg, PERS(ach,ch)))
			{
				continue;
			}
			if (!can_see(ch, ach) && (!IS_NPC(ch) || !IS_IMMORTAL(ch)))
			{
				continue;
			}
			if (++count != number)
			{
				continue;
			}
			pop_call();
			return ach;
		}
	}
	pop_call();
	return NULL;
}

/*
	Find a char in the world.
*/

CHAR_DATA *get_char_world( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *wch;
	int number;
	int count;

	push_call("get_char_world(%p,%p)",ch,argument);

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

	if ((wch = get_player_world(ch, argument)) != NULL)
	{
		pop_call();
		return wch;
	}

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

	for (wch = mud->f_char ; wch ; wch = wch->next)
	{
		if (IS_NPC(wch) && !is_name(arg, wch->name))
		{
			continue;
		}
		if (!IS_NPC(wch) && !is_name(arg, PERS(wch,ch)))
		{
			continue;
		}
		if (!can_see(ch, wch) && (!IS_NPC(ch) || !IS_IMMORTAL(ch)))
		{
			continue;
		}
		if (!can_see_world(ch, wch) && (!IS_NPC(ch) || !IS_IMMORTAL(ch)))
		{
			continue;
		}
		if (++count != number)
		{
			continue;
		}
		pop_call();
		return wch;
	}
	count = 0;

	for (wch = mud->f_char ; wch ; wch = wch->next)
	{
		if (IS_NPC(wch) && !is_multi_name_list_short(arg, wch->name))
		{
			continue;
		}
		if (!IS_NPC(wch) && !is_multi_name_list_short(arg, PERS(wch,ch)))
		{
			continue;
		}
		if (!can_see(ch, wch) || !can_see_world(ch, wch) || ++count != number)
		{
			continue;
		}
		else
		{
			pop_call();
			return wch;
		}
	}
	pop_call();
	return NULL;
}

CHAR_DATA *get_mob_vnum( CHAR_DATA *ch, char *argument )
{
	CHAR_DATA *ich;
	char arg[MAX_INPUT_LENGTH];
	int count, number, vnum;

	push_call("get_mob_vnum(%p,%p)",ch,argument);

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

	if (get_mob_index(vnum) == NULL)
	{
		pop_call();
		return NULL;
	}

	for (ich = mob_index[vnum]->first_instance ; ich ; ich = ich->next_instance)
	{
		if (++count == number && can_see(ch, ich))
		{
			pop_call();
			return ich;
		}
	}
	pop_call();
	return NULL;
}

OBJ_DATA *get_obj_vnum(CHAR_DATA *ch, char *argument )
{
	OBJ_DATA *iobj;
	char arg[MAX_INPUT_LENGTH];
	int count, number, vnum;

	push_call("get_obj_vnum(%p,%p)",ch,argument);

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

	if (get_obj_index(vnum) == NULL)
	{
		pop_call();
		return NULL;
	}

	for (iobj = obj_index[vnum]->first_instance ; iobj ; iobj = iobj->next_instance)
	{
		if (++count == number && can_see_obj(ch, iobj))
		{
			pop_call();
			return iobj;
		}
	}
	pop_call();
	return NULL;
}
		
/*
	Returns TRUE if a PC with the specified name exists
*/

int char_exists(char *arg)
{
	FILE            *charfile;
	DESCRIPTOR_DATA *d;
	char pfile[MAX_INPUT_LENGTH], buf1[200], buf2[200];

	push_call("char_exists(%p)",arg);

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

	for (d = mud->f_desc ; d ; d = d->next)
	{
		if (is_desc_valid(d->character))
		{
			if (is_name(arg, d->character->name))
			{
				pop_call();
				return TRUE;
			}
		}
	}

	sprintf(buf1, "%s/%c/%s",	PLAYER_DIR, tolower(arg[0]), capitalize_name(arg));
	sprintf(buf2, "%s/%c/%s",	PLAYER_DIR, tolower(arg[0]), capitalize_name(arg));

	if ((charfile = my_fopen(buf1, "r", TRUE)) != NULL)
	{
		rename(buf1, buf2);

		my_fclose(charfile);
	}

	sprintf(pfile,  "%s/%c/%s", PLAYER_DIR, tolower(arg[0]), capitalize(arg));

	if ((charfile = my_fopen(pfile, "r", TRUE)) == NULL)
	{
		pop_call();
		return FALSE;
	}
	if (charfile)
	{
		my_fclose(charfile);
	}
	pop_call();
	return TRUE;
}

/*
	Find a player in the world
*/

CHAR_DATA *get_player_world( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	PLAYER_GAME *fpl;
	CHAR_DATA   *wch;

	push_call("get_player_world(%p,%p)",ch,argument);

	if ((wch = get_char_room(ch, argument)) != NULL && !IS_NPC(wch))
	{
		pop_call();
		return wch;
	}

	argument = one_argument( argument, arg );

	for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
	{
		if (!is_name(arg, fpl->ch->name)) 
		{
			continue;
		}
		if (!can_see(ch, fpl->ch) && (!IS_NPC(ch) || !IS_IMMORTAL(ch)))
		{
			continue;
		}
		if (!can_see_world(ch, fpl->ch) && (!IS_NPC(ch) || !IS_IMMORTAL(ch)))
		{
			continue;
		}
		pop_call();
		return fpl->ch;
	}

	for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
	{
		if (!is_name_short(arg, fpl->ch->name) || !can_see_world(ch, fpl->ch) || !can_see(ch, fpl->ch))
		{
			continue;
		}
		pop_call();
		return fpl->ch;
	}
	pop_call();
	return NULL;
}

/*
	pvnum_index based routines - Scandum
*/

CHAR_DATA * get_char_pvnum(int pvnum)
{
	PLAYER_GAME *fpl;
	push_call("get_char_pvnum(%p)",pvnum);

	for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
	{
		if (fpl->ch->pcdata->pvnum == pvnum)
		{
			pop_call();
			return fpl->ch;
		}
	}

	pop_call();
	return NULL;
}


char *get_name_pvnum(int pvnum)
{
	push_call("get_name_pvnum(%p)",pvnum);

	if (pvnum < 0 || pvnum >= MAX_PVNUM)
	{
		pop_call();
		return "nobody";
	}

	if (pvnum_index[pvnum] == NULL)
	{
		pop_call();
		return "nobody";
	}

	pop_call();
	return pvnum_index[pvnum]->name;
}

int get_pvnum_name(char *name)
{
	int pvnum;

	push_call("get_pvnum_name(%p)",name);

	for (pvnum = 0 ; pvnum < MAX_PVNUM ; pvnum++)
	{
		if (pvnum_index[pvnum] == NULL)
		{
			continue;
		}
		if (IS_SET(pvnum_index[pvnum]->flags, PVNUM_DELETED))
		{
			continue;
		}
		if (!strcasecmp(pvnum_index[pvnum]->name, name))
		{
			pop_call();
			return pvnum;
		}
	}
	pop_call();
	return -1;
}

CHAR_DATA * get_obj_owner( OBJ_DATA *obj )
{
	int pvnum;
	CHAR_DATA *owner;

	push_call("get_obj_owner(%p)",obj);

	if (obj == NULL) /* duh! */
	{
		pop_call();
		return NULL;
	}

	if ((pvnum = obj->owned_by) == 0)
	{
		pop_call();
		return NULL;
	}
	
	if ((owner = get_char_pvnum(pvnum)) == NULL)
	{
		pop_call();
		return NULL;
	}

	pop_call();
	return owner;
}


/*
 * lookup the caster of an affect on a char - Kregor
 */
CHAR_DATA *get_caster( CHAR_DATA *ch, int sn )
{
	AFFECT_DATA *paf, *paf_next;
	CHAR_DATA *caster;
	
	push_call("get_caster(%p,%p)",ch,sn);
	
	caster = NULL;
	
	if (is_affected(ch, sn))
	{
		for ( paf = ch->first_affect; paf != NULL ; paf = paf_next )
		{
			paf_next = paf->next;
			
			if( paf->type != sn )
				continue;
				
			if (!strcasecmp(paf->caster, "None"))
				continue;

			if ((caster = get_char_world_even_blinded(ch, paf->caster)) != NULL)
				break;
		}
	}
	else
	{
		pop_call();
		return NULL;
	}
	pop_call();
	return caster;
}


/*
 * lookup the caster of an affect on an obj - Kregor
 */
CHAR_DATA *get_caster_obj( OBJ_DATA *obj, int sn )
{
	AFFECT_DATA *paf, *paf_next;
	CHAR_DATA *caster;
	
	push_call("get_caster(%p,%p)",obj,sn);
	
	caster = NULL;
	
	if (is_obj_affected(obj, sn))
	{
		set_supermob(obj);

		for ( paf = obj->first_affect; paf != NULL ; paf = paf_next )
		{
			paf_next = paf->next;
			
			if( paf->type != sn )
				continue;
				
			if (!strcasecmp(paf->caster, "None"))
				continue;

			if ((caster = get_char_world_even_blinded(supermob, paf->caster)) != NULL)
				break;
		}
		release_supermob();
	}
	else
	{
		pop_call();
		return NULL;
	}
	pop_call();
	return caster;
}


/*
 * lookup the caster of an affect on a room - Kregor
 */
CHAR_DATA *get_caster_room( ROOM_TIMER_DATA *rtd )
{
	ROOM_INDEX_DATA *room;
	CHAR_DATA *caster;
	
	push_call("get_caster_room(%p)",rtd);
	
	caster = NULL;
	
	if ((room = get_room_index(rtd->vnum)) == NULL)
	{
		pop_call();
		return NULL;
	}

	rset_supermob(room);

	if ((caster = get_char_world_even_blinded(supermob, rtd->caster)) != NULL)
	{
		release_supermob();
		pop_call();
		return caster;
	}
	release_supermob();
	pop_call();
	return NULL;
}


int get_affect_level( CHAR_DATA *ch, int sn )
{
	AFFECT_DATA *paf, *paf_next;
	int level;
	
	push_call("get_affect_level(%p,%p)",ch,sn);
	
	level = -1;
	
	if ( is_affected(ch, sn) )
	{
		for ( paf = ch->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;
			
			if( paf->type != sn )
				continue;

			if ((level = paf->level) != -1)
				break;
		}
	}

	pop_call();
	return level;
}


int get_AFFECT_level( CHAR_DATA *ch, lg_int bitv )
{
	AFFECT_DATA *paf, *paf_next;
	int level;
	
	push_call("get_affect_level(%p,%p)",ch,bitv);
	
	level = -1;
	
	if (IS_AFFECTED(ch, bitv))
	{
		for ( paf = ch->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;
			
			if(!IS_SET(paf->bitvector, bitv))
				continue;

			if ((level = paf->level) != -1)
				break;
		}
	}
	pop_call();
	return level;
}


AFFECT_DATA * get_affect_sn( CHAR_DATA *ch, int sn )
{
	AFFECT_DATA *paf, *paf_next;
	
	push_call("get_affect_sn(%p,%p)",ch,sn);
	
	if ( !is_affected(ch, sn) )
	{
		pop_call();
		return NULL;
	}
	for ( paf = ch->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;
		
		if( paf->type == sn )
			break;
	}

	pop_call();
	return paf;
}


int obj_affect_level( OBJ_DATA *obj, int sn )
{
	AFFECT_DATA *paf, *paf_next;
	int level;
	
	push_call("obj_affect_level(%p,%p)",obj,sn);
	
	level = -1;
	
	if ( is_obj_affected(obj, sn) )
	{
		for ( paf = obj->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;
			
			if( paf->type != sn )
				continue;

			if ((level = paf->level) != -1)
				break;
		}
	}

	pop_call();
	return level;
}


/* defines the visible name of an object - Kregor */
char * OBJD( OBJ_DATA *obj, CHAR_DATA *looker )
{
	static char buf[MAX_STRING_LENGTH];
	CHAR_DATA *owner;

	push_call("OBJD(%p,%p)",obj,looker);
	
	buf[0] = '\0';

	/* let's crash proof this */
	if (obj == NULL)
	{
		pop_call();
		return ( "nothing? PLEASE REPORT THIS BUG" );
	}
	
	if (looker == NULL)
	{
		sprintf(buf, "%s", obj->short_descr);
		strcat(buf, "{300}");
		pop_call();
		return buf;
	}

	if (!can_see_obj(looker, obj))
	{
		pop_call();
		return "something";
	}

	// Hallucinate results
	if (!IS_NPC(looker) && !IS_PLR(looker, PLR_HOLYLIGHT) && IS_AFFECTED(looker, AFF2_HALLUCINATE))
	{
		strcat(buf, fake_name[number_range(0, NUM_FAKE_NAME-1)]);
		pop_call();
		return buf;
	}

	switch (obj->item_type)
	{
		default:
			strcpy(buf, obj->short_descr);
			break;
		/* how cool is this? If you know the person, you recognize the corpse - Kregor */
		case ITEM_CORPSE_PC:
			if (((owner = get_char_pvnum(obj->value[4])) != NULL && knows_char(looker,owner)) || IS_GOD(looker))
			{
				sprintf(buf, "the corpse of %s", get_name(owner));
			}
			else
			{
				sprintf(buf, "%s", obj->short_descr);
			}
			break;
		/* also cool, do not put spell names in consumables, it shows if it's identified - Kregor */
		case ITEM_POTION:
		case ITEM_SCROLL:
			sprintf(buf, "%s", obj->short_descr);
			if (obj->value[1] > 0)
			{
				if (((owner = obj->carried_by) != NULL && owner == looker && obj->identified) || IS_GOD(looker))
				{
					cat_sprintf(buf, " of %s", skill_table[obj->value[1]].name);
					break;
				}
			}
			break;
		case ITEM_WAND:
		case ITEM_STAFF:
			sprintf(buf, "%s", obj->short_descr);
			if (obj->value[3] > 0)
			{
				if (((owner = obj->carried_by) != NULL && owner == looker && obj->identified) || IS_GOD(looker))
				{
					cat_sprintf(buf, " of %s", skill_table[obj->value[3]].name);
					break;
				}
			}
			break;
		case ITEM_DRINK_CON:
			sprintf(buf, "%s", obj->short_descr);
			if (obj->value[1] > 0)
			{
				if (((owner = obj->carried_by) != NULL && owner == looker) || IS_GOD(looker))
				{
					cat_sprintf(buf, " of %s", liq_table[obj->value[2]].liq_name);
					break;
				}
			}
			break;
	}
	
	strcat(buf, "{300}");
	pop_call();
	return (buf);
}


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

	push_call("get_obj_list(%p,%p,%p)",ch,argument,list);

	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)
			{
				pop_call();
				return obj;
			}
		}
	}
	count = 0;

	for (obj = list ; obj ; obj = obj->next_content)
	{
		if (can_see_obj(ch, obj) && is_multi_name_list_short(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}
	pop_call();
	return NULL;
}

/*
	Find an obj in player's inventory.
*/

OBJ_DATA *get_obj_carry_even_invis(CHAR_DATA *ch, char *argument)
{
	/*
		finds an item even if the owner can't see it
		(for steal and such)
		The function is identical to get_obj_carry, but the
		requisites for can_see(ch,obj) are removed here
	*/

	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	int number;
	int count;

	push_call("get_obj_carry_even_invis(%p,%p)",ch,argument);

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

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc == WEAR_NONE && is_name(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}

	count = 0;

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc == WEAR_NONE && is_multi_name_list_short(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}
	pop_call();
	return NULL;
}

OBJ_DATA *get_obj_carry( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	int number;
	int count;

	push_call("get_obj_carry(%p,%p)",ch,argument);

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

	for (obj = ch->first_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)
			{
				pop_call();
				return obj;
			}
		}
	}

	count = 0;

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc == WEAR_NONE && can_see_obj(ch, obj) && is_multi_name_list_short(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}
	pop_call();
	return NULL;
}

/*
 * get an obj in inventory or worn by item type - Kregor
 */
OBJ_DATA *get_obj_char_type( CHAR_DATA *ch, int type )
{
	OBJ_DATA *obj;

	push_call("get_obj_char_type(%p,%p)",ch,type);

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (can_see_obj(ch, obj) && IS_OBJ_TYPE(obj, type))
		{
			pop_call();
			return obj;
		}
	}
	pop_call();
	return NULL;
}

/*
 * get an obj in inventory by item type - Kregor
 */
OBJ_DATA *get_obj_carry_type( CHAR_DATA *ch, int type )
{
	OBJ_DATA *obj;

	push_call("get_obj_carry_type(%p,%p)",ch,type);

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc == WEAR_NONE && can_see_obj(ch, obj) && IS_OBJ_TYPE(obj, type))
		{
			pop_call();
			return obj;
		}
	}
	pop_call();
	return NULL;
}

/*
	Find an obj in keeper's inventory.
*/

OBJ_DATA *get_obj_carry_keeper( CHAR_DATA *keeper, char *argument, CHAR_DATA *ch)
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj, *oobj;
	int number, count;

	push_call("get_obj_carry_keeper(%p,%p,%p)",keeper, argument, ch);

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

	for (obj = keeper->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc != WEAR_NONE || !can_see_obj(ch, obj) || !is_name(arg, obj->name))
		{
			continue;
		}
		if (get_cost(keeper, obj, TRUE) < 1)
		{
			continue;
		}
		for (oobj = keeper->first_carrying ; oobj != obj ; oobj = oobj->next_content)
		{
			if (oobj->wear_loc != WEAR_NONE || !can_see_obj(ch, oobj) || !is_name(arg, oobj->name))
			{
				continue;
			}
			if (get_cost(keeper, oobj, TRUE) < 1)
			{
				continue;
			}
		}
		if (oobj != obj)
		{
			continue;
		}
		if (++count == number)
		{
			pop_call();
			return obj;
		}
	}

	count = 0;

	for (obj = keeper->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc != WEAR_NONE || !can_see_obj(ch, obj) || !is_multi_name_list_short(arg, obj->name))
		{
			continue;
		}
		if (get_cost(keeper, obj, TRUE) < 1)
		{
			continue;
		}

		for (oobj = keeper->first_carrying ; oobj != obj ; oobj = oobj->next_content)
		{
			if (oobj->wear_loc != WEAR_NONE || !can_see_obj(ch, oobj) || !is_multi_name_list_short(arg, oobj->name))
			{
				continue;
			}
			if (get_cost(keeper, oobj, TRUE) < 1)
			{
				continue;
			}
		}
		if (oobj != obj)
		{
			continue;
		}
		if (++count == number)
		{
			pop_call();
			return obj;
		}
	}
	pop_call();
	return NULL;
}


/*
	Find an obj in player's equipment.
*/

OBJ_DATA *get_obj_wear( CHAR_DATA *ch, char *argument )
{
	OBJ_DATA *obj;
	char arg[MAX_INPUT_LENGTH];
	int number, count;

	push_call("get_obj_wear(%p,%p)",ch,argument);

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

	for (obj = ch->first_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)
			{
				pop_call();
				return obj;
			}
		}
	}

	count = 0;

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc != WEAR_NONE && can_see_obj(ch, obj) && is_multi_name_list_short(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}
	pop_call();
	return NULL;
}

/*
	Find an obj vnum in player's equipment.
*/

OBJ_DATA *get_obj_wear_vnum( CHAR_DATA *ch, int vnum)
{
	OBJ_DATA *obj;

	push_call("get_obj_wear_vnum(%p,%p)",ch,vnum);

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

/*
	Find first of an item type in player's equipment.
*/
OBJ_DATA *get_obj_wear_type( CHAR_DATA *ch, int type )
{
	OBJ_DATA *obj;

	push_call("get_obj_wear_type(%p,%p)",ch,type);

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc != WEAR_NONE && obj->item_type == type)
		{
			pop_call();
			return obj;
		}
	}
	pop_call();
	return NULL;
}


/*
	Find first of an item with given TFLAG - Kregor
*/
OBJ_DATA *get_obj_tflag( CHAR_DATA *ch, int tflag )
{
	OBJ_DATA *obj;

	push_call("get_obj_wear_tflag(%p,%p)",ch,tflag);

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc != WEAR_NONE && obj->item_type == ITEM_TREASURE && obj->value[0] == tflag)
		{
			pop_call();
			return obj;
		}
	}
	pop_call();
	return NULL;
}


/*
	Find an obj vnum in a players inventory.
*/

OBJ_DATA *get_obj_carry_vnum( CHAR_DATA *ch, int vnum)
{
	OBJ_DATA *obj;

	push_call("get_obj_carry_vnum(%p,%p)",ch,vnum);

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

/*
	Now scans as if 1 list - Scandum
*/

OBJ_DATA *get_obj_here_vnum( CHAR_DATA *ch, int vnum, int number )
{
	OBJ_DATA *obj;
	int count;

	push_call("get_obj_here_vnum(%p,%p)",ch,vnum,number);

	count  = 0;

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

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc == WEAR_NONE && obj->pIndexData->vnum == vnum && can_see_obj(ch, obj))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}

	for (obj = ch->in_room->first_content ; obj ; obj = obj->next_content)
	{
		if (obj->pIndexData->vnum == vnum && can_see_obj(ch, obj))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}

	pop_call();
	return NULL;
}


OBJ_DATA *get_obj_here( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	int number;
	int count;

	push_call("get_obj_here(%p,%p)",ch,argument);

	number = number_argument( argument, arg );

	if (arg[0] == 'i' && atol(&arg[1]))
	{
		pop_call();
		return get_obj_here_vnum(ch, atol(&arg[1]), number);
	}

	count  = 0;

	for (obj = ch->first_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)
			{
				pop_call();
				return obj;
			}
		}
	}

	for (obj = ch->first_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)
			{
				pop_call();
				return obj;
			}
		}
	}

	for (obj = ch->in_room->first_content ; obj ; obj = obj->next_content)
	{
		if (can_see_obj(ch, obj) && is_name(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}

	count = 0;

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc != WEAR_NONE && can_see_obj(ch, obj) && is_multi_name_list_short(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->wear_loc == WEAR_NONE && can_see_obj(ch, obj) && is_multi_name_list_short(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}

	for (obj = ch->in_room->first_content ; obj ; obj = obj->next_content)
	{
		if (can_see_obj(ch, obj) && is_multi_name_list_short(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}
	pop_call();
	return NULL;
}


/*
 * Search only for an object in the room - Kregor
 */
OBJ_DATA *get_obj_room( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	int number;
	int count;

	push_call("get_obj_here(%p,%p)",ch,argument);

	number = number_argument( argument, arg );

	if (arg[0] == 'i' && atol(&arg[1]))
	{
		pop_call();
		return get_obj_here_vnum(ch, atol(&arg[1]), number);
	}

	count  = 0;

	for (obj = ch->in_room->first_content ; obj ; obj = obj->next_content)
	{
		if (can_see_obj(ch, obj) && is_name(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}

	count = 0;

	for (obj = ch->in_room->first_content ; obj ; obj = obj->next_content)
	{
		if (can_see_obj(ch, obj) && is_multi_name_list_short(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}
	pop_call();
	return NULL;
}



/*
	Find an obj in the world.
*/

OBJ_DATA *get_obj_world( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	int number;
	int count;

	push_call("get_obj_world(%p,%p)",ch,argument);

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

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

	for (obj = mud->f_obj ; obj ; obj = obj->next)
	{
		if (can_see_obj(ch, obj) && is_name(arg, obj->name))
		{
			if ( ++count == number )
			{
				pop_call();
				return obj;
			}
		}
	}

	count = 0;

	for (obj = mud->f_obj ; obj ; obj = obj->next)
	{
		if (can_see_obj(ch, obj) && is_multi_name_list_short(arg, obj->name))
		{
			if (++count == number)
			{
				pop_call();
				return obj;
			}
		}
	}
	pop_call();
	return NULL;
}

/*
 * Create a 'money' obj, revamped for three coinage - Kregor
 */
OBJ_DATA *create_money( int amount )
{
	char buf[MAX_STRING_LENGTH];
	OBJ_DATA *obj;
	int gold = amount / 100;
	int silver = amount % 100 / 10;
 	int copper = amount % 100 % 10;

	push_call("create_money(%p)",amount);

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

	obj = create_object( get_obj_index( OBJ_VNUM_MONEY_SOME ), 0 );
	sprintf( buf, "%s", format_coins(amount,TRUE) );
	STRFREE( obj->short_descr );
	obj->short_descr        = STRALLOC( buf );
	obj->value[0]           = gold;
	obj->value[1]           = silver;
	obj->value[2]           = copper;

	pop_call();
	return obj;
}

/*
	Return weight of an object, including weight of first_content - Kregor
*/
int get_obj_weight( OBJ_DATA *obj )
{
	int weight;

	push_call("get_obj_weight(%p)",obj);
	
	weight = obj->weight;
	
	/*
	 * adds 8 pounds per gallon of water,
	 * considering each point of value1 to
	 * be 8 oz.
	 */
	switch (obj->item_type)
	{
		case ITEM_DRINK_CON:
		case ITEM_FOUNTAIN:
			weight += obj->value[1] * 10 / 2;
			break;
		case ITEM_COMPONENT:
			pop_call();
			return 0;
		case ITEM_AMMO:
			pop_call();
			return 1;
		case ITEM_CONTAINER:
			if (IS_SET(obj->value[1], CONT_HOLDING))
			{
				pop_call();
				return ROUNDUP(obj->value[0]/160);
			}
			break;
		case ITEM_MONEY:
			weight = obj->value[0];
			weight += obj->value[1];
			weight += obj->value[2];
			break;
	}
	weight += obj->content_weight;

	pop_call();
	return(weight);
}


/* added obj hit points per SRD for object damage - Kregor */
int get_obj_max_hit( OBJ_DATA *obj )
{
	OBJ_INDEX_DATA *pObjIndex;
	int hitpoints;

	push_call("get_obj_max_hit(%p)",obj);

	if ((pObjIndex = obj->pIndexData) == NULL)
	{
		bug( "get_obj_max_hit : obj == NULL.",0);
		pop_call();
		return 0;
	}
	
	hitpoints = pObjIndex->hit_points;
	
	switch (obj->item_type)
	{
		case ITEM_WEAPON:
			hitpoints += actual_tohit_bonus(obj) * 10;
			break;
		case ITEM_ARMOR:
			hitpoints += obj->apply[APPLY_ENHANCE_AC] * 10;
			break;
		default:
			if (IS_OBJ_STAT(obj, ITEM_MAGIC))
				hitpoints += 10;
			break;
	}

	pop_call();
	return hitpoints;
}


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

	count = 0;

	push_call("room_is_private(%p)",pRoomIndex);

	for (rch = pRoomIndex->first_person ; rch ; rch = rch->next_in_room)
	{
		if (IS_NPC(rch) || IS_GOD(rch))
			continue;

		count++;
	}

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

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


ROOM_INDEX_DATA *get_random_room_index( CHAR_DATA *ch, int lo_room, int hi_room )
{
	ROOM_INDEX_DATA *pRoomIndex;
	int cnt;

	push_call("get_random_room(%p,%d,%d)",ch,lo_room,hi_room);

	for (cnt = 0 ; cnt < 10000 ; cnt++)
	{
		pRoomIndex = get_room_index(number_range(lo_room, hi_room));

		if (pRoomIndex)
		{
			if (hi_room - lo_room > 10000)
			{
				if (IS_SET(pRoomIndex->area->flags, AFLAG_NOTELEPORT))
				{
					continue;
				}
				if (IS_SET(pRoomIndex->area->flags, AFLAG_NORECALL))
				{
					continue;
				}
				if (IS_SET(pRoomIndex->area->flags, AFLAG_NORIP))
				{
					continue;
				}
			}
			if (IS_SET(pRoomIndex->room_flags, ROOM_NO_RECALL))
			{
				continue;
			}
			if (IS_SET(pRoomIndex->room_flags, ROOM_NO_RIP))
			{
				continue;
			}
			if (IS_SET(pRoomIndex->room_flags, ROOM_PRIVATE))
			{
				continue;
			}
			if (IS_SET(pRoomIndex->room_flags, ROOM_SOLITARY))
			{
				continue;
			}
			if (IS_SET(pRoomIndex->room_flags, ROOM_NO_SAVE))
			{
				continue;
			}
			if (IS_SET(pRoomIndex->room_flags, ROOM_CLANHALL))
			{
				continue;
			}
			if (IS_SET(pRoomIndex->room_flags, ROOM_RIP))
			{
				continue;
			}
			if (pRoomIndex->sector_type == SECT_ETHEREAL)
			{
				continue;
			}
			if (pRoomIndex->sector_type == SECT_ASTRAL)
			{
				continue;
			}
			if (pRoomIndex->area->low_hard_range != 0 || pRoomIndex->area->hi_hard_range != 0)
			{
				if (!IS_NPC(ch) && ch->level < LEVEL_IMMORTAL)
				{
					if (ch->level < pRoomIndex->area->low_hard_range)
					{
						continue;
					}
					if (ch->level > pRoomIndex->area->hi_hard_range)
					{
						continue;
					}
				}
			}
			break;
		}
	}
	if (cnt >= 10000)
	{
		log_printf("get_random_room(%s) failure", get_name(ch));
		pop_call();
		return NULL;
	}
	pop_call();
	return pRoomIndex;
}


int direction_door( char *txt )
{
	push_call("direction_door(%p)",txt);

	if (txt == NULL || *txt == '\0')
	{
		pop_call();
		return -1;
	}

	if (is_number(txt))
	{
		if (atoi(txt) < 0 || atoi(txt) > 5)
		{
			pop_call();
			return -1;
		}
		pop_call();
		return atoi(txt);
	}

	if (!strcasecmp(txt, "n") || !strcasecmp(txt, "north"))
	{
		pop_call();
		return 0;
	}
	if (!strcasecmp(txt, "e") || !strcasecmp(txt, "east"))
	{
		pop_call();
		return 1;
	}
	if (!strcasecmp(txt, "s") || !strcasecmp(txt, "south"))
	{
		pop_call();
		return 2;
	}
	if (!strcasecmp(txt, "w") || !strcasecmp(txt, "west"))
	{
		pop_call();
		return 3;
	}
	if (!strcasecmp(txt, "u") || !strcasecmp(txt, "up"))
	{
		pop_call();
		return 4;
	}
	if (!strcasecmp(txt, "d") || !strcasecmp(txt, "down"))
	{
		pop_call();
		return 5;
	}
	pop_call();
	return -1;
}


EXIT_DATA *get_exit( int vnum, bool door )
{
	push_call("get_exit(%p,%p)",vnum,door);

	if (room_index[vnum]->exit[door] == NULL)
	{
		pop_call();
		return NULL;
	}

	if (room_index[room_index[vnum]->exit[door]->to_room] == NULL)
	{
		pop_call();
		return NULL;
	}

	pop_call();
	return room_index[vnum]->exit[door];
}


bool can_use_exit( CHAR_DATA *ch, EXIT_DATA *exit )
{
	push_call("can_use_exit(%p,%p)",ch,exit);

	if (IS_GOD(ch) && IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return TRUE;
	}

	if (IS_SET(exit->exit_info, EX_RIP) && !pvnum_in_group(ch, exit->pvnum))
	{
		pop_call();
		return FALSE;
	}
	pop_call();
	return TRUE;
}

bool is_valid_exit( CHAR_DATA *ch, ROOM_INDEX_DATA *room, bool dir )
{
	EXIT_DATA *pExit;

	push_call("is_valid_exit(%p,%p,%p)", ch, room, dir);

	if ((pExit = get_exit(room->vnum, dir)) == NULL)
	{
		pop_call();
		return( FALSE );
	}

	if (IS_SET(pExit->exit_info, EX_CLOSED))
	{
		pop_call();
		return( FALSE );
	}

	if (IS_SET(pExit->exit_info, EX_HIDDEN) && !can_see_hidden(ch,dir))
	{
		pop_call();
		return FALSE;
	}

	if (!can_use_exit(ch, pExit))
	{
		pop_call();
		return FALSE;
	}

	pop_call();
	return( TRUE );
}

/*
 * Calculate the DC of a lock, container or exit - Kregor
 */
int lock_dc(EXIT_DATA *pExit, OBJ_DATA *obj)
{
	int dc;
	
	push_call("lock_dc(%p,%p)",pExit,obj);
	
	if (pExit)
	{
		if (IS_SET(pExit->exit_info, EX_AMAZING_PICK))
			dc = 40;
		else if (IS_SET(pExit->exit_info, EX_HARD_PICK))
			dc = 30;
		else if (IS_SET(pExit->exit_info, EX_EASY_PICK))
			dc = 20;
		else
			dc = 25;
			
		if (IS_SET(pExit->exit_info, EX_MAGICAL_LOCK))
			dc += 10;

		if (IS_SET(pExit->exit_info, EX_PICKPROOF))
			dc += 100;
	}
	else if (obj)
	{
		if (IS_OBJ_TYPE(obj, ITEM_CONTAINER))
		{
			if (IS_SET(obj->value[1], CONT_AMAZING_PICK))
				dc = 40;
			else if (IS_SET(obj->value[1], CONT_HARD_PICK))
				dc = 30;
			else if (IS_SET(obj->value[1], CONT_EASY_PICK))
				dc = 20;
			else
				dc = 25;
	
			if (IS_SET(obj->value[1], CONT_MAGICAL_LOCK))
				dc += 10;
	
			if (IS_SET(obj->value[1], CONT_PICKPROOF))
				dc += 100;
		}
		else if (IS_OBJ_TYPE(obj, ITEM_PORTAL))
		{
			if (IS_SET(obj->value[2], CONT_AMAZING_PICK))
				dc = 40;
			else if (IS_SET(obj->value[2], CONT_HARD_PICK))
				dc = 30;
			else if (IS_SET(obj->value[2], CONT_EASY_PICK))
				dc = 20;
			else
				dc = 25;
	
			if (IS_SET(obj->value[2], CONT_PICKPROOF))
				dc += 100;
		}
	}
	else
	{
		dc = -1;
	}
	pop_call();
	return dc;
}


/*
	Return ascii name of an affect location.
*/

char *affect_loc_name( int location )
{
	push_call("affect_loc_name(%p)",location);

	switch ( location )
	{
		case APPLY_NONE:          pop_call(); return "none";
		case APPLY_STR:           pop_call(); return "strength";
		case APPLY_DEX:           pop_call(); return "dexterity";
		case APPLY_INT:           pop_call(); return "intelligence";
		case APPLY_WIS:           pop_call(); return "wisdom";
		case APPLY_CON:           pop_call(); return "constitution";
		case APPLY_CHA:           pop_call(); return "charisma";
		case APPLY_SEX:           pop_call(); return "sex";
		case APPLY_RACE:          pop_call(); return "race";
		case APPLY_LEVEL:         pop_call(); return "level";
		case APPLY_AGE:           pop_call(); return "age";
		case APPLY_MANA:          pop_call(); return "spell points";
		case APPLY_SIZE:	        pop_call(); return "size";
		case APPLY_HIT:           pop_call(); return "hit points";
		case APPLY_MOVE:          pop_call(); return "endurance";
		case APPLY_SAVING_FORT:   pop_call(); return "circumstance to FORT";
		case APPLY_SAVING_REFL:   pop_call(); return "circumstance to REFL";
		case APPLY_SAVING_WILL:		pop_call(); return "circumstance to WILL";
		case APPLY_SAVES:  				pop_call(); return "circumstance to saves";
		case APPLY_DEFLECT: 			pop_call(); return "deflection AC";
		case APPLY_DODGE: 				pop_call(); return "dodge AC";
		case APPLY_SHIELD:  			pop_call(); return "shield AC";
		case APPLY_ARMOR:  			  pop_call(); return "armor bonus";
		case APPLY_COMP_SKILL:  	pop_call(); return "competence to skills";
		case APPLY_COMP_TOHIT:  	pop_call(); return "competence to hit";
		case APPLY_COMP_DAMG:  		pop_call(); return "competence to damage";
		case APPLY_COMP_FORT:  		pop_call(); return "competence to FORT";
		case APPLY_COMP_REFL:  		pop_call(); return "competence to REFL";
		case APPLY_COMP_WILL:  		pop_call(); return "competence to WILL";
		case APPLY_COMP_SAVES:  	pop_call(); return "competence to saves";
		case APPLY_ENHANCE_AC:   	pop_call(); return "enhancement to AC";
		case APPLY_HITROLL:       pop_call(); return "enhancement to hit";
		case APPLY_DAMROLL:       pop_call(); return "enhancement to damage";
		case APPLY_INS_AC:  			pop_call(); return "insight to AC";
		case APPLY_INS_TOHIT:  		pop_call(); return "insight to hit";
		case APPLY_INS_FORT:  		pop_call(); return "insight to FORT";
		case APPLY_INS_REFL:  		pop_call(); return "insight to REFL";
		case APPLY_INS_WILL:  		pop_call(); return "insight to WILL";
		case APPLY_INS_SAVES:  		pop_call(); return "insight to saves";
		case APPLY_INS_SKILL:  		pop_call(); return "insight to skills";
		case APPLY_LUCK_SKILL:  	pop_call(); return "luck to skills";
		case APPLY_LUCK_TOHIT:  	pop_call(); return "luck to hit";
		case APPLY_LUCK_DAMG: 	 	pop_call(); return "luck to damage";
		case APPLY_LUCK_FORT:  		pop_call(); return "luck to FORT";
		case APPLY_LUCK_REFL:  		pop_call(); return "luck to REFL";
		case APPLY_LUCK_WILL: 	 	pop_call(); return "luck to WILL";
		case APPLY_LUCK_SAVES:  	pop_call(); return "luck to save";
		case APPLY_MOR_TOHIT: 	 	pop_call(); return "morale to hit";
		case APPLY_MOR_DAMG: 	 		pop_call(); return "morale to damage";
		case APPLY_MOR_FORT:  		pop_call(); return "morale to FORT";
		case APPLY_MOR_REFL:  		pop_call(); return "morale to REFL";
		case APPLY_MOR_WILL: 		 	pop_call(); return "morale to WILL";
		case APPLY_MOR_SAVES: 	 	pop_call(); return "morale to saves";
		case APPLY_MOR_SKILL: 	 	pop_call(); return "morale to skills";
		case APPLY_NATURAL_AC: 	 	pop_call(); return "natural armor";
		case APPLY_RES_GOOD: 		 	pop_call(); return "saves vs. good";
		case APPLY_RES_EVIL: 		 	pop_call(); return "saves vs. evil";
		case APPLY_RES_LAW: 		 	pop_call(); return "saves vs. law";
		case APPLY_RES_CHAOS: 	 	pop_call(); return "saves vs. chaos";
		case APPLY_RES_SAVES: 	 	pop_call(); return "to saves";
		case APPLY_RES_SPELL: 	 	pop_call(); return "saves vs. spells";
		case APPLY_APPRAISE: 		 	pop_call(); return "to appraise skill";
		case APPLY_BLUFF: 			 	pop_call(); return "to bluff skill";
		case APPLY_CLIMB: 			 	pop_call(); return "to climb skill";
		case APPLY_CONCENTRATE: 	pop_call(); return "to concentration skill";
		case APPLY_DECIPHER: 		 	pop_call(); return "to decipher skill";
		case APPLY_DIPLOMACY: 	 	pop_call(); return "to diplomacy skill";
		case APPLY_DISABLE: 		 	pop_call(); return "to disable device skill";
		case APPLY_DISGUISE: 		 	pop_call(); return "to disguise skill";
		case APPLY_ESCAPE: 			 	pop_call(); return "to escape artist skill";
		case APPLY_FIRST_AID:		 	pop_call(); return "to first aid skill";
		case APPLY_GATHER_INFO:	 	pop_call(); return "to gather info skill";
		case APPLY_HANDLE_ANIM:	 	pop_call(); return "to handle animal skill";
		case APPLY_INTIMIDATE: 	 	pop_call(); return "to intimidate skill";
		case APPLY_JUMP: 				 	pop_call(); return "to jump skill";
		case APPLY_SIGHT: 				pop_call(); return "to sight perception";
		case APPLY_LISTEN: 				pop_call(); return "to sound perception";
		case APPLY_MOUNT: 			 	pop_call(); return "to mount skill";
		case APPLY_OPEN_LOCK: 	 	pop_call(); return "to pick lock skill";
		case APPLY_PERFORM: 		 	pop_call(); return "to perform skill";
		case APPLY_SEARCH:	 		 	pop_call(); return "to search skill";
		case APPLY_STEALTH:		 		pop_call(); return "to stealth skill";
		case APPLY_SENSE_MOT: 	 	pop_call(); return "to sense motive skill";
		case APPLY_SLEIGHT: 		 	pop_call(); return "to sleight of hand";
		case APPLY_SPELLCRAFT:		pop_call(); return "to spellcraft skill";
		case APPLY_SURVIVAL: 		 	pop_call(); return "to survival skill";
		case APPLY_SWIM:		 		 	pop_call(); return "to swim skill";
		case APPLY_TUMBLE: 			 	pop_call(); return "to tumble skill";
		case APPLY_USE_MAGIC:		 	pop_call(); return "to use magic skill";
		case APPLY_CRAFT_ALCHEMY: pop_call(); return "to alchemy skill";
		case APPLY_CRAFT_ARMOR:		pop_call(); return "to armorsmithing skill";
		case APPLY_CRAFT_BOWS:		pop_call(); return "to bowmaking skill";
		case APPLY_CRAFT_COOKING:	pop_call(); return "to cooking skill";
		case APPLY_CRAFT_FLETCHING:	pop_call(); return "to fletching skill";
		case APPLY_CRAFT_JEWELRY:	pop_call(); return "to jewelcrafting skill";
		case APPLY_CRAFT_LEATHER:	pop_call(); return "to leathermaking skill";
		case APPLY_CRAFT_MINING:	pop_call(); return "to mining skill";
		case APPLY_CRAFT_POISON:	pop_call(); return "to poisoncraft skill";
		case APPLY_CRAFT_TAILOR:	pop_call(); return "to tailoring skill";
		case APPLY_CRAFT_TRAPS:		pop_call(); return "to trapmaking skill";
		case APPLY_CRAFT_WEAPONS:	pop_call(); return "to weaponsmithing skill";
		case APPLY_DR_BASH: 		 	pop_call(); return "/bashing";
		case APPLY_DR_PIERCE:		 	pop_call(); return "/piercing";
		case APPLY_DR_SLASH: 		 	pop_call(); return "/slashing";
		case APPLY_DR_MAGIC: 		 	pop_call(); return "/magic";
		case APPLY_DR_IRON: 		 	pop_call(); return "/iron";
		case APPLY_DR_SILVER: 		pop_call(); return "/silver";
		case APPLY_DR_ADAMANTINE:	pop_call(); return "/adamantine";
		case APPLY_DR_NONE:				pop_call(); return "/-";
		case APPLY_DR_GOOD:				pop_call(); return "/good";
		case APPLY_DR_EVIL:				pop_call(); return "/evil";
		case APPLY_DR_LAW:				pop_call(); return "/law";
		case APPLY_DR_CHAOS:			pop_call(); return "/chaos";
		case APPLY_DR_ACID: 		 	pop_call(); return "acid resistance";
		case APPLY_DR_COLD: 		 	pop_call(); return "cold resistance";
		case APPLY_DR_ELECTRIC: 	pop_call(); return "electric resistance";
		case APPLY_DR_FIRE: 		 	pop_call(); return "fire resistance";
		case APPLY_DR_SONIC: 	 		pop_call(); return "sonic resistance";
		case APPLY_SPELL_RES:		 	pop_call(); return "spell resistance";
		case APPLY_WEAPON_FLAG:	 	pop_call(); return "adds weapon flag";
		case APPLY_OBJVAL_0:	 		pop_call(); return "applies to objval 0";
		case APPLY_OBJVAL_1:	 		pop_call(); return "applies to objval 1";
		case APPLY_OBJVAL_2:	 		pop_call(); return "applies to objval 2";
		case APPLY_OBJVAL_3:	 		pop_call(); return "applies to objval 3";
		case APPLY_OBJVAL_4:	 		pop_call(); return "applies to objval 4";
		case APPLY_OBJVAL_5:	 		pop_call(); return "applies to objval 5";
		case APPLY_OBJVAL_6:	 		pop_call(); return "applies to objval 6";
		case APPLY_OBJVAL_7:	 		pop_call(); return "applies to objval 7";
		case APPLY_MATERIAL:	 		pop_call(); return "applies to obj material";
		case APPLY_SPELL_SLOTS_1: pop_call(); return "1st circle spell slot";
		case APPLY_SPELL_SLOTS_2: pop_call(); return "2nd circle spell slot";
		case APPLY_SPELL_SLOTS_3: pop_call(); return "3rd circle spell slot";
		case APPLY_SPELL_SLOTS_4: pop_call(); return "4th circle spell slot";
		case APPLY_SPELL_SLOTS_5: pop_call(); return "5th circle spell slot";
		case APPLY_STR_DAMAGE: 		pop_call(); return "STR damage";
		case APPLY_DEX_DAMAGE: 		pop_call(); return "DEX damage";
		case APPLY_CON_DAMAGE: 		pop_call(); return "CON damage";
		case APPLY_INT_DAMAGE: 		pop_call(); return "INT damage";
		case APPLY_WIS_DAMAGE: 		pop_call(); return "WIS damage";
		case APPLY_CHA_DAMAGE: 		pop_call(); return "DEX damage";
		case APPLY_STR_DRAIN: 		pop_call(); return "STR drain";
		case APPLY_DEX_DRAIN: 		pop_call(); return "DEX drain";
		case APPLY_CON_DRAIN: 		pop_call(); return "CON drain";
		case APPLY_INT_DRAIN: 		pop_call(); return "INT drain";
		case APPLY_WIS_DRAIN: 		pop_call(); return "WIS drain";
		case APPLY_CHA_DRAIN: 		pop_call(); return "DEX drain";
		case APPLY_IMM_ACID:			pop_call(); return "immunity vs. acid";
		case APPLY_IMM_AIR:				pop_call(); return "immunity vs. air spells";
		case APPLY_IMM_CHAOTIC:		pop_call(); return "immunity vs. chaotic spells";		
		case APPLY_IMM_CHARM:			pop_call(); return "immunity vs. charm spells";		
		case APPLY_IMM_COLD: 		 	pop_call(); return "immunity vs. cold";
		case APPLY_IMM_COMPULSION:	pop_call(); return "immunity vs. compulsion spells";		
		case APPLY_IMM_DARKNESS:	pop_call(); return "immunity vs. darkness spells";			
		case APPLY_IMM_DEATH:			pop_call(); return "immunity vs. death magic";			
		case APPLY_IMM_DISEASE:		pop_call(); return "immunity vs. disease";			
		case APPLY_IMM_EARTH:			pop_call(); return "immunity vs. earth spells";	
		case APPLY_IMM_ELECTRIC: 	pop_call(); return "immunity vs. electricity";
		case APPLY_IMM_EVIL:			pop_call(); return "immunity vs. evil spells";	
		case APPLY_IMM_FEAR:			pop_call(); return "immunity vs. fear";	
		case APPLY_IMM_FIRE: 		 	pop_call(); return "immunity vs. fire";
		case APPLY_IMM_FORCE:			pop_call(); return "immunity vs. force spells";	
		case APPLY_IMM_GOOD:			pop_call(); return "immunity vs. good spells";	
		case APPLY_IMM_HEALING:		pop_call(); return "immunity vs. healing energy";	
		case APPLY_IMM_ILLUSION:	pop_call(); return "immunity vs. illusion";			
		case APPLY_IMM_LAWFUL:		pop_call(); return "immunity vs. lawful spells";		
		case APPLY_IMM_LIGHT:			pop_call(); return "immunity vs. light spells";	
		case APPLY_IMM_MAGIC:			pop_call(); return "immunity vs. magic";	
		case APPLY_IMM_MIND:			pop_call(); return "immunity vs. mind affects";	
		case APPLY_IMM_NEGATIVE:	pop_call(); return "immunity vs. negative energy";
		case APPLY_IMM_PARALYSIS:	pop_call(); return "immunity vs. paralysis";		
		case APPLY_IMM_PETRI:			pop_call(); return "immunity vs. pretification";		
		case APPLY_IMM_POISON:		pop_call(); return "immunity vs. poison";		
		case APPLY_IMM_POLYMORPH:	pop_call(); return "immunity vs. polymorph";		
		case APPLY_IMM_SLEEP:			pop_call(); return "immunity vs. sleep";	
		case APPLY_IMM_SONIC: 	 	pop_call(); return "immunity vs. sonic";
		case APPLY_IMM_WATER:			pop_call(); return "immunity vs. water spells";
		case APPLY_SAVE_ACID:			pop_call(); return "save vs. acid";
		case APPLY_SAVE_AIR:			pop_call(); return "save vs. air spells";
		case APPLY_SAVE_CHAOTIC:	pop_call(); return "save vs. chaotic spells";		
		case APPLY_SAVE_CHARM:		pop_call(); return "save vs. charm spells";		
		case APPLY_SAVE_COLD:			pop_call(); return "save vs. cold";		
		case APPLY_SAVE_COMPULSION:	pop_call(); return "save vs. compulsion spells";		
		case APPLY_SAVE_DARKNESS:	pop_call(); return "save vs. darkness spells";			
		case APPLY_SAVE_DEATH:		pop_call(); return "save vs. death magic";			
		case APPLY_SAVE_DISEASE:	pop_call(); return "save vs. disease";			
		case APPLY_SAVE_EARTH:		pop_call(); return "save vs. earth spells";	
		case APPLY_SAVE_ELECTRIC:	pop_call(); return "save vs. electricity";	
		case APPLY_SAVE_EVIL:			pop_call(); return "save vs. evil spells";	
		case APPLY_SAVE_FEAR:			pop_call(); return "save vs. fear";	
		case APPLY_SAVE_FIRE:			pop_call(); return "save vs. fire";	
		case APPLY_SAVE_FORCE:		pop_call(); return "save vs. force spells";	
		case APPLY_SAVE_GOOD:			pop_call(); return "save vs. good spells";	
		case APPLY_SAVE_HEALING:	pop_call(); return "save vs. healing energy";	
		case APPLY_SAVE_ILLUSION:	pop_call(); return "save vs. illusion";			
		case APPLY_SAVE_LAWFUL:		pop_call(); return "save vs. lawful spells";		
		case APPLY_SAVE_LIGHT:		pop_call(); return "save vs. light spells";	
		case APPLY_SAVE_MAGIC:		pop_call(); return "save vs. magic";	
		case APPLY_SAVE_MIND:			pop_call(); return "save vs. mind affects";	
		case APPLY_SAVE_NEGATIVE:	pop_call(); return "save vs. negative energy";
		case APPLY_SAVE_PARALYSIS:pop_call(); return "save vs. paralysis";		
		case APPLY_SAVE_PETRI:		pop_call(); return "save vs. pretification";		
		case APPLY_SAVE_POISON:		pop_call(); return "save vs. poison";		
		case APPLY_SAVE_POLYMORPH:pop_call(); return "save vs. polymorph";		
		case APPLY_SAVE_SLEEP:		pop_call(); return "save vs. sleep";	
		case APPLY_SAVE_SONIC:		pop_call(); return "save vs. sonic spells";	
		case APPLY_SAVE_WATER:		pop_call(); return "save vs. water spells";
		case APPLY_SR_GOOD:				pop_call(); return "spell resist vs. good";
		case APPLY_SR_EVIL:				pop_call(); return "spell resist vs. evil";
		case APPLY_SR_LAW:				pop_call(); return "spell resist vs. lawful";
		case APPLY_SR_CHAOS:			pop_call(); return "spell resist vs. chaotic";
		case APPLY_FAST_HEALING:	pop_call(); return "fast healing";
		case APPLY_REGENERATION:	pop_call(); return "regeneration";
		case APPLY_TURN_RESIST:		pop_call(); return "turn resistance";
		case APPLY_DARKVISION:		pop_call(); return "darkvision";
		case APPLY_LOWLIGHT_VISION:	pop_call(); return "lowlight vision";
		case APPLY_CONCEALMENT:		pop_call(); return "concealment";
		case APPLY_FORTIFICATION:	pop_call(); return "fortification";
		case APPLY_ROOM_SECTOR:		pop_call(); return "room sector value";
		case APPLY_ROOM_LIGHT:		pop_call(); return "room lighting";
	}

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


/*
 * See if skill/spell improves
 * Disabled in Mud20, because skills don't improve with use
 * but left in case someone really wants to reuse it - Kregor
 */
void check_improve( CHAR_DATA *ch, int sn )
{
	int cnt, adept;

	push_call("check_improve(%p, %d)",ch, sn);

	if (ch == NULL || IS_NPC(ch))
	{
		pop_call();
		return;
	}

	if (ch->learned[sn] == 0)
	{
		pop_call();
		return;
	}

	if (ch->pcdata->last_improve + 300 > mud->current_time || ch->pcdata->last_connect + 300 > mud->current_time)
	{
		pop_call();
		return;
	}

	if (number_range(1, 400) >= get_curr_int(ch))
	{
		pop_call();
		return;
	}

	adept = ch->level + 3;

	if ((cnt = multi(ch , sn)) == -1)
	{
		adept /= 2;
	}

	if (ch->learned[sn] < adept)
	{
		ch_printf_color(ch, "You became better at %s.\n\r", skill_table[sn].name);
		ch->learned[sn]++;
		ch->pcdata->last_improve = mud->current_time;
	}
	pop_call();
	return;
}


void char_reset(CHAR_DATA *ch)
{
	OBJ_DATA *obj;
	AFFECT_DATA *paf;
	int cnt;

	push_call("char_reset(%p)",ch);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}
	
	ch->armor					= apply_ac(ch);
	ch->armor_type 		= apply_armor_type(ch);
	ch->affected_by		= 0;
	ch->affected2_by	= 0;
// 	ch->initiative		= 0;
	ch->furniture			= NULL;
	ch->hitched				= NULL;

	apply_dex_max(ch);
	free_cast(ch);
	free_skill(ch);

	for (cnt = 0 ; *skill_table[cnt].name != '\0' ; cnt++)
	{
		ch->affected_sn[cnt] = 0;
	}

	// object applies only count if not shapechanged
	if (!IS_EQ_MELDED(ch))
	{
		for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
		{
			if (IS_WORN(obj))
			{
				for (paf = obj->first_affect ; paf ; paf = paf->next)
				{
					switch(paf->location)
					{
						case APPLY_SEX:
							break;
						default:
							affect_modify(ch, paf, TRUE);
							break;
					}
				}
			}
		}
	}

	for (paf = ch->first_affect ; paf ; paf = paf->next)
	{
		switch(paf->location)
		{
			case APPLY_SEX:
				break;
			default:
				affect_modify(ch, paf, TRUE);
				break;
		}
	}

	for (cnt = 0 ; cnt < MAX_APPLY ; cnt++)
	{
		ch->apply[cnt] = calc_apply(ch, cnt);
	}

	apply_immunity_energy(ch);
	apply_immunity_sdesc(ch);

	/* Fix actuals */
	if (!IS_NPC(ch) && !IS_IMMORTAL(ch))
	{
		int new_hit = 0;
		int new_move = 0;
		int new_class = 0;

		for ( cnt = 1 ; cnt <= ch->level ; cnt++ )
		{
			new_class = ch->pcdata->class_level[cnt];

			if (ch->pcdata->hit_level[cnt] > 0)
				new_hit += ch->pcdata->hit_level[cnt];
			else if (new_class == CLASS_MONSTER)
			{
				ch->pcdata->hit_level[cnt] = dice(1, race_type_table[race_type(ch)].hit_die);
				new_hit += ch->pcdata->hit_level[cnt];
			}
			else
			{
				ch->pcdata->hit_level[cnt] = dice(1, class_table[new_class].hp_max);
				new_hit += ch->pcdata->hit_level[cnt];
			}
			
			if (ch->pcdata->move_level[cnt] > 0)
				new_move += ch->pcdata->move_level[cnt];
			else if (new_class == CLASS_MONSTER)
			{
				ch->pcdata->move_level[cnt] = dice(1, race_type_table[race_type(ch)].hit_die / 2);
				new_move += ch->pcdata->move_level[cnt];
			}
			else
			{
				ch->pcdata->move_level[cnt] = dice(1, class_table[new_class].hp_max / 2);
				new_move += ch->pcdata->move_level[cnt];
			}
		}
		ch->max_hit = new_hit;
		ch->max_move = new_move;
	}
	pop_call();
	return;
}


/*
 * Expanded on old mud.h define - Kregor
 */
bool IS_BLIND( CHAR_DATA *ch )
{
	push_call("IS_BLIND(%p)",ch);
	
	if (IS_AFFECTED(ch, AFF_BLIND))
	{
		pop_call();
		return TRUE;
	}

	if (race_skill(ch, gsn_light_blindness))
	{
		if (!learned(ch, gsn_daylight_adaptation))
		{
			if (ch->in_room && get_room_light(ch->in_room) == LIGHT_BRIGHT)
			{
				pop_call();
				return TRUE;
			}
		}
	}
	
	pop_call();
	return FALSE;
}

/*
 * Many conditions lead to this affect - Kregor
 */
bool IS_DAZZLED( CHAR_DATA *ch )
{
	push_call("IS_DAZZLED(%p)",ch);
	
	if (IS_AFFECTED(ch, AFF2_DAZZLED))
	{
		pop_call();
		return TRUE;
	}

	if (race_skill(ch, gsn_light_blindness))
	{
		if (!learned(ch, gsn_daylight_adaptation))
		{
			if (ch->in_room && get_room_light(ch->in_room) >= LIGHT_NORMAL)
			{
				pop_call();
				return TRUE;
			}
		}
		else
		{
			if (ch->in_room && get_room_light(ch->in_room) == LIGHT_BRIGHT)
			{
				pop_call();
				return TRUE;
			}
		}
	}
	
	if (race_skill(ch, gsn_light_sensitivity))
	{
		if (!learned(ch, gsn_daylight_adaptation))
		{
			if (ch->in_room && get_room_light(ch->in_room) == LIGHT_BRIGHT)
			{
				pop_call();
				return TRUE;
			}
		}
	}
	pop_call();
	return FALSE;
}


/*
	True if char can see victim.
*/
bool can_see( CHAR_DATA *ch, CHAR_DATA *victim )
{
	ROOM_INDEX_DATA *ch_room, *vict_room;
	int ch_light, vict_light;

	push_call("can_see(%p,%p)",ch,victim);

	if (ch == victim)
	{
		pop_call();
		return TRUE;
	}
	if ((ch_room = ch->in_room) == NULL || (vict_room = victim->in_room) == NULL) //crash protection
	{
		pop_call();
		return FALSE;
	}

	if (IS_NPC(victim) && IS_PLR(ch, PLR_BUILDLIGHT) && show_build_vnum(ch, victim->pIndexData->vnum))
	{
		pop_call();
		return TRUE;
	}

	if (IS_PLR(ch, PLR_HOLYLIGHT) && get_trust(victim) <= get_trust(ch))
	{
		pop_call();
		return TRUE;
	}

	if (IS_PLR(victim, PLR_WIZINVIS))
	{
		pop_call();
		return FALSE;
	}

	if (IS_ACT(ch, ACT_MOBINVIS) && ch->desc == NULL)
	{
		pop_call();
		return TRUE;
	}

	if (IS_ACT(victim, ACT_MOBINVIS))
	{
		if (victim->pIndexData->vnum == MOB_VNUM_WIZARD_EYE && is_affected(ch, gsn_detect_scrying))
		{
			pop_call();
			return TRUE;
		}
		pop_call();
		return FALSE;
	}
	
	if (race_skill(ch, gsn_blindsight) || domain_apotheosis(ch, DOMAIN_CAVERN))
	{
		pop_call();
		return TRUE;
	}
	
	if (IS_BLIND(ch))
	{
		pop_call();
		return FALSE;
	}
	
	/* added exception for hide, because according to SRD, it doesn't work against hide - Kregor */
	if (IS_AFFECTED(ch, AFF_TRUESIGHT) && !IS_AFFECTED(victim, AFF_HIDE))
	{
		pop_call();
		return TRUE;
	}

	ch_light = get_room_light(ch_room);
	vict_light = get_room_light(vict_room);
	
	// blacklight negates faerie fire and everything else below
	if (ch_light == LIGHT_DEEP_DARK || vict_light == LIGHT_DEEP_DARK)
	{
		pop_call();
		return FALSE;
	}
	
	if (vict_light == LIGHT_BRIGHT && race_skill(victim, gsn_light_invisibility))
	{
		pop_call();
		return FALSE;
	}
	
	// faerie fire negates all the below if in same room - Kregor
	if (!is_affected(victim, gsn_faerie_fire) || ch_room != vict_room)
	{
		if (!can_see_in_room(ch, ch->in_room) || !can_see_in_room(ch, victim->in_room))
		{
			pop_call();
			return FALSE;
		}
	
		if (IS_AFFECTED(victim, AFF_INVISIBLE))
		{
			if (!IS_AFFECTED(ch, AFF_DETECT_INVIS) || ch_room != vict_room)
			{
				pop_call();
				return FALSE;
			}
			if (IS_AFFECTED(victim, AFF_NONDETECTION)) // nondetect trumps det invis, but not true sight
			{
				pop_call();
				return FALSE;
			}
		}
	}

	// make passive attempts to spot hidden chars once per room
	// members of a group always know where their members are.
	if (IS_AFFECTED(victim, AFF_HIDE) && !is_same_group(ch, victim))
	{
		if (ch_room != vict_room) //keep it simple and prevent needless loops - Kregor
		{
			pop_call();
			return FALSE;
		}
		if (!IS_NPC(ch))
		{
			if (!IS_NPC(victim))
			{
				switch (ch->pcdata->found_pvnum[victim->pcdata->pvnum])
				{
					case -1:
						pop_call();
						return FALSE;
					case 0:
						if (hide_check(victim, ch, hide_roll(victim), perception_roll(ch, SENSE_SIGHT)))
						{
							ch->pcdata->found_pvnum[victim->pcdata->pvnum] = -1;
							pop_call();
							return FALSE;
						}
						else
						{
							ch->pcdata->found_pvnum[victim->pcdata->pvnum] = 1;
						}
						break;
					default:
						break;
				}
			}
			else
			{
				switch (ch->pcdata->found_vnum[victim->pIndexData->vnum])
				{
					case -1:
						pop_call();
						return FALSE;
					case 0:
						if (hide_check(victim, ch, hide_roll(victim), perception_roll(ch, SENSE_SIGHT)))
						{
							ch->pcdata->found_vnum[victim->pIndexData->vnum] = -1;
							pop_call();
							return FALSE;
						}
						else
						{
							ch->pcdata->found_vnum[victim->pIndexData->vnum] = 1;
						}
						break;
					default:
						break;
				}
			}
		}
		else
		{
			if (hide_check(victim, ch, hide_roll(victim), perception_roll(ch, SENSE_SIGHT)))
			{
				pop_call();
				return FALSE;
			}
		}
	}
	pop_call();
	return TRUE;
}

/*
	True if char can see victim in who list and alike
*/
bool can_see_world( CHAR_DATA *ch, CHAR_DATA *victim )
{
	push_call("can_see_world(%p,%p)",ch,victim);

	if (ch == victim)
	{
		pop_call();
		return TRUE;
	}

	if (IS_ACT(victim, ACT_MOBINVIS))
	{
		if (!IS_NPC(ch) && !IS_IMMORTAL(ch) && !can_olc_modify(ch, victim->pIndexData->vnum))
		{
			pop_call();
			return FALSE;
		}
	}

	if (NEW_AUTH(victim))
	{
		if (NEW_AUTH(ch) || IS_IMMORTAL(ch) || IS_NPC(ch) || PLR_FUNCTION(ch, FUNCTION_HELPER))
		{
			pop_call();
			return TRUE;
		}
	}
		
	if (IS_PLR(ch, PLR_HOLYLIGHT) && get_trust(victim) <= get_trust(ch))
	{
		pop_call();
		return TRUE;
	}

	if (IS_PLR(victim, PLR_WIZINVIS))
	{
		pop_call();
		return FALSE;
	}

	if (IS_ACT(ch, ACT_MOBINVIS))
	{
		pop_call();
		return TRUE;
	}

	if (IS_PLR(victim, PLR_WIZCLOAK))
	{
		pop_call();
		return FALSE;
	}

	if (IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return TRUE;
	}

	pop_call();
	return TRUE;
}

/*
 * True if char can see in fogged rooms.
 * Normal vision gets a 50% chance, equal to
 * concealment miss chance.
 */
bool can_see_smoke( CHAR_DATA *ch )
{
	push_call("can_see_smoke(%p)",ch);

	if (IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return TRUE;
	}
	
	if (race_skill(ch, gsn_blindsight) || domain_apotheosis(ch, DOMAIN_CAVERN))
	{
		pop_call();
		return TRUE;
	}

	if (number_percent() > 50)
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}


/*
	True if char can see hidden exits
*/
bool can_see_hidden( CHAR_DATA *ch, int dir )
{
	EXIT_DATA *pExit;
	int vnum, dc;
	
	push_call("can_see_hidden(%p,%p)",ch,dir);

	if (!ch->in_room)
	{
		pop_call();
		return FALSE;
	}
	
	vnum = ch->in_room->vnum;

	if ((pExit = get_exit(vnum, dir)) == NULL)
	{
		pop_call();
		return FALSE;
	}
	
// 	if (IS_NPC(ch))
// 	{
// 		pop_call();
// 		return TRUE;
// 	}
// 	
		
	if (IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return TRUE;
	}

	if (IS_AFFECTED(ch, AFF_DETECT_HIDDEN))
	{
		pop_call();
		return TRUE;
	}

	if (IS_AFFECTED(ch, AFF_TRUESIGHT))
	{
		pop_call();
		return TRUE;
	}

	/* 
	 * set to where you only have to find a hidden exit once,
	 * from then on you remember - Kregor
	 */
	if (IS_SET(pExit->exit_info, EX_HIDDEN))
	{
		dc = 20;
		
		if (!IS_NPC(ch) && IS_SET(ch->pcdata->found_dir[vnum], 1 << dir))
		{
			pop_call();
			return TRUE;
		}
		else if (race_skill(ch, gsn_blindsight) || domain_apotheosis(ch, DOMAIN_CAVERN))
		{
			act( "You sense a hidden door to the $t",ch, dir_name[dir], NULL, TO_CHAR);
			if (!IS_NPC(ch))
				SET_BIT(ch->pcdata->found_dir[vnum], 1 << dir);
			pop_call();
			return TRUE;
		}
		else if (IS_RACE(ch, RACE_ELF) && perception_check(ch, NULL, perception_roll(ch, SENSE_SIGHT), dc))
		{
			act( "You spot a hidden door to the $t",ch, dir_name[dir], NULL, TO_CHAR);
			if (!IS_NPC(ch))
				SET_BIT(ch->pcdata->found_dir[vnum], 1 << dir);
			pop_call();
			return TRUE;
		}
		else if ((race_skill(ch, gsn_stonecunning) || domain_skill(ch, gsn_stonecunning))
		&& (ch->in_room->sector_type == SECT_MOUNTAIN
		|| ch->in_room->sector_type == SECT_DEEP_EARTH
		|| ch->in_room->sector_type == SECT_UNDER_GROUND)
		&& perception_check(ch, NULL, perception_roll(ch, SENSE_SIGHT) + (race_skill(ch, gsn_stonecunning) && domain_skill(ch, gsn_stonecunning)) ? 4 : 0, dc))
		{
			act( "You spot a hidden door to the $t",ch, dir_name[dir], NULL, TO_CHAR);
			if (!IS_NPC(ch))
				SET_BIT(ch->pcdata->found_dir[vnum], 1 << dir);
			pop_call();
			return TRUE;
		}
	}
	pop_call();
	return FALSE;
}

/*
	True if char can hear victim.
*/
bool can_hear( CHAR_DATA *ch, CHAR_DATA *victim )
{
	push_call("can_hear(%p,%p)",ch,victim);

	if (ch != NULL)
	{
		pop_call();
		return FALSE;
	}

	if (ch == victim)
	{
		pop_call();
		return TRUE;
	}

	if (IS_PLR(ch, PLR_HOLYLIGHT) && get_trust(victim) <= get_trust(ch))
	{
		pop_call();
		return TRUE;
	}
	
	if (IS_AFFECTED(ch, AFF_DEAF))
	{
		pop_call();
		return FALSE;
	}

	if (IS_ACT(victim, ACT_MOBINVIS))
	{
		pop_call();
		return FALSE;
	}

	if (IS_PLR(victim, PLR_WIZINVIS|PLR_WIZCLOAK))
	{
		pop_call();
		return FALSE;
	}

	if (IS_INCORPOREAL(victim))
	{
		pop_call();
		return FALSE;
	}

	if (IS_AFFECTED(victim, AFF_SNEAK) && sneak_check(victim, ch, sneak_roll(victim), 0))
	{
		pop_call();
		return FALSE;
	}

	pop_call();
	return TRUE;
}

/*
	True if char can see obj
*/
bool can_see_obj( CHAR_DATA *ch, OBJ_DATA *obj )
{
	push_call("can_see_obj(%p,%p)",ch,obj);

	if (show_build_vnum(ch, obj->pIndexData->vnum))
	{
		pop_call();
		return TRUE;
	}

	if (!IS_NPC(ch))
	{
		if (!IS_PLR(ch, PLR_HOLYLIGHT) && IS_SET(obj->extra_flags, ITEM_ETHEREAL))
		{
			pop_call();
			return FALSE;
		}

		if (IS_PLR(ch, PLR_HOLYLIGHT))
		{
			pop_call();
			return TRUE;
		}
	}

	if (IS_ACT(ch, ACT_MOBINVIS))
	{
		pop_call();
		return TRUE;
	}
	
	if (is_trap_armed(obj) && !IS_OWNER(obj, ch) && !is_affected(ch, gsn_foresight))
	{
		pop_call();
		return FALSE;
	}

	if (IS_SET(obj->extra_flags, ITEM_HIDDEN))
	{
		pop_call();
		return FALSE;
	}

	if (IS_SET(obj->extra_flags, ITEM_CONCEALED) && obj->carried_by != ch)
	{
		pop_call();
		return FALSE;
	}	

	if (obj->carried_by && obj->carried_by != ch && !can_see(ch, obj->carried_by))
	{
		pop_call();
		return FALSE;
	}	

	if (IS_BLIND(ch))
	{
		pop_call();
		return FALSE;
	}

	if (IS_AFFECTED(ch, AFF_TRUESIGHT))
	{
		pop_call();
		return TRUE;
	}

	if (obj->item_type == ITEM_LIGHT && obj->value[2] != 0)
	{
		if (!obj->value[3] || IS_BURNING(obj))
		{
			pop_call();
			return TRUE;
		}
	}

	if (obj->item_type == ITEM_FIRE && obj->value[2] != 0)
	{
		pop_call();
		return TRUE;
	}

	if (!can_see_in_room(ch, ch->in_room))
	{
		if (!IS_SET(obj->extra_flags, ITEM_GLOW) && !IS_BURNING(obj))
		{
			pop_call();
			return FALSE;
		}
	}

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

/*
	True if char can drop obj.
*/
bool can_drop_obj( CHAR_DATA *ch, OBJ_DATA *obj )
{
	push_call("can_drop_obj(%p,%p)",ch,obj);

	if (!IS_IMMORTAL(ch) && IS_SET(obj->extra_flags, ITEM_NODROP))
	{
		pop_call();
		return FALSE;
	}

	if (IS_SET(obj->extra_flags, ITEM_CONCEALED))
	{
		REMOVE_BIT(obj->extra_flags, ITEM_CONCEALED);
		act ("You produce $p from its concealment.", ch, obj, NULL, TO_CHAR);
		act ("$n produces $p from its concealment.", ch, obj, NULL, TO_ROOM);
	}

	pop_call();
	return TRUE;
}

/*
	Return ascii name of an item type.
*/

char *item_type_name( OBJ_DATA *obj )
{
	push_call("item_type_name(%p)",obj);

	switch ( obj->item_type )
	{
		case ITEM_LIGHT:
			pop_call();
			return "light";
		case ITEM_FIRE:
			pop_call();
			return "fire";
		case ITEM_SCROLL:
			pop_call();
			return "scroll";
		case ITEM_WAND:
			pop_call();
			return "wand";
		case ITEM_STAFF:      
			pop_call();
			return "staff";
		case ITEM_WEAPON:     
			pop_call();
 			return "weapon";
		case ITEM_TREASURE:   
			pop_call();
 			return "treasure";
		case ITEM_ARMOR:      
			pop_call();
 			return "armor";
		case ITEM_SPELLBOOK:      
			pop_call();
 			return "spellbook";
		case ITEM_SYMBOL:      
			pop_call();
 			return "symbol";
		case ITEM_POTION:     
			pop_call();
 			return "potion";
		case ITEM_FURNITURE:  
			pop_call();
 			return "furniture";
		case ITEM_TRASH:      
			pop_call();
 			return "trash";
		case ITEM_QUIVER:  
			pop_call();
 			return "quiver";
		case ITEM_CONTAINER:  
			pop_call();
 			return "container";
		case ITEM_SPELLPOUCH:  
			pop_call();
 			return "spellpouch";
		case ITEM_SHEATH:  
			pop_call();
 			return "sheath";
		case ITEM_CART:  
			pop_call();
 			return "cart";
		case ITEM_DRINK_CON:  
			pop_call();
 			return "drink container";
		case ITEM_KEY:        
			pop_call();
 			return "key";
		case ITEM_CRAFT:        
			pop_call();
 			return "craft in progress";
		case ITEM_PIECE:        
			pop_call();
 			return "piece of something";
		case ITEM_FOOD:       
			pop_call();
 			return "food";
		case ITEM_MONEY:      
			pop_call();
 			return "money";
		case ITEM_BOAT:       
			pop_call();
 			return "boat";
		case ITEM_CORPSE_NPC: 
			pop_call();
 			return "npc corpse";
		case ITEM_CORPSE_PC:  
			pop_call();
 			return "pc corpse";
		case ITEM_FOUNTAIN:   
			pop_call();
 			return "fountain";
		case ITEM_PILL:       
			pop_call();
 			return "pill";
		case ITEM_COMPONENT:       
			pop_call();
 			return "component";
		case ITEM_PORTAL:     
			pop_call();
 			return "portal";
 		case ITEM_WINDOW:
 			pop_call();
 			return "window";
 		case ITEM_TOOLS:
 			pop_call();
 			return "tools";
		case ITEM_AMMO:       
			pop_call();
 			return "ammunition";
 		case ITEM_TOTEM:
 			pop_call();
 			return "totem";
 		case ITEM_PAPER:
 			pop_call();
 			return "paper";
 		case ITEM_BOOK:
 			pop_call();
 			return "book";
 		case ITEM_TRAP:
 			pop_call();
 			return "trap";
	}

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

	pop_call();
	return "(unknown)";
}

/*
 * scrapped old light/sight system, simplified to more D20 feel
 * calc_room_light chaches in individual room as adj to light
 * based on chars and contents. Added to get_room_light below,
 * which is compared to can_see_in_room based on the type of
 * vision the character has - Kregor
 */
 
 /*
  * calcs and caches the individual light sources in a room - Kregor
  * should be called at any affect_modify and char_to_room to reflect
  * new light sources coming and going.
  */
int calc_room_light( CHAR_DATA *ch, OBJ_DATA *object, ROOM_INDEX_DATA *room, bool fAdd )
{
	CHAR_DATA *rch, *rch_next;
	OBJ_DATA *obj, *obj_next;
	int light, dark;

	push_call("calc_room_light(%p,%p,%p)",ch,obj,room);

	light = dark = 0;
	
	for (obj = room->first_content ; obj != NULL ; obj = obj_next)
	{
		obj_next = obj->next_content;
		
		if (obj && obj == object && !fAdd)
			continue;

		if (obj->in_obj || obj->carried_by)
			continue;

		if (obj->item_type == ITEM_LIGHT)
		{
			if (obj->value[3] && !IS_BURNING(obj))
				continue;
			if (!obj->value[0])
			{
				light = UMAX(1, light);
			}
			else
			{
				light = UMAX(obj->value[0], light);
			}
			continue;
		}
		if (obj->item_type == ITEM_FIRE)
		{
			if (obj->value[2] == 0)
				continue;
			light = UMAX(1, light);
			continue;
		}
		if (IS_OBJ_STAT(obj, ITEM_GLOW) || IS_BURNING(obj))
		{
			light = UMAX(UMAX(1,obj->apply[APPLY_ROOM_LIGHT]), light);
			continue;
		}
	}
	
	for (rch = room->first_person ; rch != NULL ; rch = rch_next)
	{
		rch_next = rch->next_in_room;

		if (ch && !fAdd && ch == rch)
			continue;
			
		if (IS_PLR(rch, PLR_HOLYLIGHT) && !IS_PLR(rch, PLR_WIZINVIS))
			light = UMAX(3, light);

		if (domain_apotheosis(rch, DOMAIN_SUN))
			light = UMAX(2, light);

		for (obj = rch->first_carrying ; obj != NULL ; obj = obj_next)
		{
			obj_next = obj->next_content;
			
			if (!IS_WORN(obj))
				continue;

			if (obj->item_type == ITEM_LIGHT)
			{
				if (obj->value[3] && !IS_BURNING(obj))
					continue;
				if (!obj->value[0])
				{
					light = UMAX(1, light);
				}
				else
				{
					light = UMAX(obj->value[0], light);
				}
				continue;
			}
			if (obj->item_type == ITEM_FIRE)
			{
				if (obj->value[2] == 0)
					continue;
				light = UMAX(1, light);
				continue;
			}
			if (IS_OBJ_STAT(obj, ITEM_GLOW) || IS_BURNING(obj))
			{
				light = UMAX(UMAX(1,obj->apply[APPLY_ROOM_LIGHT]), light);
				continue;
			}
		}
	}
	pop_call();
	return UMIN(light, 2);
}
	
int get_room_light(ROOM_INDEX_DATA *room)
{
	int light;

	push_call("get_room_light(%p)",room);

	if (!IS_SET(sector_table[room->sector_type].flags, SFLAG_NOWEATHER) && !IS_SET(room->room_flags, ROOM_INDOORS))
	{
		switch (mud->sunlight)
		{
			case SUN_DARK:
				light = LIGHT_DIM;
				break;
			case SUN_NOON:
				light = LIGHT_BRIGHT;
				break;
			default:
				light = LIGHT_NORMAL;
				break;
		}
		switch (room->area->weather_info->sky)
		{
			default:
				break;
			case SKY_CLOUDY:
				if (mud->sunlight == SUN_DARK)
					light = LIGHT_DARKNESS;
				break;
			case SKY_RAINING:
			case SKY_LIGHTNING:
				light = UMAX(LIGHT_DARKNESS, light - 1);
				break;
		}
	}
	else
	{
		light = sector_table[room->sector_type].light;
	}
	
	if (IS_SET(room->room_flags, ROOM_BLACKLIGHT))
	{
		light = UMAX(LIGHT_DEEP_DARK, light - 2);
	}
	else if (IS_SET(room->room_flags, ROOM_DARK))
	{
		light = UMAX(LIGHT_DARKNESS, light - 1);
	}
	
	if (IS_SET(room->room_flags, ROOM_DAYLIGHT))
	{
		light = UMIN(light + 2, LIGHT_BRIGHT);
	}
	else if (IS_SET(room->room_flags, ROOM_LIGHT))
	{
		light = UMIN(light + 1, LIGHT_NORMAL);
	}

	//magical darkness cannot be pierced by any light besdies daylight
	if (light != LIGHT_DEEP_DARK)
	{
		//only daylight spell can raise beyond normal light
		if (light < LIGHT_BRIGHT && room->light < 2)
		{
			light = UMIN(light + room->light, LIGHT_NORMAL);
		}
		else
		{
			light = UMIN(light + room->light, LIGHT_BRIGHT);
		}			
	}
	else if (room->light > 1)
	{
		light = UMIN(light + room->light, LIGHT_NORMAL);
	}
	pop_call();
	return URANGE(LIGHT_DEEP_DARK, light, LIGHT_BRIGHT);
}

/*
 * True if char can see in the light conditions of the room
 */
bool can_see_in_room( CHAR_DATA *ch, ROOM_INDEX_DATA *room)
{
	push_call("can_see_in_room(%p,%p)",ch,room);

	if (room == NULL)
	{
		pop_call();
		return FALSE;
	}
	
	if (IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return TRUE;
	}

	if (IS_ACT(ch, ACT_MOBINVIS))
	{
		pop_call();
		return TRUE;
	}

	if (race_skill(ch, gsn_blindsight) || domain_apotheosis(ch, DOMAIN_CAVERN))
	{
		pop_call();
		return TRUE;
	}

	if (IS_BLIND(ch))
	{
		pop_call();
		return FALSE;
	}

	if (IS_AFFECTED(ch, AFF_TRUESIGHT))
	{
		pop_call();
		return TRUE;
	}

	switch (get_room_light(room))
	{
		case LIGHT_DEEP_DARK:
			if (IS_AFFECTED(ch, AFF_SEE_DARKNESS)
			|| class_level(ch, CLASS_SHADOW_ADEPT) >= 7
			|| domain_apotheosis(ch, DOMAIN_DARKNESS))
			{
				pop_call();
				return TRUE;
			}
			break;
		case LIGHT_DARKNESS:
			if (get_apply(ch, APPLY_DARKVISION)
			|| IS_AFFECTED(ch, AFF_SEE_DARKNESS)
			|| class_level(ch, CLASS_SHADOW_ADEPT) >= 7
			|| domain_apotheosis(ch, DOMAIN_DARKNESS))
			{
				pop_call();
				return TRUE;
			}
			break;
		case LIGHT_BRIGHT:
			if (race_skill(ch, gsn_light_blindness))
			{
				pop_call();
				return FALSE;
			}
		default:
			pop_call();
			return TRUE;
	}

	pop_call();
	return FALSE;
}

/*
 * How many rooms away can the character see?
 */
int get_sight(CHAR_DATA *ch, ROOM_INDEX_DATA *room)
{
	int sight;

	push_call("get_sight(%p)",ch);
	
	if (!ch->in_room)
	{
		pop_call();
		return 0;
	}
	
	if (IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return 6;
	}
	
	//start with line of sight for sector type
	sight = sector_table[room->sector_type].sight;
	
	//modify it based on lighting conditions
	switch (get_room_light(room))
	{
		case LIGHT_DEEP_DARK:
			if (!IS_AFFECTED(ch, AFF_SEE_DARKNESS)
			&& class_level(ch, CLASS_SHADOW_ADEPT) < 7
			&& !domain_apotheosis(ch, DOMAIN_DARKNESS))
				sight = 0;
			break;
		case LIGHT_DARKNESS:
			if (!IS_AFFECTED(ch, AFF_SEE_DARKNESS)
			&& class_level(ch, CLASS_SHADOW_ADEPT) < 7
			&& !domain_apotheosis(ch, DOMAIN_DARKNESS))
				sight = get_apply(ch, APPLY_DARKVISION) / 30;
			break;
		case LIGHT_DIM:
			if (get_apply(ch, APPLY_LOWLIGHT_VISION) == 0)
				sight /= 2;
			break;
		case LIGHT_BRIGHT:
			if (race_skill(ch, gsn_light_sensitivity))
				sight = 1;
			break;
		default:
			break;
	}

	// true sight pierces darkness and fog and etc. up to 120 ft.
	if (IS_AFFECTED(ch, AFF_TRUESIGHT) && !IS_BLIND(ch))
	{
		pop_call();
		return UMAX(4, sight);
	}
	
	// Duh?
	if (IS_BLIND(ch))
		sight = 0;
		
	if (IS_SET(room->room_flags, ROOM_FOG))
		sight = 0;
	
	// if regular vision is less, blindsight has line of effect
	sight = UMAX(race_skill(ch, gsn_blindsight) / 30, sight);
	
	if (domain_apotheosis(ch, DOMAIN_CAVERN))
		sight = UMAX(2, sight);

	pop_call();
	return sight;
}

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

	push_call("obj_level_estimate(%p)",objIndex);

	lvl_neg = 0;
	lvl_pos = 1;

	for (aff = objIndex->first_affect ; aff ; aff = aff->next)
	{
		switch (aff->location)
		{
			case APPLY_STR:
			case APPLY_WIS:
			case APPLY_CON:
			case APPLY_DEX:
			case APPLY_INT:
				if (aff->modifier > 0)
				{
					lvl_pos += aff->modifier * aff->modifier * 4/3;
				}
				else
				{
					lvl_neg += aff->modifier * aff->modifier * 3/4;
				}
				break;
			case APPLY_MANA:
				if (aff->modifier > 0)
				{
					lvl_pos += aff->modifier / 3;
				}
				break;
			case APPLY_HIT:
				if (aff->modifier > 0)
				{
					lvl_pos += aff->modifier / 2;
				}
				else
				{
					lvl_neg -= aff->modifier / 5;
				}
				break;
			case APPLY_MOVE:
				if (aff->modifier > 0)
				{
					lvl_pos += aff->modifier / 4;
				}
				break;
			case APPLY_SHIELD:
			case APPLY_ARMOR:
			case APPLY_ENHANCE_AC:
			case APPLY_DEFLECT:
			case APPLY_DODGE:
				if (aff->modifier > 0)
				{
					lvl_pos += aff->modifier * aff->modifier / 4;
				}
				else
				{
					lvl_neg -= aff->modifier * aff->modifier / 8;
				}
				break;
			case APPLY_HITROLL:
				if (aff->modifier > 0)
				{
					lvl_pos += aff->modifier * aff->modifier / 2;
				}
				break;
			case APPLY_DAMROLL:
				if (aff->modifier > 0)
				{
					lvl_pos += aff->modifier * aff->modifier;
				}
				break;
			case APPLY_SAVING_FORT:
			case APPLY_SAVING_REFL:
			case APPLY_SAVING_WILL:
			case APPLY_SAVES:
				if (aff->modifier < 0)
				{
					lvl_pos += aff->modifier * aff->modifier / 3;
				}
				else
				{
					lvl_neg += aff->modifier * aff->modifier / 6;
				}
				break;
			default:
				break;
		}
	}

	value[0] = objIndex->value[0];
	value[1] = objIndex->value[1];
	value[2] = objIndex->value[2];
	value[3] = objIndex->value[3];

	switch(objIndex->item_type)
	{
		case ITEM_FIRE:
		case ITEM_LIGHT:
		case ITEM_COMPONENT:
			break;
		case ITEM_SCROLL:
			if (value[1] > 0 && valid_skill(value[1]))
			{
				lvl_pos += value[0] / 2;
			}
			break;
		case ITEM_POTION:
			if (value[1] > 0 && valid_skill(value[1]))
			{
				lvl_pos += value[0] / 2;
			}
			break;
		case ITEM_PILL:
			if (value[1] > 0 && valid_skill(value[1]))
			{
				lvl_pos += value[0] / 2;
			}
			if (value[2] > 0 && valid_skill(value[2]))
			{
				lvl_pos += value[0] / 2;
			}
			if (value[3] > 0 && valid_skill(value[3]))
			{
				lvl_pos += value[0] / 2;
			}
			break;
		case ITEM_WAND:
			lvl_pos += value[0]*value[1] / 4;
			break;
		case ITEM_STAFF:
			lvl_pos += value[0]*value[1] / 6;
			break;
		case ITEM_WEAPON:
		case ITEM_ARMOR:
		case ITEM_AMMO:
			break;
		case ITEM_TOOLS:
			lvl_pos += value[1] / 10;
			lvl_pos += value[2];
			break;
		case ITEM_CONTAINER:
		case ITEM_SPELLPOUCH:
		case ITEM_SHEATH:
		case ITEM_QUIVER:
		case ITEM_CART:
			lvl_pos += value[0] / 15;
			break;
		default:
			break;
	}

	if ((objIndex->extra_flags & ITEM_EVIL) != 0)
	{
		lvl_neg += (lvl_pos / 10);
	}
	if ((objIndex->extra_flags & ITEM_GOOD) != 0)
	{
		lvl_neg += (lvl_pos / 10);
	}
	if ((objIndex->extra_flags & ITEM_CHAOTIC) != 0)
	{
		lvl_neg += (lvl_pos / 10);
	}
	if ((objIndex->extra_flags & ITEM_LAWFUL) != 0)
	{
		lvl_neg += (lvl_pos / 10);
	}
	if ((objIndex->extra_flags & ITEM_UNCONCERNED) != 0)
	{
		lvl_neg += (lvl_pos / 10);
	}
	if ((objIndex->extra_flags & ITEM_NEUTRAL) != 0)
	{
		lvl_neg += (lvl_pos / 10);
	}
	if ((objIndex->extra_flags & ITEM_AUTO_ENGRAVE) != 0)
	{
		lvl_neg += (lvl_pos / 10);
	}
	if ((objIndex->extra_flags & ITEM_INVIS) != 0)
	{
		lvl_neg += lvl_pos / 20;
	}
	level = UMAX(1, lvl_pos - lvl_neg);

	pop_call();
	return level;
}


bool blocking(CHAR_DATA *victim,CHAR_DATA *ch)
{
	push_call("blocking(%p,%p)",victim,ch);

	if (IS_NPC(ch) || IS_NPC(victim))
	{
		pop_call();
		return FALSE;
	}

	if (ch->level >= LEVEL_IMMORTAL)
	{
		pop_call();
		return FALSE;
	}

	if (ch->desc == NULL || victim->desc == NULL)
	{
		pop_call();
		return TRUE;
 	}

	if (IS_MUTED(ch))
	{
		pop_call();
		return TRUE;
	}

	if (is_name(ch->name, victim->pcdata->block_list))
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}


bool in_camp(CHAR_DATA *ch)
{
	CHAR_DATA *gch;

	push_call("in_camp(%p)",ch);

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

/*
 * Simple little function for a safe check of
 * two people being in the same room - Kregor
 */
bool in_same_room(CHAR_DATA *ch, CHAR_DATA *victim)
{
	push_call("in_same_room(%p,%p)",ch, victim);

	if ( !ch || !victim )
	{
		pop_call();
		return( FALSE );
	}
	if ( ch->in_room == NULL || victim->in_room == NULL )
	{
		pop_call();
		return( FALSE );
	}
	if ( ch->in_room == victim->in_room )
	{
		pop_call();
		return( TRUE );
	}
	pop_call();
	return FALSE;
}


char *get_god_name(int god)
{
	return (char *) god_table[god].god_name;
}


/* The GREET code, thanks for hints to Erwin of
 * Erwin's MERC programming FAQ
 * http://www.andreasen.org/faq
 * coded by Kregor - 3/1/07
 */

/* Returns true if victim has greeted ch */
bool knows_char(CHAR_DATA *ch, CHAR_DATA *victim)
{
	push_call("knows_char(%p,%p)",ch,victim);

	if (IS_NPC(ch) || IS_NPC(victim) || IS_PLR(ch, PLR_HOLYLIGHT) || IS_PLR(victim, PLR_HOLYLIGHT))
	{
		pop_call();
		return TRUE;
	}
	if (victim == ch)
	{
		pop_call();
		return TRUE;
	}
	if (ch->pcdata->greeted[victim->pcdata->pvnum])
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
} 


/* defines the adjective of a viewed character */
char * adjective( CHAR_DATA *ch )
{
	static char buf[MAX_STRING_LENGTH];

	push_call("adjective(%p)",ch);
	
	buf[0] = '\0';

	if (IS_NPC(ch))
	{
		pop_call();
		return ch->short_descr;
	}

	if (is_polymorph(ch) && race_type(ch) != RTYPE_HUMANOID)
	{
		if (is_string(ch->pcdata->disguise))
			sprintf(buf, "%s %s", a_an(ch->pcdata->disguise), ch->pcdata->disguise);
		else
			sprintf(buf, "%s %s", a_an(race_table[get_race(ch)].race_name), race_table[get_race(ch)].race_name);
		pop_call();
		return buf;
	}

	if (IS_AFFECTED(ch, AFF_HOODED))
	{
		sprintf(buf, "a %s hooded figure", size_types[get_size(ch)]);

		pop_call();
		return ( buf );
	}

	if (is_string(ch->pcdata->disguise))
	{
		sprintf(buf, "%s %s",
			a_an(ch->pcdata->disguise), ch->pcdata->disguise);

		pop_call();
		return ( buf );
	}

	if (!is_string(ch->pcdata->adjective))
	{
		sprintf(buf, "a %s %s",
			ch->sex <= 0 ? "neuter" : ch->sex == 1 ? "male" : "female",
			race_table[get_race(ch)].race_name);

		pop_call();
		return ( buf );
	}

	sprintf(buf, "%s %s %s %s",
		a_an(ch->pcdata->adjective), ch->pcdata->adjective,
		ch->sex <= 0 ? "neuter" : ch->sex == 1 ? "male" : "female",
		race_table[get_race(ch)].race_name);

	pop_call();
	return ( buf );
}


/* defines the name of a viewed character */
char * PERS( CHAR_DATA *ch, CHAR_DATA *looker )
{
	static char buf[MAX_STRING_LENGTH];

	push_call("PERS(%p,%p)",ch,looker);
	
	buf[0] = '\0';

	/* let's crash proof this */
	if (ch == NULL || looker == NULL)
	{
		pop_call();
		return ( "no one? PLEASE REPORT THIS BUG" );
	}
	
	if (!is_same_group(ch, looker) && !can_see(looker, ch))
	{
		pop_call();
		return "someone";
	}

	// Hallucinate results
	if (!IS_NPC(looker) && looker != ch && IS_AFFECTED(looker, AFF2_HALLUCINATE) && !IS_PLR(looker, PLR_HOLYLIGHT))
	{
		pop_call();
		return fake_name[number_range(0, NUM_FAKE_NAME-1)];
	}

	/* Mobs just have their short description shown */
	if (IS_NPC(ch))
	{
		pop_call();
		return ch->short_descr;
	}

	/* You automatically know if you are a mob, or if either of you are an Imm */
	if (IS_NPC(looker) || IS_PLR(ch, PLR_HOLYLIGHT) || IS_PLR(looker, PLR_HOLYLIGHT))
	{
		pop_call();
		return ( ch->name );
	}
	
	// True Seeing foils polymorph
	if (is_polymorph(ch) && race_type(ch) != RTYPE_HUMANOID && !IS_AFFECTED(looker, AFF_TRUESIGHT))
	{
		if (is_string(ch->pcdata->disguise))
			sprintf(buf, "%s %s", a_an(ch->pcdata->disguise), ch->pcdata->disguise);
		else
			sprintf(buf, "%s %s", a_an(race_table[get_race(ch)].race_name), race_table[get_race(ch)].race_name);
		pop_call();
		return buf;
	}

	if (IS_AFFECTED(ch, AFF_HOODED))
	{
		sprintf(buf, "a %s hooded figure", size_types[get_size(ch)]);

		pop_call();
		return ( buf );
	}
	if (is_string(ch->pcdata->disguise))
	{
		sprintf(buf, "%s %s",
			a_an(ch->pcdata->disguise), ch->pcdata->disguise);

		pop_call();
		return ( buf );
	}

	if (knows_char(looker,ch))
	{
		pop_call();
		return ( ch->name );
	}

	if (!is_string(ch->pcdata->adjective))
	{
		sprintf(buf, "a %s %s",
			ch->sex <= 0 ? "neutral" : ch->sex == 1 ? "male" : "female",
			race_table[get_race(ch)].race_name);

		pop_call();
		return ( buf );
	}
	sprintf(buf, "%s %s %s %s",
		a_an(ch->pcdata->adjective), ch->pcdata->adjective,
		ch->sex <= 0 ? "neutral" : ch->sex == 1 ? "male" : "female",
		race_table[get_race(ch)].race_name);

	pop_call();
	return ( buf );
}


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

	push_call("get_name(%p)",ch);

	get_name_buffer[0] = '\0';

	if (ch == NULL) //crash insurance
	{
		pop_call();
		return "(NULL)";
	}

	if (!IS_NPC(ch))
	{
		strcat(get_name_buffer, ch->name);

		pop_call();
		return( get_name_buffer );
	}

	if (ch->short_descr)
	{
		strcpy( get_name_buffer, ch->short_descr );

		pop_call();
		return( get_name_buffer );
	}
	else
	{
		log_printf("get_name: mob %d has NULL short_descr", ch->pIndexData->vnum);

		pop_call();
		return "(NULL)";
	}
}

/*
 * Armor functions below use the OGC Sectional Armor Variant
 * http://www.dandwiki.com/wiki/Armor_Sections_%28DnD_Variant_Rule%29
 * Kregor
 */
 
/*
 *	Calculate the effective armor type worn by a character.
 */
int apply_armor_type( CHAR_DATA *ch )
{
	OBJ_DATA *obj;
	int type = 0;

	push_call("apply_armor_type(%p)",ch);

	// added automatic armor for NPCs
	if (IS_NPC(ch) && ch->npcdata->armor_type)
	{
		pop_call();
		return armor_table[ch->npcdata->armor_type].type;
	}
	
	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (IS_WORN(obj))
		{
			if ( obj->item_type == ITEM_ARMOR )
			{
				if ( obj->value[0] == ARMOR_TYPE_CLOTH )
					continue;
				switch ( obj->wear_loc )
				{
					case WEAR_BODY:
						type += 30 * armor_table[obj->value[0]].type;
							break;
					case WEAR_HEAD:
						type += 20 * armor_table[obj->value[0]].type;
							break;
					case WEAR_LEGS:
						type += 20 * armor_table[obj->value[0]].type;
							break;
					case WEAR_FEET:
						type += 5 * armor_table[obj->value[0]].type;
							break;
					case WEAR_ARMS:
						type += 20 * armor_table[obj->value[0]].type;
							break;
					case WEAR_HANDS:
						type += 5 * armor_table[obj->value[0]].type;
							break;
					default:
						type += 0;
						break;
				}
			}
		}
	}
	type = URANGE(0, type, 200);
	
	if (type < 30)
	{
		pop_call();
		return ARMOR_NONE;
	}
	else if (type <= 140)
	{
		pop_call();
		return ARMOR_LIGHT;
	}
	
	pop_call();
	return (type/100);
}
			
/*
 *	Calculate the armor check penalty of a character.
 */
int armor_check_penalty( CHAR_DATA *ch )
{
	OBJ_DATA *obj;
	int penalty = 0;
	int penalty2 = 0;

	push_call("apply_armor_penalty(%p)",ch);

	// added automatic armor for NPCs
	if (IS_NPC(ch) && ch->npcdata->armor_type)
	{
		penalty = armor_table[ch->npcdata->armor_type].armor_check;
	}
	else
	{	
		for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
		{
			if (IS_WORN(obj))
			{
				if ( obj->item_type == ITEM_ARMOR )
				{
					if ( armor_table[obj->value[0]].armor_check == 0 )
						continue;
					switch ( obj->wear_loc )
					{
						case WEAR_BODY:
							penalty += 30 * armor_table[obj->value[0]].armor_check;
							if (IS_OBJ_STAT(obj, ITEM_MASTERWORK))
								penalty -= 30;
								break;
						case WEAR_HEAD:
							penalty += 20 * armor_table[obj->value[0]].armor_check;
							if (IS_OBJ_STAT(obj, ITEM_MASTERWORK))
								penalty -= 20;
								break;
						case WEAR_LEGS:
							penalty += 20 * armor_table[obj->value[0]].armor_check;
							if (IS_OBJ_STAT(obj, ITEM_MASTERWORK))
								penalty -= 20;
								break;
						case WEAR_FEET:
							penalty += 5 * armor_table[obj->value[0]].armor_check;
							if (IS_OBJ_STAT(obj, ITEM_MASTERWORK))
								penalty -= 5;
								break;
						case WEAR_ARMS:
							penalty += 20 * armor_table[obj->value[0]].armor_check;
							if (IS_OBJ_STAT(obj, ITEM_MASTERWORK))
								penalty -= 20;
								break;
						case WEAR_HANDS:
							penalty += 5 * armor_table[obj->value[0]].armor_check;
							if (IS_OBJ_STAT(obj, ITEM_MASTERWORK))
								penalty -= 5;
								break;
						case WEAR_SHIELD:
							penalty2 = armor_table[obj->value[0]].armor_check;
							if (IS_OBJ_STAT(obj, ITEM_MASTERWORK))
								penalty2 -= 1;
								break;
						default:
							penalty += 0;
							break;
					}
				}
			}
		}
		penalty /= 100;
		penalty += penalty2;
	}

	if (learned(ch, gsn_armor_training) && armor_type_worn(ch) != ARMOR_NONE)
		penalty -= (multi_skill_level(ch, gsn_armor_training) / 4) + 1;
		
	penalty = UMAX(0, penalty);

	switch (encumberance(ch))
	{
		case VERY_ENCUMBERED:
			penalty = UMAX(6, penalty);
			break;
		case ENCUMBERED:
			penalty = UMAX(3, penalty);
			break;
	}
	
	if (is_affected(ch, gsn_iron_body))
	{
		penalty = UMAX(6, penalty);
	}

	pop_call();
	return (penalty);
}
			
/*
 *	Calculate the arcane spell failure based on all armor worn.
 */
int spell_failure( CHAR_DATA *ch )
{
	OBJ_DATA *obj;
	int chance, chance2;

	push_call("spell_failure(%p)",ch);

	// added automatic armor for NPCs
	if (IS_NPC(ch) && ch->npcdata->armor_type)
	{
		pop_call();
		return armor_table[ch->npcdata->armor_type].spell_fail;
	}
	else
	{	
		for (chance = chance2 = 0, obj = ch->first_carrying ; obj ; obj = obj->next_content)
		{
			if (IS_WORN(obj))
			{
				if ( obj->item_type == ITEM_ARMOR )
				{
					if ( obj->value[0] == ARMOR_TYPE_CLOTH )
						continue;
					if (!armor_table[obj->value[0]].spell_fail)
						continue;
					if (class_ability(ch, gsn_warmage) && armor_type_worn(ch) <= ARMOR_LIGHT && obj->wear_loc != WEAR_SHIELD)
						continue;

					switch ( obj->wear_loc )
					{
						case WEAR_BODY:
							chance += 30 * armor_table[obj->value[0]].spell_fail;
							if (obj->material == MATERIAL_MITHRAL)
								chance -= 300;
								break;
						case WEAR_HEAD:
							chance += 20 * armor_table[obj->value[0]].spell_fail;
							if (obj->material == MATERIAL_MITHRAL)
								chance -= 200;
								break;
						case WEAR_LEGS:
							chance += 20 * armor_table[obj->value[0]].spell_fail;
							if (obj->material == MATERIAL_MITHRAL)
								chance -= 200;
								break;
						case WEAR_FEET:
							chance += 5 * armor_table[obj->value[0]].spell_fail;
							if (obj->material == MATERIAL_MITHRAL)
								chance -= 50;
								break;
						case WEAR_ARMS:
							chance += 20 * armor_table[obj->value[0]].spell_fail;
							if (obj->material == MATERIAL_MITHRAL)
								chance -= 200;
								break;
						case WEAR_HANDS:
							chance += 5 * armor_table[obj->value[0]].spell_fail;
							if (obj->material == MATERIAL_MITHRAL)
								chance -= 50;
								break;
						case WEAR_SHIELD:
							chance2 = armor_table[obj->value[0]].spell_fail;
							if (obj->material == MATERIAL_MITHRAL)
								chance -= 10;
								break;
						default:
							chance += 0;
							break;
					}
				}
			}
		}
		chance /= 100;
		chance += chance2;
	}

	if (class_ability(ch, gsn_arcane_armor_mastery))
	{
		chance = UMAX(0, chance - 20);
	}
	else if (learned(ch, gsn_arcane_armor_training))
	{
		chance = UMAX(0, chance - 10);
	}

	if (is_affected(ch, gsn_iron_body))
		chance = UMAX(chance, 35);
	
	pop_call();
	return chance;
}
			
/*
 *	Apply the max dex bonus based on all armor worn.
 */
void apply_dex_max( CHAR_DATA *ch )
{
	OBJ_DATA *obj;
	int max, count;

	push_call("apply_dex_max(%p)",ch);

	max = -1;
	count = 0;
	
	// added automatic armor for NPCs
	if (IS_NPC(ch) && ch->npcdata->armor_type)
	{
		max = armor_table[ch->npcdata->armor_type].dex_max;
	}	
	else if (armor_type_worn(ch) != ARMOR_NONE)
	{
		for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
		{
			if (IS_WORN(obj))
			{
				if (!IS_OBJ_TYPE(obj, ITEM_ARMOR))
					continue;
					
				if (get_layer(obj) != LAYER_ARMOR)
					continue;
		
				if (armor_table[obj->value[0]].dex_max == -1)
					continue;

				switch ( obj->wear_loc )
				{
					case WEAR_BODY:
						max 	+= armor_table[obj->value[0]].dex_max * 30;
						if (obj->material == MATERIAL_MITHRAL)
							max += 2 * 30;
						count += 30;
							break;
					case WEAR_HEAD:
						max 	+= armor_table[obj->value[0]].dex_max * 20;
						if (obj->material == MATERIAL_MITHRAL)
							max += 2 * 20;
						count += 20;
							break;
					case WEAR_LEGS:
						max 	+= armor_table[obj->value[0]].dex_max * 20;
						if (obj->material == MATERIAL_MITHRAL)
							max += 2 * 5;
						count += 20;
							break;
					case WEAR_FEET:
						max 	+= armor_table[obj->value[0]].dex_max * 5;
						if (obj->material == MATERIAL_MITHRAL)
							max += 2 * 5;
						count += 5;
							break;
					case WEAR_ARMS:
						max 	+= armor_table[obj->value[0]].dex_max * 20;
						if (obj->material == MATERIAL_MITHRAL)
							max += 2 * 5;
						count += 20;
							break;
					case WEAR_HANDS:
						max 	+= armor_table[obj->value[0]].dex_max * 5;
						if (obj->material == MATERIAL_MITHRAL)
							max += 2 * 5;
						count += 5;
							break;
					default:
						break;
				}
			}
		}		
		max += (100 - count) * 10;
		max = max / 100;
	}
	
	if ((obj = get_eq_char(ch, WEAR_SHIELD)) != NULL && IS_OBJ_TYPE(obj, ITEM_ARMOR))
	{
		if ( armor_table[obj->value[0]].dex_max != -1 )
			max = UMIN(max, armor_table[obj->value[0]].dex_max);
	}
	
	if (learned(ch, gsn_armor_training) && armor_type_worn(ch) != ARMOR_NONE)
		max += (multi_skill_level(ch, gsn_armor_training) / 4) + 1;

	ch->dex_max = max;
	
	pop_call();
	return;
}
			
/*
	Find the weight of a piece of armor.
*/
int armor_weight( OBJ_INDEX_DATA *obj )
{
	int weight;
	
	push_call("armor_weight(%p)",obj);

	if ( obj->item_type != ITEM_ARMOR )
	{
		pop_call();
		return obj->weight;
	}

	if (IS_SET(obj->wear_flags, WEAR_BODY))
	{
		weight = 3 * armor_table[obj->value[0]].weight;
	}
	else if (IS_SET(obj->wear_flags, WEAR_HEAD))
	{
		weight = 2 * armor_table[obj->value[0]].weight;
	}
	else if (IS_SET(obj->wear_flags, WEAR_LEGS))
	{
		weight = 2 * armor_table[obj->value[0]].weight;
	}
	else if (IS_SET(obj->wear_flags, WEAR_FEET))
	{
		weight = (int) (0.5 * armor_table[obj->value[0]].weight);
	}
	else if (IS_SET(obj->wear_flags, WEAR_ARMS))
	{
		weight = 2 * armor_table[obj->value[0]].weight;
	}
	else if (IS_SET(obj->wear_flags, WEAR_HANDS))
	{
		weight = (int) (0.5 * armor_table[obj->value[0]].weight);
	}
	else
	{
		weight = armor_table[obj->value[0]].weight;
	}
	
	switch (obj->size)
	{
		default:
			break;
		case SIZE_FINE:
			weight /= 16;
			break;
		case SIZE_DIMINUTIVE:
			weight /= 8;
			break;
		case SIZE_TINY:
			weight /= 4;
			break;
		case SIZE_SMALL:
			weight /= 2;
			break;
		case SIZE_LARGE:
			weight *= 2;
			break;
		case SIZE_HUGE:
			weight *= 4;
			break;
		case SIZE_GARGANTUAN:
			weight *= 8;
			break;
		case SIZE_COLOSSAL:
			weight *= 16;
			break;
	}
	
	pop_call();
	return weight;
}


/*
 * is CH in a group with a pally for save bonuses? - Kregor
 */
bool in_skill_circle( CHAR_DATA *ch, int sn )
{
	CHAR_DATA *gch;
	
	push_call("in_skill_circle(%p,%p)",ch,sn);
	
	if (!ch->in_room)
	{
		pop_call();
		return FALSE;
	}
	for (gch = ch->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (!IS_AWAKE(gch))
			continue;

		if (!is_same_group(gch, ch))
			continue;

		if (learned(gch, sn))
		{
			pop_call();
			return TRUE;
		}
	}
	pop_call();
	return FALSE;
}



/*
 * Calculate all non-stacking bonuses for a saving throw - Kregor
 */
int save_bonus( CHAR_DATA *ch, CHAR_DATA *victim, int sn, int sdesc )
{
	int save, bonus = 0, penalty = 0;

	push_call("save_bonus(%p,%p,%p,%p)",ch,victim,sn,sdesc);

	if (IS_SET(sdesc, SDESC_ACID))
	{
		if ((save = get_apply(victim, APPLY_SAVE_ACID)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_COLD))
	{
		if ((save = get_apply(victim, APPLY_SAVE_COLD)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_ELECTRIC))
	{
		if ((save = get_apply(victim, APPLY_SAVE_ELECTRIC)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_FIRE))
	{
		if ((save = get_apply(victim, APPLY_SAVE_FIRE)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_SONIC))
	{
		if ((save = get_apply(victim, APPLY_SAVE_SONIC)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_AIR))
	{
		if ((save = get_apply(victim, APPLY_SAVE_AIR)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_CHAOTIC))
	{
		if ((save = get_apply(victim, APPLY_SAVE_CHAOTIC)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_CHARM))
	{
		if ((save = get_apply(victim, APPLY_SAVE_CHARM)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
		if (in_skill_circle(victim, gsn_aura_of_resolve))
			bonus += 4;
		if (learned(victim, gsn_stubborn_mind))
			bonus += 2;
	}
	if (IS_SET(sdesc, SDESC_COMPULSION))
	{
		if ((save = get_apply(victim, APPLY_SAVE_COMPULSION)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
		if (in_skill_circle(victim, gsn_aura_of_devotion))
			bonus += 4;
		if (!IS_SET(sdesc, SDESC_FEAR))
		{
			if (class_ability(victim, gsn_aura_of_nobility) || in_skill_circle(victim, gsn_aura_of_nobility))
				bonus += 2;
			if (domain_apotheosis(victim, DOMAIN_NOBILITY))
				bonus += 6;
		}
		if (learned(victim, gsn_stubborn_mind))
			bonus += 2;
	}
	if (IS_SET(sdesc, SDESC_DARKNESS))
	{
		if ((save = get_apply(victim, APPLY_SAVE_DARKNESS)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_DEATH))
	{
		if ((save = get_apply(victim, APPLY_SAVE_DEATH)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_DISEASE))
	{
		if ((save = get_apply(victim, APPLY_SAVE_DISEASE)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_EARTH))
	{
		if ((save = get_apply(victim, APPLY_SAVE_EARTH)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_EVIL))
	{
		if ((save = get_apply(victim, APPLY_SAVE_EVIL)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
		if (in_skill_circle(victim, gsn_aura_of_righteousness))
			bonus += 4;
	}
	if (IS_SET(sdesc, SDESC_FEAR))
	{
		if ((save = get_apply(victim, APPLY_SAVE_FEAR)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
		if (learned(victim, gsn_bravery))
			bonus += multi_skill_level(victim, gsn_bravery) / 4 + 1;
		if (in_skill_circle(victim, gsn_aura_of_courage))
			bonus += 4;
		if (!IS_SET(sdesc, SDESC_COMPULSION))
		{
			if (class_ability(victim, gsn_aura_of_nobility) || in_skill_circle(victim, gsn_aura_of_nobility))
				bonus += 2;
		}
	}
	if (IS_SET(sdesc, SDESC_FORCE))
	{
		if ((save = get_apply(victim, APPLY_SAVE_FORCE)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_GOOD))
	{
		if ((save = get_apply(victim, APPLY_SAVE_GOOD)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_ILLUSION))
	{
		if ((save = get_apply(victim, APPLY_SAVE_ILLUSION)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_LAWFUL))
	{
		if ((save = get_apply(victim, APPLY_SAVE_LAWFUL)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_LIGHT))
	{
		if ((save = get_apply(victim, APPLY_SAVE_LIGHT)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_MAGIC))
	{
		if ((save = get_apply(victim, APPLY_SAVE_MAGIC)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_MIND))
	{
		if ((save = get_apply(victim, APPLY_SAVE_MIND)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_NEGATIVE))
	{
		if ((save = get_apply(victim, APPLY_SAVE_NEGATIVE)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_PARALYSIS))
	{
		if ((save = get_apply(victim, APPLY_SAVE_PARALYSIS)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_PETRI))
	{
		if ((save = get_apply(victim, APPLY_SAVE_PETRI)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_POISON))
	{
		if ((save = get_apply(victim, APPLY_SAVE_POISON)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
		if (learned(victim, gsn_poison_resistance))
			bonus += multi_class_level(victim, gsn_poison_resistance) / 2;
	}
	if (IS_SET(sdesc, SDESC_POLYMORPH))
	{
		if ((save = get_apply(victim, APPLY_SAVE_POLYMORPH)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_HEALING))
	{
		if ((save = get_apply(victim, APPLY_SAVE_HEALING)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_SLEEP))
 	{
		if ((save = get_apply(victim, APPLY_SAVE_SLEEP)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (IS_SET(sdesc, SDESC_WATER))
	{
		if ((save = get_apply(victim, APPLY_SAVE_WATER)) != 0)
		{
			if (save < 0 && save < penalty)
				penalty = save;
			else if (save > bonus)
				bonus = save;
		}
	}
	if (ch != NULL)
	{
		if (IS_EVIL(ch))
			if (get_apply(victim, APPLY_RES_EVIL) > bonus)
				bonus = get_apply(victim, APPLY_RES_EVIL);
		if (IS_GOOD(ch))
			if (get_apply(victim, APPLY_RES_GOOD) > bonus)
				bonus = get_apply(victim, APPLY_RES_GOOD);
		if (IS_LAWFUL(ch))
			if (get_apply(victim, APPLY_RES_LAW) > bonus)
				bonus = get_apply(victim, APPLY_RES_LAW);
		if (IS_CHAOTIC(ch))
			if (get_apply(victim, APPLY_RES_CHAOS) > bonus)
				bonus = get_apply(victim, APPLY_RES_CHAOS);
	}
	if (is_spell(sn))
	{
		if (combine_apply(victim, APPLY_RES_SPELL, APPLY_RES_SAVES) > bonus)
			bonus = combine_apply(victim, APPLY_RES_SPELL, APPLY_RES_SAVES);
		if (arcane_mastery(victim, SCHOOL_NONE))
			bonus += 4;
		if (domain_apotheosis(victim, DOMAIN_RETRIBUTION) && god_table[victim->god].faith_enemy[ch->god])
			bonus += 4;
	}
	else if (get_apply(victim, APPLY_RES_SAVES) > bonus)
		bonus = get_apply(victim, APPLY_RES_SAVES);
	else if (get_apply(victim, APPLY_RES_SAVES) < penalty)
		penalty = get_apply(victim, APPLY_RES_SAVES);
	
	if (skill_table[sn].spell_school == SCHOOL_ILLUSION && learned(victim, gsn_secret_of_truth))
		bonus += 2;
	if (learned(ch, gsn_heroes_luck))
		bonus += 2;
	if (is_affected(victim, gsn_divine_wrath))
		bonus += UMAX(1, stat_bonus(TRUE, victim, APPLY_CHA));
	if (ch != NULL && faith_enemies(ch, victim))
	{
		if (is_spell(sn) && learned(victim, gsn_divine_defense))
			bonus += ROUNDUP(multi_skill_level(victim, gsn_divine_defense) / 2);
	}

	pop_call();
	return (bonus + penalty);
}

/*
 * Function to calc all modifiers to save DC
 * based on caster/attacker - Kregor
 */
bool calc_save_dc( CHAR_DATA *ch, CHAR_DATA *caster, int sn, int dc )
{
	int save, class, spell_desc, spell_school;
	int mod = 0;
	bool Shadow;
	
	push_call("calc_save_dc(%p,%p,%p,%p)",ch,caster,sn,dc);
		
	spell_desc = skill_table[sn].spell_desc;
	spell_school = skill_table[sn].spell_school;

	switch (skill_table[sn].save)
	{
		case SAVE_FORT_HALF:
		case SAVE_FORT_NONE:
		case SAVE_FORT_QUARTER:
			save = 1;
			break;
		case SAVE_REFL_HALF:
		case SAVE_REFL_NONE:
		case SAVE_REFL_QUARTER:
			save = 2;
			break;
		case SAVE_WILL_HALF:
		case SAVE_WILL_NONE:
		case SAVE_WILL_QUARTER:
			save = 3;
			break;
	}
	
	if (is_spell_like(sn))
	{
		if ((class = casting_class(caster,sn,TRUE)) == -2)
		{
			mod += stat_bonus(TRUE, caster, APPLY_CHA);
		}
		else if (class > 0)
		{
			mod += stat_bonus(TRUE, caster, class_table[class].attr_prime);

			switch (spell_school)
			{
				case SCHOOL_ABJURATION:
					mod += learned(caster, gsn_focus_abj);
					if (Shadow)
						mod -= 1;
					break;
				case SCHOOL_CONJURATION:
					mod += learned(caster, gsn_focus_conj);
					if (Shadow)
						mod -= 1;
					break;
				case SCHOOL_DIVINATION:
					mod += learned(caster, gsn_focus_div);
					if (Shadow)
						mod -= 1;
					break;
				case SCHOOL_ENCHANTMENT:
					mod += learned(caster, gsn_focus_ench);
					if (Shadow)
						mod -= 1;
					break;
				case SCHOOL_EVOCATION:
					mod += learned(caster, gsn_focus_evoc);
					if (Shadow)
						mod -= 1;
					break;
				case SCHOOL_ILLUSION:
					mod += learned(caster, gsn_focus_illus);
					if (Shadow)
						mod -= 1;
					mod += multi_class_level(ch, gsn_shadow_adept) / 3;
					break;
				case SCHOOL_NECROMANCY:
					mod += learned(caster, gsn_focus_necro);
					if (Shadow)
						mod -= 1;
					mod += multi_class_level(ch, gsn_shadow_adept) / 3;
					break;
				case SCHOOL_TRANSMUTATION:
					mod += learned(caster, gsn_focus_trans);
					if (Shadow)
						mod -= 1;
					break;
			}
			
			if (IS_SET(spell_desc, SDESC_DARKNESS) && spell_school != SCHOOL_NECROMANCY && spell_school != SCHOOL_ILLUSION)
			{
				mod += multi_class_level(ch, gsn_shadow_adept) / 3;
			}

			if (class == CLASS_BLACKGUARD)
			{
				if (IS_SET(spell_desc, SDESC_FEAR) && learned(caster, gsn_aura_of_cowardice))
				{
					mod += UMAX(0, stat_bonus(TRUE, caster, APPLY_CHA));
				}
				else if (IS_SET(spell_desc, SDESC_CHARM) && learned(caster, gsn_serpents_tongue))
				{
					mod += UMAX(0, stat_bonus(TRUE, caster, APPLY_CHA));
				}
				else if (IS_SET(spell_desc, SDESC_COMPULSION) && learned(caster, gsn_aura_of_tyranny))
				{
					mod += UMAX(0, stat_bonus(TRUE, caster, APPLY_CHA));
				}
			}
			if (class == CLASS_CLERIC)
			{
				if (IS_SET(spell_desc, SDESC_FEAR) && domain_apotheosis(caster, DOMAIN_WRATH))
				{
					mod += UMAX(0, stat_bonus(TRUE, caster, APPLY_CHA));
				}
				if ((sn == gsn_dominate_animal || sn == gsn_dominate_monster || sn == gsn_dominate_person)
				&& domain_apotheosis(caster, DOMAIN_WRATH))
				{
					mod += UMAX(0, stat_bonus(TRUE, caster, APPLY_CHA));
				}
				if (IS_SET(spell_desc, SDESC_CHARM|SDESC_COMPULSION) && has_domain(caster, DOMAIN_CHARM))
				{
					mod += 1;
				}
				if (IS_SET(spell_desc, SDESC_ILLUSION) && has_domain(caster, DOMAIN_ILLUSION))
				{
					mod += 1;
				}
				if (IS_SET(spell_desc, SDESC_MIND) && has_domain(caster, DOMAIN_MADNESS))
				{
					mod += 1;
				}
			}
			if (class == CLASS_SORCERER)
			{
				if (IS_SET(spell_desc, SDESC_NEGATIVE) && get_bloodline(caster) == BLOODLINE_UNDEAD)
				{
					mod += class_level(caster, CLASS_SORCERER) / 5 + 1;
				}
				if (IS_SET(spell_desc, SDESC_COMPULSION) && get_bloodline(caster) == BLOODLINE_FEY)
				{
					mod += class_level(caster, CLASS_SORCERER) / 5 + 1;
				}
				if (IS_SET(spell_desc, SDESC_CHARM) && get_bloodline(caster) == BLOODLINE_INFERNAL)
				{
					mod += class_level(caster, CLASS_SORCERER) / 5 + 1;
				}
				if (ch->cast_feats > 0 && get_bloodline(caster) == BLOODLINE_ARCANE)
				{
					mod += class_level(caster, CLASS_SORCERER) / 5 + 1;
				}
			}
		}
		else
		{
			mod += (10 + skill_table[sn].native_level) / 2 - 5;
		}
	}
	else if (save == 3)
	{
		// race attacks that need WILL save always CHA based - Kregor
		if (skill_table[sn].skilltype == FSKILL_RACEATTACK)
		{
			mod += stat_bonus(TRUE, caster, APPLY_CHA);
		}
	}
	else
	{
		if (skill_table[sn].skilltype == FSKILL_RACEATTACK)
		{
			if (get_curr_con(caster) < 0 || IS_SET(skill_table[sn].spell_desc, SDESC_MIND))
			{
				mod += stat_bonus(TRUE, caster, APPLY_CHA);
			}
			else
			{
				mod += stat_bonus(TRUE, caster, APPLY_CON);
			}
		}
	}
	// DC lowered if caster is trying to charm a hostile
	if (IS_SET(skill_table[sn].spell_desc, SDESC_CHARM))
	{
		if (IS_ACT(ch, ACT_AGGRESSIVE) || (who_fighting(ch) && who_fighting(ch) == caster))
			mod -= 4;
	}

	pop_call();
	return mod;
}

/*
 * Roll saving throws - Kregor
 * DC is calculated by halving level + 10.
 * pass -1 at sn to bypass calculation and
 * use value of level as straight DC.
 */
bool fort_save( CHAR_DATA *ch, CHAR_DATA *caster, int level, int sn )
{
	int roll, dc, diceroll;
	int spell_desc = SDESC_NONE;

	push_call("fort_save(%p,%p,%p)",ch,level,sn);
	
	// no CON score, automatic save
	if (IS_UNDEAD(ch) || race_type(ch) == RTYPE_CONSTRUCT || get_curr_con(ch) == -1)
	{
		pop_call();
		return TRUE;
	}
	
	dc = level / 2 + 10;
	roll = get_fort_save(ch);

	if (sn != -1)
	{
		spell_desc = skill_table[sn].spell_desc;

		if (is_immune(ch, spell_desc))
		{
			pop_call();
			return TRUE;
		}

		int bonus = save_bonus(caster, ch, sn, spell_desc);
		roll += bonus;
		
		// well versed ability = +CHA bonus vs all audible affects
		if (learned(ch, gsn_well_versed))
		{
			if (IS_SET(skill_table[sn].flags, SF_AUDIBLE|SF_LANGUAGE)
			|| skill_table[sn].skilltype == FSKILL_BARDSONG
			|| sn == gsn_bardic_song
			|| IS_SET(spell_desc, SDESC_SONIC))
				roll += URANGE(0, stat_bonus(TRUE, ch, APPLY_CHA), class_level(ch, CLASS_BARD));
		}
	}


	if (sn == -1 || sn == gsn_poison_attack || sn == gsn_disease_attack
	|| skill_table[sn].skilltype == FSKILL_BARDSONG)
	{
		dc = level;
	}
	else if (caster != NULL)
	{
		dc += calc_save_dc(ch, caster, sn, dc);
	}
	else if (is_spell(sn))
	{
		dc += (10 + skill_table[sn].native_level) / 2 - 5;
	}
	
	diceroll = dice(1,20);
	
	if (diceroll == 20)
	{
		pop_call();
		return TRUE;
	}
	
	// No, these don't stack - Kregor
	if (learned(ch, gsn_divine_luck) && diceroll == 1)
		diceroll = dice(1,20);
	else if (domain_apotheosis(ch, DOMAIN_STRENGTH) && diceroll + roll < dc)
		diceroll = UMAX(diceroll, dice(1,20));

	if (diceroll == 1 && !IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return FALSE;
	}

	wiz_printf_room(ch, "FORT Save (%s): diceroll: %d  vs. dc: %d\n\r", get_name(ch), diceroll + roll, dc);
	if (caster != NULL && caster->in_room != ch->in_room)
		wiz_printf_room(caster, "FORT Save (%s): diceroll: %d  vs. dc: %d\n\r", get_name(ch), diceroll + roll, dc);
	
	pop_call();
	return( diceroll + roll >= dc );
}

bool refl_save( CHAR_DATA *ch, CHAR_DATA *caster, int level, int sn )
{
	int roll, dc;
	int spell_desc = SDESC_NONE;

	push_call("refl_save(%p,%p,%p)",ch,level,sn);
	
	if (IS_HELPLESS(ch))
	{
		pop_call();
		return FALSE;
	}
	dc = level / 2 + 10;
	roll = dice(1,20);

	if (learned(ch, gsn_divine_luck) && roll == 1)
		roll = dice(1,20);

	if (roll == 1 && !IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return FALSE;
	}
	if (roll == 20)
	{
		pop_call();
		return TRUE;
	}
	
	roll += get_refl_save(ch);
	
	if (sn != -1)
	{
		spell_desc = skill_table[sn].spell_desc;

		if (is_immune(ch, spell_desc))
		{
			pop_call();
			return TRUE;
		}

		int bonus = save_bonus(caster, ch, sn, spell_desc);
		roll += bonus;

		// well versed ability = +CHA bonus vs all audible affects
		if (learned(ch, gsn_well_versed))
		{
			if (IS_SET(skill_table[sn].flags, SF_AUDIBLE|SF_LANGUAGE)
			|| skill_table[sn].skilltype == FSKILL_BARDSONG
			|| sn == gsn_bardic_song
			|| IS_SET(spell_desc, SDESC_SONIC))
				roll += URANGE(0, stat_bonus(TRUE, ch, APPLY_CHA), class_level(ch, CLASS_BARD));
		}
	}

	if (sn == -1 || sn == gsn_poison_attack || sn == gsn_disease_attack || sn == gsn_defensive_roll
	|| skill_table[sn].skilltype == FSKILL_BARDSONG)
	{
		dc = level;
	}
	else if (caster != NULL)
	{
		dc += calc_save_dc(ch, caster, sn, dc);
	}
	else if (is_spell(sn))
	{
		dc += (10 + skill_table[sn].native_level) / 2 - 5;
	}
	
	wiz_printf_room(ch, "REFL Save (%s): diceroll: %d  vs. dc: %d\n\r", get_name(ch), roll, dc);
	if (caster != NULL && caster->in_room != ch->in_room)
		wiz_printf_room(caster, "REFL Save (%s): diceroll: %d  vs. dc: %d\n\r", get_name(ch), roll, dc);
	
	pop_call();
	return( roll >= dc );
}

bool will_save( CHAR_DATA *ch, CHAR_DATA *caster, int level, int sn )
{
	int roll, dc, diceroll;
	int spell_desc = SDESC_NONE;

	push_call("will_save(%p,%p,%p)",ch,level,sn);
	
	dc = level / 2 + 10;
	roll = get_will_save(ch);

	if (sn != -1)
	{
		spell_desc = skill_table[sn].spell_desc;
		
		if (is_immune(ch, spell_desc))
		{
			pop_call();
			return TRUE;
		}
		
		int bonus = save_bonus(caster, ch, sn, spell_desc);
		roll += bonus;
		
		//bold rage ability
		if (IS_SET(skill_table[sn].spell_desc, SDESC_FEAR)
		&& learned(ch, gsn_bold_rage)
		&& IS_AFFECTED(ch, AFF2_BERSERK))
			roll += 4;
		
		// well versed ability = CHA bonus vs all audible affects
		if (learned(ch, gsn_well_versed))
		{
			if (IS_SET(skill_table[sn].flags, SF_AUDIBLE|SF_LANGUAGE)
			|| skill_table[sn].skilltype == FSKILL_BARDSONG
			|| sn == gsn_bardic_song
			|| IS_SET(spell_desc, SDESC_SONIC))
				roll += URANGE(0, stat_bonus(TRUE, ch, APPLY_CHA), class_level(ch, CLASS_BARD));
		}
	}

	if (sn == -1 || sn == gsn_poison_attack || sn == gsn_disease_attack
	|| skill_table[sn].skilltype == FSKILL_BARDSONG)
	{
		dc = level;
	}
	else if (caster != NULL)
	{
		dc += calc_save_dc(ch, caster, sn, dc);
	}
	else if (is_spell(sn))
	{
		dc += (10 + skill_table[sn].native_level) / 2 - 5;
	}
	
	diceroll = dice(1,20);

	if (diceroll == 20)
	{
		pop_call();
		return TRUE;
	}
	
	/*
	 * Support slippery mind feat and Luck domain benefit
	 * If one kicks in, should not get yet another reroll
	 */
	if ((diceroll + roll < dc) && IS_SET(spell_desc, SDESC_MIND) && learned(ch, gsn_slippery_mind))
		diceroll = dice(1,20);	
	else if (roll == 1 && learned(ch, gsn_divine_luck))
		diceroll = dice(1,20);

	if (diceroll == 1 && !IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return FALSE;
	}
	
	wiz_printf_room(ch, "WILL Save (%s): diceroll: %d  vs. dc: %d\n\r", get_name(ch), diceroll + roll, dc);
	if (caster != NULL && caster->in_room != ch->in_room)
		wiz_printf_room(caster, "WILL Save (%s): diceroll: %d  vs. dc: %d\n\r", get_name(ch), diceroll + roll, dc);
	
	pop_call();
	return( diceroll + roll >= dc );
}


/*
 * Conditional based on being of diametric alignment
 * the closer in alignment you are with the victim,
 * the less likely to trigger true.
 */
int OPPOSITE_ALIGN( CHAR_DATA * ch, CHAR_DATA * victim )
{
	int chance;
	
	push_call("OPPOSITE_ALIGN(%p,%p)",ch,victim);
	
	chance = 50;

	if ((IS_EVIL(ch) && IS_GOOD(victim)) || (IS_EVIL(victim) && IS_GOOD(ch)))
	{
		chance += 25;
	}

	if ((IS_LAWFUL(ch) && IS_CHAOTIC(victim)) || (IS_LAWFUL(victim) && IS_CHAOTIC(ch)))
	{
		chance += 25;
	}

	if ((IS_EVIL(ch) && IS_EVIL(victim)) || (IS_GOOD(victim) && IS_GOOD(ch)))
	{
		chance -= 25;
	}

	if ((IS_LAWFUL(ch) && IS_LAWFUL(victim)) || (IS_CHAOTIC(victim) && IS_CHAOTIC(ch)))
	{
		chance -= 25;
	}
	pop_call();
	return chance;
}

bool is_grudging( CHAR_DATA *ch, CHAR_DATA *victim )
{
	push_call("is_grudging(%p,%p)",ch,victim);
	
	if (IS_NPC(victim))
	{
		pop_call();
		return FALSE;
	}
	
	if (victim->pcdata->murder_grudge[ch->pIndexData->vnum])
	{
		command(ch, do_sayto, "%s I do not deal with killers!", short_to_name(get_name(victim),1));
		pop_call();
		return TRUE;
	}
	else if (victim->pcdata->theft_grudge[ch->pIndexData->vnum])
	{
		command(ch, do_sayto, "%s I do not deal with thieves!", short_to_name(get_name(victim),1));
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}

/*
 * Co-ordinates the action taken with
 * the wait-state for actual time delays
 * between commands - Kregor
 */
void TAKE_ACTION(CHAR_DATA *ch, int action)
{
	push_call("TAKE_ACTION(%p,%p)",ch,action);
	
	switch (action)
	{
		case ACTION_MOVE:
			if (IS_PLR(ch, PLR_WIZTIME))
				send_to_char_color("{058}DEBUG: You have taken a move action.\n\r", ch);
			if (IS_SET(ch->action, ACTION_MOVE))
			{
				SET_BIT(ch->action, ACTION_STANDARD);
				wait_state(ch, PULSE_VIOLENCE * 2 / 3);
			}
			else
			{
				SET_BIT(ch->action, ACTION_MOVE);
				wait_state(ch, PULSE_VIOLENCE / 3);
			}
			break;
		case ACTION_STANDARD:
			if (IS_PLR(ch, PLR_WIZTIME))
				send_to_char_color("{058}DEBUG: You have taken a standard action.\n\r", ch);
			SET_BIT(ch->action, ACTION_STANDARD);
			wait_state(ch, PULSE_VIOLENCE * 2 / 3);
			break;
		case ACTION_FULL:
			if (IS_PLR(ch, PLR_WIZTIME))
				send_to_char_color("{058}DEBUG: You have taken a full round action.\n\r", ch);
			SET_BIT(ch->action, ACTION_FULL);
			wait_state(ch, PULSE_VIOLENCE);
			break;
		case ACTION_SWIFT:
			if (IS_PLR(ch, PLR_WIZTIME))
				send_to_char_color("{058}DEBUG: You have taken a swift action.\n\r", ch);
			SET_BIT(ch->action, ACTION_SWIFT);
			break;
	}
	turn_complete(ch);

	pop_call();
	return;
}

/* snarf an object held by ch */
OBJ_DATA *get_hold( CHAR_DATA *ch )
{
	OBJ_DATA *obj;

	push_call("get_hold(%p)",ch);

	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (WEAR_LOC(obj, WEAR_HOLD) || WEAR_LOC(obj, WEAR_BOTH_HANDS))
		{
			pop_call();
			return obj;
		}
	}
	pop_call();
	return NULL;
}


int encumberance( CHAR_DATA *ch )
{
	int can_carry, weight;
	int encumb = 0;
	
	push_call("IS_ENCUMBERED()");
	
	can_carry = can_carry_w(ch);
	weight = ch->carry_weight;
	
	if (weight > can_carry)
	{
		pop_call();
		return OVERLOADED;
	}
	else if (weight > can_carry * 2 / 3)
	{
		encumb = VERY_ENCUMBERED;
	}
	else if (weight > can_carry / 3)
	{
		encumb = ENCUMBERED;
	}
	
	if (ch->hitched)
	{
		int cart_wt = get_obj_weight(ch->hitched);

		encumb++;
		
		if (cart_wt > can_carry)
		{
			encumb++;
		}
	}	
	pop_call();
	return URANGE(0, encumb, OVERLOADED);
}