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.     *
 ***************************************************************************/

/***************************************************************************
 * update.c: Functions for all update loops															   *
 ***************************************************************************/

#include "mud.h"

/*
	Local functions.
*/

int	hit_gain			args ((CHAR_DATA * ch));
int	spell_dice			args ((CHAR_DATA * ch, int sn, int numdice, int sizedice ));
int	nonlethal_gain			args ((CHAR_DATA * ch));
void	mana_gain			args ((CHAR_DATA * ch, int hour));
int	move_gain			args ((CHAR_DATA * ch));
void	recovery_update			args ((CHAR_DATA * ch));
void	mobile_update		args ((void));
void	shop_update		args ((void));
void	weather_update		args ((void));
void time_update		args ((void));
void	char_update		args ((void));
void	obj_update		args ((void));
void	aggr_update		args ((void));
void	bounty_update		args ((void));
void	auto_area_save		args ((void));
void auto_char_save		args ((void));
void mob_program_update	args ((void));
void obj_program_update	args ((void));
void room_program_update	args ((void));
void purger_update		args ((void));
void condition_update		args ((void));
void asn_update		args ((void));
void disease_update		args ((CHAR_DATA * ch));
void check_exposure		args ((CHAR_DATA * ch));


/*
 * Advancement for companions when character advances level - Kregor
 */
void advance_familiar(CHAR_DATA *ch, CHAR_DATA *mob)
{
	int level;
	
	push_call("advance_familiar(%p,%p)",ch,mob);
	
	if (!ch || !mob)
	{
		pop_call();
		return;
	}
	if ((level = multi_skill_level(ch,gsn_familiar)) <= 0)
	{
		pop_call();
		return;
	}

	mob->max_hit		= ch->max_hit / 2;
	mob->hit				= get_max_hit(mob);
	mob->perm_int		=	level / 2 + 5;
	mob->nat_armor	= level / 2;
	
	pop_call();
	return;
}

void advance_companion(CHAR_DATA *ch, CHAR_DATA *mob)
{
	int level, bonus;
	
	push_call("advance_companion(%p,%p)",ch,mob);
	
	if (!ch || !mob)
	{
		pop_call();
		return;
	}
	if ((level = multi_skill_level(ch,gsn_companion)) <= 0)
	{
		pop_call();
		return;
	}

	bonus = UMAX(0, (level - race_table[mob->race].hit_dice) / 3);

	mob->level			= bonus * 2 + UMAX(1, race_table[mob->race].hit_dice);
	mob->nat_armor	= bonus * 2;
	mob->perm_str		= 10 + race_table[mob->race].race_mod[0] + bonus;
	mob->perm_dex		= 10 + race_table[mob->race].race_mod[1] + bonus;
	mob->max_hit		= dice(mob->level, race_type_table[race_type(mob)].hit_die);
	mob->hit				= get_max_hit(mob);
	
	pop_call();
	return;
}

void advance_warhorse(CHAR_DATA *ch, CHAR_DATA *mob)
{
	int level;
	AFFECT_DATA af;
	
	push_call("advance_warhorse(%p,%p)",ch,mob);
	
	if (!ch || !mob)
	{
		pop_call();
		return;
	}
	if ((level = multi_skill_level(ch,gsn_warhorse)) <= 0)
	{
		pop_call();
		return;
	}

	mob->level			= (level / 2) + race_table[mob->race].hit_dice;
	mob->nat_armor	= ROUNDUP(level * 2 / 3);
	mob->perm_str		= (level / 5) + 10 + race_table[mob->race].race_mod[0];
	mob->perm_int		= URANGE(6, level*2/3, 9) ;
	mob->max_hit		= dice(mob->level, race_type_table[race_type(mob)].hit_die);
	mob->hit				= get_max_hit(mob);
	
	while (mob->first_affect)
	{
		affect_from_char(mob, mob->first_affect);
	}

	if (level >= 10)
	{
		af.type      = gsn_warhorse;
		af.duration  = -1;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = AFF_NONE;
		af.location  = APPLY_SPELL_RES;
		af.modifier  = level + 11;
		af.level     = level;
		affect_to_char( mob, mob, &af );
	}

	if (level >= 6)
	{
		af.type      = gsn_warhorse;
		af.duration  = -1;
		af.level     = level;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = AFF_NONE;
		af.location  = APPLY_DR_EVIL;
		af.modifier  = 5;
		affect_to_char( mob, mob, &af );

		af.bitvector = AFF_NONE;
		af.location  = APPLY_DR_COLD;
		af.modifier  = 10;
		affect_to_char( mob, mob, &af );

		af.bitvector = AFF_NONE;
		af.location  = APPLY_DR_ACID;
		af.modifier  = 10;
		affect_to_char( mob, mob, &af );

		af.bitvector = AFF_NONE;
		af.location  = APPLY_DR_ELECTRIC;
		af.modifier  = 10;
		affect_to_char( mob, mob, &af );
	}
	
	pop_call();
	return;
}

/*
 * The dreaded level loss - Kregor
 * now using cached data for each level's gain to remove skills/hp/and points
 */
void lose_level (CHAR_DATA * ch, bool fSave)
{
	int add_hp, add_mana, add_move, add_prac, sn, class_lvl;

	push_call("lose_level(%p,%p)",ch,fSave);

	if (ch->level <= starting_level(ch))
	{
		wiz_printf("cannot lose racial hit dice levels");
		pop_call();
		return;
	}
	add_hp = ch->pcdata->hit_level[ch->level];

	add_mana = get_max_mana(ch, ch->class);

	add_move = ch->pcdata->move_level[ch->level];

	add_prac = ch->pcdata->pract_level[ch->level];

	add_hp   = UMAX (1, add_hp);
	add_mana = UMAX (1, add_mana);
	add_move = UMAX (1, add_move);

	ch->max_hit -= add_hp;
	ch->hit -= add_hp;
	ch->max_move -= add_move;
	ch->pcdata->practice = UMAX(0, ch->pcdata->pract_level[ch->level] - add_prac);
		
	if ((ch->level-1) % 3 == 0)
	{
		ch->pcdata->feat_pts = UMAX(0, ch->pcdata->feat_pts - 1);
	}

	if (ch->level % 4 == 0)
	{
		ch->pcdata->stat_pts = UMAX(0, ch->pcdata->stat_pts - 1);
	}

	if ((class_lvl = ch->mclass[ch->class]) == class_table[ch->class].first_bonus 
	|| (class_lvl > class_table[ch->class].first_bonus && (class_lvl - class_table[ch->class].first_bonus) % class_table[ch->class].bonus_lvl == 0 ))
	{
		ch->pcdata->bonus_feat[ch->class] = UMAX(0, ch->pcdata->bonus_feat[ch->class] - 1);
	}
	
	switch (ch->class)
	{
		case CLASS_FIGHTER:
			if (ch->mclass[ch->class] == 1)
				ch->pcdata->bonus_feat[ch->class] = UMAX(0, ch->pcdata->bonus_feat[ch->class] - 1);
			break;
		case CLASS_WIZARD:
			ch->pcdata->bonus_spells -= 2;
			break;
	}
	
	for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
	{
		if (ch->pcdata->skill_level[ch->level][sn] > 0)
			ch->learned[sn] -= ch->pcdata->skill_level[ch->level][sn];
	}

	ch->level--;

	/* change active class to previous class */
	ch->class = ch->pcdata->class_level[ch->level];
			
	/*make sure if ch not a druid or a rogue anymore, take away the class language*/
	if (!class_level(ch, CLASS_DRUID) && IS_SET(ch->language, LANG_DRUIDIC))
		REMOVE_BIT(ch->language, LANG_DRUIDIC);

	if (!class_level(ch, CLASS_ROGUE) && IS_SET(ch->language, LANG_THIEVESCANT))
		REMOVE_BIT(ch->language, LANG_THIEVESCANT);

	restore_mana(ch);
	destroy_mana(ch);

	if (fSave)
	{
		sub_player (ch);
		add_player (ch);
		save_char_obj (ch, NORMAL_SAVE);
		save_char_obj (ch, BACKUP_SAVE);
	}

	ch_printf_color(ch, "Your loss is: %d/%d hp, %d/%d mv %d/%d skill points.\n\r",
		add_hp, add_move, add_prac );

	vt100prompt(ch);

	pop_call();
	return;
}

/*
	Adds monster levels to PC races with hit dice at char gen.
*/
void monster_levels (CHAR_DATA * ch, int levels)
{
	int add_hp;
	int add_move;
	int add_prac;
	int cnt;

	push_call("monster_levels(%p,%p)",ch,levels);
	
	for (cnt = 0 ; cnt < levels ; cnt++)
	{
		ch->level++;
		ch->mclass[CLASS_MONSTER]++;

		add_hp = dice(1, race_type_table[race_table[ch->race].type].hit_die);
	
		add_move = number_range(1, race_type_table[race_table[ch->race].type].hit_die / 2);
	
		add_prac = race_type_table[race_table[ch->race].type].skill_pts + stat_bonus(FALSE, ch, APPLY_INT);
	
		add_hp   = UMAX (1, add_hp);
		add_move = UMAX (1, add_move);
	
		ch->max_hit += add_hp;
		ch->hit += add_hp;
		ch->max_move += add_move;
		ch->pcdata->practice += add_prac;
	
		if ((ch->level-1) % 3 == 0) /* adding a feat every odd level - Kregor 12/1/2006 */
		{
			ch->pcdata->feat_pts++;
		}
	
		if (ch->level % 4 == 0) /* adding a stat point every four levels - Kregor 12/1/2006 */
		{
			ch->pcdata->stat_pts++;
		}
	}
	ch->pcdata->exp = exp_level(ch, ch->level-1) + 1;

	pop_call();
	return;
}

/*
	Advancement stuff.
*/
void advance_level (CHAR_DATA * ch, bool fSave)
{
	CHAR_DATA *mob;
	int class_lvl;
	int add_hp;
	int add_mana;
	int add_move;
	int add_prac;
	char colY[10];

	push_call("advance_level(%p,%p)",ch,fSave);
	
	strcpy(colY, ansi_translate_text(ch, "{138}"));

	add_hp = dice(1, class_table[ch->class].hp_max);

	add_mana = get_max_mana(ch, ch->class) - ch->max_mana[ch->class];

	add_move = number_range(1, class_table[ch->class].hp_max / 2);

	add_prac = class_table[ch->class].skill_pts + stat_bonus(FALSE, ch, APPLY_INT);

	if (ch->race == RACE_HUMAN)
		add_prac += 1;
	if (ch->race == RACE_HALFELF && ch->level % 2 == 0)
		add_prac += 1;

	add_hp   = UMAX (1, add_hp);
	add_mana = UMAX (1, add_mana);
	add_move = UMAX (1, add_move);

	ch->max_mana[ch->class]  = get_max_mana(ch, ch->class);
	ch->max_hit += add_hp;
	ch->hit += add_hp;
	ch->max_move += add_move;
	ch->mana[ch->class] += add_mana;
	ch->pcdata->practice += add_prac;
	ch->pcdata->hit_level[ch->level] = add_hp;
	ch->pcdata->move_level[ch->level] = add_move;
	ch->pcdata->class_level[ch->level] = ch->class;
	ch->pcdata->pract_level[ch->level] = add_prac;

	if (ch->lost_levels > 0) /* removing energy drained levels */
		ch->lost_levels--;

	if ((ch->level - 1) % 2 == 0) /* adding a feat every odd level - Kregor 12/1/2006 */
	{
		ch->pcdata->feat_pts++;
		ch_printf_color(ch, "%sYou gained a feat point, you now have %d.\n\r", colY, ch->pcdata->feat_pts );
	}

	if (ch->level%4 == 0) /* adding a stat point every four levels - Kregor 12/1/2006 */
	{
		ch->pcdata->stat_pts++;
		ch_printf_color(ch, "%sYou gained a stat point, you now have %d.\n\r", colY, ch->pcdata->stat_pts );
	}
	
	if ((class_lvl = ch->mclass[ch->class]) == class_table[ch->class].first_bonus 
	|| (class_lvl > class_table[ch->class].first_bonus && (class_lvl - class_table[ch->class].first_bonus) % class_table[ch->class].bonus_lvl == 0 ))
	{
		ch->pcdata->bonus_feat[ch->class]++;
		ch_printf_color(ch, "%sYou gained a bonus feat.\n\r", colY);
	}
	
	if (ch->class == CLASS_FIGHTER && class_lvl == 1)
		ch->pcdata->bonus_feat[ch->class]++;

	switch (ch->class)
	{
		case CLASS_WIZARD:
			if (class_lvl == 1)
				ch_printf_color(ch, "%sYou may select a school of specialty. (Syntax: school)\n\r", colY);
			ch->pcdata->bonus_spells += 2;
				ch_printf_color(ch, "%sYou can learn two bonus spells. (Syntax: learn)\n\r", colY);			
			break;
		case CLASS_RANGER:
			if (class_lvl == 1
			|| class_lvl % 5 == 0)
				ch_printf_color(ch, "%sYou may chose a new favored enemy. (Syntax: faveenemy)\n\r", colY);
			else if (class_lvl == 2)
				ch_printf_color(ch, "%sYou may select a fighting style, seek out a mentor.\n\r", colY);
			break;
		case CLASS_MONK:
			if (class_lvl == 1)
				ch_printf_color(ch, "%sYou may select a fighting style, seek out a mentor.\n\r", colY);
			break;
	}

	if (ch->class == CLASS_DRUID && !IS_SET(ch->language, LANG_DRUIDIC))
		SET_BIT(ch->language, LANG_DRUIDIC);

	if (ch->class == CLASS_ROGUE && !IS_SET(ch->language, LANG_THIEVESCANT))
		SET_BIT(ch->language, LANG_THIEVESCANT);

	if ((mob = get_familiar(ch)) != NULL)
		advance_familiar(ch, mob);

	if ((mob = get_companion(ch)) != NULL)
		advance_companion(ch, mob);

	if ((mob = get_warhorse(ch)) != NULL)
		advance_warhorse(ch, mob);

	if (fSave)
	{
		sub_player (ch);
		add_player (ch);
		save_char_obj (ch, NORMAL_SAVE);
		save_char_obj (ch, BACKUP_SAVE);
	}

	ch_printf_color(ch, "Your gain is: %d/%d hp, %d/%d m, %d/%d mv %d/%d skill points.\n\r",
		add_hp, get_max_hit(ch),
		add_mana, get_max_mana(ch,ch->class),
		add_move, get_max_move(ch),
		add_prac, ch->pcdata->practice );

	char_reset(ch);
	vt100prompt(ch);

	pop_call();
	return;
}

/*
	Return starting level for CH
*/
int starting_level( CHAR_DATA *ch )
{
	int level = 1;
	
	if (race_table[ch->race].hit_dice > 1)
		level = race_table[ch->race].hit_dice;
	
	return level;
}

void gain_exp (CHAR_DATA * ch, int gain)
{
	int penalty;

	push_call("gain_exp(%p,%p)",ch,gain);

	if (IS_NPC (ch) || ch->level >= LEVEL_HERO - 1 || ch->in_room->area->low_r_vnum == ROOM_VNUM_ARENA)
	{
		pop_call();
		return;
	}
	
	penalty = level_diff(ch) * 20;
	gain = gain * (100 - penalty) / 100;
	
	// add RP rating as a modifier to exp - Kregor
	if (ch->desc && ch->desc->account)
		gain = gain * (100 + ch->desc->account->rp_points) / 100;

	if (ch->pcdata->exp < exp_level(ch, ch->level + 1) -1)
	{
		ch->pcdata->exp = UMAX(0, ch->pcdata->exp + gain);
		while (ch->level < LEVEL_HERO && ch->pcdata->exp >= exp_level(ch, ch->level))
		{
			if (NOT_AUTHED(ch) && ch->level >= 2)
				send_to_char ("You may not advance a level until you are authorized!\n\r", ch);
			else	
				send_to_char ("You may advance a level! Go see a trainer! \n\r", ch);
			pop_call();
			return;
		}
	}
	else
	{
		send_to_char ("You may not gain any more experience until you've leveled.\n\r", ch);
	}
	pop_call();
	return;
}

/*
 * Regeneration stuff.
 *
 * hit_gain now checks each recovery period for recovery of
 * incapacitated character, per D20 rules, deviated to check
 * once per turn, rather than once per game hour, allowing
 * for real-time game scale - Kregor
 *
 * hit_gain much slower than other rates of recovery.
 * gains are all on accelerated scale to allow for bearable
 * real-time recovery period.
 */

int hit_gain (CHAR_DATA * ch)
{
	int gain;

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

	if (!IS_NPC(ch))
	{
		pop_call();
		return 0;
	}
	if (in_combat(ch))
	{
		pop_call();
		return 0;
	}
	if (is_affected(ch, gsn_festering_wounds))
	{
		pop_call();
		return 0;
	}

	gain = UMAX(1, get_max_hit(ch)/80 + stat_bonus(TRUE, ch, APPLY_CON));		

	if (IS_AFFECTED(ch, AFF_POISON))
	{
		gain = 0;
	}

	switch (ch->position)
	{
		case POS_STUNNED:
		case POS_SLEEPING:
		case POS_RESTING:
			break;
		case POS_SITTING:
		case POS_KNEELING:
		case POS_CROUCHING:
			gain = UMAX(1, gain/2);
			break;
		case POS_STANDING:
			gain = UMAX(1, gain/4);
			break;
		default:
			gain = 0;
			break;
	}
	pop_call();
	return UMIN(gain, get_max_hit(ch) - ch->hit);
}


