daleken/
daleken/data/notes/
daleken/data/player/
daleken/data/system/poses/
daleken/doc/Homepage/images/
daleken/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael	 ||
    || Chastain, Michael Quan, and Mitchell Tse.			 ||
    || Original Diku Mud copyright (C) 1990, 1991			 ||
    || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt,	 ||
    || Tom Madsen, and Katja Nyboe.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  Much time and thought has gone into this software and	 ||
    || you are benefitting. We hope that you share your changes too.	 ||
    || What goes around, comes around.					 ||
    || ----------------------------------------------------------------- ||
    ||                           update.c                                ||
    || Contains all regular update handling functions.                   ||
 *_/<>\_________________________________________________________________/<>\_*/


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


/*
 * Externals
 */
extern bool merc_down;

/*
 * Globals
 */
bool delete_obj;
bool delete_char;


/*
 * Semi-globals.
 */
int pulse_point;


/*
 * Local functions.
 */
void hit_gain		args( ( CHAR_DATA *ch ) );
void mana_gain		args( ( CHAR_DATA *ch ) );
void move_gain		args( ( CHAR_DATA *ch ) );
void mobile_update	args( ( void ) );
void weather_update	args( ( void ) );
void char_update	args( ( void ) );
void aggr_update	args( ( void ) );
void update_auction	args( ( void ) );
void smash_update	args( ( void ) );
void explode		args( ( OBJ_DATA *obj ) );
void quest_update	args( ( void ) );
void spam_update	args( ( void ) );


/*
 * Advancement stuff.
 */
int level_hpgain( int con, int Class, int extra )
{
    int x, y;

    x = power( class_table[Class].hp_min, 5, con - 16 );
    y = power( class_table[Class].hp_max, 5, con - 15 );
    x = ( number_range( x, y ) + number_range( x, y ) ) / 2;
    return x * ( 100 + extra / 2 ) / 100;
}


int level_managain( int intell, int wisd, int magic, int Class, int extra )
{
    int gain;

    gain = power( 112, 2, ( intell * 3 + wisd - 60 ) / 2 );
    gain = number_range( gain * 2 / 3, gain )
	+ number_range( gain, gain * 3 / 2 );
    gain = gain * ( number_fuzzy( class_table[Class].fMana + 10 ) - 10 );
    gain = gain * ( 100 + extra / 2 ) / 25000;
    gain += number_range( 0, number_range( 0, magic ) ) / 5;
    return gain;
}


void advance_level( CHAR_DATA *ch, bool multiple )
{
    char buf[MAX_STRING_LENGTH];
    int add_hp;
    int i;
    int add_mana[MAGIC_MAX];
    int add_move;
    int add_prac;
    int Class;

    if( ( Class = get_aspire_class( ch ) ) != CLASS_NONE
	|| get_first_class( ch ) == CLASS_NONE )
    {
	if( Class < 0 )
	    Class = ch->class;
	add_hp = level_hpgain( get_curr_con( ch ), Class,
			 race_table[ch->race].hp_gain );
	for( i = 0; i < MAGIC_MAX; ++i )
	    add_mana[i] = level_managain( get_curr_int( ch ),
					  get_curr_wis( ch ),
					  get_magic( ch, i ), Class,
					  race_table[ch->race].mana_gain[i] );
    }
    else
    {
	add_hp = level_hpgain( get_curr_con( ch ), ch->class,
			 race_table[ch->race].hp_gain )
	    + 2 * level_hpgain( get_curr_con( ch ), get_first_class( ch ),
			  race_table[ch->race].hp_gain );
	add_hp /= 3;
	for( i = 0; i < MAGIC_MAX; ++i )
	{
	    add_mana[i] = level_managain( get_curr_int( ch ),
					  get_curr_wis( ch ),
					  get_magic( ch, i ), ch->class,
					  race_table[ch->race].mana_gain[i] )
		+ 2 * level_managain( get_curr_int( ch ), get_curr_wis( ch ),
				      get_magic( ch, i ),
				      get_first_class( ch ),
				      race_table[ch->race].mana_gain[i] );
	    add_mana[i] /= 3;
	}
    }
    add_move = ( 90 + 2 * get_curr_dex( ch ) + get_curr_con( ch ) ) / 18;
    add_move = number_range( add_move, add_move * 2 );
    add_prac = power( 100, 8, get_curr_wis( ch ) - 15 );
    add_prac = number_range( add_prac * 11 / 12, add_prac * 13 / 12 );
    add_prac = add_prac * ( 100 + race_table[ch->race].move_gain / 2 ) / 100;

    add_hp = UMAX( 1, add_hp );
    for( i = 0; i < MAGIC_MAX; ++i )
	add_mana[i] = UMAX( 0, add_mana[i] );
    add_move = UMAX( 8, add_move );

    if( ch->sublevel )
    {
	add_hp /= 2;
	for( i = 0; i < MAGIC_MAX; ++i )
	    add_mana[i] /= 2;
	add_move /= 2;
	add_prac /= 2;
    }

    ch->max_hit += add_hp;
    ch->hit = ch->max_hit;
    for( i = 0; i < MAGIC_MAX; ++i )
    {
	ch->max_mana[i] += add_mana[i];
	ch->mana[i] = ch->max_mana[i];
    }
    ch->max_move += add_move;
    ch->move = ch->max_move;
    ch->practice += add_prac;

    if( !IS_NPC( ch ) )
	xREMOVE_BIT( ch->act, PLR_BOUGHT_PET );

    if( !multiple )
    {
	SysInfo->levels++;
	charprintf( ch, "&GYou raise a level!!! &mYour gain is: "
		    "%d/%d hp, %d/%d mv %d/%d prac.\n\r&gMana: ",
		    add_hp, get_max_hit( ch ),
		    add_move, get_max_move( ch ),
		    add_prac, ch->practice );
	for( i = 0; i < MAGIC_MAX; ++i )
	    charprintf( ch, "%s%d/%d %s%s", magic_colour[i], add_mana[i],
			get_max_mana( ch, i ), magic_name[i],
			( i < MAGIC_MAX - 1 ) ? ", " : "&g.&n\n\r" );

	if( ch->sublevel )
	{
	    sprintf( buf, "%s has gained in power to sublevel %d!",
		     ch->name, ch->sublevel );
	}
	else
	{
	    sprintf( buf, "%s is now level %d!", ch->name, ch->level );
	}
	wiznet( ch, WIZ_LEVELS, get_trust( ch ), buf );
	talk_channel( NULL, buf, CHANNEL_INFO, "INFO" );
	update_highest_list( ch );
    }
    return;
}


/*
 * Demote stuff.
 */
