www.pudn.com > mercwsrc.zip > UPDATE.C, change:1995-02-17,size:22677b


/*************************************************************************** 
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        * 
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   * 
 *                                                                         * 
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          * 
 *  Chastain, Michael Quan, and Mitchell Tse.                              * 
 *                                                                         * 
 *  In order to use any part of this Merc Diku Mud, you must comply with   * 
 *  both the original Diku license in 'license.doc' as well the Merc       * 
 *  license in 'license.txt'.  In particular, you may not remove either of * 
 *  these copyright notices.                                               * 
 *                                                                         * 
 *  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.                                                  * 
 ***************************************************************************/ 
 
#if defined(macintosh) 
#include <types.h> 
#else 
#include <sys/types.h> 
#endif 
#include <stdio.h> 
#include <string.h> 
#include <time.h> 
#include "merc.h" 
 
 
 
/* 
 * Local functions. 
 */ 
int	hit_gain	args( ( CHAR_DATA *ch ) ); 
int	mana_gain	args( ( CHAR_DATA *ch ) ); 
int	move_gain	args( ( CHAR_DATA *ch ) ); 
void	mobile_update	args( ( void ) ); 
void	weather_update	args( ( void ) ); 
void	char_update	args( ( void ) ); 
void	obj_update	args( ( void ) ); 
void	aggr_update	args( ( void ) ); 
 
 
 
/* 
 * Advancement stuff. 
 */ 
void advance_level( CHAR_DATA *ch ) 
{ 
    char buf[MAX_STRING_LENGTH]; 
    int add_hp; 
    int add_mana; 
    int add_move; 
    int add_prac; 
 
    sprintf( buf, "the %s", 
	title_table [ch->class] [ch->level] [ch->sex == SEX_FEMALE ? 1 : 0] ); 
    set_title( ch, buf ); 
 
    add_hp	= con_app[get_curr_con(ch)].hitp + number_range( 
		    class_table[ch->class].hp_min, 
		    class_table[ch->class].hp_max ); 
    add_mana	= class_table[ch->class].fMana 
		    ? number_range(2, (2*get_curr_int(ch)+get_curr_wis(ch))/8) 
		    : 0; 
    add_move	= number_range( 5, (get_curr_con(ch)+get_curr_dex(ch))/4 ); 
    add_prac	= wis_app[get_curr_wis(ch)].practice; 
 
    add_hp	= UMAX(  1, add_hp   ); 
    add_mana	= UMAX(  0, add_mana ); 
    add_move	= UMAX( 10, add_move ); 
 
    ch->max_hit 	+= add_hp; 
    ch->max_mana	+= add_mana; 
    ch->max_move	+= add_move; 
    ch->practice	+= add_prac; 
 
    if ( !IS_NPC(ch) ) 
	REMOVE_BIT( ch->act, PLR_BOUGHT_PET ); 
 
    sprintf( buf, 
	"Your gain is: %d/%d hp, %d/%d m, %d/%d mv %d/%d prac.\n\r", 
	add_hp,		ch->max_hit, 
	add_mana,	ch->max_mana, 
	add_move,	ch->max_move, 
	add_prac,	ch->practice 
	); 
    send_to_char( buf, ch ); 
    return; 
}    
 
 
 
void gain_exp( CHAR_DATA *ch, int gain ) 
{ 
    if ( IS_NPC(ch) || ch->level >= LEVEL_HERO ) 
	return; 
 
    ch->exp = UMAX( 1000, ch->exp + gain ); 
    while ( ch->level < LEVEL_HERO && ch->exp >= 1000 * (ch->level+1) ) 
    { 
	send_to_char( "You raise a level!!  ", ch ); 
	ch->level += 1; 
	advance_level( ch ); 
    } 
 
    return; 
} 
 
 
 
/* 
 * Regeneration stuff. 
 */ 
int hit_gain( CHAR_DATA *ch ) 
{ 
    int gain; 
 
    if ( IS_NPC(ch) ) 
    { 
	gain = ch->level * 3 / 2; 
    } 
    else 
    { 
	gain = UMIN( 5, ch->level ); 
 
	switch ( ch->position ) 
	{ 
	case POS_SLEEPING: gain += get_curr_con(ch);		break; 
	case POS_RESTING:  gain += get_curr_con(ch) / 2;	break; 
	} 
 
	if ( ch->pcdata->condition[COND_FULL]   == 0 ) 
	    gain /= 2; 
 
	if ( ch->pcdata->condition[COND_THIRST] == 0 ) 
	    gain /= 2; 
 
    } 
 
    if ( IS_AFFECTED(ch, AFF_POISON) ) 
	gain /= 4; 
 
    return UMIN(gain, ch->max_hit - ch->hit); 
} 
 
 
 
