ack42/
ack42/npcs/a/
ack42/npcs/c/
ack42/npcs/e/
ack42/npcs/f/
ack42/npcs/h/
ack42/npcs/i/
ack42/npcs/l/
ack42/npcs/n/
ack42/npcs/o/
ack42/npcs/p/
ack42/npcs/r/
ack42/npcs/s/
ack42/npcs/w/
ack42/player/c/
ack42/player/s/
ack42/player/z/
/* Mob/player hunting.. */
/* Copied this from one of my old MUDs..  Much cleaner than the sillymud
   hunt routines (which i moved to oldhunt.c) -- Alty */

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "merc.h"

#define NEVER_FREE_HUNT

struct h_queue
{
  struct h_queue *next;
  ROOM_INDEX_DATA *room;
  short dir;
};

struct h_queue *h_head = NULL;
struct h_queue *h_tail = NULL;
extern char * const dir_name[];

#ifdef NEVER_FREE_HUNT
struct h_queue *h_free= NULL;
void setup_hunt(void);
#endif

#ifdef DEBUG_HUNT_CODE
static FILE *h_fp;
#endif

void h_enqueue( ROOM_INDEX_DATA *room, short dir )
{
  struct h_queue *hunt;
  
#ifdef NEVER_FREE_HUNT
  if ( h_free )
  {
    hunt = h_free;
    h_free = h_free->next;
  }
  else
#endif
    hunt = getmem(sizeof(*hunt));
  hunt->next = NULL;
  hunt->room = room;
  hunt->dir = dir;
  SET_BIT(room->room_flags, ROOM_HUNT_MARK);
  if ( !h_head )
    h_head = hunt;
  else
    h_tail->next = hunt;
  h_tail = hunt;
#ifdef DEBUG_HUNT_CODE
  fprintf(h_fp, "Enqueue: %5d - %d\n", room->vnum,
          IS_SET(room->room_flags, ROOM_HUNT_MARK));
  fflush(h_fp);
#endif
  return;
}

void h_dequeue( void )
{
  struct h_queue *hunt;
  
  if ( !(hunt = h_head) )
    return;
  h_head = hunt->next;
  if ( h_tail == hunt )
    h_tail = NULL;
  REMOVE_BIT(hunt->room->room_flags, ROOM_HUNT_MARK);
#ifdef DEBUG_HUNT_CODE
  fprintf(h_fp, "Dequeue: %5d\n", hunt->room->vnum);
  fflush(h_fp);
#endif
#ifdef NEVER_FREE_HUNT
  hunt->next = h_free;
  h_free = hunt;
#else
  dispose(hunt, sizeof(*hunt));
#endif
  return;
}

void h_clear( void )
{
#ifdef DEBUG_HUNT_CODE
  fprintf(h_fp, "h_clear\n");
  fflush(h_fp);
#endif
  while ( h_head != NULL )
    h_dequeue();
#ifdef DEBUG_HUNT_CODE
  fprintf(h_fp, "Cleared\n");
  fflush(h_fp);
#endif
}

bool h_is_valid_exit( ROOM_INDEX_DATA *room, short dir, int h_flags )
{
  EXIT_DATA *exit = room->exit[dir];
  
  if ( !exit )
    return FALSE;
  if ( !exit->to_room )
    return FALSE;
#ifdef DEBUG_HUNT_CODE
  fprintf(h_fp, "IsValid: %5d - %s\n", exit->to_room->vnum,
         (IS_SET(exit->to_room->room_flags, ROOM_HUNT_MARK) ? "set"
         						    : "unset"));
  fflush(h_fp);
#endif
  if ( IS_SET(exit->to_room->room_flags, ROOM_HUNT_MARK) )
    return FALSE;
  if ( !IS_SET(h_flags, HUNT_WORLD) && room->area != exit->to_room->area )
    return FALSE;
  if ( IS_SET(exit->exit_info, EX_CLOSED) )
  {
    if ( !IS_SET(h_flags, HUNT_OPENDOOR) )
      return FALSE;
    if ( IS_SET(exit->exit_info, EX_LOCKED) )
    {
      if ( !IS_SET(h_flags, HUNT_UNLOCKDOOR | HUNT_PICKDOOR) )
        return FALSE;
      if ( !IS_SET(h_flags, HUNT_UNLOCKDOOR) &&
            IS_SET(exit->exit_info, EX_PICKPROOF) )
        return FALSE;
    }
  }
  return TRUE;
}