void demote_level( CHAR_DATA *ch )
{
    int add_hp;
    int i;
    int add_mana[MAGIC_MAX];
    int add_move;
    int add_prac;
    int Class;

    if( ch->level == 1 )
	return;

    if( ( Class = get_aspire_class( ch ) ) != CLASS_NONE
	|| get_first_class( ch ) == CLASS_NONE )
    {
	if( Class < 0 )
	    Class = ch->class;
	add_hp = level_hpgain( get_curr_con( ch ), Class,
			       race_table[ch->race].hp_gain );
	for( i = 0; i < MAGIC_MAX; ++i )
	    add_mana[i] = level_managain( get_curr_int( ch ),
					  get_curr_wis( ch ),
					  get_magic( ch, i ), Class,
					  race_table[ch->race].mana_gain[i] );
    }
    else
    {
	add_hp = level_hpgain( get_curr_con( ch ), ch->class,
			       race_table[ch->race].hp_gain )
	    + 2 * level_hpgain( get_curr_con( ch ), get_first_class( ch ),
				race_table[ch->race].hp_gain );
	add_hp /= 3;
	for( i = 0; i < MAGIC_MAX; ++i )
	{
	    add_mana[i] = level_managain( get_curr_int( ch ),
					  get_curr_wis( ch ),
					  get_magic( ch, i ), ch->class,
					  race_table[ch->race].mana_gain[i] )
		+ 2 * level_managain( get_curr_int( ch ), get_curr_wis( ch ),
				      get_magic( ch, i ),
				      get_first_class( ch ),
				      race_table[ch->race].mana_gain[i] );
	}
    }
    add_move = ( 90 + 2 * get_curr_dex( ch ) + get_curr_con( ch ) ) / 18;
    add_move = number_range( add_move, add_move * 2 );
    add_prac = power( 100, 8, get_curr_wis( ch ) - 15 );
    add_prac = number_range( add_prac * 11 / 12, add_prac * 13 / 12 );
    add_prac = add_prac * ( 100 + race_table[ch->race].move_gain / 2 ) / 100;

    add_hp = UMAX( 1, add_hp );
    for( i = 0; i < MAGIC_MAX; ++i )
	add_mana[i] = UMAX( 0, add_mana[i] );
    add_move = UMAX( 8, add_move );

    if( ch->sublevel )
    {
	add_hp /= 2;
	for( i = 0; i < MAGIC_MAX; ++i )
	    add_mana[i] /= 2;
	add_move /= 2;
	add_prac /= 2;
    }

    ch->max_hit -= add_hp;
    ch->hit = ch->max_hit;
    for( i = 0; i < MAGIC_MAX; ++i )
    {
	ch->max_mana[i] -= add_mana[i];
	ch->mana[i] = ch->max_mana[i];
    }
    ch->max_move -= add_move;
    ch->move = ch->max_move;
    ch->practice -= add_prac;

    if( !IS_NPC( ch ) )
	xREMOVE_BIT( ch->act, PLR_BOUGHT_PET );

    ch->level -= 1;
    charprintf( ch, "Your loss is: %d/%d hp, %d/%d mv %d/%d prac.\n\r",
		add_hp, get_max_hit( ch ),
		add_move, get_max_move( ch ),
		add_prac, ch->practice );
    for( i = 0; i < MAGIC_MAX; ++i )
	charprintf( ch, "%d/%d %s mana%s",
		    add_mana[i], get_max_mana( ch, i ), magic_name[i],
		    ( i < MAGIC_MAX - 1 ) ? ", " : ".\n\r" );
    wiznetf( ch, WIZ_LEVELS, get_trust( ch ), "%s has lost a level.",
	     ch->name );
    return;
}


/*
 * Find which class to assign to the successful multi-classer
 * A little complex but it works.
 * --Symposium
 */
void set_new_class( CHAR_DATA *ch, int second )
{
    int newclass, i;

    if( second > NUM_MULTI_CLASS || second < 0
	|| ch->class < 0 || ch->class > AVAIL_CLASS )
	return;

    ch->pcdata->multi_class[second] = CLASS_SECOND;
    ch->pcdata->multi_class[ch->class] = CLASS_FIRST;

    newclass = AVAIL_CLASS + UMIN( ch->class, second ) * AVAIL_CLASS;
    newclass += UMAX( ch->class, second );
    for( i = 1; i <= UMIN( ch->class, second ); ++i )
	newclass -= i;
    ch->class = newclass;
    return;
}


void gain_exp( CHAR_DATA *ch, int gain )
{
    char Info[MAX_INPUT_LENGTH];
    int i;

    if( IS_NPC( ch ) || ch->level >= LEVEL_HERO )
	return;

    ch->exp -= UMIN( get_tnl( ch ) * 2, gain );
    while( ch->level < LEVEL_HERO && ch->exp <= 0 )
    {
	ch->exp += get_tnl( ch );
	if( ch->sublevel )
	{
	    ch->sublevel++;
	    if( ch->sublevel > ch->level )
	    {
		ch->sublevel = 0;
		ch->level++;
		if( ( i = get_aspire_class( ch ) ) >= 0 )
		{
		    if( get_first_class( ch ) == CLASS_NONE )
		    {
			ch->pcdata->multi_class[i] = CLASS_SECOND;
			set_new_class( ch, i );
			act( "You are now fully a $t, congratulations!",
			     ch, class_table[ch->class].name, NULL, TO_CHAR );
			sprintf( Info, "Please congratulate %s on obtaining status as a %s!",
				 ch->name, class_table[ch->class].name );
		    }
		    else
		    {
			ch->pcdata->multi_class[i] = CLASS_ADEPT;
			act( "You are now adept at the disciplines of the $t class.",
			     ch, class_table[i].name, NULL, TO_CHAR );
			sprintf( Info, "%s has just earned status as a level %d %s!",
				 ch->name, ch->level, class_table[i].name );
		    }
		    if( Info[0] != '\0' )
			talk_channel( NULL, Info, CHANNEL_INFO, "INFO" );
		    break;
		}
	    }
	}
	else		/* they dont have a sublevel */
	{
	    ch->level++;
	    if( ch->level == 50 )
		do_help( ch, "new hero" );
	}
	advance_level( ch, FALSE );
    }

    while( ch->level == LEVEL_HERO && ch->exp <= 0 )
    {
	ch->exp += get_tnl( ch );
	if( ch->sublevel < 1000 )
	    ch->sublevel++;
	for( i = 0; i < 4 && ch->sublevel == LEVEL_HERO; ++i )
	{
	    ch->pcdata->multi_class[i] = CLASS_ASPIRING;
	}
	if( ch->exp > 0 )
	    advance_level( ch, FALSE );
	else
	    advance_level( ch, TRUE );
    }

    return;
}


/*
 * Regeneration stuff.
 */
void hit_gain( CHAR_DATA *ch )
{
    int gain;

    if( IS_AFFECTED( ch, AFF_BLEEDING ) )
	return;

    if( IS_NPC( ch ) )
    {
	gain = ch->level * 3 / 2;
    }
    else
    {
	gain = power( 30, 8, get_curr_con( ch ) - 20 );

	switch( ch->position )
	{
	default:
	    gain /= 8;
	    break;
	case POS_SLEEPING:
	    gain += ch->max_hit / 80;
	    break;
	case POS_SMASHED:
	case POS_SITTING:
	    gain /= 4;
	    break;
	case POS_RESTING:
	    gain /= 2;
	    break;
	case POS_MEDITATING:
	    gain += ch->max_hit / 30;
	    break;
	}

	if( ch->pcdata->condition[COND_FULL] == 0 )
	    gain /= 2;

	if( ch->pcdata->condition[COND_THIRST] == 0 )
	    gain /= 2;

	if( get_curr_temp( ch ) - get_room_temp( ch->in_room ) > 40 )
	{
	    gain *= 100;
	    gain /= ( 60 + get_curr_temp( ch ) - get_room_temp( ch->in_room ) );
	}
	else if( get_room_temp( ch->in_room ) - get_curr_temp( ch ) > 40 )
	{
	    gain *= 100;
	    gain /= ( 60 + get_room_temp( ch->in_room ) - get_curr_temp( ch ) );
	}
	if( ch->pcdata->religion && ch->in_room->area->order
	    && ch->pcdata->religion == ch->in_room->area->order->religion )
	    gain += gain >> 4;
    }

    if( ch->on && ch->on->item_type == ITEM_FURNITURE )
	gain = gain * ( 100 + ch->on->value[2] ) / 100;

    gain = gain * ( 100 + race_table[ch->race].hp_gain ) / 100;

    if( IS_AFFECTED( ch, AFF_POISON ) || IS_AFFECTED( ch, AFF_PLAGUE ) )
	gain /= 4;

    gain = UMAX( 1, gain );
    if( ch->hit > ch->max_hit )
	ch->hit = UMAX( ch->max_hit, ch->hit - 2000 / gain );
    else
	ch->hit = UMIN( ch->max_hit, ch->hit + gain );
}


