1stMud/CVS/
1stMud/area/CVS/
1stMud/backup/CVS/
1stMud/bin/
1stMud/bin/CVS/
1stMud/bin/extras/
1stMud/bin/extras/CVS/
1stMud/data/CVS/
1stMud/data/i3/CVS/
1stMud/doc/1stMud/
1stMud/doc/1stMud/CVS/
1stMud/doc/CVS/
1stMud/doc/Diku/
1stMud/doc/Diku/CVS/
1stMud/doc/MPDocs/CVS/
1stMud/doc/Merc/CVS/
1stMud/doc/Rom/
1stMud/doc/Rom/CVS/
1stMud/log/CVS/
1stMud/notes/
1stMud/notes/CVS/
1stMud/player/CVS/
1stMud/player/backup/CVS/
1stMud/player/deleted/CVS/
1stMud/src/CVS/
1stMud/src/config/CVS/
1stMud/src/h/CVS/
1stMud/src/o/CVS/
1stMud/win/CVS/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*          1stMud ROM Derivative (c) 2001-2004 by Markanth                *
*            http://www.firstmud.com/  <markanth@firstmud.com>            *
*         By using this code you have agreed to follow the term of        *
*             the 1stMud license in ../doc/1stMud/LICENSE                 *
***************************************************************************/

#include "merc.h"
#include "interp.h"

struct hash_link
{
  int key;
  struct hash_link *next;
  void *data;
};

struct hash_header
{
  int rec_size;
  int table_size;
  int *keylist, klistsize, klistlen;
  struct hash_link **buckets;
};

#define    	HASH_KEY(ht,key)    	((((unsigned int)(key))*17)%(ht)->table_size)

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

struct room_q
{
  int room_nr;
  struct room_q *next_q;
};

struct nodes
{
  int visited;
  int ancestor;
};

#define    	IS_DIR          (get_room_index(q_head->room_nr)->exit[i])
#define    	GO_OK           (!IsSet( IS_DIR->exit_info, EX_CLOSED ))
#define    	GO_OK_SMARTER   1

Proto (void init_hash_table, (struct hash_header *, int, int));
Proto (void init_world, (RoomIndex * room_db[]));

Proto (CharData * get_char_area_restrict, (CharData * ch, char *argument));
Proto (void destroy_hash_table, (struct hash_header *));
Proto (void _hash_enter, (struct hash_header *, int, void *));

Proto (RoomIndex * room_find, (RoomIndex * room_db[], int));
Proto (void *hash_find, (struct hash_header *, int));
Proto (int room_enter, (RoomIndex * rb[], int, RoomIndex *));
Proto (int hash_enter, (struct hash_header *, int, void *));

Proto (RoomIndex * room_find_or_create, (RoomIndex * rb[], int));
Proto (void *hash_find_or_create, (struct hash_header *, int));
Proto (int room_remove, (RoomIndex * rb[], int));
Proto (void *hash_remove, (struct hash_header *, int));
Proto (int exit_ok, (ExitData *));
Proto (int find_path, (int, int, CharData *, int, int));

void
init_hash_table (struct hash_header *ht, int rec_size, int table_size)
{
  ht->rec_size = rec_size;
  ht->table_size = table_size;

  alloc_mem (ht->buckets, struct hash_link *, table_size);
  alloc_mem (ht->keylist, int, (ht->klistsize = 128));

  ht->klistlen = 0;
}

void
init_world (RoomIndex * room_db[])
{
  memset (room_db, 0, sizeof (RoomIndex *) * top_room_index);
}

CharData *
get_char_area (CharData * ch, char *argument)
{
  char arg[MIL];
  CharData *ach;
  int number;
  int count;

  if (NullStr (argument))
    return NULL;

  if ((ach = get_char_room (ch, NULL, argument)) != NULL)
    return ach;

  number = number_argument (argument, arg);
  count = 0;
  for (ach = char_first; ach != NULL; ach = ach->next)
    {
      if (ach->in_room == NULL
	  || ach->in_room->area != ch->in_room->area
	  || !can_see (ch, ach) || !is_name (arg, ach->name))
	continue;
      if (++count == number)
	return ach;
    }

  return NULL;
}

void
destroy_hash_table (struct hash_header *ht)
{
  int i;
  struct hash_link *scan, *temp;

  for (i = 0; i < ht->table_size; i++)
    {
      for (scan = ht->buckets[i]; scan;)
	{
	  temp = scan->next;
	  free_mem (scan);
	  scan = temp;
	}
    }
  free_mem (ht->buckets);
  free_mem (ht->keylist);
}

void
_hash_enter (struct hash_header *ht, int key, void *data)
{

  struct hash_link *temp;
  int i;

  alloc_mem (temp, struct hash_link, 1);

  temp->key = key;
  temp->next = ht->buckets[HASH_KEY (ht, key)];
  temp->data = data;
  ht->buckets[HASH_KEY (ht, key)] = temp;
  if (ht->klistlen >= ht->klistsize)
    {
      ht->klistsize *= 2;
      realloc_mem (ht->keylist, int, ht->klistsize);
    }
  for (i = ht->klistlen; i >= 0; i--)
    {
      if (ht->keylist[i - 1] < key)
	{
	  ht->keylist[i] = key;
	  break;
	}
      ht->keylist[i] = ht->keylist[i - 1];
    }
  ht->klistlen++;
}

