ackmud/area/imc/
ackmud/npcs/a/
ackmud/npcs/c/
ackmud/npcs/d/
ackmud/npcs/e/
ackmud/npcs/f/
ackmud/npcs/h/
ackmud/npcs/i/
ackmud/npcs/k/
ackmud/npcs/l/
ackmud/npcs/n/
ackmud/npcs/o/
ackmud/npcs/p/
ackmud/npcs/r/
ackmud/npcs/s/
ackmud/npcs/w/
ackmud/player/c/
ackmud/player/z/
/***************************************************************************
 *  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.                                               *
 *                                                                         *
 *       _/          _/_/_/     _/    _/     _/    ACK! MUD is modified    *
 *      _/_/        _/          _/  _/       _/    Merc2.0/2.1/2.2 code    *
 *     _/  _/      _/           _/_/         _/    (c)Stephen Dooley 1994  *
 *    _/_/_/_/      _/          _/  _/             "This mud has not been  *
 *   _/      _/      _/_/_/     _/    _/     _/      tested on animals."   *
 *                                                                         *
 *                                                                         *
 *  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"
#include <sys/resource.h>
#include <signal.h>

extern POL_DATA politics_data;
extern OBJ_DATA * quest_object;

extern COUNCIL_DATA super_councils[MAX_SUPER];

/*
 * 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    gain_update     args( ( void ) );
void    obj_update      args( ( void ) );
void    aggr_update     args( ( void ) );
void    objfun_update   args( ( void ) );
void    auction_update  args( ( void ) );
void    rooms_update    args( ( void ) );
void    remember_attack args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
void    quest_update	args( ( void ) );


int	abort_threshold = BOOT_DB_ABORT_THRESHOLD;
bool	disable_timer_abort = FALSE;
int	last_checkpoint;


int get_user_seconds ()
{
	struct rusage rus;
	getrusage (RUSAGE_SELF, &rus);
	return rus.ru_utime.tv_sec;
}

/* Update the checkpoint */
void alarm_update ()
{
  extern int ssm_dup_count;
  extern int ssm_loops;

  ssm_dup_count=0;
  ssm_loops=0;

	last_checkpoint = get_user_seconds();
	if (abort_threshold == BOOT_DB_ABORT_THRESHOLD)
	{
		abort_threshold = RUNNING_ABORT_THRESHOLD;
		fprintf (stderr, "Used %d user CPU seconds.\n", last_checkpoint);
	}
}

/* Set the virtual (CPU time) timer to the standard setting, ALARM_FREQUENCY */
void reset_itimer ()
{
	struct itimerval itimer;
	itimer.it_interval.tv_usec = 0; /* miliseconds */
	itimer.it_interval.tv_sec  = ALARM_FREQUENCY;
	itimer.it_value.tv_usec = 0;
	itimer.it_value.tv_sec = ALARM_FREQUENCY;

	/* start the timer - in that many CPU seconds, alarm_handler will be called */	
	if (setitimer (ITIMER_VIRTUAL, &itimer, NULL) < 0)
	{
		perror ("reset_itimer:setitimer");
		exit (1);
	}
}

const char * szFrozenMessage = "Alarm_handler: Not checkpointed recently, aborting!\n";

/* Signal handler for alarm - suggested for use in MUDs by Fusion */
void alarm_handler (int signo)
{
	int usage_now = get_user_seconds();

	/* Has there gone abort_threshold CPU seconds without alarm_update? */
    if (!disable_timer_abort && (usage_now - last_checkpoint > abort_threshold ))
	{
		/* For the log file */
        char buf[MAX_STRING_LENGTH];
	extern int ssm_dup_count;
	extern int ssm_loops;
	extern int ssm_recent_loops;

	/* spec: log usage values */
	logf("current usage: %d, last checkpoint: %d",
	     usage_now, last_checkpoint);
	logf("SSM dups: %d, loops: %d, recent: %d",
	     ssm_dup_count, ssm_loops, ssm_recent_loops);
	
        sprintf(buf,  "%s\n\r", szFrozenMessage );
	bug(buf,0);
	raise(SIGABRT); /* kill ourselves on return */
	}
	
	/* The timer resets to the values specified in it_interval 
	 * automatically.
	 *
	 * Spec: additionally, SIGABRT is blocked in this handler, and will
	 * only be delivered on return. This should ensure a good core.
	 */
}

/* Install signal alarm handler */
void init_alarm_handler()
{
	struct sigaction sa;
	
	sa.sa_handler = alarm_handler;
	sa.sa_flags = SA_RESTART; /* Restart interrupted system calls */
	sigemptyset(&sa.sa_mask);
	sigaddset(&sa.sa_mask, SIGABRT); /* block abort() in the handler
					  * so we can get a good coredump */

	if (sigaction(SIGVTALRM, &sa, NULL) < 0) /* setup handler for virtual timer */
	{
		perror ("init_alarm_handler:sigaction");
		exit (1);
	}
	last_checkpoint = get_user_seconds();
	reset_itimer(); /* start timer */
}





/*
 * Advancement stuff.
 */
void advance_level( CHAR_DATA *ch, int class, bool show, bool remort )
{
    
    /* class used instead of ch->class.  -S- */
    /* show added to allow no display of gain ( when using setclass ) */    
    /* remort indicates remortal class or normal mortal class */

    char buf[MAX_STRING_LENGTH];
    int add_hp;
    int add_mana;
    int add_move;
    int add_prac;
    int add_bloodlust, add_max_skills;

    /* title no longer changed..... */
    if ( class == ADVANCE_WOLF )
    {
      add_bloodlust =  ( number_range( 1, ( ( MAX_WOLF_LEVEL / 2 ) - ch->pcdata->generation ) ) ) + 
                      (  ( ( MAX_WOLF_LEVEL / 2) - ch->pcdata->generation ) /2 );
      add_prac = number_range( 1, UMAX( 2, ( ( MAX_WOLF_LEVEL / 2 ) - ch->pcdata->generation ) )  );
      add_max_skills = add_prac;
      
      ch->pcdata->bloodlust_max += add_bloodlust;
      ch->pcdata->vamp_pracs += add_prac;
      ch->pcdata->vamp_skill_max += add_max_skills;
      sprintf( buf,
	"@@NYou gain: %d @@rRage Ability@@N, and %d @@bWerewolf Practices. .@@N\n\r",
	add_bloodlust, add_prac );
	
    
       send_to_char( buf, ch );
       return;
    }


    if ( ( class == 16 ) )
    {

      add_bloodlust = UMAX( ( ( MAX_VAMP_LEVEL / 2) - ( ch->pcdata->generation / 2 ) ), 1 );
    add_prac =number_range( 1, UMAX( 2, ( ( MAX_VAMP_LEVEL / 2 ) - (ch->pcdata->generation ) ) 
)  );  
      add_max_skills = number_range( 1, UMAX( 2, ( ( MAX_VAMP_LEVEL / 2 ) - (ch->pcdata->generation 
) ) )  );  
 
      ch->pcdata->bloodlust_max += add_bloodlust;
      ch->pcdata->vamp_pracs += add_prac;
      ch->pcdata->vamp_skill_max += add_max_skills;


      sprintf( buf,
	"You gain: %d @@eBloodlust@@N, and %d Vampyre Practices. .\n\r",
	add_bloodlust, add_prac );
	
    
       send_to_char( buf, ch );
       return;
    }

    if ( ( class == 32 ) )
    {
     
       add_hp      = con_app[get_curr_con(ch)].hitp + number_range(10, 50);
       add_mana    = number_range(10, (3 * get_curr_int(ch)+get_curr_wis(ch))/4);
    }		     
   
    else if ( remort )
    {
       add_hp      = con_app[get_curr_con(ch)].hitp + number_range(
		     remort_table[class].hp_min,
		     remort_table[class].hp_max );
       add_mana    = remort_table[class].fMana
		     ? number_range(2, (2*get_curr_int(ch)+get_curr_wis(ch))/16)
		     : 0;
    
    }
    else
    {
       add_hp      = con_app[get_curr_con(ch)].hitp + number_range(
  		     class_table[class].hp_min,
		     class_table[class].hp_max );
    
       add_mana    = class_table[class].fMana
		     ? number_range(2, (2*get_curr_int(ch)+get_curr_wis(ch))/16)
		     : 0;
    }
    add_move    = number_range( 2, (get_curr_con(ch)+get_curr_dex(ch))/5 );
    add_prac    = ( wis_app[get_curr_wis(ch)].practice / 2 ) + number_range(1, 3);

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

    ch->pcdata->mana_from_gain += add_mana;
    ch->pcdata->hp_from_gain   += add_hp;
    ch->pcdata->move_from_gain += 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,
	"You gain: %d Hit Points, %d Mana, %d Movement and %d pracs.\n\r",
	add_hp, add_mana, add_move, add_prac );
	
    if ( show )
       send_to_char( buf, ch );

    return;
}   



