SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |   (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 * ------------------------------------------------------------------------ *
 *			 Tracking/hunting module									    *
 ****************************************************************************/

#include	"stdafx.h"
#include	"smaug.h"
#include	"skill.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"Exits.h"
#include	"descriptor.h"
#include	"character.h"
#include	"sysdata.h"

#define BFS_ERROR	   -1
#define BFS_ALREADY_THERE  -2
#define BFS_NO_PATH	   -3

#define TRACK_THROUGH_DOORS

extern short	top_room;

/* You can define or not define TRACK_THOUGH_DOORS, above, depending on
   whether or not you want track to find paths which lead through closed
   or hidden doors.
*/

struct bfs_queue_struct {
   CRoomIndexData *room;
   char   dir;
   struct bfs_queue_struct *next;
};

static struct bfs_queue_struct	*queue_head = NULL,
				*queue_tail = NULL,
				*room_queue = NULL;

CRoomIndexData *toroom (CRoomIndexData *room, short door)
{
    return (get_exit (room, door)->GetToRoom ());
}

BOOL valid_edge (CRoomIndexData *room, short door)
{
	CExitData		*pexit;
	CRoomIndexData	*to_room;

	pexit = get_exit (room, door);
	if (pexit && (to_room = pexit->GetToRoom ()) != NULL
#ifndef TRACK_THROUGH_DOORS
		&& !IS_SET (pexit->exit_info, EX_CLOSED)
#endif
		&& ! to_room->IsMarked ())
			return TRUE;
	else
		return FALSE;
}

void bfs_enqueue (CRoomIndexData *room, char dir)
{
   struct bfs_queue_struct *curr;

   curr = new bfs_queue_struct;
   curr->room = room;
   curr->dir = dir;
   curr->next = NULL;

   if (queue_tail) {
      queue_tail->next = curr;
      queue_tail = curr;
   } else
      queue_head = queue_tail = curr;
}


void bfs_dequeue (void)
{
   struct bfs_queue_struct *curr;

   curr = queue_head;

   if (! (queue_head = queue_head->next))
      queue_tail = NULL;
   delete curr;
}


void bfs_clear_queue (void) 
{
   while (queue_head)
      bfs_dequeue ();
}

void room_enqueue (CRoomIndexData *room)
{
   struct bfs_queue_struct *curr;

   curr = new bfs_queue_struct;
   curr->room = room;
   curr->next = room_queue;

   room_queue = curr;
}


void clean_room_queue (void) 
{
	bfs_queue_struct	*curr, *curr_next;

	for (curr = room_queue; curr; curr = curr_next) {
		curr->room->UnMarkRoom ();
		curr_next = curr->next;
		delete curr;
	}
	room_queue = NULL;
}


int find_first_step (CRoomIndexData *src, CRoomIndexData *target, int maxdist)
{
   int curr_dir, count;

   if (!src || !target)
   {
      bug ("Illegal value passed to find_first_step (track.c)", 0);
      return BFS_ERROR;
   }

   if (src == target)
      return BFS_ALREADY_THERE;

   if (src->GetArea () != target->GetArea ())
      return BFS_NO_PATH;

   room_enqueue (src);
   src->MarkRoom ();

   /* first, enqueue the first steps, saving which direction we're going. */
   for (curr_dir = 0; curr_dir < 10; curr_dir++)
      if (valid_edge (src, curr_dir))
      {
         toroom (src, curr_dir)->MarkRoom ();
	 room_enqueue (toroom (src, curr_dir));
         bfs_enqueue (toroom (src, curr_dir), curr_dir);
      }

   count = 0;
   while (queue_head) {
      if (++count > maxdist)
      {
	bfs_clear_queue ();
	clean_room_queue ();
	return BFS_NO_PATH;
      }
      if (queue_head->room == target) {
	 curr_dir = queue_head->dir;
	 bfs_clear_queue ();
	 clean_room_queue ();
	 return curr_dir;
      } else {
         for (curr_dir = 0; curr_dir < 10; curr_dir++)
            if (valid_edge (queue_head->room, curr_dir)) {
               toroom (queue_head->room, curr_dir)->MarkRoom ();
	       room_enqueue (toroom (queue_head->room, curr_dir));
	       bfs_enqueue (toroom (queue_head->room, curr_dir),queue_head->dir);
            }
         bfs_dequeue ();
      }
   }
   clean_room_queue ();

   return BFS_NO_PATH;
}


void do_track (CCharacter *ch, char *argument)
{
   CCharacter *vict;
   char arg[MAX_INPUT_LENGTH];
   char buf[MAX_STRING_LENGTH];
   int dir, maxdist;

   if (!ch->IsNpc () && !ch->GetPcData ()->learned [gsn_track])
   {
	ch->SendText ("You do not know of this skill yet.\n\r");
	return;
   }

   one_argument (argument, arg);
   if (arg[0]=='\0') {
      ch->SendText ("Whom are you trying to track?\n\r");
      return;
   }

   WAIT_STATE (ch, SkillTable.GetBeats (gsn_track));

   if (! (vict = get_char_world (ch, arg))) {
      ch->SendText ("You can't find a trail of anyone like that.\n\r");
      return;
   }

   maxdist = 100 + ch->GetLevel () * 30;

   if (!ch->IsNpc ())
     maxdist = (maxdist * ch->GetPcData ()->learned[gsn_track]) / 100;

   dir = find_first_step (ch->GetInRoom (), vict->GetInRoom (), maxdist);

   switch (dir) {
      case BFS_ERROR:
         ch->SendText ("Hmm... something seems to be wrong.\n\r");
         break;
      case BFS_ALREADY_THERE:
         ch->SendText ("You're already in the same room!\n\r");
         break;
      case BFS_NO_PATH:
         sprintf (buf, "You can't sense a trail from here.\n\r");
         ch->SendText (buf);
	 learn_from_failure (ch, gsn_track);
         break;
      default:
         ch->SendTextf ("You sense a trail %s from here...\n\r", dir_name[dir]);
	 learn_from_success (ch, gsn_track);
         break;
   }
}


void found_prey (CCharacter *ch, CCharacter *victim)
{
     char buf[MAX_STRING_LENGTH];
     char victname[MAX_STRING_LENGTH];

     if (victim == NULL)
     {
	bug ("Found_prey: null victim", 0);
	return;
     }

     if (victim->GetInRoom () == NULL)
     {
        bug ("Found_prey: null victim->GetInRoom ()", 0);
        return;
     }

     sprintf (victname, victim->IsNpc () ?
		victim->GetShortDescr () : victim->GetName ());

     if (!can_see (ch, victim))
     {
        if (number_percent () < 90)
	  return;
	switch (number_bits (2))
 	{
	case 0: sprintf (buf, "Don't make me find you, %s!", victname);
		do_say (ch, buf);
	        break;
	case 1: act (AT_ACTION, "$n sniffs around the room for $N.", ch, NULL, victim, TO_NOTVICT);
		act (AT_ACTION, "You sniff around the room for $N.", ch, NULL, victim, TO_CHAR);
		act (AT_ACTION, "$n sniffs around the room for you.", ch, NULL, victim, TO_VICT);
		sprintf (buf, "I can smell your blood!");
		do_say (ch, buf);
		break;
	case 2: sprintf (buf, "I'm going to tear %s apart!", victname);
		do_yell (ch, buf);
		break;
	case 3: do_say (ch, "Just wait until I find you...");
		break;
        }
	return;
     }

     if (ch->GetInRoom ()->IsSafe ())
     {
	if (number_percent () < 90)
	  return;
	switch (number_bits (2))
	{
	case 0:	do_say (ch, "C'mon out, you coward!");
		sprintf (buf, "%s is a bloody coward!", victname);
		do_yell (ch, buf);
		break;
	case 1: sprintf (buf, "Let's take this outside, %s", victname);
		do_say (ch, buf);
		break;
	case 2: sprintf (buf, "%s is a yellow-bellied wimp!", victname);
		do_yell (ch, buf);
		break;
	case 3: act (AT_ACTION, "$n takes a few swipes at $N.", ch, NULL, victim, TO_NOTVICT);
		act (AT_ACTION, "You try to take a few swipes $N.", ch, NULL, victim, TO_CHAR);
		act (AT_ACTION, "$n takes a few swipes at you.", ch, NULL, victim, TO_VICT);
		break;
	}
	return;
     }

     switch (number_bits (2))
     {
     case 0: sprintf (buf, "Your blood is mine, %s!", victname);
	     do_yell (ch, buf);
	     break;
     case 1: sprintf (buf, "Alas, we meet again, %s!", victname);
     	     do_say (ch, buf);
     	     break;
     case 2: sprintf (buf, "What do you want on your tombstone, %s?", victname);
     	     do_say (ch, buf);
     	     break;
     case 3: act (AT_ACTION, "$n lunges at $N from out of nowhere!", ch, NULL, victim, TO_NOTVICT);
	     act (AT_ACTION, "You lunge at $N catching $M off guard!", ch, NULL, victim, TO_CHAR);
	     act (AT_ACTION, "$n lunges at you from out of nowhere!", ch, NULL, victim, TO_VICT);
     }
     stop_hunting (ch);
     set_fighting (ch, victim);
     multi_hit (ch, victim, TYPE_UNDEFINED);
     return;
} 

void hunt_victim (CCharacter *ch)
{
   BOOL found;
   CCharacter *tmp;
   short ret;

   if (!ch || !ch->hunting)
      return;

   /* make sure the char still exists */
   for (found = FALSE, tmp = first_char; tmp && !found; tmp = tmp->GetNext ())
      if (ch->hunting->who == tmp)
         found = TRUE;

   if (!found)
   {
      do_say (ch, "Damn!  My prey is gone!!");
      stop_hunting (ch);
      return;
   }

   if (ch->GetInRoom () == ch->hunting->who->GetInRoom ())
   {
     if (ch->GetFightData ())
       return;
     found_prey (ch, ch->hunting->who);
     return;
   }
   
   ret = find_first_step (ch->GetInRoom (), ch->hunting->who->GetInRoom (), 500 + ch->GetLevel () * 25);
   if (ret<0)
   {
      do_say (ch, "Damn!  Lost my prey!");
      stop_hunting (ch);
      return;
   }
   else
   {
      move_char (ch, get_exit (ch->GetInRoom (), ret), FALSE);
      if (!ch->hunting)
      {
        if (!ch->GetInRoom ())
        {
          char buf[MAX_STRING_LENGTH];
          sprintf (buf, "Hunt_victim: no ch->GetInRoom ()!  Mob #%d, name: %s.  Placing mob in limbo.",
                   ch->GetMobIndex ()->vnum, ch->GetName ());
          bug (buf, 0);
          ch->SendToRoom (RoomTable.GetRoom (SysData.m_RoomLimbo));
          return;
        } 
	do_say (ch, "Damn!  Lost my prey!");
	return;
      }
      if (ch->GetInRoom () == ch->hunting->who->GetInRoom ())
	found_prey (ch, ch->hunting->who);
      return;
   }
}