int mana_gain( CHAR_DATA *ch ) 
{ 
    int gain; 
 
    if ( IS_NPC(ch) ) 
    { 
	gain = ch->level; 
    } 
    else 
    { 
	gain = UMIN( 5, ch->level / 2 ); 
 
	switch ( ch->position ) 
	{ 
	case POS_SLEEPING: gain += get_curr_int(ch) * 2;	break; 
	case POS_RESTING:  gain += get_curr_int(ch);		break; 
	} 
 
	if ( ch->pcdata->condition[COND_FULL]   == 0 ) 
	    gain /= 2; 
 
	if ( ch->pcdata->condition[COND_THIRST] == 0 ) 
	    gain /= 2; 
 
    } 
 
    if ( IS_AFFECTED( ch, AFF_POISON ) ) 
	gain /= 4; 
 
    return UMIN(gain, ch->max_mana - ch->mana); 
} 
 
 
 
int move_gain( CHAR_DATA *ch ) 
{ 
    int gain; 
 
    if ( IS_NPC(ch) ) 
    { 
	gain = ch->level; 
    } 
    else 
    { 
	gain = UMAX( 15, 2 * ch->level ); 
 
	switch ( ch->position ) 
	{ 
	case POS_SLEEPING: gain += get_curr_dex(ch);		break; 
	case POS_RESTING:  gain += get_curr_dex(ch) / 2;	break; 
	} 
 
	if ( ch->pcdata->condition[COND_FULL]   == 0 ) 
	    gain /= 2; 
 
	if ( ch->pcdata->condition[COND_THIRST] == 0 ) 
	    gain /= 2; 
    } 
 
    if ( IS_AFFECTED(ch, AFF_POISON) ) 
	gain /= 4; 
 
    return UMIN(gain, ch->max_move - ch->move); 
} 
 
 
 
void gain_condition( CHAR_DATA *ch, int iCond, int value ) 
{ 
    int condition; 
 
    if ( value == 0 || IS_NPC(ch) || ch->level >= LEVEL_HERO ) 
	return; 
 
    condition				= ch->pcdata->condition[iCond]; 
    ch->pcdata->condition[iCond]	= URANGE( 0, condition + value, 48 ); 
 
    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; 
} 
 
 
 
/* 
 * Mob autonomous action. 
 * This function takes 25% to 35% of ALL Merc cpu time. 
 * -- Furey 
 */ 