void gain_exp( CHAR_DATA *ch, long_int gain )
{
    /* Not much happens here, as no-longer auto-level... -S- */
    
    /* -S- Mod:  mobs CAN gain exp as well as players */
    
    if ( ( IS_NPC(ch) ) && !( IS_SET( ch->act, ACT_INTELLIGENT ) ) )
	return;
	
    if ( IS_IMMORTAL(ch) )
       return;
       
    
    /* Changed exp system AGAIN old 'cap' was screwy!! -S- */

    ch->exp +=  gain;
    
    return;
}



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

    if ( IS_NPC(ch) && !IS_SET( ch->act, ACT_INTELLIGENT )  )
    
	gain = ( 5 + ch->level/30);
    
	gain = ( 5 + ch->level/20 );

	if ( IS_SET( ch->in_room->room_flags, ROOM_REGEN ) )
	   gain *= 2; 
	
	switch ( ch->position )
	{
	case POS_SLEEPING: gain += get_curr_con(ch) / 2;        break;
	case POS_RESTING:  gain += get_curr_con(ch) / 4;        break;
	}
      if ( !IS_NPC( ch )  )
      {
  	  if ( ch->pcdata->condition[COND_FULL]   == 0 )
	    gain /= 2;

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

        if ( IS_VAMP( ch ) && ch->pcdata->bloodlust < 3 )
           gain = 0;
        else if ( IS_VAMP( ch ) && ch->pcdata->bloodlust < 8 )
           gain /= 2;
        if ( IS_VAMP( ch ) && ch->pcdata->bloodlust == -10 )
           gain = ( 5 + ch->level/25 );

        
      }

    

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

    if ( IS_SET( ch->in_room->room_flags, ROOM_COLD ) 
    || ( IS_SET( ch->in_room->room_flags, ROOM_HOT ) ) )
	gain *= -2;

    if ( IS_SET( ch->in_room->affected_by, ROOM_BV_HEAL_REGEN ) )
      if ( gain < 0 )
        gain *= -2;
      else
        gain *= 2;


    if ( IS_AFFECTED( ch, AFF_CLOAK_REGEN ) )
      if ( gain < 0 )
        gain *= -2;
      else
        gain *= 2;


    if ( IS_SET( ch->in_room->affected_by, ROOM_BV_HEAL_STEAL ) )
      if ( gain > 0 )
        gain *= -1;
    if ( !IS_NPC( ch ) && ( gain > 0 ) )
    {
      if ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_FAST_HEAL ) )
       gain = gain * 1.5;
      else if ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_SLOW_HEAL ) )  
       gain = gain * .75;
    }
    if ( !IS_NPC( ch ) && ( gain > 0 ) )
    {
      if (  ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_WOODLAND )  )
         && ( ch->in_room != NULL )  )
      {
        if (  ( ch->in_room->sector_type == SECT_FIELD )
           || ( ch->in_room->sector_type == SECT_FOREST )  )
          gain = gain * 1.3;
        else if (  ( ch->in_room->sector_type == SECT_CITY )
                || ( ch->in_room->sector_type == SECT_INSIDE )  )
          gain = gain * .8;
      }
      else if (  ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_DARKNESS )  )
              && ( ch->in_room != NULL )  )
      {
        if (  ( ch->in_room->sector_type == SECT_FIELD )
           || ( ch->in_room->sector_type == SECT_HILLS )  
           || ( ch->in_room->sector_type == SECT_AIR )
           || ( ch->in_room->sector_type == SECT_DESERT ) )
          gain = gain * .8;
        else if (  ( ch->in_room->sector_type == SECT_CITY )
                || ( ch->in_room->sector_type == SECT_INSIDE )  )
          gain = gain * 1.3;
      }

    }
   
   return UMIN(gain, ch->max_hit - ch->hit);
}



int mana_gain( CHAR_DATA *ch )
{
    int gain;

    if ( IS_NPC(ch) && !IS_SET( ch->act, ACT_INTELLIGENT )   )
    {
	gain = ( 1 + ch->level / 30 );
    }
    else
    {
	gain = ( 5 + ch->level / 20 );

	if ( IS_SET( ch->in_room->room_flags, ROOM_REGEN ) )
	   gain *= 2; 

	switch ( ch->position )
	{
	case POS_SLEEPING: gain += get_curr_int(ch);        break;
	case POS_RESTING:  gain += get_curr_int(ch)/2;      break;
	}
      if ( !IS_NPC( ch )  )
      {
	  if ( ch->pcdata->condition[COND_FULL]   == 0 )
	    gain /= 2;

	  if ( ch->pcdata->condition[COND_THIRST] == 0 )
	    gain /= 2;
          if ( IS_VAMP( ch ) && ch->pcdata->bloodlust < 3 )
            gain = 0;
        else if ( IS_VAMP( ch ) && ch->pcdata->bloodlust < 8 )
           gain /= 2;
        if ( IS_VAMP( ch ) && ch->pcdata->bloodlust == -10 )
           gain = ( 5 + ch->level/25 );

        if ( IS_WOLF( ch ) && IS_RAGED( ch ) )
         gain = 0;
      }

      if ( IS_SET( ch->in_room->affected_by, ROOM_BV_MANA_REGEN ) )
      if ( gain < 0 )
        gain *= -2;
      else
        gain *= 2;
    if ( IS_SET( ch->in_room->affected_by, ROOM_BV_MANA_STEAL ) )
      if ( gain > 0 )
        gain *= -1;

    }

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

    if ( !IS_NPC( ch ) && ( gain > 0 ) )
    {
      if ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_NO_MAGIC ) )
       gain = gain * .5;
      else if ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_WEAK_MAGIC ) )  
       gain = gain * .75;
      else if ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_STRONG_MAGIC ) )  
       gain = gain * 1.25;

    }
    if ( !IS_NPC( ch ) && ( gain > 0 ) )
    {
      if (  ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_WOODLAND )  )
         && ( ch->in_room != NULL )  )
      {
        if (  ( ch->in_room->sector_type == SECT_FIELD )
           || ( ch->in_room->sector_type == SECT_FOREST )  )
          gain = gain * 1.3;
        else if (  ( ch->in_room->sector_type == SECT_CITY )
                || ( ch->in_room->sector_type == SECT_INSIDE )  )
          gain = gain * .8;
      }
      else if (  ( IS_SET( race_table[ch->race].race_flags, RACE_MOD_DARKNESS )  )
              && ( ch->in_room != NULL )  )
      {
        if (  ( ch->in_room->sector_type == SECT_FIELD )
           || ( ch->in_room->sector_type == SECT_HILLS )  
           || ( ch->in_room->sector_type == SECT_AIR )
           || ( ch->in_room->sector_type == SECT_DESERT ) )
          gain = gain * .8;
        else if (  ( ch->in_room->sector_type == SECT_CITY )
                || ( ch->in_room->sector_type == SECT_INSIDE )  )
          gain = gain * 1.3;
      }
    }
    if ( gain > 0 )
      gain = gain *  int_app[get_curr_int(ch)].mana_regen / 10;
    
    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 = ( 10 + ch->level/4 );

	if ( IS_SET( ch->in_room->room_flags, ROOM_REGEN ) )
	   gain *= 2; 

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

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

	if ( ch->pcdata->condition[COND_THIRST] == 0 )
	    gain /= 2;
	
	if ( IS_VAMP( ch ) && ch->pcdata->bloodlust < 3 )
           gain = 0;
        else if ( IS_VAMP( ch ) && ch->pcdata->bloodlust < 8 )
           gain /= 2;
      if ( IS_VAMP( ch ) && ch->pcdata->bloodlust == -10 )
           gain = ( 5 + ch->level/25 );


    }

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

    return UMIN(gain, ch->max_move - ch->move);
}