int nonlethal_gain (CHAR_DATA * ch)
{
	int gain;

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

	if (!IS_NPC(ch))
	{
		pop_call();
		return 0;
	}
	if (in_combat(ch))
	{
		pop_call();
		return 0;
	}
	if (is_affected(ch, gsn_festering_wounds))
	{
		pop_call();
		return 0;
	}

	gain = UMAX(1, ch->level + get_curr_con(ch));

	switch (ch->position)
	{
		case POS_STUNNED:
		case POS_SLEEPING:
		case POS_RESTING:
			break;
		case POS_SITTING:
		case POS_KNEELING:
		case POS_CROUCHING:
			gain = UMAX(1, gain/2);
			break;
		case POS_STANDING:
			gain = UMAX(1, gain/4);
			break;
		default:
			gain = 0;
			break;
	}

	if (IS_AFFECTED(ch, AFF_POISON))
	{
		gain = 0;
	}
	pop_call();
	return UMIN(gain, ch->nonlethal);
}


void mana_gain(CHAR_DATA *ch, int hour)
{
	int cnt;

	push_call("restore_mana(%p)",ch);
	
	for (cnt = 0 ; cnt < MAX_CLASS ; cnt++)
	{
		if (!class_level(ch, cnt)
		|| class_table[cnt].mana_table == MANA_NONE)
		{
			ch->mana[cnt] = 0;
			continue;
		}
		if (hour == 1)
		{
			if (ch->mana[cnt] < get_max_mana(ch, cnt) / 3)
				ch->mana[cnt] = get_max_mana(ch, cnt) / 3;
		}
		if (hour == 2)
		{
			if (ch->mana[cnt] < get_max_mana(ch, cnt) * 2 / 3)
				ch->mana[cnt] = get_max_mana(ch, cnt) * 2 / 3;
		}
		if (hour >= 8)
			restore_mana(ch);
	}
	pop_call();
	return;
}

void restore_mana(CHAR_DATA *ch)
{
	int cnt;

	push_call("restore_mana(%p)",ch);
	
	for (cnt = 0 ; cnt < MAX_CLASS ; cnt++)
	{
		if (!class_level(ch, cnt)
		|| class_table[cnt].mana_table == MANA_NONE)
		{
			ch->mana[cnt] = 0;
			continue;
		}

		ch->mana[cnt] = get_max_mana(ch, cnt);
	}
	pop_call();
	return;
}

void destroy_mana(CHAR_DATA *ch)
{
	int cnt;

	push_call("destroy_mana(%p)",ch);
	
	for (cnt = 0 ; cnt < MAX_CLASS ; cnt++)
	{
		ch->mana[cnt] = 0;
	}
	pop_call();
	return;
}

int move_gain (CHAR_DATA * ch)
{
	int gain, bonus;

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

	if (!IS_NPC(ch))
	{
		pop_call();
		return 0;
	}
	if (in_combat(ch))
	{
		pop_call();
		return 0;
	}
	bonus = (stat_bonus(TRUE, ch, APPLY_CON));
				
	gain = UMAX(1, get_max_move(ch)/8 + bonus);

	switch (ch->position)
	{
		case POS_STUNNED:
		case POS_SLEEPING:
		case POS_RESTING:
			break;
		case POS_SITTING:
		case POS_KNEELING:
		case POS_CROUCHING:
			gain = UMAX(1, gain/2);
			break;
		case POS_STANDING:
			gain = UMAX(1, gain/4);
			break;
		default:
			gain = 0;
			break;
	}

	if (IS_AFFECTED(ch, AFF_POISON))
	{
		gain /= 2;
	}
	pop_call();
	return UMIN (gain, get_max_move(ch) - ch->move);
}


void gain_condition (CHAR_DATA * ch, int iCond, int value)
{
	int old_condition;

	push_call("gain_condition(%p,%p,%p)",ch,iCond,value);

	if ( value == 0 || IS_NPC(ch) || IS_IMMORTAL(ch) || NEW_AUTH(ch))
	{
		pop_call();
		return;
	}
	if (!ch->in_room)
	{
		pop_call();
		return;
	}
	old_condition	= ch->pcdata->condition[iCond];
	
	switch (iCond)
	{
		case COND_AIR:
			ch->pcdata->condition[iCond] = URANGE( -10, old_condition + value, max_air(ch));
			break;
		case COND_THIRST:
			ch->pcdata->condition[iCond] = URANGE( -10, old_condition + value, max_thirst(ch) );
			break;
		case COND_DRUNK:
			ch->pcdata->condition[iCond] = URANGE( 0, old_condition + value, max_drunk(ch) );
			break;
		default:
			ch->pcdata->condition[iCond] = URANGE( -10, old_condition + value, max_hunger(ch) );
			break;
	}
	
	if (IS_RACE(ch, RACE_VAMPIRE))
	{
		if (iCond == COND_THIRST && ch->pcdata->condition[COND_THIRST] <= 3)
		{
			send_to_char("Your body is craving for blood.\n\r", ch);
			act( "$n looks pale and weak.", ch, NULL, NULL, TO_ROOM);

			damage( ch, ch, 1+ch->level/7, TYPE_NOFIGHT, NULL );
		}

		pop_call();
		return;
	}

	switch ( iCond )
	{
		case COND_FULL:
			if (ch->pcdata->condition[iCond] <= 0)
			{
				if (ch->level > 1)
				{
					send_to_char_color( "{038}You are STARVING!{300}\n\r", ch );
					act( "{038}$n is starved half to death!{300}", ch, NULL, NULL, TO_ROOM);
					if (con_roll(ch) < 10 - ch->pcdata->condition[iCond])
					{
						ch->nonlethal += dice(1,6);
					}
				}
				else
				{
					send_to_char_color( "{038}You are REALLY hungry.{300}\n\r", ch );
				}
				pop_call();
				return;
			}
			else if (ch->pcdata->condition[iCond] < old_condition)
			{
				if (ch->pcdata->condition[iCond] <= 24)
				{
					send_to_char_color( "{038}You are REALLY hungry.{300}\n\r", ch );
					act( "{038}You can hear $n's stomach growling.{300}", ch, NULL, NULL, TO_ROOM);
				}
				else if (ch->pcdata->condition[iCond] <= 32)
				{
					send_to_char_color( "{038}You are hungry.{300}\n\r", ch );
				}
				else if (ch->pcdata->condition[iCond] <= 40)
				{
					send_to_char_color( "{038}You are a mite peckish.{300}\n\r", ch );
				}
			}
			else if (old_condition < ch->pcdata->condition[iCond])
			{
				if (ch->pcdata->condition[iCond] >= 48 && old_condition < ch->pcdata->condition[iCond])
				{
					send_to_char_color( "{038}You are stuffed!{300}\n\r", ch );
				}
				else if (ch->pcdata->condition[iCond] >= 44 && old_condition < ch->pcdata->condition[iCond])
				{
					send_to_char_color( "{038}You are full.{300}\n\r", ch );
				}
			}
			break;
		case COND_THIRST:
			if (ch->pcdata->condition[iCond] <= 0)
			{
				int dc = ch->pcdata->condition[iCond];
				if (ch->in_room->sector_type == SECT_DESERT)
					dc *= 2;

				if( ch->level > 1 )
				{
					send_to_char_color( "{168}You are DYING of THIRST!{300}\n\r", ch );
					act( "{168}$n is dying of thirst!{300}", ch, NULL, NULL, TO_ROOM);
					if (con_roll(ch) < 10 - dc)
					{
						ch->nonlethal += dice(1,6);
					}
				}
				else
				{
					send_to_char_color( "{168}You are REALLY thirsty.{300}\n\r", ch );
					act( "{168}$n is visibly parched.{300}", ch, NULL, NULL, TO_ROOM);
				}
				pop_call();
				return;
			}
			else if (ch->pcdata->condition[iCond] < old_condition)
			{
				if (ch->pcdata->condition[iCond] <= get_curr_con(ch) / 2)
				{
					send_to_char_color( "{168}You are REALLY thirsty.{300}\n\r", ch );
					act( "{168}$n is visibly parched.{300}", ch, NULL, NULL, TO_ROOM);
				}
				else if (ch->pcdata->condition[iCond] <= get_curr_con(ch))
				{
					send_to_char_color( "{168}You are thirsty.{300}\n\r", ch );
				}
				else if (ch->pcdata->condition[iCond] <= get_curr_con(ch))
				{
					send_to_char_color( "{168}You could use a sip of something refreshing.{300}\n\r", ch );
				}
			}
			else if (old_condition < ch->pcdata->condition[iCond])
			{
				if (ch->pcdata->condition[iCond] >= 24 + get_curr_con(ch))
				{
					send_to_char_color( "{168}You are no longer thirsty.{300}\n\r", ch );
				}
				else if (ch->pcdata->condition[iCond] <= get_curr_con(ch) / 2 + 24)
				{
					send_to_char_color( "{168}You do not feel so thirsty.{300}\n\r", ch );
				}
			}
			break;
		case COND_DRUNK:
			if (ch->pcdata->condition[iCond] <= 0)
			{
				if ( old_condition > 0 )
				{
					send_to_char_color( "You are sober.\n\r", ch );
					pop_call();
					return;
				}
			}
			else if (ch->pcdata->condition[iCond] >= get_curr_con(ch) * 5)
			{
				if ( old_condition < ch->pcdata->condition[iCond] )
				{
					send_to_char_color( "You are totally sloshed!\n\r", ch );
					act( "$n looks totally sloshed.", ch, NULL, NULL, TO_ROOM);
					update_pos(ch,-1);
				}
				else
				{
					send_to_char_color( "You are feeling a little less sloshed.\n\r", ch );
				}				
			}
			else if (ch->pcdata->condition[iCond] >= get_curr_con(ch) * 4)
			{
				if ( old_condition < ch->pcdata->condition[iCond] )
				{
					send_to_char_color( "You are getting hammered.\n\r", ch );
				}
				else
				{
					send_to_char_color( "You feeling a little less plastered.\n\r", ch );
				}				
			}
			else if (ch->pcdata->condition[iCond] >= get_curr_con(ch) * 3)
			{
				if ( old_condition < ch->pcdata->condition[iCond] )
				{
					send_to_char_color( "You are getting drunk.\n\r", ch );
				}
				else
				{
					send_to_char_color( "You are feeling a little less hammered.\n\r", ch );
				}				
			}
			else if (ch->pcdata->condition[iCond] >= get_curr_con(ch) * 2)
			{
				if ( old_condition < ch->pcdata->condition[iCond] )
				{
					send_to_char_color( "You are feeling light headed.\n\r", ch );
				}
				else
				{
					send_to_char_color( "You are feeling a little less drunk.\n\r", ch );
				}				
			}
			else if (ch->pcdata->condition[iCond] >= get_curr_con(ch))
			{
				if ( old_condition < ch->pcdata->condition[iCond] )
				{
					send_to_char_color( "You are feeling a little tipsy.\n\r", ch );
				}
				else
				{
					send_to_char_color( "You are feeling less light headed.\n\r", ch );
				}				
			}
			break;
		case COND_AIR:
			if (ch->pcdata->condition[iCond] <= 0)
			{
				if (ch->position < POS_STUNNED)
				{
					damage( ch, ch, 9, TYPE_NOFIGHT, NULL );
					update_pos(ch,-1);
				}
				else if (!IS_AWAKE(ch))
				{
					send_to_char_color( "{118}You cease struggling as your vision fades...\n\r", ch);
					damage( ch, ch, ch->hit + 1, TYPE_NOFIGHT, NULL );
				}				
				else if (!swim_check(ch, 0 - ch->pcdata->condition[iCond]))
				{
					act( "{118}You gasp and panic trying to get air in your lungs!", ch, NULL, NULL, TO_CHAR);
					act( "{148}$n gasps and panics trying to fill $s lungs!", ch, NULL, NULL, TO_ROOM);
					ch->nonlethal = UMIN(ch->nonlethal + get_max_hit(ch)/3, ch->hit);
				}
				else
				{
					act( "{148}You manage to hold your breath just a little bit longer...", ch, NULL, NULL, TO_CHAR);
				}
			}
			else if (ch->pcdata->condition[iCond] <= 5)
			{
				act( "{118}You can't hold your breath much longer!", ch, NULL, NULL, TO_CHAR);
			}
			break;
	}
	pop_call();
	return;
}

/*
 * controls the addition/subraction of favor from
 * a PCs deity. Pass -1 in Domain to ignore domains - Kregor
 */
bool gain_favor( CHAR_DATA *ch, int Domain, int gain )
{
	int God;
	
	push_call("gain_favor(%p,%p,%p)",ch,Domain,gain);
		
	if (IS_NPC(ch))
	{
		pop_call();
		return FALSE;
	}
	if ((God = which_god(ch)) == GOD_NEUTRAL)
	{
		pop_call();
		return FALSE;
	}
	
	// Pass -1 if favor awarded regardless of domain, otherwise, trap here.	
	if (Domain != -1)
	{
		if (!god_table[God].domain[Domain])
		{
			pop_call();
			return FALSE;
		}
	}
	
	if (ch->pcdata->domain[Domain])
		gain *= 2;
	
	ch->pcdata->god_favor = URANGE(-1000, ch->pcdata->god_favor + gain, 1000);
	
	if (gain < 0 && ch->pcdata->god_favor < 0)
	{
		send_to_char("{038}Your god is ignoring you.", ch);
	}

	pop_call();
	return TRUE;
}


void obj_program_update (void)
{
	push_call("obj_program_update()");

	pop_call();
	return;
}

void mob_program_update (void)
{
	CHAR_DATA *ich;
	AREA_DATA *area;

	int vnum;

	push_call("mob_program_update()");

	for (area = mud->f_area ; area ; area = area->next)
	{
		if (!area->nplayer) // no reason to trigger if no one is in area - Kregor
			continue;

		for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++)
		{
			if (mob_index[vnum] == NULL || mob_index[vnum]->first_instance == NULL)
			{
				continue;
			}

			if (IS_SET(mob_index[vnum]->progtypes, RAND_PROG) || mob_index[vnum]->spec_fun)
			{
				for (ich = mob_index[vnum]->first_instance ; ich ; ich = mud->update_ich)
				{
					mud->update_ich = ich->next_instance;

					if (!MP_VALID_MOB(ich))
					{
						continue;
					}

					/* if rand prog triggers, it overrides spec_fun, 
					otherwise pass to spec_fun - Kregor */
					
					if (IS_SET(mob_index[vnum]->progtypes, RAND_PROG)
					&& ich->position > POS_SLEEPING && !in_combat(ich))
					{
						if (mprog_percent_check(ich, NULL, NULL, NULL, RAND_PROG))
							continue;
					}
					if (mob_index[vnum]->spec_fun)
					{	
						(*ich->pIndexData->spec_fun) (ich);
					}
				}
			}
		}
	}
	pop_call();
	return;
}

void room_program_update (void)
{
	AREA_DATA *area;
	ROOM_INDEX_DATA *room;

	int vnum;

	push_call("mob_program_update()");

	for (area = mud->f_area ; area ; area = area->next)
	{
		if (!area->nplayer) // rand progs do not trigger if no one is in area - Kregor
			continue;

		for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++)
		{
			if ((room = room_index[vnum]) == NULL || !room->nplayer) // rand progs do not trigger without someone in the room either - Kregor
			{
				continue;
			}
			
			if (IS_SET(room->progtypes, RAND_PROG))
			{
				rset_supermob(room);
				rprog_percent_check(supermob, NULL, NULL, NULL, RAND_PROG);
				release_supermob();
			}
		}
	}
	pop_call();
	return;
}

void auto_char_save (void)
{
	PLAYER_GAME *npl;
	CHAR_DATA *oldest;
	CLAN_DATA *oldest_clan, *clan;
	int oldest_time, oldest_clan_time, total_clans;
	static int save_delay;
	static int clan_delay;

	push_call("auto_char_save()");

	if (save_delay >= 0)
	{
		save_delay--;
	}
	else
	{
		save_delay	= 600 / PULSE_CHARSAVE / UMAX(1, mud->total_plr);
		oldest		= NULL;
		oldest_time	= mud->current_time;

		for (npl = mud->f_player ; npl ; npl = npl->next)
		{
			if (npl->ch->pcdata->last_saved < oldest_time)
			{
				oldest_time	= npl->ch->pcdata->last_saved;
				oldest		= npl->ch;
			}
		}

		if (oldest)
		{
			save_char_obj(oldest, NORMAL_SAVE);
		}

		for (npl = mud->f_player ; npl ; npl = npl->next)
		{
			if (npl->ch->timer > 30 && (!npl->ch->pcdata->switched || npl->ch->timer > 60) && (!IS_IMMORTAL(npl->ch) || !is_desc_valid(npl->ch)))
			{
				char_from_room(npl->ch);
				char_to_room(npl->ch, npl->ch->pcdata->was_in_room, TRUE);

				do_quit(npl->ch, NULL);
				break;
			}
		}
	}

	if (clan_delay >= 0)
	{
		clan_delay--;
	}
	else
	{
		for (total_clans = 0, clan = mud->f_clan ; clan ; clan = clan->next)
		{
			total_clans++;
		}

		clan_delay		= 3600 / PULSE_CHARSAVE / UMAX(1, total_clans);
		oldest_clan_time	= mud->current_time;
		oldest_clan		= NULL;

		for (clan = mud->f_clan ; clan ; clan = clan->next)
		{
			if (clan->last_saved < oldest_clan_time)
			{
				oldest_clan_time	= clan->last_saved;
				oldest_clan		= clan;
			}
		}

		if (oldest_clan)
		{
			save_clan(oldest_clan);
			oldest_clan->last_saved = mud->current_time;
		}
	}
	pop_call();
	return;
}

void auto_area_save (void)
{
	push_call("auto_area_save(void)");

	do_savearea(NULL, "forreal");

	pop_call();
	return;
}