RoomIndex *
room_find (RoomIndex * room_db[], int key)
{
  return ((key < top_room_index && key > -1) ? room_db[key] : 0);
}

void *
hash_find (struct hash_header *ht, int key)
{
  struct hash_link *scan;

  scan = ht->buckets[HASH_KEY (ht, key)];

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

  return scan ? scan->data : NULL;
}

int
room_enter (RoomIndex * rb[], int key, RoomIndex * rm)
{
  RoomIndex *temp;

  temp = room_find (rb, key);
  if (temp)
    return (0);

  rb[key] = rm;
  return (1);
}

int
hash_enter (struct hash_header *ht, int key, void *data)
{
  void *temp;

  temp = hash_find (ht, key);
  if (temp)
    return 0;

  _hash_enter (ht, key, data);
  return 1;
}

RoomIndex *
room_find_or_create (RoomIndex * rb[], int key)
{
  RoomIndex *rv;

  rv = room_find (rb, key);
  if (rv)
    return rv;

  alloc_mem (rv, RoomIndex, 1);
  rb[key] = rv;

  return rv;
}

void *
hash_find_or_create (struct hash_header *ht, int key)
{
  void *rval;

  rval = hash_find (ht, key);
  if (rval)
    return rval;

  alloc_mem (rval, void *, ht->rec_size);
  _hash_enter (ht, key, rval);

  return rval;
}

int
room_remove (RoomIndex * rb[], int key)
{
  RoomIndex *tmp;

  tmp = room_find (rb, key);
  if (tmp)
    {
      rb[key] = 0;
      free_mem (tmp);
    }
  return (0);
}

void *
hash_remove (struct hash_header *ht, int key)
{
  struct hash_link **scan;

  scan = ht->buckets + HASH_KEY (ht, key);

  while (*scan && (*scan)->key != key)
    scan = &(*scan)->next;

  if (*scan)
    {
      int i;
      struct hash_link *temp, *aux;

      temp = (struct hash_link *) (*scan)->data;
      aux = *scan;
      *scan = aux->next;
      free_mem (aux);

      for (i = 0; i < ht->klistlen; i++)
	if (ht->keylist[i] == key)
	  break;

      if (i < ht->klistlen)
	{
	  memcpy ((char *) ht->keylist + i + 1,
		  (char *) ht->keylist + i,
		  (ht->klistlen - i) * sizeof (*ht->keylist));
	  ht->klistlen--;
	}

      return temp;
    }

  return NULL;
}

int
exit_ok (ExitData * pexit)
{
  RoomIndex *to_room;

  if ((pexit == NULL) || (to_room = pexit->u1.to_room) == NULL)
    return 0;

  return 1;
}

int
find_path (int in_room_vnum, int out_room_vnum, CharData * ch,
	   int depth, int in_zone)
{
  struct room_q *tmp_q, *q_head, *q_tail;
  struct hash_header x_room;
  int i, tmp_room, count = 0, thru_doors;
  RoomIndex *herep;
  RoomIndex *startp;
  ExitData *exitp;

  if (depth < 0)
    {
      thru_doors = true;
      depth = -depth;
    }
  else
    {
      thru_doors = false;
    }

  startp = get_room_index (in_room_vnum);

  init_hash_table (&x_room, sizeof (int), 2048);

  hash_enter (&x_room, in_room_vnum, (void *) -1);

  alloc_mem (q_head, struct room_q, 1);

  q_tail = q_head;
  q_tail->room_nr = in_room_vnum;
  q_tail->next_q = 0;

  while (q_head)
    {
      herep = get_room_index (q_head->room_nr);

      if (herep->area == startp->area || !in_zone)
	{
	  for (i = 0; i < MAX_DIR; i++)
	    {
	      exitp = herep->exit[i];
	      if (exit_ok (exitp) && (thru_doors ? GO_OK_SMARTER : GO_OK))
		{
		  tmp_room = herep->exit[i]->u1.to_room->vnum;
		  if (tmp_room != out_room_vnum)
		    {
		      if (!hash_find (&x_room, tmp_room) && (count < depth))
			{
			  count++;

			  alloc_mem (tmp_q, struct room_q, 1);

			  tmp_q->room_nr = tmp_room;
			  tmp_q->next_q = 0;
			  q_tail->next_q = tmp_q;
			  q_tail = tmp_q;

			  hash_enter (&x_room, tmp_room, ((int)
							  hash_find
							  (&x_room,
							   q_head->room_nr)
							  ==
							  -1) ? (void *) (i +
									  1)
				      : hash_find (&x_room, q_head->room_nr));
			}
		    }
		  else
		    {

		      tmp_room = q_head->room_nr;
		      for (; q_head; q_head = tmp_q)
			{
			  tmp_q = q_head->next_q;
			  free_mem (q_head);
			}

		      if ((int) hash_find (&x_room, tmp_room) == -1)
			{
			  if (x_room.buckets)
			    {
			      destroy_hash_table (&x_room);
			    }
			  return (i);
			}
		      else
			{
			  i = (int) hash_find (&x_room, tmp_room);
			  if (x_room.buckets)
			    {
			      destroy_hash_table (&x_room);
			    }
			  return (-1 + i);
			}
		    }
		}
	    }
	}

      tmp_q = q_head->next_q;
      free_mem (q_head);
      q_head = tmp_q;
    }

  if (x_room.buckets)
    {
      destroy_hash_table (&x_room);
    }
  return -1;
}