void gain_rage( CHAR_DATA *ch )
{

  sh_int  rage_gain = 0;
  sh_int  current_rage = 0;

  if ( IS_NPC( ch ) || !IS_WOLF( ch ) )
    return;


  if ( IS_RAGED( ch ) )
    current_rage = ch->pcdata->bloodlust_max;
  else if ( IS_SHIFTED( ch ) )
    current_rage = UMAX( 1, ( ch->pcdata->bloodlust_max / 5 ) );
  else
  current_rage = UMAX( 1, ( ch->pcdata->bloodlust_max / 10 )  );

  rage_gain = number_range( 1, ( MAX_WOLF_LEVEL/2 - ch->pcdata->generation ) );

  if ( ch->pcdata->bloodlust >= current_rage )
    ch->pcdata->bloodlust = UMIN( ( ch->pcdata->bloodlust + rage_gain ), current_rage );
  else
    ch->pcdata->bloodlust = UMIN( current_rage, ( ch->pcdata->bloodlust + rage_gain ) );

}

void gain_bloodlust( CHAR_DATA *ch, int value )
{
   /* Kinda like gain_condition, but handles vampires -S- */
   
   int condition;
   
   if ( value == 0 )
      return;
      
   condition             = ch->pcdata->bloodlust;

	/* in case vamp bites off more than he can chew ;)
		-Damane-	4/26/96 */
   
    if ( ( ch->pcdata->bloodlust + value ) > ch->pcdata->bloodlust_max )
	ch->pcdata->bloodlust = ch->pcdata->bloodlust_max;
    else
   	ch->pcdata->bloodlust += value;
   
    if ( ch->pcdata->bloodlust > ch->pcdata->bloodlust_max )
      ch->pcdata->bloodlust = ch->pcdata->bloodlust_max;

    if ( ch->position == POS_BUILDING || ch->position == POS_WRITING )
       return;
    if ( ( ch->pcdata->bloodlust < 0 ) && (ch->pcdata->bloodlust != -10 ) )
      ch->pcdata->bloodlust = 0;
         if ( ch->pcdata->bloodlust < 2 )
       send_to_char( "Your body burns with the need for blood!\n\r", ch );
    else if ( ch->pcdata->bloodlust < 7 )
       send_to_char( "You start to feel weaker... more blood needed!\n\r", ch );
    else if ( ch->pcdata->bloodlust < 10 ) 
       send_to_char( "You find yourself missing the taste of blood.\n\r", ch );     
    return;
}

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->position == POS_BUILDING || ch->position == POS_WRITING )
       return;
       
    
    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;
    CHAR_DATA *target;
    EXIT_DATA *pexit;
    int door;

    /* Examine all mobs. */
    CREF( ch_next, CHAR_NEXT );

    for ( ch = first_char; ch != NULL; ch = ch_next )
    {
	ch_next = ch->next;

	if ( !IS_NPC(ch) || ch->in_room == NULL || IS_AFFECTED(ch, AFF_CHARM) )
	    continue;

	/* Examine call for special procedure */
	if ( ch->spec_fun != 0 )
	{
	    if ( (*ch->spec_fun) ( ch ) )
		continue;
	}
       
	
	/* DISABLED */
	/* Intelligent mob? */
/*	if ( IS_SET( ch->act, ACT_INTELLIGENT ) )
	   int_handler( ch ); Disabled for now, for bugs.  */	
	
	/* That's all for sleeping / busy monster */
	if ( ch->position < POS_STANDING )
	    continue;

	/* Check for rewield, and re-equip (specials not used anymore) */
	if ( IS_SET( ch->act, ACT_REWIELD ) )
	   if ( check_rewield( ch ) )
	      continue;
	
	if ( IS_SET( ch->act, ACT_RE_EQUIP ) )
	   if ( check_re_equip( ch ) )
	      continue;

	
	/* Check for remember victims */
	if ( ch->target != NULL
	&& ( target = get_char_room( ch, ch->target ) ) != NULL )
	{
	   remember_attack( ch, target );
	   continue;
	}
	
	
	/* Check to see if mob is moving somewhere */
/*	if ( mob_hunt(ch) )
	  continue;
	if ( IS_SET( ch->act_hunt, ACT_HUNT_MOVE ) 
	&& ch->move_to != NO_VNUM )
	{
	   hunt_move( ch );
	   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->first_content != NULL
	&&   number_bits( 2 ) == 0 )
	{
	    OBJ_DATA *obj;
	    OBJ_DATA *obj_best;
	    int max;

	    max         = 1;
	    obj_best    = 0;
	    for ( obj = ch->in_room->first_content; obj; obj = obj->next_in_room )
	    {
		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)
	&&   ch->leader == NULL
	&& ( 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->first_person;
		  rch != NULL;
		  rch  = rch->next_in_room )
	    {
		if ( !IS_NPC(rch) )
		{
		    found = TRUE;
		    break;
		}
	    }
	    if ( !found )
		move_char( ch, door );
	}

    }
    CUREF( ch_next );
    return;
}



/*
 * Update the weather.
 */
void weather_update( void )
{
    char buf[MAX_STRING_LENGTH];
    char buf2[MSL];
    DESCRIPTOR_DATA *d;
    int diff;
    sh_int x,y;

    buf[0] = '\0';
    buf2[0] = '\0';

    switch ( ++time_info.hour )
    {
    case  5:
	weather_info.sunlight = SUN_LIGHT;
	strcat( buf, "The sky shows signs of daybreak.\n\r" );
	break;

    case  6:
	weather_info.sunlight = SUN_RISE;
	strcat( buf, "The sun rises in the east.\n\r" );
      for (x = 1; x < MAX_CLAN; x++ )
        for (y = 1; y < MAX_CLAN; y++ )
          politics_data.daily_negotiate_table[x][y] = FALSE;

	break;
    case 12:
    
      for (x = 1; x < MAX_CLAN; x++ )
        for (y = 1; y < MAX_CLAN; y++ )
          politics_data.daily_negotiate_table[x][y] = FALSE;
      break;


    case 19:
	weather_info.sunlight = SUN_SET;
	strcat( buf, "The sun slowly disappears in the west.\n\r" );
        for (x = 1; x < MAX_CLAN; x++ )
          for (y = 1; y < MAX_CLAN; y++ )
            politics_data.daily_negotiate_table[x][y] = FALSE;
	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++;
        for (x = 1; x < MAX_CLAN; x++ )
          for (y = 1; y < MAX_CLAN; y++ )
            politics_data.daily_negotiate_table[x][y] = FALSE;
	break;
    }
    switch ( time_info.moon++ )
    {
    case 5 :
        weather_info.moon_loc = MOON_RISE;
        sprintf( buf2, "@@NA %s @@yMoon @@Nhas risen.\n\r", get_moon_phase_name( ) );
        safe_strcat( MSL, buf, buf2);
        break;
    case 10 :
        weather_info.moon_loc = MOON_LOW;
        sprintf( buf2, "@@NThe %s @@yMoon @@Nrides low on the horizon.\n\r", get_moon_phase_name( ) );
        safe_strcat( MSL, buf, buf2);
        break;
    case 15 :
        weather_info.moon_loc = MOON_PEAK;
        sprintf( buf2, "@@NThe %s @@yMoon @@Nreaches it's zenith.\n\r", get_moon_phase_name( ) );
        safe_strcat( MSL, buf, buf2);
        break;
     case 20 :
        weather_info.moon_loc = MOON_FALL;
        sprintf( buf2, "@@NThe %s @@yMoon @@Nfalls.\n\r", get_moon_phase_name( ) );
        safe_strcat( MSL, buf, buf2);
        break;
     case 25 :
        weather_info.moon_loc = MOON_SET;
        sprintf( buf2, "@@NThe %s @@yMoon @@Nis setting.\n\r", get_moon_phase_name( ) );
        safe_strcat( MSL, buf, buf2);
        break;
     case 30 :
        weather_info.moon_loc = MOON_DOWN;
        sprintf( buf2, "@@NThe %s @@yMoon @@Nhas left the sky.\n\r", get_moon_phase_name( ) );
        safe_strcat( MSL, buf, buf2);
        break;

     default :
        break;
    }
 
    if ( time_info.moon >= 50 )
    {
      time_info.moon = 0;
      weather_info.moon_loc = MOON_DOWN;
    }

    if ( time_info.day   >= 20 ) /* now 20 days = 1 month */
    {
	time_info.day = 0;
	time_info.month++;
    }

    if ( time_info.month >= 8 ) /* 8 months a year */
    {
	time_info.month = 0;
	time_info.year++;
    }
    if (  ( ( time_info.day )  % 4 ) == 0 ) 
    {
      if ( !weather_info.phase_changed )
      weather_info.moon_phase++;
      if ( weather_info.moon_phase > MOON_WAN_CRE )
        weather_info.moon_phase = MOON_NEW;
      weather_info.phase_changed = TRUE;
    }
    else
      weather_info.phase_changed = FALSE;

    /*
     * 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 = first_desc; d != NULL; d = d->next )
	{
	    if ( d->connected == CON_PLAYING
	    &&   IS_OUTSIDE(d->character)
	    &&  (d->character->position != POS_WRITING)
	    &&   IS_AWAKE(d->character) )
		send_to_char( buf, d->character );
	}
    }

    return;
}



/* New update loop to handle gains for players => smaller 'ticks' for
   hp/mana/move gain, normal 'ticks' for objects, affects, weather, etc */
void gain_update( void )
{
   CHAR_DATA *ch;
   
/* send wholist to web page :) Zen  !-!-! ONLY RUN THIS ON ONE PORT OF YOUR SERVER !-! */
#if defined(SOE) && !defined(SOETEST) && !defined(SOEBLD)
    list_who_to_output();   
#endif
/* Update super_councils info  */

   {
     
     MEMBER_DATA * imember;
     sh_int   count = 0;
     sh_int  council_index;
    
     for ( council_index = 1; council_index < MAX_SUPER; council_index++ )
     {
       if ( super_councils[council_index].council_time > 0 )
       {
         super_councils[council_index].council_time--;
       
       
         if ( super_councils[council_index].council_time == 1 )
         {
           MEMBER_DATA * imember_next;
           for ( imember = super_councils[council_index].first_member; imember != NULL; imember = imember_next )
           {
             imember_next = imember->next;
             send_to_char( "The current council is disbanded.\n\r", imember->this_member );
             UNLINK( imember, super_councils[council_index].first_member, super_councils[council_index].last_member, next, prev );
             imember->this_member = NULL;
             imember->next = NULL;
             imember->prev = NULL;
             PUT_FREE( imember, member_free );
           }
           super_councils[council_index].council_time = 0;
           super_councils[council_index].quorum = FALSE;
         }
       }
       if ( !super_councils[council_index].quorum )
       {
         super_councils[council_index].quorum = FALSE;
         for ( imember = super_councils[council_index].first_member; imember != NULL; imember = imember->next )
         {
           count++;
         }
         if ( count >= QUORUM_NUMBER )
         {
           super_councils[council_index].quorum = TRUE;
           super_councils[council_index].council_time = 10;
           for( imember = super_councils[council_index].first_member; imember != NULL; imember = imember->next )
             send_to_char( "The Council is in Session!\n\r", imember->this_member );
         }
       }
     }
   }






   for ( ch = first_char; ch != NULL; ch = ch->next )
   {

      if ( ch->position >= POS_STUNNED && !IS_SET(ch->affected_by,AFF_VAMP_HEALING) )
      {
	 if (  ( ch->hit  < ch->max_hit )
          && ( !IS_SET( ch->in_room->affected_by, ROOM_BV_NONE ) )   )
	    ch->hit  += hit_gain(ch);
          ch->hit = UMAX( 25, ch->hit );

	 if ( ( ch->mana < ch->max_mana )
          && ( !IS_SET( ch->in_room->affected_by, ROOM_BV_NONE ) )   )
	    ch->mana += mana_gain(ch);

	 if (  ( ch->move < ch->max_move )
            && ( ch->carry_weight < can_carry_w( ch ) ) )
 	    ch->move += move_gain(ch);
         else
         if ( ch->carry_weight >= can_carry_w( ch ) )
         {
           send_to_char( "You are carrying so much wieght that you are @@eEXHAUSTED@@N!!\n\r", ch );
           ch->move = 0;
         }

      }

      if ( ch->position == POS_STUNNED || IS_SET(ch->affected_by, AFF_VAMP_HEALING) )
	 update_pos( ch );
   }
   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;

    CREF( ch_next, CHAR_NEXT );
    for ( ch = first_char; ch != NULL; ch = ch_next )
    {
	AFFECT_DATA *paf;
	AFFECT_DATA *paf_next;

     if ( !IS_NPC( ch ) && IS_WOLF( ch ) )
      gain_rage( ch );


	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 ( ( !IS_NPC( ch ) )
          && ( ch->pcdata->has_exp_fix == 0 ) 
          && ( ch->level > 1 )  )
      {
        send_to_char( "@@eWE HAVE CHANGED THE EXP SYSTEM. YOUR EXP HAS BEEN RESET TO 0.@@N\n\r", ch );
        send_to_char( "@@ePLEASE CONACT AN IMMORTAL, AND YOU WILL BE ADVANCED ONE CLASS, UNLESS YOU ARE A NEW CHARACTER.@@N\n\r", ch );
        send_to_char( "@@mTHANK YOU!!!!@@N  NOTE: DO NOT ATTEMPT TO ABUSE THIS--WE ARE KEEPING TRACK!\n\r", ch );
        ch->pcdata->has_exp_fix = 1;
        ch->exp = 0;
      }
      else if ( !IS_NPC( ch ) )
       ch->pcdata->has_exp_fix = 1;

	if (  ( IS_NPC( ch ) )
         && ( ch->hit < -15 ) )
        raw_kill( ch, "" );

	if ( ch->sitting != NULL && ch->sitting->in_room != ch->in_room )
	{
	   ch->sitting->value[1] --;
	   ch->sitting = NULL;
	}
	
	if ( ch->position >= POS_STUNNED )
	{
	/* -S- mod. */
	   if ( ch->position != POS_WRITING && ch->position != POS_BUILDING )
	   {
		if ( IS_SET( ch->in_room->room_flags, ROOM_HOT ) )
		   send_to_char( "You feel your skin burning.\n\r", ch );
		else if ( IS_SET( ch->in_room->room_flags, ROOM_COLD ) )
		   send_to_char( "You feel your skin freezing.\n\r", ch );
	   }

	
	}
	
  /*    if ( ch->stunTimer > 0 )
      {
        ch->position = POS_STUNNED;
         ch->stunTimer -= 1;
      }
      else
      {
        ch->stunTimer = 0;
          ch->position = POS_STANDING;
        
      }  */


	    
	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;
	    
	/* Move this inside the if loop below to stop imms getting bloodlust */
            if ( ( IS_VAMP(ch) ) && ( !IS_NPC(ch) ) )
            {
               gain_bloodlust( ch, 0-number_range(1,2));
               check_vamp( ch );
            }	       
	       
          gain_condition( ch, COND_THIRST,0 - number_range(1,2) );
          if ( ch->pcdata->condition[COND_THIRST] <= 10 )
            ch->pcdata->condition[COND_THIRST] = 10;
	    gain_condition( ch, COND_DRUNK, 0 - number_range(1,2) );
          if ( !IS_VAMP( ch ) )
          {
	    gain_condition( ch, COND_FULL,  0 - number_range(1,2) );
	    
          }
	}

	for ( paf = ch->first_affect; paf != NULL; paf = paf_next )
	{
	    paf_next    = paf->next;
	    if ( paf->duration > 0 )
	    {
		paf->duration--;
	    
	        /* We need a check here for spells that keep working... */
	        if ( paf->type == skill_lookup( "blood leach" ) )
	        {
                   if ( paf->caster != NULL && !IS_NPC(paf->caster))
                   {
                      send_to_char( "You feel the blood leach sapping your strength.\n\r", ch );
                      act( "You feel a surge of blood, coming from your blood leach on $N.",
                         paf->caster, NULL, ch, TO_CHAR );
                      paf->caster->pcdata->bloodlust += ( 10 - paf->caster->pcdata->generation );
                      if ( paf->caster->pcdata->bloodlust > paf->caster->pcdata->bloodlust_max )
                       paf->caster->pcdata->bloodlust = paf->caster->pcdata->bloodlust_max;
                      damage( ch, ch, paf->caster->pcdata->vamp_level * 20, TYPE_UNDEFINED );
                   }	           
	        } 
              if ( paf->type == skill_lookup( "black hand" ) )
	        {
                   if ( paf->caster != NULL && !IS_NPC(paf->caster))
                   {
                      send_to_char( "You feel the Black Hand choking you.\n\r", ch );
                      ch->hit -= paf->modifier;
                   }	           
	        }    
              if (  ( paf->type == skill_lookup( "adrenaline bonus" ) )
                 && ( ch->fighting == NULL )
                 && ( ch->hit > 10 )   )
              {
                ch->hit = UMAX( 10, ( ch->hit - ( paf->duration * 30 ) ) );
                ch->move = UMAX( 10, ( ch->move - ( paf->duration * 80 ) ) );
                send_to_char( "@@NYou feel the affects of your @@eadrenaline rush@@N wear off, leaving you exhausted.\n\r", ch );
                affect_remove( ch, paf );
              }
	    } 
	    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 );
		    }
		    if ( paf->type > 0 && skill_table[paf->type].room_off )
		       act( skill_table[paf->type].room_off, ch, NULL, NULL, TO_ROOM );
		}
	  
		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_NPC( ch ) )
        {
	  if (ch->target != NULL && number_bits(4)==0)
	  {
	    free_string(ch->target);
	    ch->target=NULL;
	  }
          if ( ch->extract_timer > 0 )
          {
            ch->extract_timer--;
          }
          else if ( ch->extract_timer == 0 )
          {
/*            if ( IS_SET( ch->affected_by, AFF_CHARM ) )
            {  */
              if (  ( ch->master == NULL )
                 || ( ch->master->in_room == NULL )
                 || ( ch->in_room != ch->master->in_room )   )
              {
                if ( ch->in_room != NULL )
                {
                  do_say( ch, "Whaa?? Where am I? How did I get here?" );
                  do_say( ch, "AHHH!!! Help me!!!! I'm MELTING......" );
                }
                extract_char( ch, TRUE );
                continue;
              }
              else
              {
                if ( number_range(0, 99 ) < get_psuedo_level( ch->master ) -25 )
                {
                  CHAR_DATA * this_master;
                  this_master = ch->master;
                  do_say( ch, "Whaa?? Where am I? How did I get here?" );
                  do_scan( ch, "" );
                  check_social( ch, "growl", ch->master->name );
                  do_say( ch, "How dare you order me around!!!" );
                  stop_follower( ch );
                  multi_hit( ch, this_master, TYPE_UNDEFINED );
                  continue;
                }
              }
         /* }  */
          }
        }

	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, number_range(2, 8), gsn_poison );
	}
	else if ( ch->position == POS_INCAP && !IS_VAMP(ch) )
	{
	    damage( ch, ch, number_range(1, 4), TYPE_UNDEFINED );
	}
	else if ( ch->position == POS_MORTAL && !IS_VAMP(ch) )
	{
	    damage( ch, ch, number_range(2, 3), TYPE_UNDEFINED );
	}
	else if ( ch->position == POS_DEAD && !IS_VAMP(ch) )
	{
	    damage( ch, ch, number_range(5, 10), TYPE_UNDEFINED );
	}
	else if ( ch->hit < -10 && !IS_VAMP(ch) )
	{
	    damage( ch, ch, number_range(5, 10), TYPE_UNDEFINED );
	}
  
    }
    CUREF( ch_next );

    /*
     * Autosave and autoquit.
     * Check that these chars still exist.
     */
    if ( ch_save != NULL || ch_quit != NULL )
    {
        CREF( ch_next, CHAR_NEXT );

	for ( ch = first_char; ch != NULL; ch = ch_next )
	{
	    ch_next = ch->next;
	    if ( ch == ch_save )
		save_char_obj( ch );
	    if ( ch == ch_quit )
	    {
		send_to_char( "Idle for too long.  Bye Bye!\n\r", ch );
		do_quit( ch, "" );
	    }
	}
        CUREF( ch_next );

    }

    return;
}