void shop_update (void)
{
	OBJ_DATA *obj;
	CHAR_DATA *ich;
	AREA_DATA *area;

	int vnum;

	push_call("shop_update()");

	for (area = mud->f_area ; area ; area = area->next)
	{
		for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++)
		{
			if (mob_index[vnum] == NULL || mob_index[vnum]->pShop == NULL)
			{
				continue;
			}

			for (ich = mob_index[vnum]->first_instance ; ich ; ich = ich->next_instance)
			{
				for (obj = ich->last_carrying ; obj ; obj = obj->prev_content)
				{
					if (obj->reset == NULL)
					{
						if (number_bits(3) == 0)
						{
							act ("$n sells $p to a shopper.", ich, obj, NULL, TO_ROOM);
							junk_obj(obj);
						}
					}
					else
					{
						break;
					}
				}
			}
		}
	}
	pop_call();
	return;
}


void mobile_update (void)
{
	CHAR_DATA *ich;
	CHAR_DATA *rch;
	AREA_DATA *area;
	EXIT_DATA *pExit;
	OBJ_DATA *obj, *obj_best;
	int vnum, door, max;

	push_call("mobile_update()");

	for (area = mud->f_area ; area ; area = area->next)
	{
		for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++)
		{
			if (mob_index[vnum] == NULL || mob_index[vnum]->first_instance == NULL)
			{
				continue;
			}

			for (ich = mob_index[vnum]->first_instance ; ich ; ich = mud->update_ich)
			{
				mud->update_ich = ich->next_instance;

				if (ich->in_room && ich != supermob && ich->in_room->vnum == ROOM_VNUM_JUNK)
				{
					junk_mob(ich);
					continue;
				}
				
				// purge mprog loaded mobs if area is clear - Kregor
				if (ich->npcdata->mloaded && !ich->in_room->area->nplayer)
				{
					junk_mob(ich);
					continue;
				}

				// put hunting code here for now, may be better place - Kregor
				if (hunt_victim(ich))
					continue;

				// added to make sentinel mobiles return to their reset room, to
				// foil exploit of shoving/baiting away from their station - Kregor
				if (IS_ACT(ich, ACT_SENTINEL) && !ich->rewalkto && !ich->walkto)
				{
					if (!ich->reset)
						continue;
					if (ich->master)
						continue;
					if (in_combat(ich))
						continue;
					if (IS_AFFECTED(ich, AFF_DOMINATE))
						continue;
					if (IS_AWAKE(ich) && ich->position != ich->pIndexData->position)
					{
						switch (ich->pIndexData->position)
						{
							default:
								break;
							case POS_SITTING:
								do_sit(ich, "");
								break;
							case POS_RESTING:
								do_rest(ich, "");
								break;
							case POS_KNEELING:
								do_kneel(ich, "");
								break;
							case POS_CROUCHING:
								do_crouch(ich, "");
								break;
							case POS_STANDING:
								do_stand(ich, "");
								break;
						}
						continue;
					}
					if (ich->in_room->vnum == ich->reset->arg3)
						continue;
					if (IS_ACT(ich, ACT_WILL_DIE))
						continue;
					if ((door = findpath_room(ich, ich->reset->arg3, 400)) < 0
					|| !is_valid_exit(ich, ich->in_room, door))
					{
						act( "$n leaves.", ich, NULL, NULL, TO_ROOM);
						char_from_room(ich);
						char_to_room(ich, ich->reset->arg3, TRUE);
						act( "$n arrives.", ich, NULL, NULL, TO_ROOM);
						continue;
					}
					move_char( ich, door, TRUE, NULL );
					if (ich->reset->arg3 == ich->in_room->vnum)
					{
						mprog_arrival_trigger(ich);
					}
				}

				if (ich->rewalkto != 0 && ich->walkto == 0)
				{
					ich->walkto = ich->rewalkto;
					ich->rewalkto = 0;
					continue;
				}
				if (ich->walkto != 0)
				{
					if (in_combat(ich))
						continue;

					if ((door = findpath_room(ich, ich->walkto, 400)) == -2)
					{
						ich->walkto = 0;
						continue;
					}
					else if (!MP_VALID_MOB(ich))
					{
						ich->walkto = 0;
						continue;
					}
					else if (ich->position < POS_STANDING)
					{
						do_stand(ich, "");
						continue;
					}
					else if (door == -1)
					{
						if (CAN_TALK(ich))
							do_say(ich, "Where was I going again?");
						ich->walkto = 0;
						continue;
					}
					if( IS_SET( ich->in_room->exit[door]->exit_info, EX_CLOSED ) )
					{
						if (!is_handy(ich))
						{
							ich->walkto = 0;
							continue;
						}
						do_open( ich, (char *) dir_name[door] );
						if ( IS_SET( ich->in_room->exit[door]->exit_info, EX_CLOSED ) )
							ich->walkto = 0;
						continue;
					}
					else if (!is_valid_exit(ich, ich->in_room, door))
					{
						if (CAN_TALK(ich))
							do_say(ich, "Where was I going again?");
						ich->walkto = 0;
						continue;
					}
					else
					{
						move_char( ich, door, TRUE, NULL );
						if (ich->walkto == ich->in_room->vnum)
						{
							mprog_arrival_trigger(ich);
							ich->walkto = 0;
						}
						continue;
					}
				}	
// 			}
// 
// 			for (ich = mob_index[vnum]->first_instance ; ich ; ich = mud->update_ich)
// 			{
// 				mud->update_ich = ich->next_instance;

				if (ich->in_room && ich->in_room->area->nplayer == 0 && number_bits(6))
				{
					continue;
				}
				if (!IS_SET(mob_index[vnum]->act, ACT_SENTINEL))
				{
					if (!MP_VALID_MOB(ich))
						continue;

					if (ich->position != POS_STANDING)
						continue;

					if (ich->walkto != 0)
						continue;
						
					if (IS_SET(ich->act, ACT_SENTINEL))
						continue;
					
					if (!ich->npcdata->hate_fear && number_bits(4) != 0)
						continue;

					door = number_door();

					if ((pExit = ich->in_room->exit[door]) == NULL)
						continue;

					if (room_index[pExit->to_room] == NULL)
						continue;

					if (IS_SET(room_index[pExit->to_room]->room_flags, ROOM_NO_MOB))
						continue;

					if (IS_SET(ich->act, ACT_NOWANDER) && ich->reset && room_index[pExit->to_room]->sector_type != room_index[ich->reset->arg3]->sector_type)
						continue;

					if (IS_SET(ich->act, ACT_STAY_AREA) && room_index[pExit->to_room]->area->low_r_vnum != mob_index[ich->pIndexData->vnum]->area->low_r_vnum)
						continue;

					if (!is_valid_exit(ich, ich->in_room, door))
						continue;

					if (room_index[pExit->to_room]->area == ich->in_room->area)
					{
						move_char( ich, door, TRUE, NULL );
						continue;
					}
				}

				if (IS_SET(mob_index[vnum]->act, ACT_SCAVENGER))
				{
					if (!MP_VALID_MOB(ich))
						continue;

					if (ich->position != POS_STANDING)
						continue;

					if (ich->in_room->first_content == NULL)
						continue;

					if (ich->carry_number >= 10)
						continue;

					if (number_bits(4))
						continue;

					max = 1;
					obj_best = NULL;

					for (obj = ich->in_room->first_content ; obj ; obj = obj->next_content)
					{
						if (IS_SET(obj->wear_flags, CAN_WEAR_TAKE) && obj->cost > max)
						{
							obj_best = obj;
							max = obj->cost;
						}
					}
					if (obj_best)
					{
						char objName[MAX_INPUT_LENGTH];
						sprintf (objName, "i%u", obj_best->pIndexData->vnum);
						do_get (ich, objName);
						do_wear(ich, objName);
					}
				}

				// if no one in area with NPC, go no further with this loop - Kregor
				if (ich->in_room && ich->in_room->area->nplayer == 0)
				{
					continue;
				}

				if (!ich->npcdata->hate_fear)
					continue;

				if (!MP_VALID_MOB(ich))
					continue;

				if (ich->position < POS_STANDING)
					continue;

				if (!IS_SET(ich->act, ACT_WIMPY))
				{
					if (!in_combat(ich))
					{
						for (rch = ich->in_room->first_person ; rch ; rch = rch->next_in_room)
						{
							if (IS_NPC(rch))
								continue;

							if (ich->npcdata->hate_fear == rch->pcdata->pvnum)
							{
								found_hating(ich, rch);
								break;
							}
						}
			/* added for pet assisting of master */
						if (IS_SET(ich->act, ACT_NOASSIST))
							continue;
						if (!ich->master || !in_same_room(ich, ich->master))
							continue;
						if (!in_combat(ich->master) || !who_fighting(ich->master))
							continue;
						fight(ich, who_fighting(ich->master));
						break;
					}
					else if (ich->master)
					{
						if (!in_same_room(ich, ich->master) || !in_combat(ich->master)) //pet will not fight without master!
						{
							char_from_combat(ich);
							break;
						}
			/* end adding of pet assist */
					}
					continue;
				}
				else
				{
					if (ich->hit > get_max_hit(ich) / 2)
						continue;

					if (in_combat(ich))
					{
						do_withdraw(ich, NULL);
						continue;
					}
					door = number_door();

					if ((pExit = get_exit(ich->in_room->vnum, door)) == NULL)
						continue;

					if (IS_SET(pExit->exit_info, EX_CLOSED))
						continue;

					if (IS_SET(room_index[pExit->to_room]->room_flags, ROOM_NO_MOB))
						continue;

					if (IS_SET(ich->act, ACT_NOWANDER) && ich->reset && room_index[pExit->to_room]->sector_type != room_index[ich->reset->arg3]->sector_type)
						continue;

					if (IS_SET(ich->act, ACT_STAY_AREA) && room_index[pExit->to_room]->area->low_r_vnum != mob_index[ich->pIndexData->vnum]->area->low_r_vnum)
						continue;

					for (rch = ich->in_room->first_person ; rch ; rch = rch->next_in_room)
					{
						if (IS_NPC(rch))
							continue;

						if (ich->npcdata->hate_fear == rch->pcdata->pvnum)
						{
							char buf[MAX_INPUT_LENGTH];

							if (CAN_TALK(ich))
							{
								switch (number_bits (2))
								{
									case 0:
										sprintf (buf, "Help! %s is trying to kill me!", capitalize(adjective(rch)));
										break;
									case 1:
										sprintf (buf, "Help! I'm being attacked by %s!", adjective(rch));
										break;
									case 2:
										sprintf (buf, "Stay away from me muderer! Help!!");
										break;
									case 3:
										sprintf (buf, "Someone help me! %s is attacking me!", adjective(rch));
										break;
								}
								do_shout (ich, buf);
							}
							else
							{
								switch (number_bits(2))
								{
									case 0:
										act( "$n looks around frantically as $e searches for an escape!", ich, NULL, NULL, TO_ROOM);
										break;
									case 1:
										act( "$n darts around mindlessly in an attempt to get away!", ich, NULL, NULL, TO_ROOM);
										break;
									case 2:
										act( "$n lets out a whimper and tries to flee!", ich, NULL, NULL, TO_ROOM);
										break;
									case 3:
										act( "$n howls in fear as $e attempts to flee!", ich, NULL, NULL, TO_ROOM);
										break;
								}
							}
							break;
						}
					}
					if (rch)
					{
						move_char( ich, door, TRUE, NULL );
					}
				}
			}
		}
	}
	pop_call();
	return;
}


/*
	Update the weather - Scandum 22-06-2003
*/

void weather_area_update( AREA_DATA *area )
{
	char buf[MAX_STRING_LENGTH];

	PLAYER_GAME *gch;

	int temp, season, daily;

	buf[0] = '\0';

	/*
		Calculate temperature in degrees Celcius
	*/
	season = area->weather_info->temp_summer - area->weather_info->temp_winter;
	daily  = area->weather_info->temp_daily;

	temp   = area->weather_info->temp_winter;

	temp  += season * ( 8 - abs(mud->time_info->month -  6)) /  6;
	temp  += daily  * (12 - abs(mud->time_info->hour  - 12)) / 12;

	/*
		Calculate the wind speed based on current wind speed 0 - 10
	*/

	if (area->weather_info->wind_speed > area->weather_info->wind_scale + 1)
	{
		area->weather_info->wind_speed += number_range(0, 3) - 2;
	}
	else if (area->weather_info->wind_speed < area->weather_info->wind_scale - 1)
	{
		area->weather_info->wind_speed += number_range(0, 3) - 1;
	}
	else
	{
		area->weather_info->wind_speed += number_range(0, 2) - 1;
	}

	area->weather_info->wind_speed = URANGE(-10, area->weather_info->wind_speed, 20);

	/*
		Calculate the wind direction, based on current wind direction
	*/

	area->weather_info->wind_dir   = abs(area->weather_info->wind_dir + number_range(0, 2) - 1) % 8;

	/*
		Calculate the weather, based on current weather
	*/

	if (area->weather_info->change < area->weather_info->wet_scale - 1)
	{
		area->weather_info->change += number_range(0, 3) - 1;
	}
	else if (area->weather_info->change > area->weather_info->wet_scale + 1)
	{
		area->weather_info->change += number_range(0, 3) - 2;
	}
	else
	{
		area->weather_info->change += number_range(0, 4) - 2;
	}

	area->weather_info->change = URANGE(-100, area->weather_info->change, 110);

	/*
		Modify temperature based on weather
	*/

	temp += 5 - URANGE(0, area->weather_info->change, 10);

	area->weather_info->temperature = temp;

	switch (area->weather_info->sky)
	{
		default:
			bug ("Weather_update: bad sky %d.", area->weather_info->sky);
			area->weather_info->sky = SKY_CLOUDLESS;
			break;

		case SKY_CLOUDLESS:
			if (area->weather_info->change > 3)
			{
				switch (number_bits(2))
				{
					case 0:
						cat_sprintf(buf, "Threatening clouds gather on the %s horizon, blocking the sky from view.\n\r", wind_dir_name[area->weather_info->wind_dir]);
						break;
					case 1:
						cat_sprintf(buf, "Low clouds form on the %s horizon and slowly drift towards you.\n\r", wind_dir_name[area->weather_info->wind_dir]);
						break;
					case 2:
						cat_sprintf(buf, "Clouds sweep quickly across the %s horizon, darkening the skies above.\n\r", wind_dir_name[area->weather_info->wind_dir]);
						break;
					case 3:
						cat_sprintf(buf, "Bright white clouds drift lazily towards you from the %s horizon.\n\r", wind_dir_name[area->weather_info->wind_dir]);
						break;
				}
				area->weather_info->sky = SKY_CLOUDY;
			}
			break;

		case SKY_CLOUDY:
			if (area->weather_info->change > 6)
			{
				if (area->weather_info->temperature < 0)
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "Snowflakes flutter down from the leaden skies above.\n\r");
							break;
						case 1:
							strcat(buf, "Lacy flakes of snow silently fall to the ground from the clouds above.\n\r");
							break;
						case 2:
							strcat(buf, "The clouds above give way to softly falling snow.\n\r");
							break;
						case 3:
							strcat(buf, "Snowflakes whisper down from the skies in a soft white dance.\n\r");
							break;
					}
				}
				else if (area->weather_info->temperature < 4)
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "Snow begins to meld with icy rain as sleet falls to the ground.\n\r");
							break;
						case 1:
							strcat(buf, "Sharp sleet stings as it falls from the clouds above.\n\r");
							break;
						case 2:
							strcat(buf, "Icy sleet rains down from the heavens.\n\r");
							break;
						case 3:
							strcat(buf, "Sheets of stinging sleet fall from the dark clouds.\n\r");
							break;
					}
				}
				else
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "Dark clouds break open, pouring rain down upon the lands.\n\r");
							break;
						case 1:
							strcat(buf, "Silvery drops of rain fall from angry looking clouds above.\n\r");
							break;
						case 2:
							strcat(buf, "Soft ribbons of rain slip unheeded from the dark skies overhead.\n\r");
							break;
						case 3:
							strcat(buf, "Fat raindrops merrily pepper the lands from the clouds looming above.\n\r");
							break;
					}
				}
				area->weather_info->change += 2;
				area->weather_info->sky     = SKY_RAINING;
			}
			else if (area->weather_info->change < 4)
			{
				switch (number_bits(2))
				{
					case 0:
						strcat(buf, "The clouds recede, revealing a crystal clear sky.\n\r");
						break;
					case 1:
						strcat(buf, "Dark clouds dip below the horizon as the skies clear.\n\r");
						break;
					case 2:
						strcat(buf, "The clouds swiftly move across the skies as it grows clear.\n\r");
						break;
					case 3:
						strcat(buf, "Clouds sigh softly as they disperse, leaving the skies clear.\n\r");
						break;
				}
				area->weather_info->change -= 2;
				area->weather_info->sky     = SKY_CLOUDLESS;
			}
			break;

		case SKY_RAINING:
			if (area->weather_info->change < 7)
			{
				if (area->weather_info->temperature < 0)
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "A few last snowflakes swirl over the ground as the snow stops.\n\r");
							break;
						case 1:
							strcat(buf, "The world is left in silence as the snows stop falling.\n\r");
							break;
						case 2:
							strcat(buf, "The lands are blanketed in white as the last snowflakes fall.\n\r");
							break;
						case 3:
							strcat(buf, "With a final whisper, the snow stops falling.\n\r");
							break;
					}
				}
				else if (area->weather_info->temperature < 4)
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "The final slivers of sleet fall from the dark clouds as it stops.\n\r");
							break;
						case 1:
							strcat(buf, "The miserable sleet falling from the clouds above stops.\n\r");
							break;
						case 2:
							strcat(buf, "The slippery razor-sharp sleet ends its assault on the lands.\n\r");
							break;
						case 3:
							strcat(buf, "The stinging sleet suddenly ceases as quickly as it began.\n\r");
							break;
					}
				}
				else
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "The air smells fresh as the rain stops falling.\n\r");
							break;
						case 1:
							strcat(buf, "The clouds above dry their tears as the rain ceases.\n\r");
							break;
						case 2:
							strcat(buf, "The greyness above seems to ease as the rains halt.\n\r");
							break;
						case 3:
							strcat(buf, "A sweet scent fills the air as the rain suddenly stops.\n\r");
							break;
					}
				}
				area->weather_info->change -= 1;
				area->weather_info->sky     = SKY_CLOUDY;
			}
			else if (area->weather_info->wind_speed > 6)
			{
				if (area->weather_info->temperature < 0)
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "A ferocious blizzard sweeps through the lands, burying them in white.\n\r");
							break;
						case 1:
							strcat(buf, "Heavy snows swirl in the air as the blizzard rages.\n\r");
							break;
						case 2:
							strcat(buf, "The snows fall heavily upon the ground as a blizzard takes hold.\n\r");
							break;
						case 3:
							strcat(buf, "Snowdrifts form quickly as the blizzard increases its fierceness.\n\r");
							break;
					}
				}
				else if (area->weather_info->temperature < 4)
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "The clouds darken and thicken as they fill with hail.\n\r");
							break;
						case 1:
							strcat(buf, "Huge chunks of hail fall to the grounds with a sizzle.\n\r");
							break;
						case 2:
							strcat(buf, "Hail bombards the ground as creatures run for cover.\n\r");
							break;
						case 3:
							strcat(buf, "A shower of hail falls from the sky to pelts the lands below.\n\r");
							break;
					}
				}
				else
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "The lands glow blue-white as lightning cracks across the sky.\n\r");
							break;
						case 1:
							strcat(buf, "Electricity fills the air as forks of lightning dance over the sky.\n\r");
							break;
						case 2:
							strcat(buf, "The horizon shines brightly as sheet lightning flashes in a quick stutter.\n\r");
							break;
						case 3:
							strcat(buf, "The winds gather speed as lightning paints the skies with broad strokes.\n\r");
							break;
					}
				}
				area->weather_info->wind_speed += 2;
				area->weather_info->sky         = SKY_LIGHTNING;
			}
			break;

		case SKY_LIGHTNING:
			if (area->weather_info->change < 7 || area->weather_info->wind_speed < 7)
			{
				if (area->weather_info->temperature < 0)
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "The thick, swirling snow peters out as the blizzard ends.\n\r");
							break;
						case 1:
							strcat(buf, "The ferocity of the blizzard gives way as the snows diminish.\n\r");
							break;
						case 2:
							strcat(buf, "With a last gasp, the blizzard dwindles away to nothing.\n\r");
							break;
						case 3:
							strcat(buf, "With a final stroke of white, the blizzard dies out.\n\r");
							break;
					}
				}
				else if (area->weather_info->temperature < 4)
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "The cannonade of hail comes to a sudden halt.\n\r");
							break;
						case 1:
							strcat(buf, "The burning hail ends its furious salvo of the lands.\n\r");
							break;
						case 2:
							strcat(buf, "The icy hot hail diminishes, retreating to the clouds above.\n\r");
							break;
						case 3:
							strcat(buf, "As swiftly as it started, the hailstorm recedes.\n\r");
							break;
					}
				}
				else
				{
					switch (number_bits(2))
					{
						case 0:
							strcat(buf, "With a few last flashes, the lightning moves down the horizon.\n\r");
							break;
						case 1:
							strcat(buf, "The skies turn ice blue and brilliant white with a final flash of lightning.\n\r");
							break;
						case 2:
							strcat(buf, "The heavens calm as the lightning storm ends.\n\r");
							break;
						case 3:
							strcat(buf, "The air is alive with electricity as the last fork of lightning appears.\n\r");
							break;
					}
				}
				area->weather_info->wind_speed -= 2;
				area->weather_info->sky         = SKY_RAINING;
			}
			break;
	}
	return; //disable echoes for now! - Kregor

	if (area->nplayer <= 0 || buf[0] == '\0')
	{
		return;
	}

	for (gch = mud->f_player ; gch ; gch = gch->next)
	{
		if (gch->ch->in_room->area != area)
		{
			continue;
		}
		if (!IS_OUTSIDE(gch->ch) || NO_WEATHER_SECT(gch->ch->in_room->sector_type))
		{
			continue;
		}
		if (!IS_AWAKE(gch->ch))
		{
			continue;
		}
		send_to_char(justify(buf, get_page_width(gch->ch)), gch->ch);
	}
}