Do_Fun (do_hunt)
{
  char buf[MSL];
  char arg[MSL];
  CharData *victim;
  int direction;
  bool fArea;

  one_argument (argument, arg);

  if (get_skill (ch, gsn_hunt) == 0 || !can_use_skpell (ch, gsn_hunt))
    {
      chprintln (ch, "You don't know how to hunt.");
      return;
    }

  if (NullStr (arg))
    {
      chprintln (ch, "Whom are you trying to hunt?");
      return;
    }

  fArea = (get_trust (ch) < MAX_LEVEL);

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

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

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

  if (ch->move > 2)
    ch->move -= 3;
  else
    {
      chprintln (ch, "You're too exhausted to hunt anyone!");
      return;
    }

  act ("$n carefully sniffs the air.", ch, NULL, NULL, TO_ROOM);
  WaitState (ch, skill_table[gsn_hunt].beats);
  direction =
    find_path (ch->in_room->vnum, victim->in_room->vnum, 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 >= MAX_DIR)
    {
      chprintln (ch, "Hmm... Something seems to be wrong.");
      return;
    }

  if ((IsNPC (ch) && number_percent () > 50) ||
      (!IsNPC (ch) && number_percent () > get_skill (ch, gsn_hunt)))
    {
      do
	{
	  direction = number_door ();
	}
      while ((ch->in_room->exit[direction] == NULL) ||
	     (ch->in_room->exit[direction]->u1.to_room == NULL));
    }

  sprintf (buf, "$N is %s from here.", dir_name[direction]);
  act (buf, ch, NULL, victim, TO_CHAR);
  check_improve (ch, gsn_hunt, true, 1);
  return;
}

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

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

  for (found = 0, tmp = char_first; tmp && !found; tmp = tmp->next)
    if (ch->hunting == tmp)
      found = 1;

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

  if (ch->in_room == ch->hunting->in_room)
    {
      if (number_percent () < 60)
	{
	  act (CTAG (_SAY1) "$n" CTAG (_SAY1) " glares at $N"
	       CTAG (_SAY1) " and says, '" CTAG (_SAY2)
	       "Ye shall DIE!" CTAG (_SAY1) "'{x", ch, NULL,
	       ch->hunting, TO_NOTVICT);
	  act (CTAG (_SAY1) "$n" CTAG (_SAY1)
	       " glares at you and says, '" CTAG (_SAY2)
	       "Ye shall DIE!" CTAG (_SAY1) "'{x", ch, NULL,
	       ch->hunting, TO_VICT);
	  act (CTAG (_SAY1) "You glare at $N" CTAG (_SAY1)
	       " and say, '" CTAG (_SAY2) "Ye shall DIE!"
	       CTAG (_SAY1) "'{x", ch, NULL, ch->hunting, TO_CHAR);
	}
      else
	{
	  act (CTAG (_SAY1) "$n" CTAG (_SAY1) " glares at $N"
	       CTAG (_SAY1) " and says, '" CTAG (_SAY2)
	       "Hey, I remember you!" CTAG (_SAY1) "'{x", ch, NULL,
	       ch->hunting, TO_NOTVICT);
	  act (CTAG (_SAY1) "$n" CTAG (_SAY1)
	       " glares at you and says, '" CTAG (_SAY2)
	       "Hey, I remember you!" CTAG (_SAY1) "'{x", ch, NULL,
	       ch->hunting, TO_VICT);
	  act (CTAG (_SAY1) "You glare at $N" CTAG (_SAY1)
	       " and say, '" CTAG (_SAY2) "Hey, I remember you!"
	       CTAG (_SAY1) "'{x", ch, NULL, ch->hunting, TO_CHAR);
	}
      multi_hit (ch, ch->hunting, TYPE_UNDEFINED);
      ch->hunting = NULL;
      return;
    }

  WaitState (ch, skill_table[gsn_hunt].beats);
  dir =
    find_path (ch->in_room->vnum, ch->hunting->in_room->vnum, ch,
	       -40000, true);

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

  if (number_percent () > 50)
    {
      do
	{
	  dir = number_door ();
	}
      while ((ch->in_room->exit[dir] == NULL) ||
	     (ch->in_room->exit[dir]->u1.to_room == NULL));
    }

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

  move_char (ch, dir, false);
  return;
}