void check_vamp( CHAR_DATA *ch )
{
   /* If vampire is outside, then (s)he suffers damage */

   if ( is_affected( ch, skill_lookup( "cloak:darkness" ) ) )
     return;

   if (   IS_OUTSIDE( ch ) 
        && !IS_SET( ch->in_room->affected_by, ROOM_BV_SHADE )
        && time_info.hour > 5
        && time_info.hour < 19 )
   {
      /* Oh dear */
      int dam;
      
      switch ( weather_info.sky )
      {
         case SKY_CLOUDLESS:
            dam = 4; break;
         case SKY_CLOUDY:
            dam = 3; break;
         case SKY_RAINING:
            dam = 3; break;
         default:
            dam = 1; break;
      }
      /* Take bloodlust into account when calculating dam! */
      
      dam*= 40 - ch->pcdata->vamp_level;
        
      /* So dam ranges from 2 (lightning;no bloodlust)
       * to 200 (sunny;complete bloodlust)
       * And that's each tick!
       */
	           
       act( "$n's skin burns with it's contact with daylight!", ch, NULL, NULL, TO_ROOM );
       send_to_char( "Your skin burns with it's contact with daylight!", ch );
       if (ch->pcdata->bloodlust <= -5 )
         return;
       damage( ch, ch, dam, -1 );
    }
    return;
 }  