void weather_update (void)
{
	AREA_DATA *area;

	push_call("weather_update()");

	for (area = mud->f_area ; area ; area = area->next)
	{
		weather_area_update(area);
	}

	pop_call();
	return;
}

void strip_greater (char *str)
{
	char *pt;

	push_call("strip_greater(%p)",str);

	for (pt = str; *pt != '\0'; pt++)
	{
		if (*pt == '<')
		{
			*pt = '(';
		}
		else if (*pt == '>')
		{
			*pt = ')';
		}
	}
	pop_call();
	return;
}


/* Make a html web page - Chaos  3/28/96 */

void save_html_who (void)
{
	FILE *fp;
	char buf[MAX_STRING_LENGTH], buf_race[20];
	char buf2[MAX_STRING_LENGTH];
	char t1[MAX_STRING_LENGTH];
	int leng;
	CHAR_DATA *fch;
	DESCRIPTOR_DATA *d;
	int nMatch, nTotal, cnt;
	CHAR_DATA *wch;
	char class[MAX_INPUT_LENGTH];
	PLAYER_GAME *fpl;
	char *pt;

	push_call("save_html_who()");
	
	pop_call();
	return;

	close_reserve();

	fp = my_fopen ("../public_html/who.html", "w",TRUE);

	if (fp == NULL)
	{
		open_reserve();
		pop_call();
		return;
	}

	fprintf (fp, "<!DOCTYPE html PUBLIC \"-//IETF//DTD// HTML 2.0//EN\">\n");
	fprintf (fp, "<BODY BACKGROUND=\"bumps1.jpg\" text=#000000 alink=#80FF30 vlink=#90FF30 link=#FFFF30 >\n");

	fprintf (fp, "<HTML><HEAD><TITLE>E-Mud Who List</TITLE></HEAD>\n");
	fprintf (fp, "<BODY><FONT SIZE=+2><CENTER>\n");
	fprintf (fp, "Shining Sands WHO Page<p>\n");

	/* Set default arguments. */

	fch = NULL;

	/* Now show matching chars. */

	nMatch = 0;
	nTotal = 0;
	buf[0] = '\0';
	leng = 0;
	for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
	{
		wch = fpl->ch;
		d = NULL;
		if (is_desc_valid (wch))
			d = wch->desc;
		/* Check for match against restrictions.
		 * Don't use trust as that exposes trusted mortals.
		 * Chaos set to see all chars, invis or not.
		 */

		if (IS_ACT(wch, PLR_WIZINVIS) || IS_ACT(wch, PLR_WIZCLOAK))
		{
			continue;
		}
		nTotal++;
		nMatch++;

		//string the multiclasses together.
		for (class[0] = '\0', cnt = 0 ; cnt < MAX_CLASS ; cnt++)
		{
			if (!class_level(wch, cnt))
				continue;

			cat_sprintf(class, "%3s %-2d ", class_table[cnt].who_name, class_level(wch, cnt));
		}

		strcpy (buf_race, str_resize(race_table[wch->race].race_name, t1, -7));

		/*
		* Format it up.
		*/
		sprintf (buf2, "%s%s",
			wch->name, IS_NPC(wch) ? "the monster" : wch->pcdata->title);
		buf2[71] = '\0';
		strip_greater (buf2);
		while (strlen (buf2) < 71)
		{
			str_cat_max (buf2, " ", MAX_STRING_LENGTH);
		}
		leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH);

		if (wch->pcdata->switched || wch->desc != NULL)
		{
			strcpy (buf2, wch->in_room->area->name);
			buf2[8] = '\0';
			while (strlen (buf2) < 8)
			{
				str_cat_max (buf2, "&nbsp", MAX_STRING_LENGTH);
			}
		}
		else if (wch->desc == NULL)
		{
			strcpy (buf2, "LinkLost");
		}
		else
		{
			strcpy (buf2, "Unknown ");	/* Stealth Mode */
		}
		
		leng = str_apd_max (buf, " [", leng, MAX_STRING_LENGTH);
		leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH);
		leng = str_apd_max (buf, "] ", leng, MAX_STRING_LENGTH);

		if (wch->pcdata->html_address != NULL && *wch->pcdata->html_address != '\0')
		{
			strcpy (buf2, wch->pcdata->html_address);
			for (pt = buf2; *pt != '\0'; pt++)
			{
				if (*pt == '*')
				{
					*pt = '~';
				}
			}
			leng = str_apd_max (buf, " Home Page: <a href=\"http://", leng, MAX_STRING_LENGTH);
			leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH);
			leng = str_apd_max (buf, "\">", leng, MAX_STRING_LENGTH);
			leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH);
			leng = str_apd_max (buf, "</a>", leng, MAX_STRING_LENGTH);
		}
		sprintf(buf2, "<BR>&nbsp&nbsp%s - %s", buf_race, class);
		leng = str_apd_max (buf, buf2, leng, MAX_STRING_LENGTH);

		leng = str_apd_max (buf, "<p>\n", leng, MAX_STRING_LENGTH);
	}

	sprintf (buf2, "Players: %d</CENTER></font><p><font size=-1>\n", nTotal);


	fprintf (fp, "%s", buf2);
	fprintf (fp, "%s", buf);

	fprintf (fp, "</font></BODY></HTML>\n");

	if(fp) /* this prevents the possibility to my_fclose(NULL) - Manwe, 15-10-2000 */
	{
		my_fclose (fp);
	}

	open_reserve();

	pop_call();
	return;
}


void vengeance_storm( ROOM_TIMER_DATA *rtd )
{
	ROOM_INDEX_DATA *room;
	CHAR_DATA *ch;
	CHAR_DATA *vch;
	
	push_call("vengeance_storm(%p,%p)",rtd);
	
	if ((room = room_index[rtd->vnum]) == NULL)
	{
		pop_call();
		return;
	}
	if ((ch = get_caster_room(rtd)) == NULL)
	{
		send_to_room("{108}The storm suddenly dissipates!\n\r", room);
		del_room_timer(rtd->vnum, rtd->type);
		pop_call();
		return;
	}
	if ((vch = room->first_person) == NULL)
	{
		pop_call();
		return;
	}
	switch (rtd->duration)
	{
		default:
			break;
		case 8:
			act("{128}Caustic acid rain falls on the area!", vch, NULL, NULL, TO_ALL);
			spell_damage(ch, vch, dice(1,6), gsn_acid_rain, rtd->level);
			break;
		case 6:
			act("{178}Lightning cracks wildly!", vch, NULL, NULL, TO_ALL);
			spell_damage(ch, vch, dice(1,10), gsn_call_lightning, rtd->level);
			break;
		case 4:
			act("{168}Hail beats down upon you!", vch, NULL, NULL, TO_ALL);
			spell_damage(ch, vch, dice(5,6), gsn_ice_storm, rtd->level);
			break;
	}
	pop_call();
	return;
}


void lightning_storm( ROOM_TIMER_DATA *rtd )
{
	ROOM_INDEX_DATA *room;
	CHAR_DATA *ch;
	CHAR_DATA *vch, *vch_next;
	int cnt, rdm, dam, sizedice, numdice;
	
	push_call("vengeance_storm(%p,%p)",rtd);
	
	if ((room = room_index[rtd->vnum]) == NULL)
	{
		del_room_timer(rtd->vnum, rtd->type);
		pop_call();
		return;
	}
	if ((ch = get_caster_room(rtd)) == NULL || ch->in_room != room)
	{
		send_to_room("{108}The storm suddenly dissipates!\n\r", room);
		del_room_timer(rtd->vnum, rtd->type);
		pop_call();
		return;
	}

	if ( ch->in_room->area->weather_info->sky >= SKY_RAINING )
		sizedice = 10;
	else
		sizedice = 6;
		
	if (rtd->type == gsn_call_lightning)
		numdice = 3;
	else
		numdice = 5;
		
	dam = spell_dice(ch, rtd->type, numdice, sizedice);

	do_mpareaecho(ch, "{178}Lightning crashes wildly in the area!");

	for (cnt = 0, vch = room->first_person ; vch ; vch = vch_next)
	{
		vch_next = vch->next;

		if (!can_mass_cast(ch, vch, rtd->type))
			continue;
		if (is_same_group(ch, vch))
			continue;			
		if (!who_fighting(vch))
			continue;
		if (who_fighting(vch) != ch && !is_same_group(who_fighting(vch), ch))
			continue;
		cnt++;
	}

	rdm = number_range(1,cnt);

	for (cnt = 0, vch = room->first_person ; vch ; vch = vch_next)
	{
		vch_next = vch->next;

		if (!can_mass_cast(ch, vch, rtd->type))
			continue;
		if (is_same_group(ch, vch))
			continue;			
		if (!who_fighting(vch))
			continue;
		if (who_fighting(vch) != ch && !is_same_group(who_fighting(vch), ch))
			continue;
		cnt++;
		if (cnt == rdm)
		{
			spell_damage(ch, vch, dam, rtd->type, rtd->level);
			break;
		}
	}	
	pop_call();
	return;
}


/*
 * triggered every 6 seconds per D20 SRD
 * character updates switch to turn-based when
 * in combat - Kregor
 */
void round_update( void )
{
	CHAR_DATA *ch;
	OBJ_DATA *obj;

	push_call("round_update()");

	/*
	 * Let's base affect durations on the melee round,
	 * instead of by the hour - Kregor 4/15/07
	 */
	for (ch = mud->f_char ; ch ; ch = mud->update_wch)
	{
		mud->update_wch = ch->next;
		
		if (ch->hit > get_max_hit(ch))
			ch->hit = get_max_hit(ch);
		
		/* updates in combat are in turn time */
		if (in_combat(ch))
			continue;

		ch->action = 0;
		if (IS_STAGGERED(ch))
			ch->action = ACTION_MOVE;
		ch->attack = 0;

		damage_update(ch);
		affect_update(ch);
		obj_char_update(ch);
		pray_update(ch);
	}

	ROOM_TIMER_DATA *rtd, *rtd_next;
	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next)
	{
		rtd_next = rtd->next;

		if (--rtd->duration <= 0)
		{
			if (skill_table[rtd->type].msg_off)
			{
				rset_supermob(room_index[rtd->vnum]);
				act( skill_table[rtd->type].msg_off, supermob, NULL, NULL, TO_ROOM);
				release_supermob();
			}
			del_room_timer(rtd->vnum, rtd->type);
		}
		else
		{
			if (rtd->type == gsn_storm_of_vengeance)
			{
				vengeance_storm(rtd);
			}
			if (rtd->type == gsn_call_lightning_storm)
			{
				lightning_storm(rtd);
			}
		}
	}

	for (obj = mud->f_obj ; obj ; obj = mud->update_obj)
	{
		AFFECT_DATA *paf, *paf_next;
		mud->update_obj = obj->next;

		if (obj->carried_by) // controlled by obj_char_update
			continue;

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

			/* obj carried are updated by obj_char_update */
			if (paf->duration >= 0 && --paf->duration <= 0)
			{
				if (obj->in_room && obj->in_room->first_person)
				{
					act( skill_table[paf->type].msg_obj_off, obj->in_room->first_person, obj, NULL, TO_ALL);
				}
				affect_from_obj(obj, paf);
			}
		}
	}
	pop_call();
	return;
}