void mobile_update( void ) 
{ 
    CHAR_DATA *ch; 
    CHAR_DATA *ch_next; 
    EXIT_DATA *pexit; 
    int door; 
 
    /* Examine all mobs. */ 
    for ( ch = char_list; ch != NULL; ch = ch_next ) 
    { 
//    SpinTheMessageLoop(); // @@@ 
	ch_next = ch->next; 
 
   /* Had to add test for animated corpses so their spec-fun would get called */ 
	if ( !IS_NPC(ch) || ch->in_room == NULL || 
      IS_AFFECTED(ch, AFF_CHARM) /*&& ch->pIndexData->vnum != MOB_VNUM_WALKING_DEAD*/) 
	    continue; 
 
	/* Examine call for special procedure */ 
	if ( ch->spec_fun != 0 ) 
	{ 
	    if ( (*ch->spec_fun) ( ch ) ) 
		continue; 
	} 
 
	/* That's all for sleeping / busy monster */ 
	if ( ch->position < POS_STANDING || IS_SWITCHED(ch)) // @@@ Added IS_SWITCHED 
	    continue; 
 
	/* MOBprogram random trigger */ 
	if (ch->in_room->area->nplayer>0) 
	{ 
	    mprog_random_trigger(ch); 
	                                     /* If ch dies or changes 
						position due to it's random 
						trigger continue - Kahn */ 
	    if ( ch->position < POS_STANDING ) 
	        continue; 
	} 
 
	/* Scavenge */ 
	if ( IS_SET(ch->act, ACT_SCAVENGER) 
	&&   ch->in_room->contents != NULL 
	&&   number_bits( 2 ) == 0 ) 
	{ 
	    OBJ_DATA *obj; 
	    OBJ_DATA *obj_best; 
	    int max; 
 
	    max         = 1; 
	    obj_best    = 0; 
	    for ( obj = ch->in_room->contents; obj; obj = obj->next_content ) 
	    { 
		if ( CAN_WEAR(obj, ITEM_TAKE) && obj->cost > max ) 
		{ 
		    obj_best    = obj; 
		    max         = obj->cost; 
		} 
	    } 
 
	    if ( obj_best ) 
	    { 
		obj_from_room( obj_best ); 
		obj_to_char( obj_best, ch ); 
		act( "$n gets $p.", ch, obj_best, NULL, TO_ROOM ); 
	    } 
	} 
 
	/* Wander */ 
	if ( !IS_SET(ch->act, ACT_SENTINEL) 
	&& ( door = number_bits( 5 ) ) <= 5 
	&& ( pexit = ch->in_room->exit[door] ) != NULL 
	&&   pexit->to_room != NULL 
	&&   !IS_SET(pexit->exit_info, EX_CLOSED) 
	&&   !IS_SET(pexit->to_room->room_flags, ROOM_NO_MOB) 
	&& ( !IS_SET(ch->act, ACT_STAY_AREA) 
	||   pexit->to_room->area == ch->in_room->area ) ) 
	{ 
	    move_char( ch, door ); 
	                                       /* If ch changes position due 
						  to it's or someother mob's 
						  movement via MOBProgs, 
						  continue - Kahn */ 
	    if ( ch->position < POS_STANDING ) 
	        continue; 
	} 
 
	/* Flee */ 
	if ( ch->hit < ( ch->max_hit / 2 ) 
	&& ( door = number_bits( 3 ) ) <= 5 
	&& ( pexit = ch->in_room->exit[door] ) != NULL 
	&&   pexit->to_room != NULL 
	&&   !IS_SET(pexit->exit_info, EX_CLOSED) 
	&&   !IS_SET(pexit->to_room->room_flags, ROOM_NO_MOB) ) 
	{ 
	    CHAR_DATA *rch; 
	    bool found; 
 
	    found = FALSE; 
	    for ( rch  = pexit->to_room->people; 
		  rch != NULL; 
		  rch  = rch->next_in_room ) 
	    { 
		if ( !IS_NPC(rch) ) 
		{ 
		    found = TRUE; 
		    break; 
		} 
	    } 
	    if ( !found ) 
		move_char( ch, door ); 
	} 
 
    } 
 
    return; 
} 
 
 
 
/* 
 * Update the weather. 
 */ 