/* Check for objfuns.... this is probably performance sensitive too. */
void objfun_update( void )
{
   OBJ_DATA *obj;
   
   for ( obj = first_obj; obj != NULL; obj = obj->next )
      if ( obj->obj_fun != NULL )
      {
        if ( obj->carried_by != NULL ) 
        {
          if (  !IS_NPC( obj->carried_by ) && IS_WOLF( obj->carried_by )
             && ( IS_SHIFTED( obj->carried_by ) || IS_RAGED( obj->carried_by )  )  )
          {
            continue;
          }
        }
        (*obj->obj_fun) (obj, obj->carried_by );
      }
	 
   return;
}


/*
 * Update all objs.
 * This function is performance sensitive.
 */
void obj_update( void )
{   
  
    OBJ_DATA *marker;
    OBJ_DATA *obj;
    extern OBJ_DATA * auction_item;

    /*
     * Create dummy object and add to end of list.  This object is
     * only a marker, and will not actually be processed by this
     * routine.
     */
    GET_FREE(marker, obj_free);
    LINK(marker, first_obj, last_obj, next, prev);

    /*
     * Repeatedly remove object from front of list, add to tail, and process
     * until the marker is at the head of the list.  That will indicate all
     * objects have been processed.
     */
    disable_timer_abort = FALSE;
    while ((obj = first_obj) != marker) {

	CHAR_DATA *rch;
	char *message;

      UNLINK(obj, first_obj, last_obj, next, prev);
      LINK(obj, first_obj, last_obj, next, prev);

      if  ( obj == auction_item )
        continue;
     if ( IS_SET( obj->item_apply, ITEM_APPLY_HEATED ) 
         && number_range( 0, 100 ) < 25   )
      {
        REMOVE_BIT( obj->item_apply, ITEM_APPLY_HEATED );
	  if ( obj->carried_by != NULL )
	  {
	    act( "Your $p @@acools off@@N!!", obj->carried_by, obj, NULL, TO_CHAR );
	  }
	  else 
        if ( obj->in_room != NULL
	       &&      ( rch = obj->in_room->first_person ) != NULL )
	  {
	    act( "$p @@acools off@@N!!", rch, obj, NULL, TO_ROOM );
	    act( "$p @@acools off@@N!!", rch, obj, NULL, TO_CHAR );
	  }

      }


	if ( obj->timer <= 0 || --obj->timer > 0 )
	    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 vapourises, and goes to heaven."; break;
	case ITEM_PORTAL:     message = "$p implodes suddenly."; 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->first_person ) != NULL )
	{
	    act( message, rch, obj, NULL, TO_ROOM );
	    act( message, rch, obj, NULL, TO_CHAR );
	}