void time_update (void)
{
	char buf[MAX_INPUT_LENGTH];
	CHAR_DATA			*ch;
	OBJ_DATA			*obj;
	SHOP_DATA			*pShop;
	AREA_DATA 		*area;
	PLAYER_GAME		*gpl;
	ROOM_INDEX_DATA	*room;
	int vnum;

	push_call("time_update()");

	mud->time_info->hour++;

	if (mud->time_info->hour >= 24)
	{
		mud->time_info->hour = 0;
		mud->time_info->day++;
	}

	if (mud->time_info->day >= 30)
	{
		mud->time_info->day = 0;
		mud->time_info->month++;
	}

	if (mud->time_info->month >= 12)
	{
		mud->time_info->month = 0;
		mud->time_info->year++;
	}

	switch (mud->time_info->hour)
	{
		case 5:
			mud->sunlight = SUN_RISE;
			break;
		case 6:
			mud->sunlight = SUN_LIGHT;
			break;
		case 11:
			mud->sunlight = SUN_NOON;
			break;
		case 15:
			mud->sunlight = SUN_LIGHT;
			break;
		case 19:
			mud->sunlight = SUN_SET;
			break;
		case 20:
			mud->sunlight = SUN_DARK;
			break;
	}

	for (area = mud->f_area ; area ; area = area->next)
	{
		if (area->nplayer == 0)
		{
			continue;
		}

		buf[0] = '\0';

		switch (mud->time_info->hour)
		{
			case 5:
				sprintf(buf, "{168}The day has begun.{300}\n\r");
				break;

			case 6:
				sprintf(buf, "{138}The sun rises in the east.{300}\n\r");
				break;

			case 19:
				sprintf(buf, "{018}The sun slowly disappears in the west.{300}\n\r");
				break;

			case 20:
				sprintf(buf, "{048}The night has begun.{300}\n\r");
				break;
		}

		if (buf[0] == '\0')
		{
			continue;
		}

		for (gpl = mud->f_player ; gpl ; gpl = gpl->next)
		{
			if (gpl->ch->in_room->area == area
			&&  IS_OUTSIDE(gpl->ch)
			&& !NO_WEATHER_SECT(gpl->ch->in_room->sector_type)
			&& IS_AWAKE(gpl->ch))
			{
				send_to_char_color(buf, gpl->ch);
			}
		}
	}
	
	for (ch = mud->f_char ; ch ; ch = mud->update_wch)
	{
		mud->update_wch = ch->next;

		if (!MP_VALID_MOB(ch))
		{
			if (ch->desc && CH(ch->desc)->pcdata->vt100 == 1)
			{
				vt100prompt (ch);
			}
			continue;
		}

		if (IS_NPC(ch) && (pShop = ch->pIndexData->pShop) != NULL)
		{
			if (pShop->open_hour != 0 || pShop->close_hour != 23)
			{
				if (mud->time_info->hour == pShop->open_hour - 1)
					act( "$n prepares to open $s shop.", ch, NULL, NULL, TO_ROOM);
				else if (mud->time_info->hour == pShop->open_hour)
					act( "$n opens $s shop.", ch, NULL, NULL, TO_ROOM);
				else if (mud->time_info->hour == pShop->close_hour - 1)
					act( "$n prepares to close $s shop.", ch, NULL, NULL, TO_ROOM);
				else if (mud->time_info->hour == pShop->close_hour)
					act( "$n closes $s shop.", ch, NULL, NULL, TO_ROOM);	
			}
		}

		if (IS_SET(ch->pIndexData->progtypes, TIME_PROG))
		{
			if (!MP_VALID_MOB(ch) || in_combat(ch))
			{
				continue;
			}
			mprog_time_check(ch, NULL, NULL, NULL, TIME_PROG);
		}

		if (IS_SET(ch->pIndexData->progtypes, DAY_PROG) && mud->time_info->hour == 0)
		{
			if (!MP_VALID_MOB(ch) || in_combat(ch))
			{
				continue;
			}
			mprog_time_check(ch, NULL, NULL, NULL, DAY_PROG);
		}

		if (IS_SET(ch->pIndexData->progtypes, MONTH_PROG) && mud->time_info->hour == 0 && mud->time_info->day == 0)
		{
			if (!MP_VALID_MOB(ch) || in_combat(ch))
			{
				continue;
			}
			mprog_time_check(ch, NULL, NULL, NULL, MONTH_PROG);
		}
	}

	for (obj = mud->f_obj ; obj ; obj = mud->update_obj)
	{
		if (IS_SET(obj->pIndexData->progtypes, TIME_PROG))
		{
			oprog_time_check(NULL, NULL, obj, NULL, TIME_PROG);
		}

		if (IS_SET(obj->pIndexData->progtypes, DAY_PROG) && mud->time_info->hour == 0)
		{
			oprog_time_check(NULL, NULL, obj, NULL, DAY_PROG);
		}

		if (IS_SET(obj->pIndexData->progtypes, MONTH_PROG) && mud->time_info->hour == 0 && mud->time_info->day == 0)
		{
			oprog_time_check(NULL, NULL, obj, NULL, MONTH_PROG);
		}
	}
	
	/* room progs only trigger if there is a char to see them */
	for (vnum = 0 ; vnum < MAX_VNUM ; vnum++)
	{
		if ((room = room_index[vnum]) == NULL)
			continue;
		
		if (!room->first_person)
			continue;

		if (IS_SET(room->progtypes, TIME_PROG))
		{
			rprog_time_check(NULL, room->first_person, NULL, NULL, TIME_PROG);
		}

		if (IS_SET(room->progtypes, DAY_PROG))
		{
			rprog_time_check(NULL, room->first_person, NULL, NULL, DAY_PROG);
		}

		if (IS_SET(room->progtypes, MONTH_PROG))
		{
			rprog_time_check(NULL, room->first_person, NULL, NULL, MONTH_PROG);
		}
	}
	pop_call();
	return;
}

/*
 * hourly character updates
 */
void condition_update (void)
{
	CHAR_DATA *ch;

	push_call("condition_update()");

	for (ch = mud->f_char ; ch ; ch = mud->update_wch)
	{
		mud->update_wch = ch->next;

		// keep hunger and thirst out of newbie area - Kregor
		if (in_area(ch, ROOM_VNUM_SCHOOL))
		{
			continue;
		}
		
		if (!IS_NPC(ch))
		{
			// Delay Poison staves drunkenness, but delays recovery also.
			if (!is_affected(ch, gsn_delay_poison))
			{
				gain_condition (ch, COND_DRUNK, -8);
			}
			if (must_eat(ch))
			{
				if (ch->in_room && (ch->in_room->vnum != ROOM_VNUM_LIMBO && ch->in_room->area->low_r_vnum != ROOM_VNUM_ABYSS))
				{
					gain_condition (ch, COND_FULL,   -1);
					gain_condition (ch, COND_THIRST, -1);
				}
			}
		}
		disease_update(ch);
		check_exposure(ch);
		gain_favor(ch, -1, -1);
	}
	pop_call();
	return;
}

/*
 * Per minute character updates
 */
void char_update (void)
{
	CHAR_DATA *ch, *attacker;

	push_call("char_update()");

	for (ch = mud->f_char ; ch ; ch = mud->update_wch)
	{
		mud->update_wch = ch->next;

		if (!IS_NPC(ch))
		{
			if (ch->gold < 0)
			{
				ch->gold = 0;
			}

			if (ch->level < LEVEL_IMMORTAL)
			{
				if (ch->level < ch->in_room->area->low_hard_range || ch->level > ch->in_room->area->hi_hard_range)
				{
					char_to_room(ch, ROOM_VNUM_TEMPLE, TRUE);
					ch->pcdata->death_room = ch->pcdata->recall = ROOM_VNUM_TEMPLE;
				}
			}

// 			if (IS_SET(ch->act, PLR_KILLER) && (ch->pcdata->played - ch->pcdata->killer_played) > 60 * 60 * 24)
// 			{
// 				REMOVE_BIT (ch->act, PLR_KILLER);
// 			}
// 
// 			if (IS_SET(ch->act, PLR_OUTCAST) && (ch->pcdata->played - ch->pcdata->outcast_played) > 60 * 60 * 24)
// 			{
// 				REMOVE_BIT (ch->act, PLR_OUTCAST);
// 			}
// 
			if (ch->pcdata->just_died_ctr > 0)
			{
				if (--ch->pcdata->just_died_ctr == 0)
				{
					send_to_char ("The Gods are no longer protecting you.\n\r", ch); break;
				}
			}

			if (++ch->timer > 20)
			{
				if (ch->in_room && (ch->in_room->vnum != ROOM_VNUM_LIMBO && ch->in_room->area->low_r_vnum != ROOM_VNUM_ABYSS))
				{
					if (!ch->pcdata->switched && !IS_IMMORTAL(ch))
					{
						ch->pcdata->was_in_room = ch->in_room->vnum;
						if (in_combat(ch))
						{
							withdraw_combat(ch);
						}
						act( "$n wanders off distractedly.", ch, NULL, NULL, TO_CAN_SEE);
						act( "You wander off distractedly.", ch, NULL, NULL, TO_CHAR);
						save_char_obj(ch, NORMAL_SAVE);
						char_from_room(ch);
						char_to_room(ch, ROOM_VNUM_LIMBO, TRUE);
					}
				}
			}
			ch->pcdata->idle++;
		}

		if (ch->in_room && (ch->in_room->vnum != ROOM_VNUM_LIMBO && ch->in_room->area->low_r_vnum != ROOM_VNUM_ABYSS))
		{
			if (ch->position > POS_INCAP)
			{
				recovery_update(ch);

				if (ch->move < get_max_move(ch))
				{
					ch->move += move_gain(ch);
				}
			}
			if (ch->position == POS_INCAP)
			{
				if (ch->hit < 0)
				{
					if ((attacker = get_char_pvnum(ch->critical_hit_by)) == NULL)
					{
						attacker = ch;
					}
					if (get_apply(ch, APPLY_REGENERATION) > 0)
					{
						damage(attacker, ch, 0, TYPE_NOFIGHT, NULL);
					}
					else if (learned(ch, gsn_diehard) || fort_save(ch, NULL, 10 - ch->hit, -1))
					{
						act( "{018}You become more stable...", ch, NULL, NULL, TO_CHAR);
						act( "{018}$n stirs in $s comatose state.", ch, NULL, NULL, TO_ROOM);
						ch->hit++;
					}
					else
					{
						act( "{018}You slip a little further towards oblivion...", ch, NULL, NULL, TO_CHAR);
						act( "{018}$n is slipping away slowly...", ch, NULL, NULL, TO_ROOM);
						damage(attacker, ch, 1, TYPE_NOFIGHT, NULL);
					}
				}
			}
		}

		if (IS_NPC(ch))
		{
			if (ch->npcdata->pvnum_last_hit != 0)
			{
				if (ch->hit == get_max_hit(ch))
				{
					ch->npcdata->pvnum_last_hit = 0;
					ch->npcdata->hate_fear = 0;
				}
			}

			if (!in_combat(ch))
			{
				if (IS_SET(ch->pIndexData->affected_by, AFF_INVISIBLE) && !IS_SET(ch->affected_by, AFF_INVISIBLE))
				{
					act( "$n {108}fades out of view.", ch, NULL, NULL, TO_ROOM);
					SET_BIT(ch->affected_by, AFF_INVISIBLE );
				}
				if (IS_SET(ch->pIndexData->affected_by, AFF_HIDE) && !IS_SET(ch->affected_by, AFF_HIDE))
				{
					act( "$n {108}slips quietly into the shadows.", ch, NULL, NULL, TO_ROOM);
					SET_BIT(ch->affected_by, AFF_HIDE);
				}
			}
		}

		update_pos(ch,-1);

		if (ch->position == POS_DEAD)
		{
			raw_kill(ch, -1);
			continue;
		}

		if (ch->poison)
			poison_update(ch);

		if (!MP_VALID_MOB(ch))
		{
			if (ch->desc && CH(ch->desc)->pcdata->vt100 == 1)
			{
				vt100prompt (ch);
			}
			continue;
		}

		if (ch->timer > 0)
		{
			ch->timer--;
			if (ch->timer == 0)
			{
				mprog_delay_trigger(ch, ch->npcdata->delay_index);
			}
		}
	}

	static int lastHour;

	mud->usage->players[mud->time.tm_hour][mud->time.tm_wday] = mud->total_plr;

	if (lastHour != mud->time.tm_hour)
	{
		save_usage();
		save_hiscores();
		bounty_update();
		save_timeinfo();

		/*
			Check for Clan Rent, Purger, Backup - Scandum 03-09-2002
		*/

		if (mud->time.tm_hour == 0)
		{
			log_printf("Backing up player files.");

			system("cd ..;./backup&");
		}

		if (mud->time.tm_wday == 0 && mud->time.tm_hour == 0)
		{
			if (IS_SET(mud->flags, MUD_CLANRENT))
			{
				if (mud->f_clan == NULL)
				{
					log_printf("There are no clans.");
				}
				else
				{
					do_forcerent(NULL, NULL);
				}
				REMOVE_BIT(mud->flags, MUD_CLANRENT);

				start_purger();
			}
		}
		else if (mud->time.tm_hour != 0)
		{
			SET_BIT(mud->flags, MUD_CLANRENT);
		}
	}
	lastHour = mud->time.tm_hour;

	/*
		Minutely update on the WHO HTML Page    -   Chaos 3/26/96
	*/
	if (IS_SET(mud->flags, MUD_EMUD_REALGAME))
	{
		save_html_who();
	}
	pop_call();
	return;
}

/*
 * poison continuous damage, brand new treatment - Kregor
 * Mechanic - Must make a new save each minute or take
 * secondary damage. Poison purges from system after two
 * successful saves.
 */
void poison_update( CHAR_DATA *ch )
{
	POISON_DATA *pd, *pd_next;
	CHAR_DATA *attacker;
	AFFECT_DATA af;
	int location;

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

	if (!valid_victim(ch))
	{
		pop_call();
		return;
	}
	
	if (!valid_victim(ch))
	{
		pop_call();
		return;
	}
	
	if (!ch->poison)
	{
		pop_call();
		return;
	}
	
	for (pd = ch->poison ; pd ; pd = pd_next)
	{
		pd_next = pd->next;
		
		if ((attacker = get_char_pvnum(pd->poisoner)) == NULL)
		{
			attacker = ch;
		}

		/* delay poison keeps poison from affecting, but also from going away */
		if (is_affected(ch, gsn_delay_poison))
		{
			continue;
		}

		if (!fort_save(ch, NULL, pd->dc ? pd->dc : poison_table[pd->type].dc, gsn_poison_attack))
		{
			if ((location = poison_table[pd->type].sec_loc) > APPLY_NONE)
			{
				act( "{128}You don't feel so well...", ch, NULL, NULL, TO_CHAR);
				act( "{128}$n doesn't look so well.", ch, NULL, NULL, TO_ROOM);
	
				if (pd->type == POISON_SLEEP_POISON)
				{
					if (IS_AWAKE(ch) && !is_immune(ch, SDESC_SLEEP))
					{
						act("You feel woozy and collapse.", ch, NULL, NULL, TO_CHAR);
						act("$n collapses into a deep sleep.", ch, NULL, NULL, TO_ROOM);
						ch->position = POS_SLEEPING;
					}
				}
				else if (location == APPLY_HIT)
				{
					int roll = dice(poison_table[pd->type].sec_nodice, poison_table[pd->type].sec_sizedice);
					damage(attacker, ch, 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].sec_nodice, poison_table[pd->type].sec_sizedice);
					af.bittype   = AFFECT_TO_NONE;
					af.bitvector = AFF_NONE;
					af.level     = pd->dc ? pd->dc : poison_table[pd->type].dc;
					affect_join( attacker, ch, &af );
				}
			}
		}
		else // Should come to a 2 save total
		{
			if (pd->constant_duration == -1)
			{
				pd->constant_duration = 1;
			}
			else
			{
				pd->constant_duration--;
			}
			if (pd->constant_duration == 0)
			{
				ch->poison = ch->poison->next;
				FREEMEM(pd);
			}
		}
		update_pos(ch,-1);
	}
	pop_call();
	return;
}

/*
 * Disease continuous damage - Kregor
 * Mechanic - each update ticks up incubation, once
 * incubation reaches the incubation time of disease,
 * damage is dealt unless save. Each update thereafter
 * check save again. Tally consecutive saves. If meets
 * the number needed to overcome the disease, it purges.
 *
 * Still need to run check across other char_in_room to
 * see if they catch from infected char.
 */
void disease_update( CHAR_DATA *ch )
{
	DISEASE_DATA *dis, *dis_next;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int location = APPLY_NONE;

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

	if (!valid_victim(ch))
	{
		pop_call();
		return;
	}
	
	if (!ch->first_disease)
	{
		pop_call();
		return;
	}
	
	for (dis = ch->first_disease ; dis ; dis = dis_next)
	{
		dis_next = dis->next;
		
		if (dis->incubation < disease_table[dis->type].incubation)
		{
			dis->incubation++;
			continue;
		}

		if (!fort_save(ch, NULL, dis->dc ? dis->dc : disease_table[dis->type].dc, gsn_disease_attack))
		{
			act( disease_table[dis->type].echo_char, ch, NULL, NULL, TO_CHAR);
			act( disease_table[dis->type].echo_room, ch, NULL, NULL, TO_ROOM);

			af.type      = gsn_ability_damage;
			af.duration  = -1;
			af.location  = location;
			af.modifier  = 0 - dice(1, disease_table[dis->type].damdice);
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.level     = dis->dc ? dis->dc : disease_table[dis->type].dc;
			affect_join( ch, ch, &af );
			
			if (dis->type == DIS_BLINDING_SICKNESS && af.modifier < -2)
			{
				if (!fort_save(ch, NULL, dis->dc ? dis->dc : disease_table[dis->type].dc, gsn_disease_attack))
				{
					SET_AFFECT(ch, AFF_BLIND);
					act("{108}You can no longer see anything!", ch, NULL, NULL, TO_CHAR);
				}
			}
			dis->saves = 0; // failed save cancels save tally
		}
		else // disease cured after consecutive saves.
		{
			dis->saves++;

			if (dis->saves >= disease_table[dis->type].saves_cure)
			{
				disease_from_char(ch, dis);
			}
		}
		update_pos(ch,-1);
	}

	//And now the nasty part, infecting others 
	if (ch->in_room)
	{
		for (vch = ch->in_room->first_person ; vch ; vch = vch_next)
		{
			vch_next = vch->next;
			
			if (!fort_save(vch, NULL, dis->dc ? dis->dc : disease_table[dis->type].dc, gsn_disease_attack))
			{
				infect_char(NULL, vch, dis->type);
			}
		}
	}

	pop_call();
	return;
}


/*
 * Use in round_update when PC is is praying position - Kregor
 */
void pray_update ( CHAR_DATA *ch )
{
	OBJ_DATA *altar = NULL;
	int God, Dom;
	
	push_call("pray_update(%p)",ch);

	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}
	
	if ((altar = ch->furniture) == NULL || !IS_SET(altar->value[2], FURN_KNEEL_AT) || ch->position != POS_KNEELING)
	{
		pop_call();
		return;
	}
	
	// If deity matches, award favor and go no further
	if ((God = altar->value[5]) > GOD_NEUTRAL && God == which_god(ch))
	{
		gain_favor(ch, -1, 4);
		pop_call();
		return;
	}
	
	// if deity opposes CH deity, award no favor
	if (god_table[God].faith_enemy[which_god(ch)])
	{
		pop_call();
		return;
	}
	
	// If at least one aspect of deity matches CH deity, award half favor.
	for (Dom = 0 ; Dom < MAX_DOMAIN ; Dom++)
	{
		if (god_table[God].domain[Dom] && god_table[which_god(ch)].domain[Dom])
		{
			gain_favor(ch, -1, 2);
			pop_call();
			return;
		}
	}
	
	gain_favor(ch, -1, 1);
	pop_call();
	return;
}


