HUNTING
				-------

Some time ago someone posted graph.c on the list, and it was pretty nice.
The code was clean and well commented, but I found it too slow. A longer
while ago there was another hunt code posted here, and it was a lot faster.
(Though it contained a whole bunch of files and the code didn't look as nice.)
Well, here I post it again, in one file and re-indended to look a bit
cleaner.

The code was obtained from ftp.atinc.com:/pub/mud/outgoing/track.merc21.tar.gz.
It seems to originate from SillyMUD.

			Installation (Merc22)
			---------------------

The code should work as well with envies and older mercs, though some changes
may be needed. The following files will be modified:
Makefile, act_wiz.c, const.c, db.c, fight.c, interp.c, and merc.h

	Makefile
	--------

- add hunt.o to O_FILES list

	act_wiz.c
	---------

- add to do_mstat right after showing the mob spec:
    if ( IS_NPC(victim) && victim->hunting != NULL )
    {
        sprintf(buf, "Hunting victim: %s (%s)\n\r",
                IS_NPC(victim->hunting) ? victim->hunting->short_descr
                                        : victim->hunting->name,
                IS_NPC(victim->hunting) ? "MOB" : "PLAYER" );
        strcat(buf1, buf);
    }

- change the line
        send_to_char( "  thirst drunk full",                            ch );
  in do_mset to:
        send_to_char( "  thirst drunk full hunt",                       ch );

- add to do_mset:
    /* Change their hunting status. */
    else if (!str_cmp(arg2, "hunt"))
    {
        CHAR_DATA *hunted = 0;

        if ( !IS_NPC(victim) )
        {
            send_to_char( "Not on PC's.\n\r", ch );
            return;
        }

        if ( str_cmp( arg3, "." ) )
          if ( (hunted = get_char_area(victim, arg3)) == NULL )
            {
              send_to_char("Mob couldn't locate the victim to hunt.\n\r", ch);
              return;
            }

        victim->hunting = hunted;
        return;
    }

	const.c
	-------

- add skill for hunting in skill table:
    {
        "hunt",                 { 37, 37, 30, 20 },
        spell_null,             TAR_IGNORE,             POS_RESTING,
        &gsn_hunt,              SLOT( 0),        0,     12,
        "",                     "!Hunt!"
    },

	db.c
	----
- add gsn declaration:
sh_int                  gsn_hunt;

	fight.c
	-------

- add the violence_update before lines
        if ( ( victim = ch->fighting ) == NULL || ch->in_room == NULL )
            continue;
  right after lines
    for ( ch = char_list; ch != NULL; ch = ch->next )
    {
        ch_next = ch->next;
  the following:
        /*
         * Hunting mobs.
         */
        if ( IS_NPC(ch)
            && ch->fighting == NULL
            && IS_AWAKE(ch)
            && ch->hunting != NULL )
          {
            hunt_victim(ch);
            continue;
          }

	interp.c
	--------

- add command for hunting:
    { "hunt",           do_hunt,        POS_STANDING,    0,  LOG_NORMAL },

	merc.h
	------
- add hunting data to char_data structure:
    CHAR_DATA *         hunting;                /* Used by hunting routine */

- add declaration:
extern	sh_int	gsn_hunt;

- add declaration for do_hunt:
DECLARE_DO_FUN( do_hunt         );

- add hunt_victim definition:
/* hunt.c */
void    hunt_victim     args( ( CHAR_DATA *ch ) );

I hope I didn't leave anything vital out, but if anything fails to work,
feel free to consult me. I myself have the code installed in my mud, and
it works fine. The times needed to hunt down someone are really a _lot_
quicker that with graph.c.

				hunt.c
				------

And here is the hunt.c file itself:

/*
  SillyMUD Distribution V1.1b             (c) 1993 SillyMUD Developement
  See license.doc for distribution terms.   SillyMUD is based on DIKUMUD

  Modifications by Rip in attempt to port to merc 2.1
*/

/*
  Modified by Turtle for Merc22 (07-Nov-94)

  I got this one from ftp.atinc.com:/pub/mud/outgoing/track.merc21.tar.gz.
  It cointained 5 files: README, hash.c, hash.h, skills.c, and skills.h.
  I combined the *.c and *.h files in this hunt.c, which should compile
  without any warnings or errors.
*/

/*
  Modified by Tyche for Merc22 - 9/5/2011

  Removed extraneous code.
  Added application specific hash and queue implementations.
  Cleaned up and tweaked code for performance.

*/


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#ifdef TIME_HUNT
#include <sys/time.h>
#endif
#include "merc.h"

extern const char *dir_name[];

#define ROOMS_TABLE_SIZE 1063
#define	MAKE_ROOM_HASH(key) (((unsigned int)(key))%ROOMS_TABLE_SIZE)

struct rooms_node {
  ROOM_INDEX_DATA *key;
  int value;
  struct rooms_node *next;
};

struct rooms_table {
  struct rooms_node *buckets[ROOMS_TABLE_SIZE];
};

struct search_node {
  ROOM_INDEX_DATA *room;
  struct search_node *next;
};

struct search_queue {
  struct search_node *head;
  struct search_node *tail;
};

void init_rooms_table (struct rooms_table *rt)
{
  int i;
  for (i = 0; i < ROOMS_TABLE_SIZE; i++)
    rt->buckets[i] = NULL;
}

void destroy_rooms_table (struct rooms_table *rt)
{
  int i;
  struct rooms_node *entry, *temp;

  for (i = 0; i < ROOMS_TABLE_SIZE; i++)
    for (entry = rt->buckets[i]; entry;) {
      temp = entry->next;
      free (entry);
      entry = temp;
    }
}

void rooms_table_add (struct rooms_table *rt, ROOM_INDEX_DATA * key,
  int value)
{
  /* precondition: there is no entry for <key> yet */
  struct rooms_node *temp;
  unsigned int idx;

  idx = MAKE_ROOM_HASH (key);
  temp = (struct rooms_node *) malloc (sizeof (struct rooms_node));
  temp->key = key;
  temp->next = rt->buckets[idx];
  temp->value = value;
  rt->buckets[idx] = temp;
}

int rooms_table_find (struct rooms_table *rt, ROOM_INDEX_DATA * key)
{
  struct rooms_node *entry;
  unsigned int idx;

  idx = MAKE_ROOM_HASH (key);

  entry = rt->buckets[idx];

  while (entry && entry->key != key)
    entry = entry->next;

  return entry ? entry->value : 0;
}

void init_search_queue (struct search_queue *sq)
{
  sq->head = NULL;
  sq->tail = NULL;
}

void destroy_search_queue (struct search_queue *sq)
{
  struct search_node *entry, *temp;

  for (entry = sq->head; entry;) {
    temp = entry->next;
    free (entry);
    entry = temp;
  }
}

ROOM_INDEX_DATA *search_queue_pop (struct search_queue *sq)
{
  ROOM_INDEX_DATA *room;
  struct search_node *entry;

  if (sq->head == NULL)
    return NULL;

  room = sq->head->room;
  entry = sq->head->next;
  free (sq->head);
  sq->head = entry;
  return room;
}

void search_queue_push (struct search_queue *sq, ROOM_INDEX_DATA * room)
{
  struct search_node *entry;

  entry = (struct search_node *) malloc (sizeof (struct search_node));
  entry->room = room;
  if (sq->head == NULL) {
    entry->next = NULL;
    sq->head = entry;
    sq->tail = entry;
  } else {
    entry->next = sq->tail->next;
    sq->tail->next = entry;
    sq->tail = sq->tail->next;
  }
}

struct hunting_data {
  char *name;
  struct char_data **victim;
};

bool exit_ok (EXIT_DATA * pexit, bool go_thru_doors)
{
  if (pexit == NULL || pexit->to_room == NULL)
    return FALSE;
  if (go_thru_doors)
    return TRUE;
  if (IS_SET (pexit->exit_info, EX_CLOSED))
    return FALSE;
  return TRUE;
}

int find_path (ROOM_INDEX_DATA * startp, ROOM_INDEX_DATA * endp,
  CHAR_DATA * ch, int depth, bool in_zone)
{
  struct search_queue s_queue;
  struct rooms_table x_room;

  int i, count = 0, direction = -1;
  bool thru_doors, first_pass = TRUE;
  ROOM_INDEX_DATA *herep, *tmp_room;

#ifdef TIME_HUNT
  char buf[MAX_STRING_LENGTH];
  struct timeval start = { 0, 0 };
  struct timeval stop = { 0, 0 };
  struct timeval result = { 0, 0 };
  double timing;

  gettimeofday (&start, 0);
#endif

  if (depth < 0) {
    thru_doors = TRUE;
    depth = -depth;
  } else {
    thru_doors = FALSE;
  }

  init_rooms_table (&x_room);
  rooms_table_add (&x_room, startp, -1);

  /* initialize queue */
  init_search_queue (&s_queue);
  search_queue_push (&s_queue, startp);

  while ((herep = search_queue_pop (&s_queue)) != NULL) {
    /* only look in the same zone...unless option picked */
    if (herep->area == startp->area || !in_zone) {
      /* for each room look in all directions */
      for (i = 0; i <= 5; i++) {
        /* make sure exit is valid */
        if (exit_ok (herep->exit[i], thru_doors)) {
          tmp_room = herep->exit[i]->to_room;
          if (tmp_room != endp) {
            /* shall we add room to queue?
               count determines total breadth and depth */
            if (!rooms_table_find (&x_room, tmp_room)
              && (count < depth)) {
              count++;
              /* put room on queue for further checking of it's exits */
              search_queue_push (&s_queue, tmp_room);
              /* add room to list of already searched, using it's ancestor for
                 the direction, unless this is the first pass */
              rooms_table_add (&x_room, tmp_room,
                first_pass ? (i + 1) : rooms_table_find (&x_room, herep));
            }
          } else {
            /* we have found our target room */
            direction = rooms_table_find (&x_room, herep);
            /* return direction if first layer */
            if (direction == -1)
              direction = i;
            else
              --direction;
            goto finished;
          }
        }
      }
    }
    first_pass = FALSE;
  }
finished:
  /* couldn't find path */
  destroy_search_queue (&s_queue);
  destroy_rooms_table (&x_room);
#ifdef TIME_HUNT
  gettimeofday (&stop, 0);
  timersub (&stop, &start, &result);
  timing = ((double) result.tv_sec * 1000000) + ((double) result.tv_usec);
  if (IS_IMMORTAL (ch)) {
    sprintf (buf, "Total time was %f microseconds in search of %d rooms.\n",
      timing, count);
    send_to_char (buf, ch);
  }
#endif
  return direction;
}


void do_hunt (CHAR_DATA * ch, char *argument)
{
  char buf[MAX_STRING_LENGTH];
  char arg[MAX_STRING_LENGTH];
  CHAR_DATA *victim;
  int direction;
  bool fArea;

  one_argument (argument, arg);

  if (arg[0] == '\0') {
    send_to_char ("Whom are you trying to hunt?\n\r", ch);
    return;
  }

  /* only imps can hunt to different areas */
  fArea = (get_trust (ch) < MAX_LEVEL);

  if (fArea)
    victim = get_char_area (ch, arg);
  else
    victim = get_char_world (ch, arg);

  if (victim == NULL) {
    send_to_char ("No-one around by that name.\n\r", ch);
    return;
  }

  if (ch->in_room == victim->in_room) {
    act ("$N is here!", ch, NULL, victim, TO_CHAR);
    return;
  }

  /*
   * Deduct some movement.
   */
  if (ch->move > 2)
    ch->move -= 3;
  else {
    send_to_char ("You're too exhausted to hunt anyone!\n\r", ch);
    return;
  }

  act ("$n carefully sniffs the air.", ch, NULL, NULL, TO_ROOM);
  WAIT_STATE (ch, skill_table[gsn_hunt].beats);
  direction = find_path (ch->in_room, victim->in_room, ch, -40000, fArea);

  if (direction == -1) {
    act ("You couldn't find a path to $N from here.",
      ch, NULL, victim, TO_CHAR);
    return;
  }

  if (direction < 0 || direction > 5) {
    send_to_char ("Hmm... Something seems to be wrong.\n\r", ch);
    return;
  }

  /*
   * Give a random direction if the player misses the die roll.
   */
  if ((IS_NPC (ch) && number_percent () > 75)   /* NPC @ 25% */
    ||(!IS_NPC (ch) && number_percent () >      /* PC @ norm */
      ch->pcdata->learned[gsn_hunt])) {
    do {
      direction = number_door ();
    }
    while ((ch->in_room->exit[direction] == NULL)
      || (ch->in_room->exit[direction]->to_room == NULL));
  }

  /*
   * Display the results of the search.
   */
  sprintf (buf, "$N is %s from here.", dir_name[direction]);
  act (buf, ch, NULL, victim, TO_CHAR);

  return;
}



void hunt_victim (CHAR_DATA * ch)
{
  int dir;
  bool found;
  CHAR_DATA *tmp;

  if (ch == NULL || ch->hunting == NULL || !IS_NPC (ch))
    return;

  /*
   * Make sure the victim still exists.
   */
  for (found = 0, tmp = char_list; tmp && !found; tmp = tmp->next)
    if (ch->hunting == tmp)
      found = 1;

  if (!found || !can_see (ch, ch->hunting)) {
    do_say (ch, "Damn!  My prey is gone!!");
    ch->hunting = NULL;
    return;
  }

  if (ch->in_room == ch->hunting->in_room) {
    act ("$n glares at $N and says, 'Ye shall DIE!'",
      ch, NULL, ch->hunting, TO_NOTVICT);
    act ("$n glares at you and says, 'Ye shall DIE!'",
      ch, NULL, ch->hunting, TO_VICT);
    act ("You glare at $N and say, 'Ye shall DIE!",
      ch, NULL, ch->hunting, TO_CHAR);
    multi_hit (ch, ch->hunting, TYPE_UNDEFINED);
    ch->hunting = NULL;
    return;
  }

  WAIT_STATE (ch, skill_table[gsn_hunt].beats);
  dir = find_path (ch->in_room, ch->hunting->in_room, ch, -40000, TRUE);

  if (dir < 0 || dir > 5) {
    act ("$n says 'Damn!  Lost $M!'", ch, NULL, ch->hunting, TO_ROOM);
    ch->hunting = NULL;
    return;
  }

  /*
   * Give a random direction if the mob misses the die roll.
   */
  if (number_percent () > 75) { /* @ 25% */
    do {
      dir = number_door ();
    }
    while ((ch->in_room->exit[dir] == NULL)
      || (ch->in_room->exit[dir]->to_room == NULL));
  }


  if (IS_SET (ch->in_room->exit[dir]->exit_info, EX_CLOSED)) {
    do_open (ch, (char *) dir_name[dir]);
    return;
  }

  move_char (ch, dir);
  return;
}

That's all there is to hunt... Read you later :)
--
Mikko Kilpikoski                                __     Sig?  Naaah...
    E-Mail: turtle@modeemi.cs.tut.fi           /__\@     I feel too relaxed
       WWW: http://modeemi.cs.tut.fi/~turtle   L  L        for that...
       MUD: vichy.modeemi.cs.tut.fi (130.230.11.124) 2011