void mana_gain( CHAR_DATA *ch )
{
    int gain, i, magic;
    int mod = 100;

    if( !IS_NPC( ch ) )
    {
	if( get_curr_temp( ch ) - get_room_temp( ch->in_room ) > 40 )
	    mod = mod * 100 / ( 60 + get_curr_temp( ch )
				- get_room_temp( ch->in_room ) );
	else if( get_room_temp( ch->in_room ) - get_curr_temp( ch ) > 40 )
	    mod = mod * 100 / ( 60 + get_room_temp( ch->in_room )
				- get_curr_temp( ch ) );
    }
    if( IS_AFFECTED( ch, AFF_POISON ) || IS_AFFECTED( ch, AFF_PLAGUE ) )
	mod /= 4;
    if( IS_AFFECTED( ch, AFF_BLEEDING ) )
	mod /= 2;
    if( ch->on && ch->on->item_type == ITEM_FURNITURE )
	mod = mod * ( 100 + ch->on->value[3] ) / 100;

    for( i = 0; i < MAGIC_MAX; ++i )
    {
	if( ch->mana[i] == ch->max_mana[i] )
	    continue;
	magic = get_magic( ch, i );
	if( IS_NPC( ch ) )
	{
	    gain = ch->level;
	}
	else
	{
	    gain = get_curr_int( ch );
	    gain = gain * ( gain + magic ) / 10;
	    gain = gain * 3 / 4 + gain * class_table[ch->class].fMana / 15;
	    gain += magic;

	    switch( ch->position )
	    {
	    default:
		gain /= 8;
		break;
	    case POS_SLEEPING:
		gain += ch->max_mana[i] / 40;
		break;
	    case POS_SMASHED:
	    case POS_SITTING:
		gain /= 4;
		break;
	    case POS_RESTING:
		gain /= 2;
		break;
	    case POS_MEDITATING:
		gain += ch->max_mana[i] / 25;
		gain += gain / 2;
		break;
	    }
	    if( ch->pcdata->religion && ch->in_room->area->order
		&& ch->pcdata->religion == ch->in_room->area->order->religion )
		gain += gain >> 4;
	}

	gain = gain * mod * ( 100 + race_table[ch->race].mana_gain[i] )
	    / 10000;

	gain = UMAX( 1, gain );
	if( ch->mana[i] > ch->max_mana[i] )
	    ch->mana[i] = UMAX( ch->max_mana[i], ch->mana[i] - 2000 / gain );
	else
	    ch->mana[i] = UMIN( ch->max_mana[i], ch->mana[i] + gain );
    }
}


void move_gain( CHAR_DATA *ch )
{
    int gain;

    if( IS_NPC( ch ) )
    {
	gain = ch->level;
    }
    else
    {
	gain = UMAX( 15 + ( ch->level / 10 ), 2 * ch->level );
	gain += ch->max_move / 50;

	switch( ch->position )
	{
	case POS_SLEEPING:
	    gain += get_curr_dex( ch ) * 2;
	    break;
	case POS_RESTING:
	case POS_SITTING:
	    gain += get_curr_dex( ch );
	    break;
	default:
	    break;
	}

	if( ch->pcdata->condition[COND_FULL] == 0 )
	    gain /= 2;

	if( ch->pcdata->condition[COND_THIRST] == 0 )
	    gain /= 2;

	if( get_curr_temp( ch ) - get_room_temp( ch->in_room ) > 40 )
	{
	    gain *= 100;
	    gain /= ( 60 + get_curr_temp( ch ) - get_room_temp( ch->in_room ) );
	}
	else if( get_room_temp( ch->in_room ) - get_curr_temp( ch ) > 40 )
	{
	    gain *= 100;
	    gain /= ( 60 + get_room_temp( ch->in_room ) - get_curr_temp( ch ) );
	}
	if( ch->pcdata->religion && ch->in_room->area->order
	    && ch->pcdata->religion == ch->in_room->area->order->religion )
	    gain += gain >> 4;

	if( ch->on && ch->on->item_type == ITEM_FURNITURE )
	    gain = gain * ( 100 + ch->on->value[2] ) / 100;

	gain = gain * ( 100 + race_table[ch->race].move_gain ) / 100;
    }

    if( IS_AFFECTED( ch, AFF_POISON ) || IS_AFFECTED( ch, AFF_PLAGUE ) )
	gain /= 4;
    if( IS_AFFECTED( ch, AFF_BLEEDING ) )
	gain /= 2;

    gain = UMAX( 1, gain );
    if( ch->move > ch->max_move )
	ch->move = UMAX( ch->max_move, ch->move - 2000 / gain );
    else
	ch->move = UMIN( ch->max_move, ch->move + gain );
}


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

    if( value == 0 || IS_NPC( ch ) ||
	( ch->level >= LEVEL_HERO && iCond != COND_DRUNK ) )
	return;

    condition = ch->pcdata->condition[iCond];

    switch( iCond )
    {
    case COND_FULL:
	ch->pcdata->condition[iCond] =
	    URANGE( 0, condition + value, race_table[ch->race].hunger_mod * 10 );
	break;
    case COND_THIRST:
	ch->pcdata->condition[iCond] =
	    URANGE( 0, condition + value, race_table[ch->race].thirst_mod * 10 );
	break;
    case COND_DRUNK:
	ch->pcdata->condition[iCond] = URANGE( 0, condition + value, 1000 );
	break;
    }
    if( ch->pcdata->condition[iCond] == 0 )
    {
	switch( iCond )
	{
	case COND_FULL:
	    send_to_char( "You are hungry.\n\r", ch );
	    break;

	case COND_THIRST:
	    send_to_char( "You are thirsty.\n\r", ch );
	    break;

	case COND_DRUNK:
	    if( condition != 0 )
		send_to_char( "You are sober.\n\r", ch );
	    break;
	}
    }

    return;
}


/*
 * Update the weather.
 */