void weather_update( void ) 
{ 
    char buf[MAX_STRING_LENGTH]; 
    DESCRIPTOR_DATA *d; 
    int diff; 
 
    buf[0] = '\0'; 
 
    switch ( ++time_info.hour ) 
    { 
    case  5: 
	weather_info.sunlight = SUN_LIGHT; 
	strcat( buf, "The day has begun.\n\r" ); 
	break; 
 
    case  6: 
	weather_info.sunlight = SUN_RISE; 
	strcat( buf, "The sun rises in the east.\n\r" ); 
	break; 
 
    case 19: 
	weather_info.sunlight = SUN_SET; 
	strcat( buf, "The sun slowly disappears in the west.\n\r" ); 
	break; 
 
    case 20: 
	weather_info.sunlight = SUN_DARK; 
	strcat( buf, "The night has begun.\n\r" ); 
	break; 
 
    case 24: 
	time_info.hour = 0; 
	time_info.day++; 
	break; 
    } 
 
    if ( time_info.day   >= 35 ) 
    { 
	time_info.day = 0; 
	time_info.month++; 
    } 
 
    if ( time_info.month >= 17 ) 
    { 
	time_info.month = 0; 
	time_info.year++; 
    } 
 
    /* 
     * Weather change. 
     */ 
    if ( time_info.month >= 9 && time_info.month <= 16 ) 
	diff = weather_info.mmhg >  985 ? -2 : 2; 
    else 
	diff = weather_info.mmhg > 1015 ? -2 : 2; 
 
    weather_info.change   += diff * dice(1, 4) + dice(2, 6) - dice(2, 6); 
    weather_info.change    = UMAX(weather_info.change, -12); 
    weather_info.change    = UMIN(weather_info.change,  12); 
 
    weather_info.mmhg += weather_info.change; 
    weather_info.mmhg  = UMAX(weather_info.mmhg,  960); 
    weather_info.mmhg  = UMIN(weather_info.mmhg, 1040); 
 
    switch ( weather_info.sky ) 
    { 
    default:  
	bug( "Weather_update: bad sky %d.", weather_info.sky ); 
	weather_info.sky = SKY_CLOUDLESS; 
	break; 
 
    case SKY_CLOUDLESS: 
	if ( weather_info.mmhg <  990 
	|| ( weather_info.mmhg < 1010 && number_bits( 2 ) == 0 ) ) 
	{ 
	    strcat( buf, "The sky is getting cloudy.\n\r" ); 
	    weather_info.sky = SKY_CLOUDY; 
	} 
	break; 
 
    case SKY_CLOUDY: 
	if ( weather_info.mmhg <  970 
	|| ( weather_info.mmhg <  990 && number_bits( 2 ) == 0 ) ) 
	{ 
	    strcat( buf, "It starts to rain.\n\r" ); 
	    weather_info.sky = SKY_RAINING; 
	} 
 
	if ( weather_info.mmhg > 1030 && number_bits( 2 ) == 0 ) 
	{ 
	    strcat( buf, "The clouds disappear.\n\r" ); 
	    weather_info.sky = SKY_CLOUDLESS; 
	} 
	break; 
 
    case SKY_RAINING: 
	if ( weather_info.mmhg <  970 && number_bits( 2 ) == 0 ) 
	{ 
	    strcat( buf, "Lightning flashes in the sky.\n\r" ); 
	    weather_info.sky = SKY_LIGHTNING; 
	} 
 
	if ( weather_info.mmhg > 1030 
	|| ( weather_info.mmhg > 1010 && number_bits( 2 ) == 0 ) ) 
	{ 
	    strcat( buf, "The rain stopped.\n\r" ); 
	    weather_info.sky = SKY_CLOUDY; 
	} 
	break; 
 
    case SKY_LIGHTNING: 
	if ( weather_info.mmhg > 1010 
	|| ( weather_info.mmhg >  990 && number_bits( 2 ) == 0 ) ) 
	{ 
	    strcat( buf, "The lightning has stopped.\n\r" ); 
	    weather_info.sky = SKY_RAINING; 
	    break; 
	} 
	break; 
    } 
 
    if ( buf[0] != '\0' ) 
    { 
	for ( d = descriptor_list; d != NULL; d = d->next ) 
	{ 
	    if ( d->connected == CON_PLAYING 
	    &&   IS_OUTSIDE(d->character) 
	    &&   IS_AWAKE(d->character) ) 
		send_to_char( buf, d->character ); 
	} 
    } 
 
    return; 
} 
 
 
 
/* 
 * Update all chars, including mobs. 
 * This function is performance sensitive. 
 */ 
