250 lines
6.6 KiB
C
250 lines
6.6 KiB
C
|
/* ************************************************************************
|
||
|
* File: graph.c Part of CircleMUD *
|
||
|
* Usage: various graph algorithms *
|
||
|
* *
|
||
|
* All rights reserved. See license.doc for complete information. *
|
||
|
* *
|
||
|
* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
|
||
|
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
|
||
|
************************************************************************ */
|
||
|
|
||
|
#include "conf.h"
|
||
|
#include "sysdep.h"
|
||
|
|
||
|
|
||
|
#include "structs.h"
|
||
|
#include "utils.h"
|
||
|
#include "comm.h"
|
||
|
#include "interpreter.h"
|
||
|
#include "handler.h"
|
||
|
#include "db.h"
|
||
|
#include "spells.h"
|
||
|
|
||
|
|
||
|
/* external functions */
|
||
|
ACMD(do_say);
|
||
|
|
||
|
/* external variables */
|
||
|
extern const char *dirs[];
|
||
|
extern int track_through_doors;
|
||
|
|
||
|
/* local functions */
|
||
|
int VALID_EDGE(room_rnum x, int y);
|
||
|
void bfs_enqueue(room_rnum room, int dir);
|
||
|
void bfs_dequeue(void);
|
||
|
void bfs_clear_queue(void);
|
||
|
int find_first_step(room_rnum src, room_rnum target);
|
||
|
ACMD(do_track);
|
||
|
void hunt_victim(struct char_data *ch);
|
||
|
|
||
|
struct bfs_queue_struct {
|
||
|
room_rnum room;
|
||
|
char dir;
|
||
|
struct bfs_queue_struct *next;
|
||
|
};
|
||
|
|
||
|
static struct bfs_queue_struct *queue_head = 0, *queue_tail = 0;
|
||
|
|
||
|
/* Utility macros */
|
||
|
#define MARK(room) (SET_BIT(ROOM_FLAGS(room), ROOM_BFS_MARK))
|
||
|
#define UNMARK(room) (REMOVE_BIT(ROOM_FLAGS(room), ROOM_BFS_MARK))
|
||
|
#define IS_MARKED(room) (ROOM_FLAGGED(room, ROOM_BFS_MARK))
|
||
|
#define TOROOM(x, y) (world[(x)].dir_option[(y)]->to_room)
|
||
|
#define IS_CLOSED(x, y) (EXIT_FLAGGED(world[(x)].dir_option[(y)], EX_CLOSED))
|
||
|
|
||
|
int VALID_EDGE(room_rnum x, int y)
|
||
|
{
|
||
|
if (world[x].dir_option[y] == NULL || TOROOM(x, y) == NOWHERE)
|
||
|
return 0;
|
||
|
if (track_through_doors == FALSE && IS_CLOSED(x, y))
|
||
|
return 0;
|
||
|
if (ROOM_FLAGGED(TOROOM(x, y), ROOM_NOTRACK) || IS_MARKED(TOROOM(x, y)))
|
||
|
return 0;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
void bfs_enqueue(room_rnum room, int dir)
|
||
|
{
|
||
|
struct bfs_queue_struct *curr;
|
||
|
|
||
|
CREATE(curr, struct bfs_queue_struct, 1);
|
||
|
curr->room = room;
|
||
|
curr->dir = dir;
|
||
|
curr->next = 0;
|
||
|
|
||
|
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 = 0;
|
||
|
free(curr);
|
||
|
}
|
||
|
|
||
|
|
||
|
void bfs_clear_queue(void)
|
||
|
{
|
||
|
while (queue_head)
|
||
|
bfs_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_rnum src, room_rnum target)
|
||
|
{
|
||
|
int curr_dir;
|
||
|
room_rnum curr_room;
|
||
|
|
||
|
if (src == NOWHERE || target == NOWHERE || src > top_of_world || target > top_of_world) {
|
||
|
log("SYSERR: Illegal value %d or %d passed to find_first_step. (%s)", src, target, __FILE__);
|
||
|
return (BFS_ERROR);
|
||
|
}
|
||
|
if (src == target)
|
||
|
return (BFS_ALREADY_THERE);
|
||
|
|
||
|
/* clear marks first, some OLC systems will save the mark. */
|
||
|
for (curr_room = 0; curr_room <= top_of_world; curr_room++)
|
||
|
UNMARK(curr_room);
|
||
|
|
||
|
MARK(src);
|
||
|
|
||
|
/* first, enqueue the first steps, saving which direction we're going. */
|
||
|
for (curr_dir = 0; curr_dir < NUM_OF_DIRS; curr_dir++)
|
||
|
if (VALID_EDGE(src, curr_dir)) {
|
||
|
MARK(TOROOM(src, curr_dir));
|
||
|
bfs_enqueue(TOROOM(src, curr_dir), curr_dir);
|
||
|
}
|
||
|
|
||
|
/* now, do the classic BFS. */
|
||
|
while (queue_head) {
|
||
|
if (queue_head->room == target) {
|
||
|
curr_dir = queue_head->dir;
|
||
|
bfs_clear_queue();
|
||
|
return (curr_dir);
|
||
|
} else {
|
||
|
for (curr_dir = 0; curr_dir < NUM_OF_DIRS; curr_dir++)
|
||
|
if (VALID_EDGE(queue_head->room, curr_dir)) {
|
||
|
MARK(TOROOM(queue_head->room, curr_dir));
|
||
|
bfs_enqueue(TOROOM(queue_head->room, curr_dir), queue_head->dir);
|
||
|
}
|
||
|
bfs_dequeue();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (BFS_NO_PATH);
|
||
|
}
|
||
|
|
||
|
|
||
|
/********************************************************
|
||
|
* Functions and Commands which use the above functions. *
|
||
|
********************************************************/
|
||
|
|
||
|
ACMD(do_track)
|
||
|
{
|
||
|
char arg[MAX_INPUT_LENGTH];
|
||
|
struct char_data *vict;
|
||
|
int dir;
|
||
|
|
||
|
/* The character must have the track skill. */
|
||
|
if (IS_NPC(ch) || !GET_SKILL(ch, SKILL_TRACK)) {
|
||
|
send_to_char(ch, "You have no idea how.\r\n");
|
||
|
return;
|
||
|
}
|
||
|
one_argument(argument, arg);
|
||
|
if (!*arg) {
|
||
|
send_to_char(ch, "Whom are you trying to track?\r\n");
|
||
|
return;
|
||
|
}
|
||
|
/* The person can't see the victim. */
|
||
|
if (!(vict = get_char_vis(ch, arg, NULL, FIND_CHAR_WORLD))) {
|
||
|
send_to_char(ch, "No one is around by that name.\r\n");
|
||
|
return;
|
||
|
}
|
||
|
/* We can't track the victim. */
|
||
|
if (AFF_FLAGGED(vict, AFF_NOTRACK)) {
|
||
|
send_to_char(ch, "You sense no trail.\r\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* 101 is a complete failure, no matter what the proficiency. */
|
||
|
if (rand_number(0, 101) >= GET_SKILL(ch, SKILL_TRACK)) {
|
||
|
int tries = 10;
|
||
|
/* Find a random direction. :) */
|
||
|
do {
|
||
|
dir = rand_number(0, NUM_OF_DIRS - 1);
|
||
|
} while (!CAN_GO(ch, dir) && --tries);
|
||
|
send_to_char(ch, "You sense a trail %s from here!\r\n", dirs[dir]);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* They passed the skill check. */
|
||
|
dir = find_first_step(IN_ROOM(ch), IN_ROOM(vict));
|
||
|
|
||
|
switch (dir) {
|
||
|
case BFS_ERROR:
|
||
|
send_to_char(ch, "Hmm.. something seems to be wrong.\r\n");
|
||
|
break;
|
||
|
case BFS_ALREADY_THERE:
|
||
|
send_to_char(ch, "You're already in the same room!!\r\n");
|
||
|
break;
|
||
|
case BFS_NO_PATH:
|
||
|
send_to_char(ch, "You can't sense a trail to %s from here.\r\n", HMHR(vict));
|
||
|
break;
|
||
|
default: /* Success! */
|
||
|
send_to_char(ch, "You sense a trail %s from here!\r\n", dirs[dir]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void hunt_victim(struct char_data *ch)
|
||
|
{
|
||
|
int dir;
|
||
|
byte found;
|
||
|
struct char_data *tmp;
|
||
|
|
||
|
if (!ch || !HUNTING(ch) || FIGHTING(ch))
|
||
|
return;
|
||
|
|
||
|
/* make sure the char still exists */
|
||
|
for (found = FALSE, tmp = character_list; tmp && !found; tmp = tmp->next)
|
||
|
if (HUNTING(ch) == tmp)
|
||
|
found = TRUE;
|
||
|
|
||
|
if (!found) {
|
||
|
char actbuf[MAX_INPUT_LENGTH] = "Damn! My prey is gone!!";
|
||
|
|
||
|
do_say(ch, actbuf, 0, 0);
|
||
|
HUNTING(ch) = NULL;
|
||
|
return;
|
||
|
}
|
||
|
if ((dir = find_first_step(IN_ROOM(ch), IN_ROOM(HUNTING(ch)))) < 0) {
|
||
|
char buf[MAX_INPUT_LENGTH];
|
||
|
|
||
|
snprintf(buf, sizeof(buf), "Damn! I lost %s!", HMHR(HUNTING(ch)));
|
||
|
do_say(ch, buf, 0, 0);
|
||
|
HUNTING(ch) = NULL;
|
||
|
} else {
|
||
|
perform_move(ch, dir, 1);
|
||
|
if (IN_ROOM(ch) == IN_ROOM(HUNTING(ch)))
|
||
|
hit(ch, HUNTING(ch), TYPE_UNDEFINED);
|
||
|
}
|
||
|
}
|