void h_enqueue_room( ROOM_INDEX_DATA *room, short dir, int h_flags )
{
  short edir;
  
#ifdef DEBUG_HUNT_CODE
  fprintf(h_fp, "h_enqueue_room\n");
  fflush(h_fp);
#endif
  for ( edir = 0; edir < 6; edir++ )
    if ( h_is_valid_exit(room, edir, h_flags) )
      h_enqueue(room->exit[edir]->to_room, (dir == -1 ? edir : dir));
  return;
}

short h_find_dir( ROOM_INDEX_DATA *room, ROOM_INDEX_DATA *target,
                   int h_flags )
{
  struct h_queue *hunt;
  
  if ( room == target )
    return -1;
#ifdef DEBUG_HUNT_CODE
  if ( !h_fp )
    h_fp = fopen("hunt.out", "w");
  fprintf(h_fp, "h_find_dir\n");
  fflush(h_fp);
#endif
#ifdef NEVER_FREE_HUNT
  if ( !h_free && !h_head && !h_tail )
    setup_hunt();
#endif
  SET_BIT(room->room_flags, ROOM_HUNT_MARK);
  h_enqueue_room(room, -1, h_flags);
  for ( hunt = h_head; hunt; hunt = hunt->next )
  {
    if ( hunt->room == target )
    {
      short dir = hunt->dir;
      
#ifdef DEBUG_HUNT_CODE
      fprintf(h_fp, "Found dir %d\n", dir);
      fflush(h_fp);
#endif
      h_clear();
      REMOVE_BIT(room->room_flags, ROOM_HUNT_MARK);
      return dir;
    }
    h_enqueue_room(hunt->room, hunt->dir, h_flags);
  }
#ifdef DEBUG_HUNT_CODE
  fprintf(h_fp,"Invalid dir\n");
  fflush(h_fp);
#endif
  h_clear();
  REMOVE_BIT(room->room_flags, ROOM_HUNT_MARK);
  return -1;
}

bool set_hunt( CHAR_DATA *ch, CHAR_DATA *fch, CHAR_DATA *vch, OBJ_DATA *vobj,
               int set_flags, int rem_flags )
{
  int nflags;
  ROOM_INDEX_DATA *troom;
  char buf[MAX_STRING_LENGTH];
  
  troom = (vch ? vch->in_room : vobj ? vobj->in_room : NULL);
  nflags = ((ch ? ch->hunt_flags : 0) | set_flags) & ~rem_flags;
  if ( !ch || !troom || h_find_dir(ch->in_room, troom, nflags) < 0 )
    return FALSE;
  ch->hunting = vch;
  ch->hunt_obj = vobj;
  ch->hunt_for = fch;
  if ( IS_NPC(ch) )
    ch->hunt_home = (ch->hunt_home ? ch->hunt_home : ch->in_room);
  else
    ch->hunt_home = NULL;
  if ( ch->searching )
  {
    free_string(ch->searching);
    ch->searching = NULL;
  }
  ch->hunt_flags = nflags;
  sprintf(buf, "%s has started hunting (%s) %s",
          NAME(ch),
          (vch ? IS_NPC(vch) ? "mobile" : "player" : "object"),
          (vch ? NAME(vch) : vobj->short_descr));
  if ( fch )
    sprintf(buf+strlen(buf), " for %s", NAME(fch));
  monitor_chan( buf, MONITOR_HUNTING );
/*  bug(buf, 0);  */
  return TRUE;
}

void end_hunt( CHAR_DATA *ch )
{
  ch->hunting = NULL;
  ch->hunt_obj = NULL;
  ch->hunt_for = NULL;
  if ( !IS_NPC(ch) )
  {
    ch->hunt_home = NULL;
    ch->hunt_flags = 0;
  }
  else
    ch->hunt_flags = ch->pIndexData->hunt_flags;
  if ( ch->searching )
  {
    free_string(ch->searching);
    ch->searching = NULL;
  }
}