/*
	Update all objs per minute
*/
void obj_update (void)
{
	OBJ_DATA *obj;
	CHAR_DATA *carrier;
	char *message;

	push_call("obj_update()");

	for (obj = mud->f_obj ; obj ; obj = mud->update_obj)
	{
		mud->update_obj = obj->next;

		if (obj->in_room && obj->in_room->vnum == ROOM_VNUM_JUNK)
		{
			junk_obj(obj);
			continue;
		}
		/*
			Look for enhanced objects
		*/
		if (obj->item_type != obj->pIndexData->item_type)
		{
			obj->item_type = obj->pIndexData->item_type;

			obj->value[0] = obj->pIndexData->value[0];
			obj->value[1] = obj->pIndexData->value[1];
			obj->value[2] = obj->pIndexData->value[2];
			obj->value[3] = obj->pIndexData->value[3];

			log_printf("obj_update: bad item type: %d", obj->pIndexData->vnum);
		}

		switch (obj->item_type)
		{
			case ITEM_CONTAINER:
			case ITEM_QUIVER:
			case ITEM_SHEATH:
			case ITEM_SPELLPOUCH:
				if (obj->carried_by == NULL && IS_SET(obj->value[1], CONT_CLOSEABLE) && !IS_SET(obj->value[1], CONT_CLOSED))
				{
					SET_BIT(obj->value[1], CONT_CLOSED);

					if (obj->value[2] > 0)
					{
						SET_BIT(obj->value[1], CONT_LOCKED);
					}
				}
				break;

			case ITEM_LIGHT:
				if (obj->value[0] != obj->pIndexData->value[0])
				{
					obj->value[0] = obj->pIndexData->value[0];
				}
				if (obj->value[3] && !IS_BURNING(obj))
					break;
				if (IS_WORN(obj) && (carrier = obj->carried_by) != NULL && obj->carried_by->desc && obj->carried_by->desc->connected >= CON_PLAYING)
				{
					if (obj->value[2] > 0)
					{
						if (--obj->value[2] == 0)
						{
							act ("$p goes out.", obj->carried_by, obj, NULL, TO_ALL);
							if (!obj->value[1])
								junk_obj(obj);
							if (carrier->in_room)
								carrier->in_room->light = calc_room_light(carrier, NULL, carrier->in_room, TRUE);
							continue;
						}
						if (obj->value[2] == 1)
						{
							act ("$p flickers.", obj->carried_by, obj, NULL, TO_CHAR);
						}
					}
				}
				break;

			case ITEM_TREASURE:
			  // should reset uses per day at midnight - Kregor
			  if (!IS_SET(obj->value[0], TFLAG_SPELL_RECAST))
			  {
			  	continue;
			  }
				if (obj->value[2] >= obj->value[1])
				{
					continue;
				}
				if (mud->time_info->hour != 0)
				{
					continue;
				}
				obj->value[2] = obj->value[1];
				break;

			case ITEM_FIRE:
				if (obj->in_room)
				{
					if (obj->value[2] > 0)
					{
						if (--obj->value[2] == 0)
						{
							send_to_room(format("%s goes out.\n\r", obj->short_descr), obj->in_room);
							obj->in_room->light = calc_room_light(NULL, obj, obj->in_room, FALSE);
							junk_obj(obj);
							continue;
						}
						if (obj->value[2] == 1)
						{
							send_to_room(format("%s flickers.\n\r", obj->short_descr), obj->in_room);
						}
					}
				}
				break;

			case ITEM_TOTEM:
				totem_cast_spell(obj);
				break;
		}

		if (!IS_SET(obj->extra_flags, ITEM_NOT_VALID)
		&&  !IS_SET(obj->pIndexData->extra_flags, ITEM_NOT_VALID))
		{
			if (obj->timer == 0 && obj->sac_timer == 0)
			{
				continue;
			}

			if (obj->in_room == NULL)
			{
				if (obj->timer == 0 || (obj->timer > 0 && --obj->timer > 0))
				{
					continue;
				}
			}
			if (obj->in_room != NULL)
			{
				if (IS_SET(obj->in_room->room_flags, ROOM_CLANSTOREROOM))
				{
					if (obj->timer == 0 || (obj->timer > 0 && --obj->timer > 0))
					{
						continue;
					}
				}
				else
				{
					if ((obj->timer == 0     || (obj->timer > 0     && --obj->timer     > 0))
					&&  (obj->sac_timer == 0 || (obj->sac_timer > 0 && --obj->sac_timer > 0)))
					{
						continue;
					}
				}
			}
		}

		switch (obj->item_type)
		{
			default:
				message = "$p vanishes.";
				break;
			case ITEM_FOUNTAIN:
				message = "$p dries up.";
				break;
			case ITEM_CORPSE_NPC:
			case ITEM_CORPSE_PC:
				message = "$p decays into dust.";
				break;
			case ITEM_FOOD:
				message = "$p decomposes.";
				break;
			case ITEM_TRAP:
				message = "";
				break;
		}

		/* traps do not echo when they purge */
		if (!IS_OBJ_TYPE(obj, ITEM_TRAP))
		{
			if (obj->carried_by != NULL)
			{
				act (message, obj->carried_by, obj, NULL, TO_CHAR);
			}
			else if (obj->in_room != NULL && obj->in_room->first_person != NULL)
			{
				act (message, obj->in_room->first_person, obj, NULL, TO_ROOM);
				act (message, obj->in_room->first_person, obj, NULL, TO_CHAR);
			}
		}

		while (obj->first_content)
		{
			if (obj->carried_by)
			{
				obj_to_char(obj->first_content, obj->carried_by);
			}
			else if (obj->in_room)
			{
				obj->first_content->sac_timer = OBJ_SAC_TIME;
				obj_to_room(obj->first_content, obj->in_room->vnum);
			}
			else
			{
				junk_obj(obj->first_content);
			}
		}

		junk_obj(obj);
	}
	pop_call();
	return;
}

void aggr_update (void)
{
	CHAR_DATA *ich;
	AREA_DATA *area;
	int vnum;

	push_call("aggr_update()");

	for (area = mud->f_area ; area ; area = area->next)
	{
		for (vnum = area->low_m_vnum ; vnum <= area->hi_m_vnum ; vnum++)
		{
			if (mob_index[vnum] == NULL)
			{
				continue;
			}

			if (!IS_SET(mob_index[vnum]->progtypes, DELAY_PROG))
			{
				continue;
			}

			for (ich = mob_index[vnum]->first_instance ; ich ; ich = mud->update_ich)
			{
				mud->update_ich = ich->next_instance;
	
				if (ich->wait > 0 && !ich->desc)
				{
					ich->wait--;

					if (ich->wait == 0 && MP_VALID_MOB(ich))
					{
						mprog_delay_trigger(ich, ich->npcdata->delay_index);
					}
				}
			}
		}
	}
// moved to mprog greet function
// 	for (npl = mud->f_player ; npl ; npl = next_npl)
// 	{
// 		next_npl = npl->next;
// 
// 		if (npl->ch->level >= LEVEL_IMMORTAL)
// 		{
// 			continue;
// 		}
// 
// 		if (npl->ch->desc == NULL && npl->ch->wait > 0)
// 		{
// 			npl->ch->wait = UMAX(0, npl->ch->wait - PULSE_AGGRESSIVE);
// 		}
// 
// 		for (ch = npl->ch->in_room->first_person ; ch ; ch = mud->update_rch)
// 		{
// 			mud->update_rch = ch->next_in_room;
// 
// 			if (!IS_NPC(ch) || !IS_SET(ch->act, ACT_AGGRESSIVE))
// 			{
// 				continue;
// 			}
// 
// 			if (!MP_VALID_MOB(ch))
// 			{
// 				continue;
// 			}
// 
// 			if (!IS_AWAKE(ch) || in_combat(ch) || ch->hit < get_max_hit(ch)/2)
// 			{
// 				continue;
// 			}
// 
// 			if (IS_SET(ch->act, ACT_WIMPY) && IS_AWAKE(npl->ch))
// 			{
// 				continue;
// 			}
// 
// 			if (!can_see(ch, npl->ch) || number_bits(1) == 0)
// 			{
// 				continue;
// 			}
// 			fight(ch, npl->ch);
// 		}
// 	}
	pop_call();
	return;
}


void purger_update (void)
{
	JUNK_DATA *junk;

	push_call("purger_update(void)");

	while (mud->f_junk)
	{
		junk = mud->f_junk;

		if (junk->mob)
		{
			extract_char(junk->mob);
		}
		else
		{
			extract_obj(junk->obj);
		}
		UNLINK(junk, mud->f_junk, mud->l_junk, next, prev);
		FREEMEM(junk);
	}
	pop_call();
	return;
}

/*
	Called once per pulse from game loop.
	Update routines spread out over 10 pulses
	for psuedo-randomness and fluidity - Kregor
*/

void update_handler (void)
{
	static sh_int pulse_areasave		= 9 + PULSE_AREASAVE;
	static sh_int pulse_shops		= 9 + PULSE_SHOPS;
	static sh_int pulse_area			= 8 + PULSE_TICK;
	static sh_int pulse_weather		= 7 + PULSE_TICK;
	static sh_int pulse_time		= 9 + PULSE_TIME;
	static sh_int pulse_obj			= 8 + PULSE_TICK;
	static sh_int pulse_tick			= 7 + PULSE_TICK;
	static sh_int pulse_charsave		= 6 + PULSE_CHARSAVE;
	static sh_int pulse_mobprog		= 4 + PULSE_PROGRAM;
	static sh_int pulse_objprog		= 3 + PULSE_PROGRAM;
	static sh_int pulse_violence		= 2 + PULSE_VIOLENCE;
	static sh_int pulse_mobile		= 1 + PULSE_MOBILE;
	static sh_int pulse_aggressive	= 0 + PULSE_AGGRESSIVE;

	push_call("update_handler()");

	if (--pulse_areasave <= 0)
	{
		pulse_areasave = PULSE_AREASAVE;
		start_timer(TIMER_AREA_SAVE);
		auto_area_save();
		close_timer(TIMER_AREA_SAVE);
	}

	if (--pulse_shops <= 0)
	{
		pulse_shops = PULSE_SHOPS;
		start_timer (TIMER_SHOP_UPD);
		shop_update ();
		close_timer (TIMER_SHOP_UPD);
	}

	if (--pulse_tick <= 0)
	{
		pulse_tick = PULSE_TICK;
		start_timer (TIMER_CHAR_UPD);
		char_update();
		auth_update();
		close_timer(TIMER_CHAR_UPD);
	}

	if (--pulse_area <= 0)
	{
		pulse_area = PULSE_AREA;
		start_timer (TIMER_AREA_UPD);
		area_update ();
		close_timer (TIMER_AREA_UPD);
	}

	if (--pulse_weather <= 0)
	{
		pulse_weather = PULSE_TICK;
		start_timer (TIMER_WEATHER_UPD);
		weather_update();
		close_timer (TIMER_WEATHER_UPD);
	}

	if (--pulse_time <= 0)
	{
		pulse_time = PULSE_TIME;
		start_timer (TIMER_TIME_UPD);
		time_update();
		condition_update();
		close_timer (TIMER_TIME_UPD);
	}

	if (--pulse_obj <= 0)
	{
		pulse_obj = PULSE_TICK;
		start_timer (TIMER_OBJ_UPD);
		obj_update ();
		close_timer (TIMER_OBJ_UPD);
	}

	if (--pulse_charsave <= 0)
	{
		pulse_charsave = PULSE_CHARSAVE;
		start_timer( TIMER_CHAR_SAVE );
		auto_char_save();
		close_timer( TIMER_CHAR_SAVE );
	}

	if (--pulse_violence <= 0)
	{
		pulse_violence = PULSE_VIOLENCE;
		start_timer (TIMER_VIOL_UPD);
		round_update ();
		close_timer (TIMER_VIOL_UPD);
	}

	if (--pulse_mobprog <= 0)
	{
		pulse_mobprog = PULSE_PROGRAM;
		start_timer(TIMER_MOB_PROG);
		mob_program_update();
		close_timer(TIMER_MOB_PROG);
	}

	if (--pulse_objprog <= 0)
	{
		pulse_objprog = PULSE_PROGRAM;
		start_timer(TIMER_OBJ_PROG);
		room_program_update();
		close_timer(TIMER_OBJ_PROG);
	}

	if (--pulse_mobile <= 0)
	{
		pulse_mobile = PULSE_MOBILE;
		start_timer (TIMER_MOB_UPD);
		mobile_update();
		combat_update();
		close_timer (TIMER_MOB_UPD);
	}

	if (--pulse_aggressive <= 0)
	{
		pulse_aggressive = PULSE_AGGRESSIVE;
		start_timer (TIMER_AGGR_UPD);
		update_casting();
		update_skill_timer();
		aggr_update();
		asn_update();
		close_timer (TIMER_AGGR_UPD);
	}

	start_timer (TIMER_PURGE);
	purger_update();
	if (IS_SET(mud->flags, MUD_PURGER))
	{
		update_purger();
	}
	close_timer (TIMER_PURGE);

	pop_call();
	return;
}

/*
 * return exp to level for char - Kregor
 */
int exp_level (CHAR_DATA * ch, int level)
{
	int num, lvl_adj;

	push_call("exp_level(%p,%p)",ch,level);

	if (level == 0)
	{
		pop_call();
		return (0);
	}

	lvl_adj = race_table[ch->race].lvl_adj;
	
	level += lvl_adj;

	// dnd formula: level * (level + 1) * 500 = 1000xp * next level
	num = level * level * 1000; // equivalent to 1000xp * (level + previous level)

	pop_call();
	return ((int) num);
}

/*
 * counts down the value of bounty til expiration
 */
void bounty_update( void )
{
	BOUNTY_DATA *bounty, *bounty_next;

	push_call("bounty_update()");

	for (bounty = mud->f_bounty ; bounty ; bounty = bounty_next)
	{
		bounty_next = bounty->next;

		if (bounty->expires < mud->current_time)
		{
			remove_bounty( bounty );
			save_bounties();
		}
	}
	pop_call();
	return;
}

/*
 * applies continuous damage per round - Kregor
 */
bool continuous_damage( CHAR_DATA *ch )
{
	AFFECT_DATA *paf;
	CHAR_DATA *attacker;
	OBJ_DATA *obj;
	int dam, dt;
	bool dmg = FALSE;

	push_call("continuous_damage(%p)",ch);
	
	for (paf = ch->first_affect ; paf ; paf = paf->next)
	{
		if (!valid_victim(ch))
			break;

		if ((attacker = get_char_world_even_blinded(ch, paf->caster)) == NULL)
			attacker = ch;

		dt = paf->type;

		if (paf->type == gsn_acid_arrow)
		{
			dam = dice(2,4);
			dt = gsn_acid_hit;
		}
		else if (paf->type == gsn_insect_plague)
		{
			dam = dice(1,6);
			dt = gsn_insect_plague;
		}
		else if (paf->type == gsn_heat_metal || paf->type == gsn_chill_metal)
		{
			switch (paf->duration)
			{
				default:
					dam = 0;
					break;
				case 2:
				case 6:
					dam = dice(1,4);
					break;
				case 3:
				case 4:
				case 5:
					dam = dice(2,4);
					break;
			}
		}
		else if (paf->type == gsn_poison)
		{
			if (fort_save(ch, attacker, paf->level, paf->type))
			{
				act("You feel poison leaving your system.", ch, NULL, NULL, TO_CHAR);
				act("$n seems to shake off $s poison.", ch, NULL, NULL, TO_ROOM);
				affect_strip(ch, gsn_poison);
			}
			else
			{
				act("{128}You shiver and suffer.", ch, NULL, NULL, TO_CHAR);
				act("{128}$n shivers and suffers.", ch, NULL, NULL, TO_ROOM);
				stat_damage(attacker, ch, APPLY_CON_DAMAGE, gsn_poison);
			}
			continue;
		}
		else
		{
			continue;
		}
		dam = damage_modify(attacker, ch, dt, dam, NULL);
		dam_message(attacker, ch, dam, dt, NULL);
		damage(attacker, ch, dam, TYPE_NOFIGHT, NULL);

		if (dam > 0)
			dmg = TRUE;
		else
			continue;
			
		if (dt == gsn_heat_metal || dt == gsn_chill_metal)
		{
			if (IS_AWAKE(ch))
			{
				if ((obj = get_wield(ch, FALSE)) != NULL && obj->material 
				&& material_table[obj->material].parent == MATERIAL_TYPE_METAL)
				{
					if (!fort_save(ch, attacker == ch ? NULL : attacker, 2 * dam, dt))
					{
						act("{118}You cannot bear to hold onto $p as it sears you!", ch, obj, NULL, TO_CHAR);
						act("{118}$n cries out in pain as $e drops $p!", ch, obj, NULL, TO_CHAR);
						unequip_char(ch, obj, TRUE);
					}
				}
			}
		}
	}
	pop_call();
	return dmg;
}

/*
 * Per hour environmental effects - Kregor
 */