void char_update( void ) 
{    
    CHAR_DATA *ch; 
    CHAR_DATA *ch_next; 
    CHAR_DATA *ch_save; 
    CHAR_DATA *ch_quit; 
    time_t save_time; 
 
    save_time	= current_time; 
    ch_save	= NULL; 
    ch_quit	= NULL; 
    for ( ch = char_list; ch != NULL; ch = ch_next ) 
    { 
	AFFECT_DATA *paf; 
	AFFECT_DATA *paf_next; 
 
	ch_next = ch->next; 
 
	/* 
	 * Find dude with oldest save time. 
	 */ 
	if ( !IS_NPC(ch) 
	&& ( ch->desc == NULL || ch->desc->connected == CON_PLAYING ) 
	&&   ch->level >= 2 
	&&   ch->save_time < save_time ) 
	{ 
	    ch_save	= ch; 
	    save_time	= ch->save_time; 
	} 
 
	if ( ch->position >= POS_STUNNED ) 
	{ 
	    if ( ch->hit  < ch->max_hit ) 
		ch->hit  += hit_gain(ch); 
 
	    if ( ch->mana < ch->max_mana ) 
		ch->mana += mana_gain(ch); 
 
	    if ( ch->move < ch->max_move ) 
		ch->move += move_gain(ch); 
	} 
 
	if ( ch->position == POS_STUNNED ) 
	    update_pos( ch ); 
 
	if ( !IS_NPC(ch) && ch->level < LEVEL_IMMORTAL ) 
	{ 
	    OBJ_DATA *obj; 
 
	    if ( ( obj = get_eq_char( ch, WEAR_LIGHT ) ) != NULL 
	    &&   obj->item_type == ITEM_LIGHT 
	    &&   obj->value[2] > 0 ) 
	    { 
		if ( --obj->value[2] == 0 && ch->in_room != NULL ) 
		{ 
		    --ch->in_room->light; 
		    act( "$p goes out.", ch, obj, NULL, TO_ROOM ); 
		    act( "$p goes out.", ch, obj, NULL, TO_CHAR ); 
		    extract_obj( obj ); 
		} 
	    } 
 
	    if ( ++ch->timer >= 12 ) 
	    { 
		if ( ch->was_in_room == NULL && ch->in_room != NULL ) 
		{ 
		    ch->was_in_room = ch->in_room; 
		    if ( ch->fighting != NULL ) 
			stop_fighting( ch, TRUE ); 
		    act( "$n disappears into the void.", 
			ch, NULL, NULL, TO_ROOM ); 
		    send_to_char( "You disappear into the void.\n\r", ch ); 
		    save_char_obj( ch ); 
		    char_from_room( ch ); 
		    char_to_room( ch, get_room_index( ROOM_VNUM_LIMBO ) ); 
		} 
	    } 
 
	    if ( ch->timer > 30 ) 
		ch_quit = ch; 
 
	    gain_condition( ch, COND_DRUNK,  -1 ); 
	    gain_condition( ch, COND_FULL,   -1 ); 
	    gain_condition( ch, COND_THIRST, -1 ); 
	} 
 
	for ( paf = ch->affected; paf != NULL; paf = paf_next ) 
	{ 
	    paf_next	= paf->next; 
	    if ( paf->duration > 0 ) 
		paf->duration--; 
	    else if ( paf->duration < 0 ) 
		; 
	    else 
	    { 
		if ( paf_next == NULL 
		||   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 ); 
		    } 
		} 
	   
		affect_remove( ch, paf ); 
	    } 
	} 
 
	/* 
	 * Careful with the damages here, 
	 *   MUST NOT refer to ch after damage taken, 
	 *   as it may be lethal damage (on NPC). 
	 */ 
	if ( IS_AFFECTED(ch, AFF_POISON) ) 
	{ 
	    act( "$n shivers and suffers.", ch, NULL, NULL, TO_ROOM ); 
	    send_to_char( "You shiver and suffer.\n\r", ch ); 
	    damage( ch, ch, 2, gsn_poison ); 
	} 
	else if ( ch->position == POS_INCAP ) 
	{ 
	    damage( ch, ch, 1, TYPE_UNDEFINED ); 
	} 
	else if ( ch->position == POS_MORTAL ) 
	{ 
	    damage( ch, ch, 2, TYPE_UNDEFINED ); 
	} 
    } 
 
    /* 
     * Autosave and autoquit. 
     * Check that these chars still exist. 
     */ 
    if ( ch_save != NULL || ch_quit != NULL ) 
    { 
	for ( ch = char_list; ch != NULL; ch = ch_next ) 
	{ 
	    ch_next = ch->next; 
	    if ( ch == ch_save ) 
		save_char_obj( ch ); 
	    if ( ch == ch_quit ) 
		do_quit( ch, "" ); 
	} 
    } 
 
    return; 
} 
 
 
 
/* 
 * Update all objs. 
 * This function is performance sensitive. 
 */ 