bool	has_key	args( ( CHAR_DATA *ch, int key ) );
void hunt_move( CHAR_DATA *mob, short dir )
{
  EXIT_DATA *exit = mob->in_room->exit[dir];

  if ( IS_SET(exit->exit_info, EX_CLOSED) )
  {
    if ( IS_SET(exit->exit_info, EX_LOCKED) )
    {
      if ( IS_SET(mob->hunt_flags, HUNT_UNLOCKDOOR) &&
           has_key(mob, exit->key) )
        do_unlock(mob, dir_name[dir]);
      else if ( IS_SET(mob->hunt_flags, HUNT_PICKDOOR) &&
               !IS_SET(exit->exit_info, EX_PICKPROOF) )
        do_pick(mob, dir_name[dir]);
    }
    else if ( IS_SET(mob->hunt_flags, HUNT_OPENDOOR) )
      do_open(mob, dir_name[dir]);
  }
  else
    move_char(mob, dir);
  return;
}

/*#define NAME(ch) (IS_NPC(ch) ? ch->short_descr : ch->name)*/
bool mob_hunt( CHAR_DATA *mob )
{
  short dir;
  char buf[128];
  
  if ( !mob || !IS_NPC(mob) )
    return FALSE;
  if ( !mob->hunting )
  {
    if ( mob->hunt_obj != NULL )
    {
      if ( mob->hunt_obj->in_room == NULL )
      {
        if ( IS_SET(mob->hunt_flags, HUNT_CR) && mob->hunt_for &&
             mob->hunt_obj->item_type == ITEM_CORPSE_PC )
          act( "$N tells you 'Someone else seems to have gotten to your "
               "corpse before me.'", mob->hunt_for, NULL, mob, TO_CHAR );
        end_hunt(mob);
        return TRUE;
      }
      if ( can_see_obj(mob, mob->hunt_obj) &&
           mob->in_room == mob->hunt_obj->in_room )
      {
        if ( IS_SET(mob->hunt_flags, HUNT_CR) && mob->hunt_for &&
             mob->hunt_obj->item_type == ITEM_CORPSE_PC )
          act( "$N tell you 'I have found your corpse.  I shall return it "
               "to you now.", mob->hunt_for, NULL, mob, TO_CHAR );
        obj_from_room(mob->hunt_obj);
        obj_to_char(mob->hunt_obj, mob);
        act( "$n gets $o.", mob, mob->hunt_obj, NULL, TO_ROOM );
        set_hunt(mob, NULL, mob->hunt_for, mob->hunt_obj, 0, 0);
        return TRUE;
      }
      if ( !can_see_obj(mob, mob->hunt_obj) ||
           (dir = h_find_dir(mob->in_room, mob->hunt_obj->in_room,
            mob->hunt_flags)) < 0 )
      {
        if ( IS_SET(mob->hunt_flags, HUNT_CR) && mob->hunt_for &&
             mob->hunt_obj->item_type == ITEM_CORPSE_PC )
          act( "$N tells you 'I seem to have lost the way to your corpse.'",
               mob->hunt_for, NULL, mob, TO_CHAR );
        end_hunt(mob);
        return TRUE;
      }
      hunt_move(mob, dir);
      if ( mob->in_room == mob->hunt_obj->in_room )
        mob_hunt(mob);
    }
    else if ( mob->searching &&
             (mob->hunting = get_char_world(mob, mob->searching)) != NULL )
    {
      free_string(mob->searching);
      mob->searching = NULL;
    }
    else if ( mob->hunt_home )
    {
      dir = -1;
      if ( mob->in_room == mob->hunt_home ||
          (dir = h_find_dir(mob->in_room, mob->hunt_home,
           mob->hunt_flags)) < 0 )
      {
        mob->hunt_home = NULL;
        mob->hunt_flags = mob->pIndexData->hunt_flags;
      }
      else if ( dir >= 0 )
        hunt_move(mob, dir);
    }
    else
    {
      return FALSE;
    }
    return TRUE;
  }
  if ( !can_see(mob, mob->hunting) )
  {
    if ( IS_SET(mob->hunt_flags, HUNT_MERC) && mob->hunt_for )
    {
      /* 6.25% chance of giving up, 18.75% chance of telling employer. */
      switch(number_bits(4))
      {
      case 0:
        sprintf(buf, "$N tells you '%s seems to have disappeared!'",
                NAME(mob->hunting));
        act( buf, mob->hunt_for, NULL, mob, TO_CHAR );
        end_hunt(mob);
        return TRUE;
      case 1: case 2: case 3:
        sprintf(buf, "$N tells you '%s seems to have disappeared!  I shall "
                "find %s though!'", NAME(mob->hunting),
                (mob->hunting->sex == SEX_MALE   ? "him" :
                 mob->hunting->sex == SEX_FEMALE ? "her" : "it"));
        act(buf, mob->hunt_for, NULL, mob, TO_CHAR);
        return TRUE;
      }
    }
    if ( IS_SET(mob->hunt_flags, HUNT_INFORM) )
    {
      switch(number_bits(5))
      {
      case 0:
        sprintf(buf, "Where are you, %s?", NAME(mob->hunting));
        break;
      case 1:
        sprintf(buf, "Why can't I find you, %s?", NAME(mob->hunting));
        break;
      case 2:
        sprintf(buf, "I know you're out there, %s!", NAME(mob->hunting));
        break;
      case 3:
        sprintf(buf, "I'll find you, %s, just wait!", NAME(mob->hunting));
        break;
      default:
        return FALSE;
      }
      do_yell(mob, buf);
      return TRUE;
    }
    return FALSE;
  }
  if ( mob->in_room == mob->hunting->in_room )
  {
    if ( IS_SET(mob->hunt_flags, HUNT_CR) && mob->hunt_obj &&
         mob->hunt_obj->item_type == ITEM_CORPSE_PC )
    {
      act( "$N tells you 'I have returned with your corpse!'",
           mob->hunting, NULL, mob, TO_CHAR );
      /* Ok maybe im a little paranoid here.. :).. -- Alty */
      if ( mob->hunt_obj->carried_by == mob )
      {
        if ( mob->hunt_obj->wear_loc != WEAR_NONE )
          unequip_char(mob, mob->hunt_obj);
        act( "$n drops $o.", mob, mob->hunt_obj, NULL, TO_ROOM );
        obj_from_char( mob->hunt_obj );
        obj_to_room( mob->hunt_obj, mob->in_room );
      }
      end_hunt(mob);
      return TRUE;
    }
    if ( IS_SET(mob->hunt_flags, HUNT_MERC) && mob->hunt_for )
    {
      sprintf(buf, "$N tells you 'I have found %s!  Now %s shall die!'",
              NAME(mob->hunting),
              (mob->hunting->sex == SEX_FEMALE ? "she" :
               mob->hunting->sex == SEX_MALE   ? "he"  : "it"));
      act(buf, mob->hunt_for, NULL, mob, TO_CHAR);
    }
    switch(number_bits(2))
    {
    case 0:
      sprintf(buf, "Now I have you, %s!", NAME(mob->hunting));
      break;
    case 1:
      sprintf(buf, "I knew you'd be here, %s!", NAME(mob->hunting));
      break;
    case 2:
      sprintf(buf, "Did you really think you were safe, %s?",
              NAME(mob->hunting));
      break;
    case 3:
      sprintf(buf, "So here you are, %s!", NAME(mob->hunting));
      break;
    }
    if ( IS_SET(mob->hunt_flags, HUNT_INFORM) )
      do_yell(mob, buf);
    else
      do_say(mob, buf);
    multi_hit(mob, mob->hunting, TYPE_UNDEFINED);
    end_hunt(mob);
    return TRUE;
  }
      
  if ( (dir = h_find_dir(mob->in_room, mob->hunting->in_room,
                         mob->hunt_flags)) == -1 )
  {
    if ( IS_SET(mob->hunt_flags, HUNT_MERC) && mob->hunt_for )
    {
      /* 6.25% chance of giving up, 18.75% chance of informing employer */
      switch(number_bits(4))
      {
      case 0:
        sprintf(buf, "$N tells you 'I seem to have lost %s's trail.'",
                NAME(mob->hunting));
        act(buf, mob->hunt_for, NULL, mob, TO_CHAR);
        end_hunt(mob);
        return TRUE;
      case 1: case 2: case 3:
        sprintf(buf, "$N tells you 'I seem to have lost %s's trail.  I shall "
                "find it again, though!'", NAME(mob->hunting));
        act(buf, mob->hunt_for, NULL, mob, TO_CHAR);
        return TRUE;
      }
    }
    if ( IS_SET(mob->hunt_flags, HUNT_INFORM) )
    {
      switch(number_bits(6))
      {
      case 0:
        sprintf(buf, "Where are you hiding, %s?", NAME(mob->hunting));
        break;
      case 1:
        sprintf(buf, "You can't run forever, %s!", NAME(mob->hunting));
        break;
      case 2:
        sprintf(buf, "Come out, come out, wherever you are, %s!",
                NAME(mob->hunting));
        break;
      case 3:
        sprintf(buf, "I promise I won't hurt you, %s.", NAME(mob->hunting));
        break;
      default:
        return FALSE;
      }
      do_yell(mob, buf);
      return TRUE;
    }
    return FALSE;
  }
  hunt_move(mob, dir);
  if ( mob->in_room == mob->hunting->in_room )
    mob_hunt(mob);
  return TRUE;
}
/*#undef NAME*/