/*
      if ( obj->in_room == NULL )
        continue; 
      if ( obj->item_type == ITEM_CORPSE_NPC )
        continue;
*/
	extract_obj( obj );  
    }

    /*
     * All objects have been processed.  Remove the marker object and
     * put it back on the free list.
     */
    UNLINK(marker, first_obj, last_obj, next, prev);
    PUT_FREE(marker, obj_free);

    disable_timer_abort = FALSE;
    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 )
{
    
    /* Check to see if ch has encountered a mob with ACT_REMEMBER set,
     * and with victim->target == ch->name...    tbc ;)
     * -- Stephen
     */
    
    CHAR_DATA *wch;
    CHAR_DATA *wch_next;
    CHAR_DATA *ch;
    CHAR_DATA *ch_next;
    CHAR_DATA *vch;
    CHAR_DATA *vch_next;
    CHAR_DATA *victim;
     OBJ_DATA *wield;

    CREF( wch_next, CHAR_NEXT );

    for ( wch = first_char; wch != NULL; wch = wch_next )
    {
	wch_next = wch->next;

	if ( IS_NPC(wch) && wch->mpactnum > 0 &&
	     wch->in_room->area->nplayer > 0 )
	{
	  MPROG_ACT_LIST *mpact;
	  
	  while ( (mpact = wch->first_mpact) != NULL )
	  {
	    mprog_wordlist_check( mpact->buf, wch, mpact->ch, mpact->obj,
	                          mpact->vo, ACT_PROG );
	    /* Lets hope this check works until something better is in place.
	     * -- Alty */
	    if ( wch->hit < -10 )
	      break;
	    wch->first_mpact = mpact->next;
	    free_string(mpact->buf);
	    PUT_FREE(mpact, mpact_free);
	  }
	  if ( wch->hit < -10 )
	    continue;
	  wch->mpactnum = 0;
	  wch->first_mpact = NULL;
	  wch->last_mpact = NULL;
	}
       
	if ( (IS_NPC( wch ) )
	||   wch->level >= LEVEL_IMMORTAL
	||   wch->in_room == NULL )
	    continue;
        CREF( ch_next, CHAR_NEXTROOM );
	for ( ch = wch->in_room->first_person; 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
	    ||   ch->hunting != NULL
	    ||   IS_AFFECTED(ch, AFF_CHARM)
	    ||   !IS_AWAKE(ch)
	    ||   ( IS_SET(ch->act, ACT_WIMPY) && IS_AWAKE(wch) )
	    ||   !can_see( ch, wch ) )
		continue;

	    
	   if (  ( IS_AFFECTED( wch, AFF_SNEAK )  || item_has_apply( wch, ITEM_APPLY_SNEAK ) )
	      && ( number_percent() < 50 + ( 2 * ( get_psuedo_level( wch ) - get_psuedo_level( ch ) ) )   )  )
	      continue;
	    /*
	     * Ok we have a 'wch' player character and a 'ch' npc aggressor.
	     * MAG - wch can be an intelligent NPC.
	     * Now make the aggressor fight a RANDOM pc victim in the room,
	     *   giving each 'vch' an equal chance of selection.
	     */
	    count       = 0;
	    victim      = NULL;
            CREF( vch_next, CHAR_NEXTROOM );
	    for ( vch = wch->in_room->first_person; vch != NULL; vch = vch_next )
	    {
		vch_next = vch->next_in_room;

		if ( (!IS_NPC(vch) || IS_SET(vch->act,ACT_INTELLIGENT))
		&&   vch->level < LEVEL_IMMORTAL
		&&   ( !IS_SET(ch->act, ACT_WIMPY) || !IS_AWAKE(vch) )
		&&   can_see( ch, vch )
            &&   ( !( IS_UNDEAD( ch ) && IS_VAMP( vch ) )  )  )
		{
		    if ( number_range( 0, count ) == 0 )
			victim = vch;
		    count++;
		}
	    }
            CUREF( vch_next );
	    if ( victim == NULL )
	    {
	/*	bug( "Aggr_update: null victim.", count );    */
		continue;
	    }
	    if ( IS_SET( victim->in_room->room_flags, ROOM_SAFE ) )
            continue;

	    act( "$n growls at $N!", victim, NULL, ch, TO_NOTVICT );
	    act( "$N growls at you!  Uh-oh!!", victim, NULL, ch, TO_CHAR );
	    act( "You growl at $N.  Get $M!!", ch, NULL, victim, TO_CHAR );
	    
	    wield = get_eq_char( ch, WEAR_WIELD );
	    if ( wield != NULL
	      && wield->item_type == ITEM_WEAPON
	      && wield->value[3] == 11 
              && victim->fighting == NULL )
	       do_backstab( ch, victim->name );
	    else
	       multi_hit( ch, victim, TYPE_UNDEFINED );
	}
        CUREF( ch_next );
    }
    CUREF( wch_next );
    return;
}

/*
 * Check ALL rooms for affects... the ratio of affects to rooms should
 * be relatively low, so this shouldn't hit performance too much.
 * -S-
 */

void rooms_update( void )
{
     ROOM_INDEX_DATA *room;
     AREA_DATA *area;
     BUILD_DATA_LIST *thing;
     ROOM_AFFECT_DATA *raf;
     ROOM_AFFECT_DATA *raf_next;
     MARK_LIST_MEMBER	*this_mark;
     MARK_LIST_MEMBER	*next_mark;

     
   for ( area = first_area; area != NULL; area = area->next )
   {
      for ( thing = area->first_area_room; thing != NULL; thing = thing->next )
      {
         room = thing->data;
           
 /*        if ( room->first_room_affect == NULL )
            continue;   */

         for ( this_mark = room->first_mark_list ; this_mark != NULL; this_mark = next_mark )
         {
           next_mark = this_mark->next;
            if ( this_mark->mark->duration > 0 )
               this_mark->mark->duration--;
            else if ( this_mark->mark->duration < 0 )
                ;
            else
            {
              mark_from_room( room->vnum, this_mark->mark );
            }
         }
      
          
           
         for ( raf = room->first_room_affect; raf != NULL; raf = raf_next )
         {
            raf_next = raf->next;
              
            if ( raf->duration > 0 )
               raf->duration--;
            else if ( raf->duration < 0 )
                ;
            else
            {
               if (    raf_next           == NULL    
                    || raf_next->type     != raf->type
                    || raf_next->duration > 0 )
               {
                  if ( raf->type > 0 && skill_table[raf->type].msg_off )
                  {
                     send_to_room( skill_table[raf->type].msg_off, room );
                     send_to_room( "\n\r", room );
                  }
               r_affect_remove( room, raf );
               }
            }
         }
      }
   }
   return;
} 


extern void build_save_flush(void);

