/************************************************************************* * 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; } }