void char_hunt( CHAR_DATA *ch )
{
  short dir;
  char buf[MAX_STRING_LENGTH];
  
  if ( IS_NPC(ch) )
    return;
  if ( !ch->hunting )
  {
    if ( ch->hunt_obj )
    {
      if ( !can_see_obj(ch, ch->hunt_obj) || !ch->hunt_obj->in_room )
      {
        sprintf(buf, "@@RYou seem to have lost the trail to %s.@@N\n\r",
                ch->hunt_obj->short_descr);
        send_to_char( buf, ch );
        end_hunt(ch);
      }
      else if ( ch->hunt_obj->in_room == ch->in_room )
      {
        sprintf(buf, "@@RAhhh.  You have found %s!@@N\n\r",
                ch->hunt_obj->short_descr);
        send_to_char(buf, ch);
        end_hunt(ch);
      }
      else if ( (dir = h_find_dir(ch->in_room, ch->hunt_obj->in_room,
                 ch->hunt_flags)) < 0 )
      {
        sprintf(buf, "@@RYou seem to have lost the trail to %s.@@N\n\r",
                ch->hunt_obj->short_descr);
        send_to_char(buf, ch);
        end_hunt(ch);
      }
      else
      {
        sprintf(buf, "@@RYou sense that %s is %s of here.@@N\n\r",
                ch->hunt_obj->short_descr, dir_name[dir]);
        send_to_char(buf, ch);
      }
    }
    return;
  }
  if ( !can_see(ch, ch->hunting) )
  {
    send_to_char( "@@RYou seem to have lost your prey.@@N\n\r", ch );
    end_hunt(ch);
  }
  else if ( ch->in_room == ch->hunting->in_room )
  {
    send_to_char( "@@RAhhh.  You have found your prey!@@N\n\r", ch );
    end_hunt(ch);
  }
  else if ( (dir = h_find_dir(ch->in_room, ch->hunting->in_room,
             ch->hunt_flags)) < 0 )
  {
    send_to_char( "@@RYou seem to have lost your prey.@@N\n\r", ch );
    end_hunt(ch);
  }
  else
  {
    sprintf(buf, "@@RYou sense your prey is %s of here.@@N\n\r",
            dir_name[dir]);
    send_to_char(buf, ch);
  }
  return;
}