/*
 * 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_message;
    static  int     objfun_check;
    static  int     pulse_area;
    static  int	    pulse_rooms;
    static  int     pulse_mobile;
    static  int     pulse_gain;
    static  int     pulse_violence;
    static  int     pulse_point;
    static  int     pulse_auction;
    extern  int     saving_area;
    extern  bool    auction_flop;

    if ( saving_area )
	build_save(); /* For incremental area saving */


    if ( --pulse_area     <= 0 )
    {
	pulse_area      = number_range( PULSE_AREA / 2, 3 * PULSE_AREA / 2 );
	area_update     ( );
	build_save_flush();
    }

    if ( --pulse_rooms   <= 0 )
    {
       pulse_rooms 	= PULSE_ROOMS;
       rooms_update();
    }
        
    if ( --pulse_message <= 0 )
    {
       pulse_message = PULSE_MESSAGE;
       message_update();
    }

    if ( auction_flop )
    {
       pulse_auction = PULSE_AUCTION;
       auction_flop = FALSE;
    }
    
    if ( --pulse_auction <= 0 )
    {
       pulse_auction = PULSE_AUCTION;
       auction_update();
    }
    
    if ( --objfun_check <= 0 )
    {
       objfun_check = PULSE_OBJFUN;
       objfun_update();
    }
    
    
    if ( --pulse_violence <= 0 )
    {
      alarm_update();
	pulse_violence  = PULSE_VIOLENCE;
	violence_update ( );
    }

    if ( --pulse_mobile   <= 0 )
    {
	pulse_mobile    = PULSE_MOBILE;
	mobile_update   ( );
    }

    if ( --pulse_gain    <= 0 )
    {
       gain_update();
       pulse_gain = PULSE_PER_SECOND * number_range(5,8);
    }
    
    if ( --pulse_point    <= 0 )
    {
	pulse_point     = PULSE_TICK;
	weather_update  ( );
	char_update     ( );
	obj_update      ( );
        quest_update    ( );
        
        /* This will log the number of perms being used...
         * fgrep the log file to get results...
         */
         
     /*   perm_update( ); */
    }

    aggr_update( );
    tail_chain( );
    return;
}

bool check_rewield( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    OBJ_DATA *weapon = NULL;
    int dam;
    int chance;
    bool pickup;
    char buf[MAX_STRING_LENGTH];
    
    
    pickup = TRUE;
    dam = 0;  
    
    chance = ( ch->fighting == NULL ? 35 : 60 );

    if ( number_percent() < chance )
    {
       for ( obj = ch->first_carry; obj != NULL; obj = obj->next_in_carry_list)
	  if ( obj->item_type == ITEM_WEAPON && dam < obj->value[2] )
	  {
	     dam = obj->value[2];  pickup = FALSE; weapon = obj;
	  }
       

       /* Then check inventory and room for any weapons */
       for ( obj = ch->in_room->first_content; obj != NULL; obj = obj->next_in_room )
       {
	  if ( obj->item_type == ITEM_WEAPON )
	  {
	     if ( obj->value[2] > dam )
	     { dam = obj->value[2]; weapon = obj; pickup = TRUE; }
	  }
       }
 
       
       
       if ( weapon == NULL )
	  return FALSE;
	  
       if ( weapon->wear_loc == WEAR_WIELD )
	  return FALSE;   
	  
       
       if ( pickup )
       {
	  sprintf( buf, "Great!  %s!  Just what i've always wanted!",
		   weapon->short_descr );       
	  do_say( ch, buf );
       }
       
       if ( weapon != NULL )
       {    
	  /* Now make the mob get the weapon */
	  if ( pickup )
	     get_obj( ch, weapon, NULL );
       
	  do_wear( ch, weapon->name );
	  
	  /* Check is mob wielded weapon ok... */
	  if ( weapon->wear_loc == WEAR_NONE && weapon != quest_object )
	  {
	     act( "$n sniffs sadly.  'Baah!  It's no good to me!'", 
		ch, NULL, NULL, TO_ROOM );
	     extract_obj( weapon );
	     act( "$n sacrifices $p.", ch, weapon, NULL, TO_ROOM );
	  }
	  return TRUE;
       }
	  
    } 

     
    
    return FALSE;
}

bool check_re_equip( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    OBJ_DATA *obj2;
    OBJ_DATA *armor = NULL;
    OBJ_DATA *light = NULL;
    int ac;
    int chance;
    bool pickup;
    bool ident;
    int best;
    char buf[MAX_STRING_LENGTH];
    int objnum;



    best = -1;
    pickup = TRUE;
    ac = 0;  
    
    chance = ( ch->fighting == NULL ? 35 : 60 );
    if ( number_percent() < chance )
    {
       /* Check each armor in room against ch's equipment */

       ident = FALSE;
       for ( obj = ch->in_room->first_content; obj != NULL; obj = obj->next_in_room )
       {
        if ( !can_see_obj(ch,obj) )
         continue;
         
        if ( (obj->wear_flags & ~ITEM_TAKE)==0 )
         continue;    /* Check to see if item cannot be worn */
        if ( obj->item_type == ITEM_PIECE )
          continue;
        if (obj->item_type == ITEM_ARMOR )
        {
           /* Check this object against our equiped objects */
           ident=FALSE;  
	   for ( obj2 = ch->first_carry; obj2 != NULL; obj2 = obj2->next_in_carry_list )
	   {
	    /* Only scan against worn objects. 
	     * If obj2 is being worn in a position that obj can be worn in,
	     * and obj2->value[0] is better, then choose it.
	     */
	     
	       if (      obj2->wear_loc != WEAR_NONE
		  &&     obj2->item_type == ITEM_ARMOR 
		  &&     can_wear_at(ch,obj,obj2->wear_loc)
		  &&     obj->value[0] > obj2->value[0] )
	       {
		  ident = TRUE;         /* identical wear_loc */
		  armor = obj; 
		  break;
	       }
	   }
	   
	   /* Found no match for locations, so get and wear. */
	   if ( !ident )
	   {
	      armor = obj;
	      break;
	   }
	}
	
	if ( obj->item_type == ITEM_LIGHT 
	   && ( get_eq_char( ch, WEAR_LIGHT ) == NULL ) )
	{
	      light = obj;
	      break;
	}
       }
      
      
       /* MAG Modification. Only check one item each time, against currently
          worn object. */
       
       /* Check one inv item against worn eq, incase we've picked up some nicer
          stuff */
       
       objnum=number_percent() * ch->carry_number / 100;
       for ( obj = ch->first_carry; obj != NULL && objnum > 0; obj = obj->next_in_carry_list)
        objnum--;
        
       
       if (   obj != NULL 
           && obj->wear_loc == WEAR_NONE
           && obj->item_type == ITEM_ARMOR)
       {
           ident=FALSE;
	   for ( obj2 = ch->first_carry; obj2 != NULL; obj2 = obj2->next_in_carry_list )
	   {
	       if (      obj2->wear_loc != WEAR_NONE
		  &&     can_wear_at(ch,obj,obj2->wear_loc)
		  &&     obj->value[0] > obj2->value[0] )
	       {
		  ident = TRUE;        
		  armor = obj; 
		  break;
	       }
	   }
	   if ( !ident )
	   {
	      armor = obj;
	   }
	}
	
	if ( obj != NULL
	  && obj->item_type == ITEM_LIGHT 
	  && ( get_eq_char( ch, WEAR_LIGHT ) == NULL ) )
	  {
	      light = obj;
	  }
     }
      
       
     if (armor != NULL)
     {
      if (armor->carried_by != ch)
      {
       /* Pick up off ground */
       if ( pickup )
       {
	  sprintf( buf, "Great!  %s!  Just what i've always wanted!",
		   armor->short_descr );       
	  do_say( ch, buf );
       }
       
	/* Now make the mob get the armor */
	if ( pickup )
	   get_obj( ch, armor, NULL );
      }

      do_wear( ch, armor->name );
	  
      /* Check is mob wielded weapon ok... */
      if ( armor->wear_loc == WEAR_NONE && armor != quest_object )
	 {
	     act( "$n sniffs sadly.  'Baah!  It's no good to me!'", 
		ch, NULL, NULL, TO_ROOM );
	     extract_obj( armor );
	     act( "$n sacrifices $p.", ch, armor, NULL, TO_ROOM );
	 }
	 return TRUE;
     }
     
    if (light != NULL)
    {
      if (light->carried_by != ch)
      {
       /* Pick up off ground */
       if ( pickup )
       {
	  sprintf( buf, "Great!  %s!  Just what i've always wanted!",
		   light->short_descr );       
	  do_say( ch, buf );
       }
       
	/* Now make the mob get the light */
	if ( pickup )
	   get_obj( ch, light, NULL );
      }

      do_wear( ch, light->name );
	  
      /* Check is mob wielded weapon ok... */
      if ( light->wear_loc == WEAR_NONE && light != quest_object )
	 {
	     act( "$n sniffs sadly.  'Baah!  It's no good to me!'", 
		ch, NULL, NULL, TO_ROOM );
	     extract_obj( light );
	     act( "$n sacrifices $p.", ch, light, NULL, TO_ROOM );
	 }
	 return TRUE;
     }
	  
    return FALSE;
 }
 
