/*************************************************************************
* Original track.c code from CircleMUD *
* *
* Modified for EmberMUD by Zak *
* *
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
*************************************************************************/
#define TRACK_THROUGH_DOORS
/* #define TRACK_IS_SKILL */
#undef TRACK_IS_SKILL
/* 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.
*/
#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "merc.h"
DECLARE_DO_FUN(do_say );
struct track_queue_struct
{
ROOM_INDEX_DATA *room;
char dir;
struct track_queue_struct *next;
};
static struct track_queue_struct *queue_head = NULL, *queue_tail = NULL;
#define TRACK_NO_PATH -1
#define TRACK_ALREADY_THERE -2
#define TRACK_ERROR -3
/* Utility macros */
#define MARK(room) (SET_BIT((room)->room_flags, ROOM_MARK))
#define UNMARK(room) (REMOVE_BIT((room)->room_flags, ROOM_MARK))
#define IS_MARKED(room) (IS_SET((room)->room_flags, ROOM_MARK))
#define TOROOM(x, y) ((x)->exit[(y)] ? (x)->exit[(y)]->u1.to_room : NULL)
#define IS_CLOSED(x, y) (IS_SET((x)->exit[(y)]->exit_info, EX_CLOSED))
#define IS_SAME_AREA(x,y) ((x)->area == (y)->area)
#ifdef TRACK_THROUGH_DOORS
#define VALID_EDGE(x, y) ((x)->exit[(y)] && \
(TOROOM((x), (y)) != NULL) && \
(!IS_MARKED(TOROOM((x), (y)))) && \
(IS_SAME_AREA((x), TOROOM((x),(y)))))
#else
#define VALID_EDGE(x, y) ((x)->exit[(y)] && \
(TOROOM((x), (y)) != NULL) && \
(!IS_CLOSED((x), (y))) && \
(!IS_MARKED(TOROOM((x), (y)))) && \
(IS_SAME_AREA((x), TOROOM((x),(y)))))
#endif
bool can_go( CHAR_DATA *ch, int dir )
{
ROOM_INDEX_DATA *room;
EXIT_DATA *exit;
if ( !(room = ch->in_room) )
return FALSE;
if ( !(exit = ch->in_room->exit[dir]) )
return FALSE;
if ( !exit->u1.to_room )
return FALSE;
if ( !IS_SAME_AREA(room, exit->u1.to_room) )
return FALSE;
#ifdef TRACK_THROUGH_DOORS
if ( IS_CLOSED(room, dir) )
return FALSE;
#endif
return TRUE;
}
void track_enqueue(ROOM_INDEX_DATA *room, char dir)
{
struct track_queue_struct *curr;
curr = alloc_mem( sizeof( *curr ) );
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 track_dequeue(void)
{
struct track_queue_struct *curr;
curr = queue_head;
if (!(queue_head = queue_head->next))
queue_tail = NULL;
free_mem(curr,sizeof (*curr));
}
void track_clear_queue(void)
{
while (queue_head)
track_dequeue();
}
/* find_first_step: given a source room and a target room, find the first
* step on the shortest path from the source to the target.
*
* Intended usage: in mobile_activity, give a mob a dir to go if they're
* tracking another mob or a PC. Or, a 'track' skill for PCs.
*/
int find_first_step(ROOM_INDEX_DATA *src, ROOM_INDEX_DATA *target)
{
int curr_dir;
ROOM_INDEX_DATA *curr_room;
int vnum;
if (!src || !target)
{
bug("Illegal value passed to find_first_step (track.c)",0);
return TRACK_ERROR;
}
if (src == target)
return TRACK_ALREADY_THERE;
/* clear marks first */
for (vnum = src->area->lvnum; vnum!=src->area->uvnum+1; vnum++)
{
if ((curr_room = get_room_index(vnum))!=NULL) UNMARK(curr_room);
}
MARK(src);
/* first, enqueue the first steps, saving which direction we're going. */
for (curr_dir = 0; curr_dir < 6; curr_dir++)
if (VALID_EDGE(src, curr_dir))
{
MARK(TOROOM(src, curr_dir));
track_enqueue(TOROOM(src, curr_dir), curr_dir);
}
/* now, do the track. */
while (queue_head)
{
if (queue_head->room == target)
{
curr_dir = queue_head->dir;
track_clear_queue();
return curr_dir;
}
else
{
for (curr_dir = 0; curr_dir < 6; curr_dir++)
if (VALID_EDGE(queue_head->room, curr_dir))
{
MARK(TOROOM(queue_head->room, curr_dir));
track_enqueue(TOROOM(queue_head->room, curr_dir),queue_head->dir);
}
track_dequeue();
}
}
return TRACK_NO_PATH;
}
/************************************************************************
* Functions and Commands which use the above fns *
************************************************************************/
void do_track( CHAR_DATA *ch, char *argument )
{
char buf[MAX_STRING_LENGTH];
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *vict;
int dir;
const char *dir_text[]={"north","east","south","west","up","down"};
one_argument(argument, arg);
if (!*arg)
{
send_to_char( "Whom are you trying to track?\n\r", ch);
return;
}
if (!(vict = get_char_world(ch, arg)))
{
send_to_char( "You can't find them.\n\r", ch);
return;
}
dir = find_first_step(ch->in_room, vict->in_room);
switch(dir)
{
case TRACK_ERROR:
send_to_char( "Hmm.. something seems to be wrong.\n\r", ch);
break;
case TRACK_ALREADY_THERE:
send_to_char( "You're already in the same room!!\n\r", ch);
break;
case TRACK_NO_PATH:
sprintf(buf, "You can't sense a trail to %s from here.\n\r",
PERS(vict, ch));
send_to_char( buf, ch);
break;
default:
/* if you want to make this into a skill instead of a command,
the next few lines make it give you a random direction if you
fail the random skill roll.
*/
#ifdef TRACK_IS_SKILL
{
int counter;
if(!IS_NPC(ch) && number_percent() < ch->pcdata->learned[gsn_track])
for( counter = 0; counter < 50; counter++ )
{
dir = number_door();
if ( can_go( ch, dir ) )
break;
dir = -1;
}
else
update_skpell( ch, gsn_track );
if ( dir < 0 )
{
sprintf(buf, "You can't sense a trail to %s from here.\n\r",
PERS(vict,ch) );
send_to_char( buf, ch );
return;
}
}
#endif
sprintf(buf, "You sense a trail %s from here!\n\r", dir_text[dir]);
send_to_char( buf, ch);
break;
}
}