void weather_update( void )
{
    DESCRIPTOR_DATA *d;
    char buf[MAX_STRING_LENGTH];
    int diff;
    PLANE_DATA *pPlane;

    for( pPlane = plane_list; pPlane; pPlane = pPlane->next )
    {
	buf[0] = '\0';

	switch( ++pPlane->time.hour )
	{
	case 6:
	    pPlane->weather.sunlight = SUN_RISE;
	    strcat( buf, "&bThe &Ysun&b peeks over the eastern horizon.&n\n\r" );
	    break;

	case 7:
	    pPlane->weather.sunlight = SUN_LIGHT;
	    strcat( buf, "&bThe sky brightens into &yday&b.&n\n\r" );
	    break;

	case 19:
	    pPlane->weather.sunlight = SUN_SET;
	    strcat( buf, "&bThe &Ysun&b sinks below the western horizon.&n\n\r" );
	    break;

	case 20:
	    pPlane->weather.sunlight = SUN_DARK;
	    strcat( buf, "&KNight&b covers the land in a blanket of &Kdarkness&b.&n\n\r" );
	    break;

	case 24:
	    pPlane->time.hour = 0;
	    pPlane->time.day++;
	    break;
	}

	if( pPlane->time.day >= 35 )
	{
	    pPlane->time.day = 0;
	    pPlane->time.month++;
	}

	if( pPlane->time.month >= 17 )
	{
	    pPlane->time.month = 0;
	    pPlane->time.year++;
	}

	/*
	 * Weather change.
	 */
	pPlane->weather.winddir += number_range( 0, 2 ) - 1;
	if( pPlane->time.month >= 9 && pPlane->time.month <= 16 )
	    diff = pPlane->weather.mmhg > 985 ? -2 : 2;
	else
	    diff = pPlane->weather.mmhg > 1015 ? -2 : 2;

	pPlane->weather.change += diff * dice( 1, 4 ) + dice( 2, 6 ) - dice( 2, 6 );
	pPlane->weather.change = UMAX( pPlane->weather.change, -12 );
	pPlane->weather.change = UMIN( pPlane->weather.change, 12 );

	pPlane->weather.mmhg += pPlane->weather.change;
	pPlane->weather.mmhg = UMAX( pPlane->weather.mmhg, 960 );
	pPlane->weather.mmhg = UMIN( pPlane->weather.mmhg, 1040 );

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

	case SKY_CLOUDLESS:
	    if( pPlane->weather.mmhg < 990
		|| ( pPlane->weather.mmhg < 1010 && number_bits( 2 ) == 0 ) )
	    {
		if( pPlane->time.month <= 4 || pPlane->time.month >= 15 )
		{
		    strcat( buf, "&bA few &wsnow flakes&b drift down from the grey sky.&n\n\r" );
		    pPlane->weather.temperature -= number_fuzzy( 5 );
		}
		else
		    strcat( buf, "&wClouds&b begin to build in the sky.&n\n\r" );
		pPlane->weather.sky = SKY_CLOUDY;
		pPlane->weather.windspeed += number_fuzzy( 10 );
	    }
	    break;

	case SKY_CLOUDY:
	    if( pPlane->weather.mmhg < 970
		|| ( pPlane->weather.mmhg < 990 && number_bits( 2 ) == 0 ) )
	    {
		if( pPlane->time.month <= 4 || pPlane->time.month >= 15 )
		{
		    strcat( buf, "&wIcy cold &Wsnow&b begins to fall.&n\n\r" );
		    pPlane->weather.temperature -= number_fuzzy( 5 );
		}
		else
		    strcat( buf, "&bThe sky opens up and it starts &craining&b.&n\n\r" );
		pPlane->weather.sky = SKY_RAINING;
		pPlane->weather.windspeed += number_fuzzy( 10 );
	    }

	    if( pPlane->weather.mmhg > 1030 && number_bits( 2 ) == 0 )
	    {
		if( pPlane->time.month <= 4 || pPlane->time.month >= 15 )
		{
		    strcat( buf, "&bThe &wsnow&b gradually lets up.&n\n\r" );
		    pPlane->weather.temperature += number_fuzzy( 5 );
		}
		else
		    strcat( buf, "&bThe &wclouds&b clear and &cblue sky&b shows through.&n\n\r" );
		pPlane->weather.sky = SKY_CLOUDLESS;
		pPlane->weather.windspeed -= number_fuzzy( 10 );
	    }
	    break;

	case SKY_RAINING:
	    if( pPlane->weather.mmhg < 970 && number_bits( 2 ) == 0 )
	    {
		if( pPlane->time.month <= 4 || pPlane->time.month >= 15 )
		{
		    strcat( buf, "&bA churning &f&Wblizzard&n&b surrounds you.&n\n\r" );
		    pPlane->weather.temperature -= number_fuzzy( 16 );
		}
		else
		    strcat( buf, "&bHuge bolts of &Wlightning&b &fflash&n&b in the sky.&n\n\r" );
		pPlane->weather.sky = SKY_LIGHTNING;
		pPlane->weather.windspeed += number_fuzzy( 10 );
	    }

	    if( pPlane->weather.mmhg > 1030
		|| ( pPlane->weather.mmhg > 1010 && number_bits( 2 ) == 0 ) )
	    {
		if( pPlane->time.month <= 4 || pPlane->time.month >= 15 )
		{
		    strcat( buf, "&bThe rain swiftly subsides, leaving leaden skies.&n\n\r" );
		    pPlane->weather.temperature += number_fuzzy( 16 );
		}
		else
		    strcat( buf, "&bThe &crain&b stops.&n\n\r" );
		pPlane->weather.sky = SKY_CLOUDY;
		pPlane->weather.windspeed -= number_fuzzy( 10 );
	    }
	    break;

	case SKY_LIGHTNING:
	    if( pPlane->weather.mmhg > 1010
		|| ( pPlane->weather.mmhg > 990 && number_bits( 2 ) == 0 ) )
	    {
		if( pPlane->time.month <= 4 || pPlane->time.month >= 15 )
		{
		    strcat( buf, "&bThe sheets of churning &wice&b slowly turns to a light mist.&n\n\r" );
		    pPlane->weather.temperature += number_fuzzy( 5 );
		}
		else
		    strcat( buf, "&bThe storm quietens and the &Wlightning&b stops.&n\n\r" );
		pPlane->weather.sky = SKY_RAINING;
		pPlane->weather.windspeed -= number_fuzzy( 10 );
		break;
	    }
	    break;
	}

	if( buf[0] != '\0' )
	{
	    for( d = descriptor_list; d; d = d->next )
	    {
		if( !IS_SET( d->interpreter->flags, INTERPRETER_NOMESSAGE )
		    && IS_OUTSIDE( d->character )
		    && IS_AWAKE( d->character )
		    && !xIS_SET( CH(d)->act, PLR_BUSY )
		    && CH(d)->in_room->area->plane == pPlane )
		    send_to_char( buf, d->character );
	    }
	}

	pPlane->weather.temperature = ( pPlane->time.hour < 3 ) ? 0 - pPlane->time.hour
	    : ( ( pPlane->time.hour < 16 ) ? pPlane->time.hour / 2 - 3
		: ( 23 - pPlane->time.hour ) / 2 );
	switch( pPlane->weather.sky )
	{
	case SKY_CLOUDLESS:
	    pPlane->weather.temperature += number_fuzzy( 3 );
	    break;

	case SKY_RAINING:
	    pPlane->weather.temperature -= number_fuzzy( 5 );
	    break;

	default:
	    break;
	}
	if( pPlane->weather.mmhg < 980 )
	{
	    pPlane->weather.temperature -= number_range( 2, 6 );
	}
	else if( pPlane->weather.mmhg > 1020 )
	{
	    pPlane->weather.temperature += number_range( 2, 6 );
	}
	else
	{
	    pPlane->weather.temperature = number_fuzzy( pPlane->weather.temperature );
	}
    }
    return;
}


/*
 * Update all chars, including mobs.
 * This function is performance sensitive.
 */