void auction_update( void )
 {
    extern OBJ_DATA     * auction_item;
    extern CHAR_DATA    * auction_owner;
    extern CHAR_DATA    * auction_bidder;
    extern int            auction_bid;
    extern int            auction_reserve;
    extern int            auction_stage;
    char                  buf[MAX_STRING_LENGTH];
    
      CHAR_DATA * ach;
      bool  good_seller =FALSE, good_buyer = FALSE;
       
    /* Stages: 0) No/New bid.  
	       1) Waiting.  (If no bid here, then give up next time)
	       2) Going once.  
	       3) Going Twice.  
	       4) GONE! 
     */
    
    if ( auction_item == NULL )
       return;
       
    switch ( auction_stage )
    {
       case 0:
	  if ( auction_bidder == NULL )
	  {
	     sprintf( buf, "@@N%s (level:%d, valued at %dGP) has been offered for auction.  A @@e10%% fee@@N will be charged, the higher of the reserve price or highest bid.",
		auction_item->short_descr, auction_item->level, auction_item->cost );
	  }
	  else
	  {
	     sprintf( buf, "%s has bid %d for %s.",  auction_bidder->name,
		auction_bid, auction_item->short_descr );
	  }
	  break;
       case 1:
	  if ( auction_bidder == NULL )
	     sprintf( buf, "Last chance to bid for %s.", auction_item->short_descr );
	  else
	     sprintf( buf, "Last bid for %s was %d.  Any more offers?",
		auction_item->short_descr, auction_bid );
	  break;
       case 2:
	  if ( auction_bidder == NULL )
	  {
	     auction( "No bidders.  Auction Ended." );

          for ( ach = first_char; ach != NULL; ach = ach->next )
            {
              if ( auction_owner == ach )
                good_seller = TRUE;
              if ( auction_bidder == ach )
                good_buyer = TRUE;
            }

           if( good_seller)
	     {
             auction_owner->gold = UMAX( 0, auction_owner->gold - abs(auction_reserve * .1 ) );

	       obj_to_char( auction_item, auction_owner );
           }
           else
           {
             auction( "Oh, well..guess they didn't want it anymore, since they LEFT!!  Well, it's mine now! " );
             extract_obj( auction_item );
           }
	     auction_item = NULL;
	     return;
	  }
	  sprintf( buf, "%s - Going Once!", auction_item->short_descr );
	  break;
       case 3:
	  sprintf( buf, "%s - Going TWICE!", auction_item->short_descr );
	  break;
       case 4:
	  if ( auction_bid < auction_reserve )
	  {
          for ( ach = first_char; ach != NULL; ach = ach->next )
            {
              if ( auction_owner == ach )
                good_seller = TRUE;
              if ( auction_bidder == ach )
                good_buyer = TRUE;
            }

	     sprintf( buf, "%s - CANCELLED.  Reserve price not matched.",
		auction_item->short_descr );
           if ( good_seller )
           {
             auction_owner->gold = UMAX( 0, auction_owner->gold - abs(auction_reserve * .1 ) );

	       obj_to_char( auction_item, auction_owner );
           }
           else 
             extract_obj ( auction_item );
           if ( good_buyer )
	       auction_bidder->gold += auction_bid;
           
	  }   
	  else
	  {

          for ( ach = first_char; ach != NULL; ach = ach->next )
            {
              if ( auction_owner == ach )
                good_seller = TRUE;
              if ( auction_bidder == ach )
                good_buyer = TRUE;
            }
           
	    if ( good_buyer )
          {
             sprintf( buf, "%s - SOLD! to %s.", auction_item->short_descr,
		     auction_bidder->name );

	       obj_to_char( auction_item, auction_bidder );
          }
          else
          {
             sprintf( buf, "%s - SOLD!, but the buyer has left us.  Oh Well!!!",
                auction_item->short_descr );
             extract_obj( auction_item );
          }
          if ( good_seller )
	         auction_owner->gold += ( auction_bid - (auction_bid * .1 )  );

	  }
	  
	  auction_stage = 0;
	  auction_bidder  = NULL;
	  auction_owner   = NULL;
	  auction_item    = NULL;
	  auction_reserve = 0;
	  auction_bid     = 0;
	  break;
    }
    auction( buf );
    auction_stage++;
    return;
 }
		    
void remember_attack( CHAR_DATA *ch, CHAR_DATA *victim )
{
   /* Called when an NPC ch encounters a PC victim, that tried to
    * kill it previously.
    * --Stephen
    */
    
    char        buf[MAX_STRING_LENGTH];
    
    /* Pick a random response for ch to give, before attacking */    
    
    switch( number_range( 0, 7 ) )
    {
    case 0:
       sprintf( buf, "%s returns!  I shall have my revenge at last!",
	  victim->name );
       do_yell( ch, buf );
       break;
    case 1:
       sprintf( buf, "%s You should never have returned.  Ye shall DIE!",
	  victim->name );
       do_whisper( ch, buf );
       break;
    case 2:
       act( "$n looks at $N, remembering $S attack",  ch, NULL, victim, TO_ROOM );
       act( "$n looks at you, remembering your attack", ch, NULL, victim, TO_VICT );
       act( "You look at $N, remembering $S attack.", ch, NULL, victim, TO_CHAR );
       do_say( ch, "I SHALL HAVE MY REVENGE!!!" );
       break;
    case 3:
       sprintf( buf, "%s has wronged me, and now I will seek my revenge!", victim->name );
       do_gossip( ch, buf );
       sprintf( buf, "Prepare to die, %s.", victim->name );
       do_say( ch, buf );
       break;
    case 4:
       sprintf( buf, "So, %s.  You have returned.  Let us finish our fight this time!",
	  victim->name );
       do_say( ch, buf );
       break;
    case 5:
       sprintf( buf, "Only cowards flee from me, %s!", victim->name );
       do_say( ch, buf);
       break;
    case 6:
       act( "$n looks at $N, and recognizes $M!!", ch, NULL, victim, TO_ROOM );
       act( "$n looks at you, and recognizes you!!", ch, NULL, victim, TO_VICT );
       act( "You look at $N, and recognize $M!", ch, NULL, victim, TO_CHAR );
       sprintf( buf, "There can only be one winner, %s.", victim->name );
       do_say( ch, buf );
       break;
    }
    
    /* Check if has intelligence, and call correct attack? */
    
    multi_hit( ch, victim, TYPE_UNDEFINED );
    /* spec- plug leak here */
    if (ch->target)
    {
	free_string(ch->target);
	ch->target = NULL;
    }
    return;
}    
    
void quest_update()
{
   extern bool auto_quest;
   extern bool quest;
   extern CHAR_DATA *quest_mob;
   extern OBJ_DATA *quest_object;
   extern int quest_timer;
   extern int quest_wait;
   
   if ( !quest && !auto_quest )
      return;
   
   if ( quest )
   {
      /* Make sure the mobile and obj still exist! */
      if ( quest_mob == NULL || quest_object == NULL )
      {
         quest_cancel();
         return;
      }
      
      quest_inform();
      if ( quest_timer > 15 )
         quest_cancel();
      return;
   }
   
   if (!quest)
   {
     if (quest_wait > 0)
     {
       quest_wait--;
       return;
     }

     if (auto_quest)
       generate_auto_quest();
   }
}