void obj_update( void ) 
{    
    OBJ_DATA *obj; 
    OBJ_DATA *obj_next; 
 
    for ( obj = object_list; obj != NULL; obj = obj_next ) 
    { 
	CHAR_DATA *rch; 
	char *message; 
 
	obj_next = obj->next; 
 
	if ( obj->timer <= 0 || --obj->timer > 0 ) 
            { 
#if 1 // @@@ 
            if (obj->in_room && 
                obj->in_room->sector_type == SECT_AIR && 
                (obj->wear_flags & ITEM_TAKE) && 
                obj->in_room->exit[5] && 
                obj->in_room->exit[5]->to_room) 
               { 
               ROOM_INDEX_DATA *new_room = obj->in_room->exit[5]->to_room; 
 
               if (( rch = obj->in_room->people ) != NULL ) 
                  { 
                  act( "$p falls away.", rch, obj, NULL, TO_ROOM ); 
                  act( "$p falls away.", rch, obj, NULL, TO_CHAR ); 
                  } 
 
               obj_from_room(obj); 
               obj_to_room(obj, new_room); 
 
               if (( rch = obj->in_room->people ) != NULL ) 
                  { 
                  act( "$p floats by.", rch, obj, NULL, TO_ROOM ); 
                  act( "$p floats by.", rch, obj, NULL, TO_CHAR ); 
                  } 
               } 
#endif 
	    continue; 
            } 
 
	switch ( obj->item_type ) 
	{ 
	default:              message = "$p vanishes.";         break; 
	case ITEM_FOUNTAIN:   message = "$p dries up.";         break; 
	case ITEM_CORPSE_NPC: message = "$p decays into dust."; break; 
	case ITEM_CORPSE_PC:  message = "$p decays into dust."; break; 
	case ITEM_FOOD:       message = "$p decomposes.";	break; 
	} 
 
	if ( obj->carried_by != NULL ) 
	{ 
	    act( message, obj->carried_by, obj, NULL, TO_CHAR ); 
	} 
	else if ( obj->in_room != NULL 
	&&      ( rch = obj->in_room->people ) != NULL ) 
	{ 
	    act( message, rch, obj, NULL, TO_ROOM ); 
	    act( message, rch, obj, NULL, TO_CHAR ); 
	} 
 
   // Sands fix...  Corpse stuff stays in the room 
  if (obj->item_type == ITEM_CORPSE_PC && obj->contains) 
       /* save the contents */ 
    { 
      OBJ_DATA *t_obj, *next_obj; 
  
      for (t_obj = obj->contains; t_obj != NULL; t_obj = next_obj) 
	 { 
	  next_obj = t_obj->next_content; 
	  obj_from_obj(t_obj); 
  
	  if (obj->in_obj) /* in another object */ 
	      obj_to_obj(t_obj,obj->in_obj); 
  
	  if (obj->carried_by)  /* carried */ 
              obj_to_char(t_obj,obj->carried_by); 
  
	  if (obj->in_room == NULL)  /* destroy it */ 
              extract_obj(t_obj); 
  
	   else /* to a room */ 
	      obj_to_room(t_obj,obj->in_room); 
	  } 
     } 
 
	extract_obj( obj ); 
    } 
 
    return; 
} 
 
 
 
/* 
 * Aggress. 
 * 
 * for each mortal PC 
 *     for each mob in room 
 *         aggress on some random PC 
 * 
 * This function takes 25% to 35% of ALL Merc cpu time. 
 * Unfortunately, checking on each PC move is too tricky, 
 *   because we don't the mob to just attack the first PC 
 *   who leads the party into the room. 
 * 
 * -- Furey 
 */ 