void do_hunt( CHAR_DATA *ch, char *argument )
{
  CHAR_DATA *victim;
  char arg[MAX_INPUT_LENGTH];
  short chance;
  
  if ( IS_NPC(ch) && (ch->hunting || ch->hunt_obj) )
    return;
  argument = one_argument(argument, arg);
  if ( !*arg )
  {
    send_to_char( "Hunt for whom?\n\r", ch );
    return;
  }
  if ( !IS_NPC( ch ) && IS_WOLF( ch ) && ( IS_SHIFTED( ch ) || IS_RAGED( ch ) )  )
    victim = get_char_world( ch, arg );
  else
    victim = (get_trust(ch) >= MAX_LEVEL ? get_char_world(ch, arg)
                                         : get_char_area (ch, arg));
  if ( victim == ch )
  {
    if ( ch->hunting )
    {
      sprintf(arg, "You stop hunting %s.\n\r", NAME(ch->hunting));
      send_to_char( arg, ch );
    }
    else if ( ch->hunt_obj )
    {
      sprintf(arg, "You stop looking for %s.\n\r", ch->hunt_obj->short_descr);
      send_to_char(arg, ch);
    }
    else
    {
      send_to_char( "You find yourself right where you're standing!\n\r",
                    ch );
    }
    end_hunt(ch);
    return;
  } else if ( !IS_IMMORTAL(ch) &&
              (victim != NULL) &&
              !IS_NPC(victim)  &&
              IS_IMMORTAL(victim)) {
    sprintf(arg, "You can't hunt Immortal %s!\n\r", NAME(victim));
    send_to_char( arg, ch );
    return;
  }

  /* 100% chance of finding them if you're looking right at them.. :) */
  if ( victim != NULL && victim->in_room == ch->in_room )
  {
    send_to_char( "You're already there!\n\r", ch );
    return;
  }
  /* Chance of picking up wrong trail.. (25% for NPC, PC @ normal) */
  chance = (IS_NPC(ch) ? 75 : ch->pcdata->learned[gsn_hunt]);
  if ( !IS_NPC( ch ) && IS_WOLF( ch ) && ( IS_SHIFTED( ch ) || IS_RAGED( ch ) )  )
    chance = (( MAX_WOLF_LEVEL  - ch->pcdata->generation ) *4 ) + ch->pcdata->vamp_level;

  if ( chance < number_percent() )
  {
    CHAR_DATA *vch;
    short vcnt = 0;
    
    victim = NULL;
    for ( vch = first_char; vch; vch = vch->next )
    {
      if ( IS_NPC(vch) && vch->in_room &&
           vch->in_room->area == ch->in_room->area &&
           vch->in_room != ch->in_room && number_range(0, vcnt) == 0 )
        victim = vch;
      vcnt++;
    }
  }
  /* Max_level people can hunt through the world, and anyone who has
     practiced over 70% can hunt through doors.. -- Alty */
  if ( !IS_NPC( ch ) && IS_WOLF( ch ) && ( IS_SHIFTED( ch ) || IS_RAGED( ch ) )  )
    ch->hunt_flags = HUNT_WORLD|HUNT_OPENDOOR|HUNT_UNLOCKDOOR|HUNT_PICKDOOR;
  else
    if ( !IS_NPC(ch) )
      ch->hunt_flags = (get_trust(ch) >= MAX_LEVEL ? HUNT_WORLD : 0)
                       | (ch->pcdata->learned[gsn_hunt] >= 70 ?
                         HUNT_OPENDOOR|HUNT_UNLOCKDOOR|HUNT_PICKDOOR : 0);
  if ( !victim || !set_hunt(ch, NULL, victim, NULL, 0, HUNT_CR|HUNT_MERC) )
  {
    send_to_char( "You couldn't find a trail.\n\r", ch );
    return;
  }
  return;
}

#ifdef NEVER_FREE_HUNT
#define MAX_BUCKET_SIZE	4096
#define TOP_BUCKET_LIST	4095
void setup_hunt( void )
{
  struct h_queue *bucket;
  int bcnt;

  bucket = getmem(MAX_BUCKET_SIZE*sizeof(*bucket));
  for ( bcnt = 0; bcnt < MAX_BUCKET_SIZE; bcnt++ )
    bucket[bcnt].next = (bcnt < TOP_BUCKET_LIST ? &bucket[bcnt+1] : h_free);
  h_free = &bucket[0];
  return;
}
#undef TOP_BUCKET_SIZE
#undef MAX_BUCKET_SIZE
#endif