void char_update( void )
{
    CHAR_DATA *ch;
    CHAR_DATA *ch_save;
    CHAR_DATA *ch_quit;
    time_t save_time;
    int dmg;

    ch_save = NULL;
    ch_quit = NULL;
    save_time = current_time;

    for( ch = char_list; ch; ch = ch->next )
    {
	AFFECT_DATA *paf;

	if( ch->deleted )
	    continue;

	/*
	 * Find dude with oldest save time.
	 */
	if( !IS_NPC( ch )
	    && ( !ch->desc
		 || !IS_SET( ch->desc->interpreter->flags,
			     INTERPRETER_SAFE ) )
	    && ch->level >= 2
	    && ch->save_time < save_time )
	{
	    ch_save = ch;
	    save_time = ch->save_time;
	}

	if( ch->fighting && IS_AFFECTED( ch, AFF_FAST_TALK )
	    && UMIN( 18, ( ch->fighting->level - ch->level + 12 ) )
	    < number_range( 0, 25 ) )
	{
	    act( "$n is convinced by $N not to attack $M.",
		 ch->fighting, NULL, ch, TO_NOTVICT );
	    act( "$n is convinced of your harmlessness.",
		 ch->fighting, NULL, ch, TO_VICT );
	    stop_fighting( ch, TRUE );
	}
	if( !IS_NPC( ch ) && ch->pcdata->familiar > 0 )
	{
	    ch->pcdata->familiar -= power( 60 + ch->level, 5, 20 - get_curr_int( ch ) );
	    if( ch->pcdata->familiar <= 0 )
	    {
		ch->pcdata->familiar = 0;
		send_to_char( "Your familiar sighs and disappears in a puff of smoke.\n\r", ch );
	    }
	}
	if( IS_AFFECTED( ch, AFF_BLEEDING ) )
	{
	    OBJ_DATA *blood = NULL;

	    if( ch->in_room &&
		!( blood = get_obj_list( ch, "blood", ch->in_room->contents ) ) )
	    {
		blood = create_object( get_obj_index( OBJ_VNUM_BLOOD_STAIN ), 0 );
		obj_to_room( blood, ch->in_room );
	    }
	    if( blood )
		set_timer_tick( blood, number_range( 3, 5 ) );
	    act( "$n is bleeding profusely.", ch, NULL, NULL, TO_ROOM );
	    send_to_char( "You are bleeding quite badly.\n\r", ch );
	    if( IS_NPC( ch ) && !ch->fighting && ch->position >= POS_STANDING
		&& number_bits( 5 ) < 9 )
		do_tend( ch, "" );
	}
	if( IS_AFFECTED( ch, AFF_PLAGUE ) && ch->in_room->area->nplayer > 0 )
	    spell_mass_plague( gsn_mass_plague, ch->level, ch, NULL );
	if( IS_AFFECTED( ch, AFF_POISON )
	    && IS_AFFECTED( ch, AFF_POISONRESIST ) && number_bits( 1 ) )
	{
	    act( "$n fights off the effects of the poison.",
		 ch, NULL, NULL, TO_ALL );
	    affect_strip( ch, gsn_poison );
	    xREMOVE_BIT( ch->affected_by, AFF_POISON );
	}

	if( ch->position >= POS_STUNNED )
	{
	    hit_gain( ch );
	    mana_gain( ch );
	    move_gain( ch );
	}

	if( ch->position == POS_STUNNED )
	    update_pos( ch );

	if( !IS_NPC( ch ) && ( ch->level < LEVEL_IMMORTAL
			       || ( !ch->desc && !IS_SWITCHED( ch ) ) ) )
	{
	    OBJ_DATA *obj;

	    for( obj = ch->carrying; obj; obj = obj->next )
	    {
		if( obj->deleted || obj->item_type != ITEM_LIGHT )
		    continue;
		if( obj->value[2] <= 0 || obj->wear_loc == WEAR_NONE )
		{
		    switch( obj->value[0] )
		    {
		    case 1:
			act( "$p carried by $n flickers and sputters.",
			     ch, obj, NULL, TO_ROOM );
			act( "$p flickers and sputters.",
			     ch, obj, NULL, TO_CHAR );
			break;
		    case 2:
			act( "$p carried by $n flickers.",
			     ch, obj, NULL, TO_ROOM );
			act( "$p flickers.", ch, obj, NULL, TO_CHAR );
			break;
		    case 3:
			act( "$p carried by $n flickers slightly.",
			     ch, obj, NULL, TO_ROOM );
			act( "$p flickers slightly.", ch, obj, NULL, TO_CHAR );
			break;
		    }
		    continue;
		}
		if( --obj->value[2] == 0 && ch->in_room )
		{
		    if( IS_SET( obj->extra_flags, ITEM_DARK ) )
			ch->in_room->light += 30;
		    else if( IS_SET( obj->extra_flags, ITEM_GLOW ) )
			ch->in_room->light -= 20;
		    else
			ch->in_room->light -= 10;
		    act( "$p goes out.", ch, obj, NULL, TO_ROOM );
		    act( "$p goes out.", ch, obj, NULL, TO_CHAR );
		    extract_obj( obj );
		}
	    }

	    if( ++ch->pcdata->timer >= 10 && !IS_IMMORTAL( ch ) )
	    {
		if( !ch->was_in_room && ch->in_room )
		{
		    ch->was_in_room = ch->in_room;
		    if( ch->fighting )
			stop_fighting( ch, TRUE );
		    act( "$n disappear$% into the void.",
			 ch, NULL, NULL, TO_ALL );
		    save_char_obj( ch );
		    char_from_room( ch );
		    char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) );
		}
	    }

	    if( ch->pcdata->timer > 20 && !IS_IMMORTAL( ch ) )
		ch_quit = ch;

	    gain_condition( ch, COND_DRUNK, -100 - get_curr_con( ch ) * 2 );
	    if( !IS_AFFECTED( ch, AFF_GHOUL ) )
	    {
		float i;

		i = race_table[ch->race].hunger_mod;
		i = -35 * log( i / 100 ) - 50;
		gain_condition( ch, COND_FULL, ( int )i );
		i = race_table[ch->race].thirst_mod;
		i = -35 * log( i / 100 ) - 50;
		gain_condition( ch, COND_THIRST, ( int )i );
	    }
	}

	for( paf = ch->affected; paf; paf = paf->next )
	{
	    if( paf->deleted )
		continue;
	    /*
	     * Incubus' continuous spell effect.
	     * there is the possibility that this function could create
	     * problems if it kills the character (deleted checks are
	     * required from here on in when a ch is referred to).
	     * If you know of a better way, tell me.
	     */
	    if( paf->type == gsn_continuous_effect )
	    {
		/* hit the character with the spell... */
		( *skill_table[paf->location].spell_fun )
		    ( paf->location, URANGE( 1, paf->modifier, LEVEL_HERO * 2 ),
		      ch, ch );
		if( ch->deleted || ch->position == POS_DEAD )
		    break;
	    }
	    if( paf->duration > 0 )
		paf->duration--;
	    else if( paf->duration < 0 )	/* permanent */
		    ;
	    else
	    {
		if( !paf->next
		    || paf->next->type != paf->type
		    || paf->next->duration > 0 )
		{
		    if( paf->type > 0 && skill_table[paf->type].msg_off )
		    {
			send_to_char( skill_table[paf->type].msg_off, ch );
			send_to_char( "\n\r", ch );
		    }
		}

		if( paf->type == gsn_vampiric_bite )
		{
		    AFFECT_DATA *pnaf;

		    pnaf = new_affect( );
		    pnaf->type = gsn_vampiric_bite;
		    pnaf->location = APPLY_RACE;
		    pnaf->modifier = race_lookup( "Vampire" ) - ch->race;
		    pnaf->duration = 1000;
		    vset( pnaf->bitvector, AFF_POLYMORPH );
		    xSET_BIT( pnaf->bitvector, AFF_VAMP_BITE );
		    pnaf->next = ch->affected;
		    ch->affected = pnaf;
		}

		affect_remove( ch, paf );
		if( ch->deleted || ch->position == POS_DEAD )
		    break;
	    }
	}
	if( !ch->deleted && !IS_NPC( ch ) )
	    juggle_shuffle( ch );
	if( ch->deleted || ch->position == POS_DEAD )
	    continue;

	/*
	 * Careful with the damages here,
	 *   MUST NOT refer to ch after damage taken,
	 *   as it may be lethal damage (on NPC).
	 */
	if( ( ch->in_room->area->plane->time.hour > 5
		 && ch->in_room->area->plane->time.hour < 21 )
	    && IS_SET( race_table[ch->race].race_abilities, RACE_NO_SUN )
	    && ch->in_room->sector_type != SECT_INSIDE
	    && !IS_SET( ch->in_room->room_flags, ROOM_UNDERGROUND )
	    && !IS_SET( ch->in_room->room_flags, ROOM_INDOORS )
	    && !room_is_dark( ch->in_room ) )
	{
	    if( IS_NPC( ch ) )
		dmg = 2;
	    else if( ch->in_room->sector_type == SECT_INSIDE )
		dmg = 5;
	    else if( ch->in_room->sector_type == SECT_FOREST )
		dmg = 8;
	    else
		dmg = 15;

	    if( ch->in_room->area->plane->weather.sky == SKY_CLOUDY )
		dmg /= 2;
	    if( ch->in_room->area->plane->weather.sky == SKY_RAINING )
	    {
		dmg *= 3;
		dmg /= 4;
	    }

	    damage( ch, ch, ch->hit * dmg / 100, gsn_plague, WEAR_NONE );
	}
	else if( IS_AFFECTED( ch, AFF_POISON ) )
	{
	    dmg = number_range( ch->hit / 30, ch->hit / 20 );

	    act( "$n shiver$% and suffer$%.", ch, NULL, NULL, TO_ALL );
	    damage( ch, ch, dmg, gsn_poison, WEAR_NONE );
	}
	else if( IS_AFFECTED( ch, AFF_PLAGUE ) )
	{
	    dmg = number_range( ch->hit / 30, ch->hit / 20 );
	    if( IS_NPC( ch ) )
		dmg /= 20;

	    act( "You feel your blood turning to molten lead.",
		 ch, NULL, NULL, TO_CHAR );
	    act( "$n shiver$% and suffer$%.", ch, NULL, NULL, TO_ROOM );
	    damage( ch, ch, dmg, gsn_plague, WEAR_NONE );
	}
	else if( ch->position == POS_INCAP )
	    damage( ch, ch, 1, TYPE_UNDEFINED, WEAR_NONE );
	else if( ch->position <= POS_MORTAL )
	    damage( ch, ch, 2, TYPE_UNDEFINED, WEAR_NONE );
    }

    /*
     * Autosave and autoquit.
     * Check that these chars still exist.
     */
    if( ch_save || ch_quit )
    {
	for( ch = char_list; ch; ch = ch->next )
	{
	    if( ch->deleted )
		continue;
	    if( ch == ch_save )
		save_char_obj( ch );
	    if( ch == ch_quit )
		do_quit( ch, "" );
	}
    }

    return;
}