void aggr_update( void ) 
{ 
    CHAR_DATA *wch; 
    CHAR_DATA *wch_next; 
    CHAR_DATA *ch; 
    CHAR_DATA *ch_next; 
    CHAR_DATA *vch; 
    CHAR_DATA *vch_next; 
    CHAR_DATA *victim; 
//    CHAR_DATA *prev = 0;   // @@@ 
//    int nCount = 0; 
 
    for ( wch = char_list; wch != NULL; wch = wch_next ) 
    { 
#if 0 // @@@ 
    if (IsBadWritePtr(wch, sizeof *wch)) 
       DebugBreak(); 
 
    nCount++; 
    if (nCount > 5000) 
       DebugBreak(); 
#endif 
 
	wch_next = wch->next; 
 
	/* MOBProgram ACT_PROG trigger */ 
        if ( IS_NPC( wch ) && wch->mpactnum > 0 
	    && wch->in_room->area->nplayer > 0 ) 
	{ 
            MPROG_ACT_LIST * tmp_act, *tmp2_act; 
            for ( tmp_act = wch->mpact; tmp_act != NULL; 
		 tmp_act = tmp_act->next ) 
	    { 
		 mprog_wordlist_check( tmp_act->buf, wch, tmp_act->ch, 
				      tmp_act->obj, tmp_act->vo, ACT_PROG ); 
		 free_string( tmp_act->buf ); 
	    } 
            for ( tmp_act = wch->mpact; tmp_act != NULL; tmp_act = tmp2_act ) 
	    { 
		 tmp2_act = tmp_act->next; 
		 free_mem( tmp_act, sizeof( MPROG_ACT_LIST ) ); 
            } 
            wch->mpactnum = 0; 
            wch->mpact = NULL; 
	} 
 
	if ( IS_NPC( wch ) 
	||   wch->level >= LEVEL_IMMORTAL 
	||   wch->in_room == NULL ) 
	    continue; 
 
	for ( ch = wch->in_room->people; ch != NULL; ch = ch_next ) 
	{ 
	    int count; 
 
	    ch_next	= ch->next_in_room; 
 
	    if ( !IS_NPC(ch) 
	    ||   !IS_SET(ch->act, ACT_AGGRESSIVE) 
	    ||   ch->fighting != NULL 
	    ||   IS_AFFECTED(ch, AFF_CHARM) 
	    ||   !IS_AWAKE(ch) 
	    ||   ( IS_SET(ch->act, ACT_WIMPY) && IS_AWAKE(wch) ) 
	    ||   !can_see( ch, wch ) ) 
		continue; 
 
	    /* 
	     * Ok we have a 'wch' player character and a 'ch' 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 = wch->in_room->people; vch != NULL; vch = vch_next ) 
	    { 
		vch_next = vch->next_in_room; 
 
      // There are two commented-out ch->level + 5s from the Sands Patch.  I 
      // didn't like them. 
		if ( !IS_NPC(vch) 
		&&   vch->level < LEVEL_IMMORTAL 
//      &&   ch->level + 5 >= vch->level 
		&&   ( !IS_SET(ch->act, ACT_WIMPY) || !IS_AWAKE(vch) ) 
		&&   can_see( ch, vch ) ) 
		{ 
		    if ( number_range( 0, count ) == 0 ) 
			victim = vch; 
		    count++; 
		} 
	    } 
 
	    if ( victim == NULL /*&& ch->level + 5 < vch->level */ ) 
	    { 
		bug( "Aggr_update: null victim.", count ); 
		continue; 
	    } 
 
	    multi_hit( ch, victim, TYPE_UNDEFINED ); 
	} 
//    prev = wch; 
    } 
 
    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_area; 
    static  int     pulse_mobile; 
    static  int     pulse_violence; 
    static  int     pulse_point; 
//    HDC hDC; 
 
    if ( --pulse_area     <= 0 ) 
    { 
	pulse_area	= number_range( PULSE_AREA / 2, 3 * PULSE_AREA / 2 ); 
//   hDC = GetDC(0); 
//   TextOut(hDC, 0, 0, "area_update", 11); 
//   ReleaseDC(0, hDC); 
	area_update	( ); 
    } 
 
    if ( --pulse_violence <= 0 ) 
    { 
	pulse_violence	= PULSE_VIOLENCE; 
//   hDC = GetDC(0); 
//   TextOut(hDC, 0, 0, "violence_update", 15); 
//   ReleaseDC(0, hDC); 
	violence_update	( ); 
    } 
 
    if ( --pulse_mobile   <= 0 ) 
    { 
	pulse_mobile	= PULSE_MOBILE; 
//   hDC = GetDC(0); 
//   TextOut(hDC, 0, 0, "mobile_update", 13); 
//   ReleaseDC(0, hDC); 
	mobile_update	( ); 
    } 
 
    if ( --pulse_point    <= 0 ) 
    { 
	pulse_point     = number_range( PULSE_TICK / 2, 3 * PULSE_TICK / 2 ); 
//   hDC = GetDC(0); 
//   TextOut(hDC, 0, 0, "weather_update", 14); 
	weather_update	( ); 
//   TextOut(hDC, 0, 0, "char_update", 11); 
	char_update	( ); 
//   TextOut(hDC, 0, 0, "obj_update", 10); 
//   ReleaseDC(0, hDC); 
	obj_update	( ); 
    } 
 
//   hDC = GetDC(0); 
//   TextOut(hDC, 0, 0, "aggr_update", 11); 
//   ReleaseDC(0, hDC); 
    aggr_update( ); 
    tail_chain( ); 
    return; 
}