void check_exposure( CHAR_DATA *ch )
{
	int dam = 0;

	push_call("check_exposure(%p)",ch);
	
	if (!valid_victim(ch))
	{
		pop_call();
		return;
	}
	
	if (is_affected(ch, gsn_time_stop))
	{
		pop_call();
		return;
	}
	
	if (ch->in_room)
	{
		pop_call();
		return;
	}
	
	// keep exposure effects from happening to newbies - Kregor
	if (ch->in_room->area->low_r_vnum == ROOM_VNUM_SCHOOL)
	{
		pop_call();
		return;
	}

	if (!IS_NPC(ch))
	{
		switch (ch->in_room->sector_type)
		{
			case SECT_DESERT:
				if (IS_OUTSIDE(ch) && (mud->sunlight == SUN_LIGHT || mud->sunlight == SUN_NOON))
				{
					if (!is_immune(ch, SDESC_FIRE) && !IS_SET(race_table[get_race(ch)].sectors, 1LL << SECT_DESERT) && !has_domain(ch, DOMAIN_WEATHER))
					{
						act("The hot desert sun beats down upon you!", ch, NULL, NULL, TO_CHAR);
						dam = damage_modify(ch, ch, gsn_heatstroke, dice(1,4), NULL);
						if (ch->nonlethal < ch->hit)
							ch->nonlethal += dam;
						else
							ch->hit -= dam;
						update_pos(ch,-1);
					}
				}
				break;
	
			case SECT_TUNDRA:
				if (IS_OUTSIDE(ch))
				{
					if (!is_immune(ch, SDESC_COLD) && !IS_SET(race_table[get_race(ch)].sectors, 1LL << SECT_TUNDRA) && !has_domain(ch, DOMAIN_WEATHER))
					{
						act("The frozen air rattles you to your bones!", ch, NULL, NULL, TO_CHAR);
						dam = damage_modify(ch, ch, gsn_frostbite, dice(1,6), NULL);
						if (ch->nonlethal < ch->hit)
							ch->nonlethal += dam;
						else
							ch->hit -= dam;
						update_pos(ch,-1);
					}
				}
				break;
	
			default:
				if (ch->in_room->area->weather_info->temperature < 0)
				{
					if (!is_immune(ch, SDESC_COLD) && !IS_SET(race_table[get_race(ch)].sectors, 1LL << SECT_TUNDRA) && !has_domain(ch, DOMAIN_WEATHER))
					{
						act("The frozen air rattles you to your bones!", ch, NULL, NULL, TO_CHAR);
						dam = damage_modify(ch, ch, gsn_frostbite, dice(1,6), NULL);
						if (ch->nonlethal < ch->hit)
							ch->nonlethal += dam;
						else
							ch->hit -= dam;
						update_pos(ch,-1);
					}
				}
				if (ch->in_room->area->weather_info->temperature >= 40)
				{
					if (!is_immune(ch, SDESC_FIRE) && !IS_SET(race_table[get_race(ch)].sectors, 1LL << SECT_DESERT) && !has_domain(ch, DOMAIN_WEATHER))
					{
						act("The hot desert sun beats down upon you!", ch, NULL, NULL, TO_CHAR);
						dam = damage_modify(ch, ch, gsn_heatstroke, dice(1,4), NULL);
						if (ch->nonlethal < ch->hit)
							ch->nonlethal += dam;
						else
							ch->hit -= dam;
						update_pos(ch,-1);
					}
				}
				break;
		}
	}
	pop_call();
	return;
}
	
/*
 * Per round damage updates including suffocation and drowning - Kregor
 */
void damage_update( CHAR_DATA *ch )
{
	CHAR_DATA *victim;
	OBJ_DATA *obj;
	int dam;
	bool dmg;

	push_call("damage_update(%p)",ch);
	
	if (!valid_victim(ch))
	{
		pop_call();
		return;
	}
	
	if (is_affected(ch, gsn_time_stop))
	{
		pop_call();
		return;
	}

	if (is_affected(ch, gsn_nightmare))
	{
		if (number_percent() < 5 && (obj = get_eq_char(ch, WEAR_FLOAT)) != NULL)
		{
			act( "$n screams and flings $p away!", ch, obj, NULL, TO_ROOM);
			switch (number_range(1,3))
			{
				case 1:
					act( "The evil leer of a demon appears within the glaring light of $p!", ch, obj, NULL, TO_CHAR);
					break;
				case 2:
					act( "A soul rending shriek is torn from $p!", ch, obj, NULL, TO_CHAR);
					break;
				case 3:
					act( "Images of your painful demise emanate from $p!", ch, obj, NULL, TO_CHAR);
					break;
			}
			act( "You scream and fling $p away!", ch, obj, NULL, TO_CHAR);
			unequip_char(ch, obj, TRUE);
		}
		else if (number_percent() < 5 && (obj = get_eq_char(ch, WEAR_HOLD)) != NULL)
		{
			act( "$n screams and flings $p away!", ch, obj, NULL, TO_ROOM);
			switch (number_range(1,3))
			{
				case 1:
					act( "$p starts to slowly drain away your dwindling vitality!", ch, obj, NULL, TO_CHAR);
					break;
				case 2:
					act( "A whisper hisses from $p 'You shall do my bidding weakling!", ch, obj, NULL, TO_CHAR);
					break;
				case 3:
					act( "A stabbing pain lances through you from $p!", ch, obj, NULL, TO_CHAR);
					break;
			}
			act( "You scream and fling $p away!", ch, obj, NULL, TO_CHAR);
			unequip_char(ch, obj, TRUE);
		}
		else if ( number_percent() < 5 && (obj = get_eq_char(ch, WEAR_WIELD )) != NULL)
		{
			act( "$n screams and flings $p away!", ch, obj, NULL, TO_ROOM);
			switch (number_range(1,3))
			{
				case 1:
					act( "The hilt of $p glows red starting to smolder with heat!", ch, obj, NULL, TO_CHAR);
					break;
				case 2:
					act( "Like a viper, $p turns on you and tries to strike!", ch, obj, NULL, TO_CHAR);
					break;
				case 3:
					act( "$p takes on a life of its own, struggling within your grasp!", ch, obj, NULL, TO_CHAR);
					break;
			}
			act( "You scream and fling $p away!", ch, obj, NULL, TO_CHAR);
			unequip_char(ch, obj, TRUE);
		}
	}

	if (IS_AFFECTED(ch, AFF2_BLEEDING))
	{
		if (get_apply(ch, APPLY_REGENERATION) > 0) // regenerating creatures do not bleed to death
		{
			AFFECT_STRIP(ch, AFF2_BLEEDING);
		}
		else
		{
			AFFECT_DATA *paf;
	
			ch_printf_color(ch, "%sYour life is bleeding out of nasty wounds!\n\r", get_color_string(ch, COLOR_YOU_ARE_HIT, VT102_BOLD));
			act( "$n's life is bleeding out of nasty wounds!", ch, NULL, NULL, TO_ROOM);
	
			for (dam = 0, paf = ch->first_affect ; paf ; paf = paf->next)
			{
				if (paf->bitvector == AFF2_BLEEDING)
				{
					dam += paf->modifier;
				}
			}
			if (dam < 1)
			{
				dam = 1;
			}
			if ((victim = get_char_pvnum(ch->critical_hit_by)) == NULL)
			{
				victim = ch;
			}
			damage(victim, ch, dam, TYPE_NOFIGHT, NULL);
		}
	}

	if (!valid_victim(ch))
	{
		pop_call();
		return;
	}
	
	dmg = continuous_damage(ch);

	if (!IS_NPC(ch) && ch->in_room)
	{
		switch (ch->in_room->sector_type)
		{
			case SECT_ASTRAL:
				if (!CAN_ASTRAL_WALK(ch))
				{
					if (in_combat(ch))
					{
						withdraw_combat(ch);
					}
					char_from_room (ch);
					if (room_index[ch->pcdata->last_real_room]->sector_type == SECT_ASTRAL)
					{
						char_to_room (ch, ROOM_VNUM_TEMPLE, TRUE);
					}
					else
					{
						char_to_room (ch, ch->pcdata->last_real_room, TRUE);
					}
				}
				break;
	
			case SECT_UNDER_WATER:
				if (!CAN_BREATH_WATER(ch) && must_breathe(ch))
				{
					gain_condition(ch, COND_AIR, -1);
				}
				break;
	
			case SECT_LAVA:
				if (!CAN_FIREWALK(ch))
				{
					if ((dam = damage_modify(ch, ch, gsn_fire_hit, dice(2,6), NULL)) > 0)
					{
						send_to_char_color("{118}That lava is REALLY hot!\n\r", ch);
						damage( ch, ch, dam, TYPE_NOFIGHT, NULL );
					}
				}
				break;
	
			case SECT_LAKE:
			case SECT_OCEAN:
				if (!IS_FLYING(ch))
				{
					if (!CAN_BREATH_WATER(ch) && must_breathe(ch))
					{
						if (!CAN_SWIM(ch))
						{
							OBJ_DATA *obj;
							for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
							{
								if (obj->item_type == ITEM_BOAT)
								{
									break;
								}
							}
							if (obj == NULL)
							{
								swim_check(ch, 0);
							}
						}
					}
				}
				break;
	
			default:
				if (must_breathe(ch))
				{
					if (IS_SET(ch->in_room->room_flags, ROOM_NO_AIR) || (IS_AFFECTED(ch, AFF2_DROWNING) && !CAN_BREATH_WATER(ch)))
					{
						if (IS_AFFECTED(ch, AFF2_DROWNING))
						{
							ch->pcdata->condition[COND_AIR] = -10;
						}
						gain_condition(ch, COND_AIR, -1);
					}
					else if (ch->pcdata->condition[COND_AIR] < get_curr_con(ch)*2)
					{
						send_to_char("You take a deep breath of fresh air.\n\r", ch);
						ch->pcdata->condition[COND_AIR] = get_curr_con(ch)*2;
					}
				}
				break;
		}
	}
	
	if (!valid_victim(ch))
	{
		pop_call();
		return;
	}
	
	if (IS_NPC(ch))
	{
		if (ch->npcdata->sac_timer > 0)
		{
			ch->npcdata->sac_timer--;
			
			if (ch->npcdata->sac_timer == 0)
			{
				if (is_string(ch->npcdata->sac_string))
					act( ch->npcdata->sac_string, ch, NULL, NULL, TO_ROOM);

				if (ch->desc && ch->desc->original)
					do_return(ch, NULL);
				
				junk_mob(ch);
				pop_call();
				return;
			}
		}
		if (must_breathe(ch))
		{
			if (IS_AWAKE(ch) && (IS_AFFECTED(ch, AFF2_DROWNING) && !CAN_BREATH_WATER(ch)))
			{
				act("{118}$n collapses for lack of air!", ch, NULL, NULL, TO_ROOM);
				ch->hit = -1;
				update_pos(ch, -1);
			}
		}
	}

	int regen;
	
	/* regeneration prevents blood loss, dying */
	if ((regen = UMIN(get_apply(ch, APPLY_REGENERATION), get_max_hit(ch) - ch->hit)) > 0)
	{
		ch_printf_color(ch, "You regenerate %d hitpoint%s.\n\r", regen, regen > 1 ? "s" : "");
		ch->hit += regen;
		update_pos(ch,-1);
		pop_call();
		return;
	}
	else if ((regen = get_apply(ch, APPLY_FAST_HEALING)) > 0 && ch->position > POS_MORTAL)
	{
		if (ch->hit < get_max_hit(ch))
		{
			regen = UMIN(regen, get_max_hit(ch) - ch->hit);
			ch_printf_color(ch, "You regain %d hitpoint%s.", regen, regen > 1 ? "s" : "");
			ch->hit += regen;
		}		
	}

	/* variant rule uses FORT save to stabilize rather than 10% chance. */
	if (ch->position == POS_MORTAL) 
	{
		int dc = 15 - ch->hit;

		if (!dmg && (learned(ch, gsn_diehard) || fort_save(ch, NULL, dc, -1)))
		{
			ch->position = POS_INCAP;
			act( "$n has stabilized.", ch, NULL, NULL, TO_ROOM);
			act( "You have stabilized.", ch, NULL, NULL, TO_CHAR);
		}
		else 
		{
			if ((victim = get_char_pvnum(ch->critical_hit_by)) == NULL)
			{
				victim = ch;
			}
			act( "{018}$n is bleeding to death!", ch, NULL, NULL, TO_ROOM);
			act( "{018}You are bleeding to death!", ch, NULL, NULL, TO_CHAR);
			damage( victim, ch, 1, TYPE_NOFIGHT, NULL );
		}
	}

	update_pos(ch,-1);

	if (ch->position == POS_DEAD)
	{
		raw_kill(ch, -1);
	}
	pop_call();
	return;
}