/*
 * Aggress.
 *
 * for each mortal PC
 *     for each mob in room
 *	   aggress on some random PC
 *
 * This function takes .2% of total CPU time.
 *
 * -Kahn
 */
void aggr_update( void )
{
    CHAR_DATA *ch;
    CHAR_DATA *mch, *mchnext;
    CHAR_DATA *vch;
    CHAR_DATA *victim;
    DESCRIPTOR_DATA *d;

    /*
     * Let's not worry about link dead characters. -Kahn
     */
    for( d = descriptor_list; d; d = d->next )
    {
	ch = d->character;

	if( IS_SET( d->interpreter->flags, INTERPRETER_SAFE )
	    || ch->level >= LEVEL_IMMORTAL
	    || !ch->in_room || number_bits( 1 ) )
	    continue;

	/* mch wont get hurt */
	for( mch = ch->in_room->people; mch; mch = mchnext )
	{
	    int count;
	    bool hate = FALSE;

	    mchnext = mch->next_in_room;

	    if( !IS_NPC( mch ) || mch->deleted
		|| mch->fighting || !IS_AWAKE( mch )
		|| IS_AFFECTED( mch, AFF_CHARM )
		|| IS_AFFECTED( mch, AFF_CALM )
		|| ( xIS_SET( mch->act, ACT_WIMPY ) && IS_AWAKE( ch )
		     && !IS_AFFECTED( ch, AFF_TAUNT ) )
		|| !can_see( mch, ch ) )
		continue;

	    if( ( xIS_SET( mch->act, ACT_RACIST )
		  || IS_AFFECTED( ch, AFF_TAUNT ) )
		&& !str_infix( race_table[ch->race].name,
			       race_table[mch->race].hate ) )
		hate = TRUE;

	    if( !xIS_SET( mch->act, ACT_AGGRESSIVE ) && !hate
		&& ( !xIS_SET( mch->act, ACT_ASSIST )
		     || !IS_AFFECTED( ch, AFF_TAUNT )
		     || number_bits( 3 ) != 0 ) )
		continue;

	    /*
	     * Ok we have a 'ch' player character and a 'mch' npc aggressor.
	     * Now make the aggressor fight a RANDOM pc victim in the room,
	     *	 giving each 'vch' an equal chance of selection.
	     */
	    count = 0;
	    victim = NULL;
	    for( vch = mch->in_room->people; vch; vch = vch->next_in_room )
	    {
		if( IS_NPC( vch ) || vch->deleted
		    || vch->level >= LEVEL_IMMORTAL
		    || ( IS_AFFECTED( vch, AFF_SNEAK )
			 && number_bits( 4 ) != 0 )
		    || !can_see( mch, vch ) )
		    continue;

		if( ( !hate && ( !xIS_SET( mch->act, ACT_WIMPY )
				 || !IS_AWAKE( vch ) ) )
		    || ( hate && !str_infix( race_table[vch->race].name,
					     race_table[mch->race].hate ) ) )
		{
		    if( IS_AFFECTED( vch, AFF_TAUNT ) )
		    {
			if( number_range( 0, count / 2 ) == 0 )
			{
			    victim = vch;
			    break;
			}
		    }
		    else if( number_range( 0, count ) == 0 )
			victim = vch;
		    count++;
		}
	    }

	    if( !victim || IS_AFFECTED( victim, AFF_GHOUL ) )
		continue;

	    if( !IS_AFFECTED( victim, AFF_FAST_TALK )
		|| UMIN( ( mch->level - victim->level + 8 ), 30 )
		>= ( number_percent( ) / 2 ) )
	    {
		if( hate )
		    act( "$N screams wildly and attacks $n!",
			 victim, NULL, mch, TO_ALL );
		multi_hit( mch, victim, TYPE_UNDEFINED );
	    }

	}		/* mch loop */

    }			/* descriptor loop */

    return;
}


/* Update the check on time for autoshutdown */
void time_update( void )
{
    FILE *fp;
    char buf[MAX_STRING_LENGTH];

    if( SysInfo->down_time <= 0 )
	return;
    if( current_time == SysInfo->down_time - 225 )
    {
	sprintf( buf, "First Warning!\n\r%s in %d minutes or %d seconds.\n\r",
		 IS_SET( SysInfo->flags, SYSINFO_REBOOT )
		 ? "Reboot" : "Shutdown",
		 (int)( SysInfo->down_time - current_time ) / 60,
		 (int)( SysInfo->down_time - current_time ) );
	send_to_all_char( buf );
    }
    if( current_time == SysInfo->down_time - 150 )
    {
	sprintf( buf, "Second Warning!\n\r%s in %d minutes or %d seconds.\n\r",
		 IS_SET( SysInfo->flags, SYSINFO_REBOOT )
		 ? "Reboot" : "Shutdown",
		 (int)( SysInfo->down_time - current_time ) / 60,
		 (int)( SysInfo->down_time - current_time ) );
	send_to_all_char( buf );
    }
    if( current_time == SysInfo->down_time - 10 )
    {
	sprintf( buf, "Final Warning!\n\r%s in 10 seconds.\n\r",
		 IS_SET( SysInfo->flags, SYSINFO_REBOOT )
		 ? "Reboot" : "Shutdown" );
	send_to_all_char( buf );
    }
    if( current_time > SysInfo->down_time )
    {
	sprintf( buf, "%s by system.\n\r",
		 IS_SET( SysInfo->flags, SYSINFO_REBOOT )
		 ? "Reboot" : "Shutdown" );
	send_to_all_char( buf );
	log_string( buf );

	end_of_game( );

	if( !IS_SET( SysInfo->flags, SYSINFO_REBOOT ) )
	{
	    if( !( fp = open_file( SHUTDOWN_FILE, "a", FALSE ) ) )
	    {
		perror( SHUTDOWN_FILE );
		bug( "Could not open the Shutdown file!" );
	    }
	    else
	    {
		fprintf( fp, "Shutdown by System\n" );
		close_file( fp );
	    }
	}
	merc_down = TRUE;
    }

    return;
}


/*
 * Remove deleted EXTRA_DESCR_DATA from objects.
 * Remove deleted AFFECT_DATA from chars and objects.
 * Remove deleted CHAR_DATA and OBJ_DATA from char_list and object_list.
 */
void list_update( void )
{
    CHAR_DATA *ch;
    CHAR_DATA *ch_next;
    CHAR_DATA *ch_prev = NULL;
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    OBJ_DATA *obj_prev = NULL;
    AFFECT_DATA *paf;
    AFFECT_DATA *paf_next;
    AFFECT_DATA *paf_prev;

    if( delete_char )
	for( ch = char_list; ch; ch = ch_next )
	{
	    paf_prev = NULL;
	    for( paf = ch->affected; paf; paf = paf_next )
	    {
		paf_next = paf->next;

		if( paf->deleted || ch->deleted )
		{
		    if( !paf_prev )
			ch->affected = paf->next;
		    else
			paf_prev->next = paf->next;

		    paf->next = affect_free;
		    affect_free = paf;
		}
		else
		    paf_prev = paf;
	    }

	    ch_next = ch->next;

	    if( ch->deleted )
	    {
		if( !ch_prev )
		    char_list = ch->next;
		else
		    ch_prev->next = ch->next;

		free_char( ch );
	    }
	    else
		ch_prev = ch;
	}

    if( delete_obj )
	for( obj = object_list; obj; obj = obj_next )
	{
	    EXTRA_DESCR_DATA *ed;
	    EXTRA_DESCR_DATA *ed_next;

	    if( obj->deleted )
		for( ed = obj->extra_descr; ed; ed = ed_next )
		{
		    ed_next = ed->next;

		    free_string( ed->description );
		    free_string( ed->keyword );
		    ed->next = extra_descr_free;
		    extra_descr_free = ed;
		}

	    paf_prev = NULL;
	    for( paf = obj->affected; paf; paf = paf_next )
	    {
		paf_next = paf->next;

		if( paf->deleted || obj->deleted )
		{
		    if( !paf_prev )
			obj->affected = paf->next;
		    else
			paf_prev->next = paf->next;

		    paf->next = affect_free;
		    affect_free = paf;
		}
		else
		    paf_prev = paf;
	    }

	    obj_next = obj->next;

	    if( obj->deleted )
	    {
		if( !obj_prev )
		    object_list = obj->next;
		else
		    obj_prev->next = obj->next;

		free_string( obj->name );
		free_string( obj->description );
		free_string( obj->short_descr );
		--obj->pIndexData->count;

		obj->next = obj_free;
		obj_free = obj;
	    }
	    else
		obj_prev = obj;
	}

    delete_obj = FALSE;
    delete_char = FALSE;
    return;
}


/*
 * Handle all kinds of updates.
 * Called once per pulse from game loop.
 * Random times to defeat tick-timing clients and players.
 */
void update_handler( void )
{
    static int pulse_auction;
    static int pulse_db_dump;	/* OLC 1.1b */
    static int pulse_quest;

    event_update();

    if( --pulse_point <= 0 )
    {
	wiznet( NULL, WIZ_TICKS, L_SEN, "Update tick." );
	pulse_point = number_range( PULSE_TICK / 2, 3 * PULSE_TICK / 2 );
	weather_update( );
	list_update( );
    }
    else if( pulse_point == 2 )
	char_update( );


    if( --pulse_auction <= 0 )
    {
	pulse_auction = PULSE_VIOLENCE * 3;
	update_auction( );
    }

    /* OLC 1.1b */
    if( --pulse_db_dump <= 0 )
    {
	wiznet( NULL, WIZ_TICKS, L_BLD, "Dump Area pulse (OLC)" );
	pulse_db_dump = PULSE_DB_DUMP;
	db_dump();
    }
    else if( pulse_db_dump == 5 )
	wiznet( NULL, WIZ_TICKS, L_JUN, "Area dump incoming..." );
    else if( pulse_db_dump == 10 )
	do_dupefind( NULL, "" );

    if( --pulse_quest <= 0 )
    {
	pulse_quest = 60 * PULSE_PER_SECOND;
	quest_update();
    }

    update_act_progs();
    aggr_update( );
    time_update( );
    spam_update( );
    return;
}


void pop( OBJ_DATA *vobj, OBJ_DATA *obj )
{
    CHAR_DATA *carried, *victim, *vnext;

    if( vobj->in_obj )
    {
	extract_obj( vobj );
	return;
    }

    if( ( carried = vobj->carried_by ) )
    {
	( *skill_table[gsn_explosive].spell_fun )
	    ( gsn_explosive, vobj->value[0] * 2, carried, (void *)carried );
	victim = carried->in_room->people;
    }
    else
    {
	victim = vobj->in_room->people;
    }
    for( ; victim; victim = vnext )
    {
	vnext = victim->next_in_room;
	if( vobj != obj )
	    act( "$p is set off by $P!", victim, vobj, obj, TO_CHAR );
	if( victim->deleted || victim == carried )
	    continue;
	( *skill_table[gsn_explosive].spell_fun )
	    ( gsn_explosive, vobj->value[0], victim, (void *)victim );
    }
    extract_obj( vobj );
}


void explode( OBJ_DATA *obj )
{
    CHAR_DATA *victim, *vnext;
    ROOM_INDEX_DATA *room = NULL;
    int i;
    OBJ_DATA *vobj = NULL, *obj_next;

    if( obj->in_room )
	room = obj->in_room;
    else if( obj->carried_by && obj->carried_by->in_room )
	room = obj->carried_by->in_room;
    for( i = 0; room && i < 6; ++i )
    {
	if( room->exit[i] )
	    send_to_room( "You hear a loud explosion nearby.",
			  room->exit[i]->to_room );
    }

    if( obj->carried_by )
	vobj = obj->carried_by->carrying;
    else if( obj->in_room )
	vobj = obj->in_room->contents;
    else if( obj->in_obj )
    {
	OBJ_DATA *in_obj;
	vobj = obj->in_obj->contains;
	for( in_obj = vobj; in_obj->in_obj; in_obj = in_obj->in_obj )
	    ;
	if( in_obj->carried_by )
	    act( "You hear a muffled explosion from inside $p",
		 in_obj->carried_by, in_obj, NULL, TO_ALL );
	else if( in_obj->in_room->people )
	    act( "You hear a muffled explosion from inside $p",
		 in_obj->in_room->people, in_obj, NULL, TO_ALL );
    }
    for( ; vobj; vobj = obj_next )
    {
	obj_next = vobj->next_content;
	if( !vobj->deleted && vobj->item_type == ITEM_EXPLOSIVE
	    && obj != vobj )
	    pop( vobj, obj );
    }
    /* it was muffled, just extract the obj and leave. */
    if( obj->in_obj )
    {
	extract_obj( obj );
	return;
    }

    /* go through the inventories of everyone in the room.
     * all explosives carried go off too.
     */
    if( obj->in_room )
	victim = obj->in_room->people;
    else
	victim = obj->carried_by->in_room->people;
    for( ; victim; victim = vnext )
    {
	vnext = victim->next_in_room;
	if( victim->deleted || victim == obj->carried_by )
	    continue;
	for( vobj = victim->carrying; vobj; vobj = obj_next )
	{
	    obj_next = vobj->next_content;
	    if( !vobj->deleted && vobj->item_type == ITEM_EXPLOSIVE
		&& obj != vobj )
		pop( vobj, obj );
	}
    }
    pop( obj, obj );
    return;
}


/*
 * Slot a character into the highest list where they should go
 * This assumes that they will be higher than they last were,
 * or at the very least the same position in the list
 * any modifications downward will need editing of the HIGHEST_FILE
 */
#define NEW_HIGHESTLIST( hightype )			\
for( high = highest_first; high; high = high->next )	\
{							\
    if( !str_cmp( high->type, (hightype) ) )		\
	break;						\
}							\
if( !high )						\
{							\
    high = alloc_perm( sizeof( *high ) );		\
    high->type = str_dup( (hightype) );			\
    high->entries[0] = new_highent( );			\
    high->entries[0]->name = str_dup( ch->name );	\
    high->entries[0]->level = ch->level;		\
    high->entries[0]->sublevel = ch->sublevel;		\
    high->entries[0]->class = ch->class;		\
    high->entries[0]->race = ch->race;			\
    for( j = 1; j < 10; ++j )				\
	high->entries[j] = NULL;			\
    if( !highest_first )				\
	highest_first = high;				\
    if( highest_last )					\
	highest_last->next = high;			\
    highest_last = high;				\
}