bool check_entangle( CHAR_DATA *ch, ROOM_TIMER_DATA *rtd )
{
	CHAR_DATA *caster;
	
	push_call("check_entangle(%p,%p)",ch,rtd);
	
	if (!valid_victim(ch))
	{
		pop_call();
		return FALSE;
	}
	
	if ((caster = get_caster_room(rtd)) == NULL)
	{
		pop_call();
		return FALSE;
	}
	if (caster != ch || can_mass_cast(caster, ch, rtd->type))
	{
		if (!IS_ENTANGLED(ch))
		{
			if (rtd->type == gsn_entangle)
				act( "{128}Plants and vines whip around and snag at you!", ch, NULL, NULL, TO_CHAR);
			if (rtd->type == gsn_black_tentacles)
				act( "{108}Black rubbery tentacles flail around you!", ch, NULL, NULL, TO_CHAR);
	
			if (!refl_save(ch, NULL, rtd->level, rtd->type))
			{
				AFFECT_DATA af;
				
				if (rtd->type == gsn_entangle)
					act( "{128}Plants and vines whip around and snag $n.", ch, NULL, NULL, TO_ROOM);
				if (rtd->type == gsn_black_tentacles)
					act( "{108}Rubbery black tentacles wrap around $n.", ch, NULL, NULL, TO_ROOM);
	
				af.type      = rtd->type;
				af.duration  = rtd->duration;
				af.modifier  = 0;
				af.location  = APPLY_NONE;
				af.bittype   = AFFECT_TO_CHAR;
				af.bitvector = AFF2_ENTANGLED;
				af.level		 = rtd->level;
				af.caster		 = STRALLOC(rtd->caster);
				affect_join( caster, ch, &af );
			}
		}
		if (IS_ENTANGLED(ch) && rtd->type == gsn_black_tentacles)
		{
			damage(caster, ch, dice(1,6)+4, rtd->type, NULL);
		}
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}


bool check_cloud_kill( CHAR_DATA *ch, ROOM_TIMER_DATA *rtd )
{
	CHAR_DATA *caster;
	AFFECT_DATA af;
	int save, dam;
	
	push_call("check_cloud_kill(%p,%p)",ch,rtd);
	
	if (!valid_victim(ch))
	{
		pop_call();
		return FALSE;
	}
	
	if ((caster = get_caster_room(rtd)) == NULL)
	{
		pop_call();
		return FALSE;
	}
	if (caster != ch || can_mass_cast(caster, ch, rtd->type))
	{
		if ((save = save_resist(caster, ch, rtd->type, rtd->level)) == TRUE)
		{
			pop_call();
			return FALSE;
		}
		act("{128}You choke and gag in the noxious clouds.", ch, NULL, NULL, TO_CHAR);
		act("{128}$n chokes and gags in the noxious clouds.", ch, NULL, NULL, TO_ROOM);

		if (ch->level < 4 || (ch->level < 6 && !save))
		{
			damage(caster, ch, ch->hit + 11, gsn_cloudkill, NULL);
			pop_call();
			return TRUE;
		}
		
		dam = dice(1,4);
		
		if (ch->level > 5 && save)
			dam /= 2;
		
		af.type      = gsn_cloudkill;
		af.duration  = -1;
		af.modifier  = 0 - dam;
		af.location  = APPLY_CON_DAMAGE;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = AFF_NONE;
		af.level		 = rtd->level;	
		affect_join( caster, ch, &af );

		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}
	

bool check_creeping_doom( CHAR_DATA *ch, ROOM_TIMER_DATA *rtd )
{
	CHAR_DATA *caster;
	int dam;
	
	push_call("check_creeping_doom(%p,%p)",ch,rtd);
	
	if (!valid_victim(ch))
	{
		pop_call();
		return FALSE;
	}
	
	if ((caster = get_caster_room(rtd)) == NULL)
	{
		affect_room_strip(room_index[rtd->vnum], gsn_creeping_doom);
		pop_call();
		return FALSE;
	}
	if (caster->in_room->vnum != rtd->vnum || !IS_AWAKE(caster))
	{
		act("{138}The creeping doom disperses without your command.", caster, NULL, NULL, TO_CHAR);
		affect_room_strip(room_index[rtd->vnum], gsn_creeping_doom);
		pop_call();
		return FALSE;
	}
	if (caster != ch || can_mass_cast(caster, ch, rtd->type))
	{
		if (IS_FLYING(ch))
		{
			pop_call();
			return FALSE;
		}
		if (!CAN_CRITICAL(ch))
		{
			pop_call();
			return FALSE;
		}
		dam = dice(1,6) + rtd->level;

		act("{038}Tiny vermin crawl and slither over your body!", ch, NULL, NULL, TO_CHAR);
		damage(caster, ch, dam, gsn_creeping_doom, NULL);
		
		rtd->modifier -= dam;
		
		if (rtd->modifier <= 0)
		{
			affect_room_strip(room_index[rtd->vnum], gsn_creeping_doom);
		}
	}
	pop_call();
	return TRUE;
}
	

/*
 * Called at end of affect_update to check
 * damage done by room affects - Kregor
 */
void room_timer_check( CHAR_DATA *ch )
{
	ROOM_TIMER_DATA *rtd, *rtd_next;
	
	push_call("room_timer_check(%p)",ch);

	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next)
	{
		rtd_next = rtd->next;
		
		if (rtd->vnum != ch->in_room->vnum)
			continue;
		
		if (IS_SET(rtd->bitvector, ROOM_ENTANGLE))
		{
			check_entangle(ch, rtd);
		}
		
		if (rtd->type == gsn_creeping_doom)
		{
			check_creeping_doom(ch, rtd);
			continue;
		}

		if (rtd->type == gsn_cloudkill)
		{
			check_cloud_kill(ch, rtd);
			continue;
		}
		
		if (rtd->type == gsn_sanctify || rtd->type == gsn_faithful_hound)
		{
			CHAR_DATA *caster;
			
			if ((caster = get_caster_room(rtd)) == NULL || caster->in_room->vnum != rtd->vnum)
				affect_room_strip(room_index[rtd->vnum], rtd->type);
		}
	}
	pop_call();
	return;
}


/*
 * Update char affects per round
 * Extensively updated and revised
 * to support camping and inns - Kregor
 */
void affect_update( CHAR_DATA *ch )
{
	AFFECT_DATA *paf, *paf_next;
	sh_int RecRate;
	CHAR_DATA *caster;
	CHAR_DATA *attacker;
	int aff_level, sn;

	push_call("affect_update()");

	if (!ch->in_room)
	{
		pop_call();
		return;
	}
	
	// check PC for assist of master if dominated - Kregor
	if (!IS_NPC(ch) && IS_AFFECTED(ch, AFF_DOMINATE))
	{
		if ((attacker = who_fighting(ch->master)) != NULL)
		{
			if (is_affected(ch, gsn_dominate_person))
			{
				sn = gsn_dominate_person;
			}
			if (is_affected(ch, gsn_dominate_monster))
			{
				sn = gsn_dominate_monster;
			}
			aff_level = get_affect_level(ch, sn);
	
			// PC will only attack master's foes if fails a WILL save
			if (in_combat(ch) && in_same_room(attacker, ch) && IS_NPC(attacker) && !will_save(ch, ch->master, aff_level, sn))
			{
				fight(ch, attacker);
			}
		}
	}

	if (IS_AFFECTED(ch, AFF2_CAMPING) || IS_SET(ch->in_room->room_flags, ROOM_INN))
	{
		switch(ch->position)
		{
			case POS_RESTING:
			case POS_SLEEPING:
				RecRate = 10;
				break;
			default:
				RecRate = 1;
				break;
		}
	}
	else
	{
		RecRate = 1;
	}

	// time stop prevents ticking of effects - Kregor
	if (is_affected(ch, gsn_time_stop))
	{
		if ((paf = get_affect_sn(ch, gsn_time_stop)) != NULL)
		{
			if ((paf->duration -= RecRate) <= 0)
			{
				if (paf->next == NULL || paf->type != paf->next->type)
				{
					if (is_string(skill_table[paf->type].msg_off))
					{
						act( skill_table[paf->type].msg_off, ch, NULL, NULL, TO_CHAR);
					}
					if (is_string(skill_table[paf->type].msg_off_room))
					{
						act( skill_table[paf->type].msg_off_room, ch, NULL, NULL, TO_ROOM);
					}
				}
				affect_from_char(ch, paf);
			}
		}
		pop_call();
		return;
	}
	
	// creatures w/stench force checks every round - Kregor
	check_stench(ch);

	if (ch->distracted > 0)
	{
		ch->distracted--;

		if (ch->distracted == 0 && IS_NPC(ch) && ch->position < ch->pIndexData->position)
		{
			do_stand(ch, "");
		}
	}
	if (!IS_AFFECTED(ch, AFF2_FEAR) && ch->fear_level > 0)
	{	
		ch->fear_level--;
		
		if (!ch->fear_level)
		{
			act( "You regain your composure from your fear.", ch, NULL, NULL, TO_CHAR);
			act( "$n regains $s composure from $s fear.", ch, NULL, NULL, TO_ROOM);
		}
	}
		
	for (paf = ch->first_affect ; paf ; paf = paf_next)
	{
		paf_next = paf->next;

		if (paf->duration < 0)
			continue;

		if (paf->type == gsn_circle_against_good
		|| paf->type == gsn_circle_against_evil
		|| paf->type == gsn_circle_against_chaos
		|| paf->type == gsn_circle_against_law
		|| paf->type == gsn_undeath_ward)
		{
			if ((caster = get_char_world_even_blinded(ch, paf->caster)) != NULL && caster != ch && !in_same_room(ch, caster))
			{
				act("You've left your protective circle, losing your ward.", ch, NULL, NULL, TO_CHAR);
				affect_from_char(ch, paf);
				continue;
			}
		}
		
		if (paf->type == gsn_mass_invis)
		{
			if ((caster = get_char_world_even_blinded(ch, paf->caster)) != NULL && caster != ch && !in_same_room(ch, caster))
			{
				act("You've left your invisibility sphere.", ch, NULL, NULL, TO_CHAR);
				affect_from_char(ch, paf);
				continue;
			}
		}
		
		if (paf->type == gsn_insect_plague)
		{
			if ((caster = get_char_world_even_blinded(ch, paf->caster)) == NULL || !in_same_room(ch, caster))
			{
				act("{038}The swarm around you scatters without its master to command it.", ch, NULL, NULL, TO_CHAR);
				affect_from_char(ch, paf);
				continue;
			}
		}
		
		if (paf->type == gsn_entangle || paf->type == gsn_black_tentacles)
		{
			if (IS_SET(paf->bitvector, AFF2_ENTANGLED) && !IS_SET(ch->in_room->room_flags, ROOM_ENTANGLE))
			{
				affect_from_char(ch, paf);
				continue;
			}
		}

		if ((paf->duration -= RecRate) <= 0)
		{
			if (paf->type > 0 && (paf->next == NULL || paf->type != paf->next->type))
			{
				if (is_string(skill_table[paf->type].msg_off))
				{
					act( skill_table[paf->type].msg_off, ch, NULL, NULL, TO_CHAR);
				}
				if (is_string(skill_table[paf->type].msg_off_room))
				{
					act( skill_table[paf->type].msg_off_room, ch, NULL, NULL, TO_ROOM);
				}
			}

			if (paf->bitvector == AFF_SLEEP)
			{
				if (IS_NPC(ch))
				{
					ch->position = ch->pIndexData->position;
				}
				else if (ch->position == POS_SLEEPING)
				{
					ch->position = POS_RESTING;
				}
			}

			if (IS_SET(skill_table[paf->type].spell_desc, SDESC_POLYMORPH))
			{
				revert_morph(ch, TRUE);
				continue;
			}
			if (IS_SET(paf->bitvector, AFF_DOMINATE))
			{
				stop_follower(ch);
			}
			affect_from_char(ch, paf);

			if (paf->type == gsn_defensive_stance && !learned(ch, gsn_tireless_defense))
			{
				int cost = get_max_move(ch) / 2;
				move_loss(ch, NULL, cost);
			}
		}
		else
		{
			if (paf->type == gsn_elemental_swarm && paf->duration % 10 == 0 && paf->modifier > 0)
			{
				(*skill_table[paf->type].spell_fun) (paf->type, paf->level, ch, NULL, TAR_IGNORE);
				paf->modifier--;
	
				if (paf->modifier <= 0)
					affect_from_char(ch, paf);
			}
			if (paf->type == gsn_improved_invis && !IS_AFFECTED(ch, AFF_INVISIBLE))
			{
				act("{068}$n's image flickers away once more!", ch, NULL, NULL, TO_ROOM);
				SET_BIT(ch->affected_by, AFF_INVISIBLE);
			}
			if (paf->type == gsn_irresistible_dance && !in_combat(ch))
			{
				irresistible_dance(ch);
			}
		}
	}
	if (IS_FLYING(ch) && !CAN_FLY(ch))
	{
		REMOVE_BIT(ch->affected_by, AFF_FLYING);
	}

	room_timer_check(ch); 

	pop_call();
	return;
}

/*
 * Update affects on objs carried by char
 */
void obj_char_update( CHAR_DATA *ch )
{
	OBJ_DATA *obj, *obj_next;
	AFFECT_DATA *paf, *paf_next;
	int paf_type = 0;

	push_call("obj_char_update()");
	
	if (is_affected(ch, gsn_time_stop))
	{
		pop_call();
		return;
	}

	for (obj = ch->first_carrying ; obj ; obj = obj_next)
	{
		obj_next = obj->next_content;

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

			if (paf->duration >= 0 && --paf->duration <= 0)
			{
				if (paf_type && paf_type == paf->type && is_string(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);
				paf_type = paf->type;
			}
		}
		if (ch->desc && IS_SET(obj->pIndexData->progtypes, RAND_PROG))
		{
			set_supermob(obj);
			oprog_percent_check(supermob, NULL, obj, NULL, RAND_PROG);
			release_supermob();
		}
	}
	pop_call();
	return;
}

/*
 * Variant recovery system for SRD works with
 * spell and endurance points. Gain 1/3 of mana 
 * and move with 1 game hour rest, 2/3 on the
 * second hour. Must rest a full 8 game hours
 * to get full point recovery. Hitpoints and
 * nonlethal damage only gain with full 8 hours,
 * per standard recovery rates.
 * Camping or inn rest speeds recovery time 10x - Kregor
 */
void recovery_update ( CHAR_DATA *ch )
{
	int gain, cnt, hpgain, mvgain, dc, sn;
	AFFECT_DATA *paf, *paf_next;

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

	if (IS_NPC(ch) || in_camp(ch) || IS_SET(ch->in_room->room_flags, ROOM_INN))
	{
		gain = 10;
	}
	else
	{
		gain = 1;
	}

	mvgain = UMAX(1, stat_bonus(TRUE, ch, APPLY_CON));
	if (learned(ch, gsn_endurance))
		mvgain *= 2;
	
	if (IS_NPC(ch) || (ch->pcdata->condition[COND_FULL] > 0 && ch->pcdata->condition[COND_THIRST] > 0))
	{
		if (ch->position != POS_FIGHTING)
		{
			if (ch->position <= POS_RESTING)
			{
				mvgain *= 2;
			}
			ch->move = UMIN(mvgain * gain + ch->move, get_max_move(ch));
		}
	}
	
	switch (ch->position)
	{
		case POS_STUNNED:
		case POS_SLEEPING:
		case POS_RESTING:
			break;
		default:
			pop_call();
			return;
	}

	if (!IS_NPC(ch) && (ch->pcdata->condition[COND_FULL] <= 0 || ch->pcdata->condition[COND_THIRST] <= 0))
	{
		send_to_char("You cannot gain benefit from your rest until you eat and drink.", ch);
		pop_call();
		return;
	}
	
	ch->rest += gain;
	
	if (gain == 10 && ch->rest % 10 != 0)
		ch->rest -= ch->rest % 10;
	
	// Here's where all the good healing is done!
	if (ch->rest >= 80)
	{
		send_to_char("You've rested fully.\n\r", ch);
		
		if (!IS_AFFECTED(ch, AFF2_LONGTERM_CARE))
		{
			hpgain = ch->level + stat_bonus(FALSE, ch, APPLY_CON);
			if (ch->hit < get_max_hit(ch))
			{
				ch->hit = UMIN(ch->hit + hpgain, get_max_hit(ch));
			}
			if (ch->nonlethal > 0)
			{
				ch->nonlethal = UMAX(0, ch->nonlethal - (hpgain * 2));
			}
		}
		else
		{
			ch->hit = get_max_hit(ch);
			ch->nonlethal = 0;
			REMOVE_AFFECT(ch, AFF2_LONGTERM_CARE);
		}
		restore_mana(ch);
		
		for (cnt = 0 ; *skill_table[cnt].name != '\0' ; cnt++)
			ch->uses[cnt] = 0;
			
		ch->move = get_max_move(ch);

		ch->rest = 0;
		
		if (!IS_NPC(ch))
			ch->pcdata->condition[COND_DRUNK]		= 0;

		// auto recovery of level drain and stat damage (but not stat drain)
		for (paf = ch->first_affect ; paf ; paf = paf_next)
		{
			paf_next = paf->next;
			
// 			if ((sn = paf->type) != gsn_energy_drain)
				if ((sn = paf->type) != gsn_cha_damage)
					if ((sn = paf->type) != gsn_con_damage)
						if ((sn = paf->type) != gsn_dex_damage)
							if ((sn = paf->type) != gsn_int_damage)
								if ((sn = paf->type) != gsn_str_damage)
									if ((sn = paf->type) != gsn_wis_damage)
										continue;

			dc = paf->level;
			
			if (fort_save(ch, NULL, dc, sn))
			{
				if (paf->modifier++ >= 0)
				{
					act( "You recover fully from $t.", ch, skill_table[sn].noun_damage, NULL, TO_CHAR);
					affect_from_char(ch, paf);
				}
				else
				{
					act( "You recover partially from $t.", ch, skill_table[sn].noun_damage, NULL, TO_CHAR);
				}
			}
		}
		if (!IS_NPC(ch) && IS_SET(ch->in_room->room_flags, ROOM_INN))
		{
			ch->pcdata->condition[COND_FULL]		= max_hunger(ch);
			ch->pcdata->condition[COND_THIRST]	= max_thirst(ch);
		}
		else if (!IS_NPC(ch) && in_camp(ch))
		{
			if (survival_check(ch, NULL, survival_roll(ch), 10))
			{
				ch->pcdata->condition[COND_FULL]		= max_hunger(ch);
				ch->pcdata->condition[COND_THIRST]	= max_thirst(ch);
			}
		}
	}
	else if (ch->rest == 10)
	{
		mana_gain(ch, 1);
		if (ch->move < get_max_move(ch) / 3)
			ch->move = get_max_move(ch) / 3;
	}
	else if (ch->rest == 20)
	{
		mana_gain(ch, 2);
		if (ch->move < get_max_move(ch) * 2 / 3)
			ch->move += get_max_move(ch) * 2 / 3;
	}
	pop_call();
	return;
}
		
/*
 * Countdown an assassination attempt - Kregor
 * victim gets a sense motive roll each round to detect the attempt
 */
void asn_update(void)
{
	CHAR_DATA *ch;
	CHAR_DATA *victim;
	
	push_call("asn_update()");
	
	for (ch = mud->f_char ; ch ; ch = mud->update_wch)
	{
		mud->update_wch = ch->next;

		if (ch->asn_count > 0)
		{
			ch->asn_count--;
	
			if ((victim = ch->assassinate) == NULL)
			{
				pop_call();
				return;
			}
			
			if (ch->asn_count > 0 && victim->in_room != ch->in_room)
			{
				act( "Your target must have slipped out of sight.", ch, NULL, NULL, TO_CHAR);
				ch->assassinate = NULL;
				ch->asn_count = 0;
				pop_call();
				return;
			}
			
			if (in_combat(ch))
			{
				act( "You lost your opportunity to assassinate $N.", ch, NULL, victim, TO_CHAR);
				ch->assassinate = NULL;
				ch->asn_count = 0;
				pop_call();
				return;
			}
		
			if (ch->asn_count <= 0)
			{
				act( "You lost your opportunity to assassinate $N.", ch, NULL, victim, TO_CHAR);
				ch->assassinate = NULL;
				pop_call();
				return;
			}
	
			if (ch->asn_count % 12 == 0)
			{
				if (ch->asn_count <= 36)
				{
					act( "You have determined $N to be opportune for a killing blow.", ch, NULL, victim, TO_CHAR);
					pop_call();
					return;
				}
				else
				{
					if (IS_AWAKE(victim) && can_see(victim, ch) && sense_motive_check(victim, ch, sense_motive_roll(victim), bluff_roll(ch)))
					{
						act( "$N {118}eyes you suspiciously.", ch, NULL, victim, TO_CHAR);
						act( "{138}$n eyes you like a stalker sizing up $s kill.", ch, NULL, victim, TO_VICT);
						ch->assassinate = NULL;
						ch->asn_count = 0;
						pop_call();
						return;
					}
					else
					{
						act( "You continue to study $N for an opportunity...", ch, NULL, victim, TO_CHAR);
					}
				}
			}
		}
	}
	pop_call();
	return;
}


/*
 * free the time delay of a skill
 */
void free_skill( CHAR_DATA *ch )
{
	push_call("free_skill(%p)",ch);

	ch->concentrating	= FALSE;

	ch->skill_timer		= 0;
	ch->timer_fun			= NULL;
	STRFREE(ch->cmd_argument);
	pop_call();
	return;
}

/* 
 * Controls when characters complete a timed skill.
 */
void update_skill_timer(void)
{
	CHAR_DATA *ch;
	CHAR_DATA *rch, *rch_next;
	int roll;

	push_call("update_skill_timer()");
	
	for (ch = mud->f_char ; ch ; ch = mud->update_wch)
	{
		mud->update_wch = ch->next;

		if (ch->concentrating)
		{
			if (!valid_victim(ch))
			{
				free_skill(ch);
			}
	
			if (ch->timer_fun == NULL)
			{
				free_skill(ch);
			}

			if (in_combat(ch) && !is_active(ch))
				continue;

			ch->skill_timer--;
			
			/* NPC opponents might take opportunity if they perceive they can. */
			if (ch->skill_timer == 6)
			{
				for (rch = ch->in_room->first_person ; rch ; rch = rch_next)
				{
					rch_next = rch->next_in_room;

					if (!IS_NPC(rch))
						continue;

					if (wis_roll(rch) < 10)
						continue;

					if (who_fighting(rch) && who_fighting(rch) == ch)
					{
						attack_of_opportunity(rch, ch);
					}
				}
			}
			
			if (ch->skill_timer <= 0)
			{
				if (IS_ENTANGLED(ch) || drunk_level(ch) >= DRUNK_TIPSY || ch->grappling || ch->grappled_by || is_affected(ch, gsn_insect_plague))
				{
					roll = concentration_roll(ch);
					int DC;
			
					if (ch->grappled_by)
						DC = 10 + combat_maneuver_bonus(ch->grappled_by);
					else
						DC = 15;
				
					if ((ch->timer_fun == do_pick || ch->timer_fun == do_disable) && learned(ch, gsn_skill_mastery))
						roll += 4;
					if (!concentration_check(ch, NULL, roll, DC))
					{
						send_to_char_color("{138}You lost your concentration!\n\r", ch);
						free_skill(ch);
						turn_complete(ch);
						pop_call();
						return;
					}
				}
				command(ch, ch->timer_fun, ch->cmd_argument);
				free_skill(ch);
				turn_complete(ch);
				continue;
			}
			else if (ch->skill_timer % 8 == 0)
			{
				if (in_combat(ch) && is_active(ch))
				{
					turn_end(ch->in_battle->turn_list, 200 - ch->initiative);
					continue;
				}
			}
		}
	}
	pop_call();
	return;
}