void update_highest_list( CHAR_DATA *ch )
{
    HIGHEST_DATA *high;
    int i, j;
    char buf[MAX_INPUT_LENGTH];
    bool congrats = FALSE;

    if( IS_NPC( ch ) || get_trust( ch ) >= L_APP || ch->level < 5 )
	return;
    for( high = highest_first; high; high = high->next )
    {
	if( ( strcmp( high->type, "TopTen" ) || IS_IMMORTAL( ch ) )
	    && str_cmp( high->type, class_table[ch->class].name )
	    && str_cmp( high->type, race_table[ch->race].name ) )
	    continue;

	for( i = 0; high->entries[i] && i < 10; ++i )
	{
	    if( ch->level >= high->entries[i]->level ||
		( ch->level == high->entries[i]->level
		 && ch->sublevel >= high->entries[i]->sublevel ) )
		break;
	}

	if( i >= 10 )
	    continue;

	for( j = i; j < 10; ++j )
	{
	    if( high->entries[j]
		&& !str_cmp( high->entries[j]->name, ch->name ) )
		break;
	}

	if( j == i )
	    continue;

	if( j > i )		/* if they are moving up */
	{
	    char temp[MAX_INPUT_LENGTH];

	    if( !congrats )
	    {
		sprintf( buf, "Congratulations %s!\n\r", ch->name );
		send_to_char( buf, ch );
	    }
	    else
		congrats = TRUE;
	    if( i )
		sprintf( temp, "You are now the %d%s highest",
			 i + 1, ( i + 1 == 2 ) ? "nd"
			 : ( i + 1 == 3 ) ? "rd" : "th" );
		else
		    strcpy( temp, "You are now the highest" );
	    strcpy( buf, temp );
	    if( !strcmp( high->type, "TopTen" ) )
		strcat( buf, " character overall.\n\r" );
	    else
		sprintf( buf, "%s %s overall.\n\r", temp, high->type );
	    send_to_char( buf, ch );
	}

	j = UMIN( j, 9 );
	if( high->entries[j] )
	    free_highent( high->entries[j] );
	for( ; j > i; --j )
	    high->entries[j] = high->entries[j - 1];

	high->entries[i] = new_highent( );
	high->entries[i]->name = str_dup( ch->name );
	high->entries[i]->level = ch->level;
	high->entries[i]->sublevel = ch->sublevel;
	high->entries[i]->class = ch->class;
	high->entries[i]->race = ch->race;
    }
    NEW_HIGHESTLIST( "TopTen" );
    NEW_HIGHESTLIST( class_table[ch->class].name );
    NEW_HIGHESTLIST( race_table[ch->race].name );
    return;
}


void quest_update( void )
{
    QUEST_DATA * qq;
    DESCRIPTOR_DATA *d;

    for( d = descriptor_list; d; d = d->next )
    {
	if( IS_SET( d->interpreter->flags, INTERPRETER_SAFE ) )
	    continue;
	qq = CH( d )->pcdata->quest;
	if( qq->time <= 0 )
	    continue;
	if( --qq->time == 0 )
	{
	    if( qq->type != QUEST_NONE )
	    {
		send_to_char( "You have run out of time for your quest.\n\r",
			      d->character );
		switch( qq->type )
		{
		case QUEST_RACE_CORPSE:
		case QUEST_BODY_PART:
		    free_mem( qq->target, sizeof( struct quest_race_corpse ) );
		    break;
		case QUEST_OBJ_MATERIAL:
		    free_mem( qq->target, sizeof( struct quest_obj_material ) );
		    break;
		}
		qq->target = NULL;
		qq->type = QUEST_NONE;
		qq->time = 30;
	    }
	    else
		send_to_char( "You may now quest again.\n\r", d->character );
	}
	else
	{
	    qq->reward = qq->reward * number_fuzzy( 9 ) / 10;
	    if( qq->time <= 3 && qq->type != QUEST_NONE )
		send_to_char( "Better hurry, you are almost"
			      " out of time for your quest.\n\r",
			      d->character );
	}
    }
    return;
}


void spam_update( void )
{
    DESCRIPTOR_DATA *d;

    for( d = descriptor_list; d; d = d->next )
    {
	if( !IS_SET( d->interpreter->flags, INTERPRETER_NOMESSAGE ) )
	    purge_spam( d->character->in_room );
    }
}


void purge_spam( ROOM_INDEX_DATA *room )
{
    CHAR_DATA *ch, *vch, *victim;
    static const char *const him_her[] = { "it", "him", "her" };
    char buf[ MAX_INPUT_LENGTH ];
    char buf1[256];
    char buf2[256];
    char buf3[256];
    char buf4[256];
    char buf5[256];
    char tmp[25];
    char punct, *point;
    int i;

    for( ch = room->people; ch; ch = ch->next_in_room )
    {
	if( !ch->fighting || ch->num_hits < 0 )
	    continue;
	victim = ch->fighting;
	for( i = 0; dam_table[i].max_dam >= 0; i++ )
	{
	    if( dam_table[i].max_dam >= ch->tot_dam )
		break;
	}
	punct = ( ch->tot_dam <= 100 ) ? '.' : '!';

	if( ch->tot_dam > 0 )
	{
	    sprintf( buf1, "Your %d attack%s %s %%s&n%c%%s\n\r",
		     ch->num_hits, ch->num_hits > 1 ? "s" : "",
		     dam_table[i].mesg, punct );
	    sprintf( buf2, "%%s&n's %d attack%s %s you%c%%s\n\r",
		     ch->num_hits, ch->num_hits > 1 ? "s" : "",
		     dam_table[i].mesg, punct );
	    sprintf( buf3, "%%s&n's %d attack%s %s %%s&n%c%%s\n\r",
		     ch->num_hits, ch->num_hits > 1 ? "s" : "",
		     dam_table[i].mesg, punct );
	    sprintf( buf4, "Your %d attack%s %s you%c%%s\n\r",
		     ch->num_hits, ch->num_hits > 1 ? "s" : "",
		     dam_table[i].mesg, punct );
	    sprintf( buf5, "%%s&n's %d attack%s %s %%s%c%%s\n\r",
		     ch->num_hits, ch->num_hits > 1 ? "s" : "",
		     dam_table[i].mesg, punct );
	}
	else
	{
	    strcpy( buf1, "Your attacks haven't hurt %s&n!%s\n\r" );
	    strcpy( buf2, "%s&n's attacks haven't hurt you!%s\n\r" );
	    strcpy( buf3, "%s&n's attacks haven't hurt %s&n!%s\n\r" );
	    strcpy( buf4, "Your attacks haven't hurt you!%s\n\r" );
	    strcpy( buf5, "%s&n's attacks haven't hurt %s!%s\n\r" );
	}

	for( vch = room->people; vch; vch = vch->next_in_room )
	{
	    if( !vch->desc || !IS_AWAKE( vch )
		|| ( ( vch == ch || vch == victim ) &&
		     !xIS_SET( CH(vch->desc)->act, PLR_BATTLESELF ) )
		|| ( ( vch != ch && vch != victim ) &&
		     !xIS_SET( CH(vch->desc)->act, PLR_BATTLEOTHER ) ) )
		continue;

	    if( vch == ch )
	    {
		if( ch == victim )
		    sprintf( buf, buf4, dam_amount( tmp, vch, ch->tot_dam ) );
		else
		    sprintf( buf, buf1, PERS( victim, vch ),
			     dam_amount( tmp, vch, ch->tot_dam ) );
	    }
	    else if( vch == victim )
		sprintf( buf, buf2, PERS( ch, vch ),
			 dam_amount( tmp, vch, ch->tot_dam ) );
	    else if( ch == victim )
		sprintf( buf, buf5, PERS( ch, vch ),
			 him_her[URANGE( 0, ch->sex, 2 )],
			 dam_amount( tmp, vch, ch->tot_dam ) );
	    else
		sprintf( buf, buf3, PERS( ch, vch ),
			 PERS( victim, vch ), dam_amount( tmp, vch, ch->tot_dam ) );

	    point = &buf[0];
	    while( *point == '&' )
		point += 2;
	    *point = UPPER( *point );
	    send_to_char( buf, vch );
	}
	ch->num_hits = -1;
	ch->tot_dam = 0;
    }
    for( ch = room->people; ch; ch = ch->next_in_room )
    {
	if( ch->desc && ch->tot_xp > 0
	    && !IS_SET( ch->desc->interpreter->flags, INTERPRETER_SAFE )
	    && xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF|PLR_BATTLEOTHER ) )
	{
	    if( ch->tot_xp >= 2500 )
		charprintf( ch, "&gYou gained %d experience that round.&n\n\r",
			 ch->tot_xp / 100 );
	    else
		charprintf( ch, "&gYou gained %d.%2.2d experience that round.&n\n\r",
			 ch->tot_xp / 100, ch->tot_xp % 100 );
	}
	ch->tot_xp = 0;
    }
}