/* ************************************************************************ * File: graph.c Part of CircleMUD * * Usage: various graph algorithms * * * * All rights reserved. See license.doc for complete information. * * * * Copyright (C) 1993 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ /* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */ #define TRACK_THROUGH_DOORS /* 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. */ #include <stdio.h> #include <stdlib.h> #include "structs.h" #include "utils.h" #include "comm.h" #include "interpreter.h" #include "handler.h" #include "db.h" #include "spells.h" int find_first_step(sh_int src, sh_int target); /* Externals */ extern int top_of_world; extern const char *dirs[]; extern struct room_data *world; struct bfs_queue_struct { sh_int 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(world[room].room_flags, BFS_MARK)) #define UNMARK(room) (REMOVE_BIT(world[room].room_flags, BFS_MARK)) #define IS_MARKED(room) (IS_SET(world[room].room_flags, BFS_MARK)) #define TOROOM(x, y) (real_room(world[(x)].dir_option[(y)]->to_room)) #define IS_CLOSED(x, y) (IS_SET(world[(x)].dir_option[(y)]->exit_info, EX_CLOSED)) #ifdef TRACK_THROUGH_DOORS #define VALID_EDGE(x, y) (world[(x)].dir_option[(y)] && \ (TOROOM(x, y) != NOWHERE) && \ (!IS_MARKED(TOROOM(x, y)))) #else #define VALID_EDGE(x, y) (world[(x)].dir_option[(y)] && \ (TOROOM(x, y) != NOWHERE) && \ (!IS_CLOSED(x, y)) && \ (!IS_MARKED(TOROOM(x, y)))) #endif void bfs_enqueue(sh_int room, char 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_track_length will find the length of the shortest route in steps, from src to target rooms. Returns NO_PATH if route exceeds 10 hops or any other error. Closed exits are weighted as two hops.*/ int find_track_length(sh_int src, sh_int target, int max_len) { int dir, len = 0; bool not_done = TRUE; if (src == target) return(0); while(not_done) { dir = find_first_step(src, target); switch (dir) { case BFS_ERROR: case BFS_NO_PATH: len = -1; not_done = FALSE; break; case BFS_ALREADY_THERE: not_done = FALSE; break; default: if (IS_CLOSED(src,dir)) len += 2; else len++; src = TOROOM(src,dir); if (len > max_len) return(-1); break; } } return(len); } /* 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(sh_int src, sh_int target) { int curr_dir; sh_int curr_room; if (src < 0 || src > top_of_world || target < 0 || target > top_of_world) { logg("Illegal value passed to find_first_step (graph.c)"); return BFS_ERROR; } if (src == target) return BFS_ALREADY_THERE; /* clear marks first */ 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 fns * ************************************************************************/ ACMD(do_track) { struct char_data *vict; int dir,num, skill, len; bool found=FALSE; one_argument(argument, arg); if (!*arg && !ch->specials.hunting) { send_to_char("Whom are you trying to track?\r\n", ch); return; } else if (!*arg && ch->specials.hunting) vict = ch->specials.hunting; if (*arg) if (!(vict = get_char_vis(ch, arg))) { send_to_char("You can't sense a trail from here.\r\n", ch); return; } ch->specials.hunting = vict; if (!IS_NPC(ch)) skill = GET_SKILL(ch, SKILL_TRACK) + 5; else skill = GET_LEVEL(ch) / 10 + 10; skill = MIN(30, skill); skill = MAX(5, skill); len = find_track_length(ch->in_room, vict->in_room, skill); if ( len < 0 && (GET_LEVEL(ch) < (LEVEL_BUILDER +1))) { send_to_char("You can't sense a trail from here.\r\n", ch); return; } dir = find_first_step(ch->in_room, vict->in_room); switch(dir) { case BFS_ERROR: send_to_char("Hmm.. something seems to be wrong.\r\n", ch); ch->specials.hunting = 0; break; case BFS_ALREADY_THERE: send_to_char("Success!!\r\n", ch); ch->specials.hunting = 0; break; case BFS_NO_PATH: send_to_char("You can't sense a trail from here.\r\n", ch); ch->specials.hunting = 0; 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.*/ num = number(0, 31); if (GET_LEVEL(ch) > LEVEL_BUILDER) num = 0; if (GET_SKILL(ch, SKILL_TRACK) < num) { do { dir = number(0, NUM_OF_DIRS-1); } while (!EXIT(ch, dir)); sprintf(buf, "You detect a trail %s from here.\r\n", dirs[dir]); send_to_char(buf, ch); } } } void hunt_victim(struct char_data *ch) { ACMD(do_say); ACMD(do_move); extern struct char_data *character_list; char bufr[40]; int dir; byte found; struct char_data *tmp; if (!ch || !ch->specials.hunting) return; /* make sure the char still exists */ for (found = 0, tmp = character_list; tmp && !found; tmp = tmp->next) if (ch->specials.hunting == tmp) found = 1; if (IS_NPC(ch->specials.hunting) && !ch->specials.hunting->specials.fighting) found = 0; if (!found) { if (!IS_NPC(ch->specials.hunting)) do_say(ch, "Damn! My prey is gone!!", 0, 0); else do_say(ch, "Damn! I lost track of them!", 0, 0); ch->specials.hunting = 0; return; } dir = find_first_step(ch->in_room, ch->specials.hunting->in_room); if (dir < 0) { if (ch->in_room == ch->specials.hunting->in_room && hates(ch, ch->specials.hunting)) { if (CAN_SEE(ch, ch->specials.hunting)){ if (CAN_SPEAK(ch)){ sprintf(bufr, "HA! Found you!", HMHR(ch->specials.hunting)); do_say(ch, bufr, 0, 0); sprintf(bufr, "Now die!", HMHR(ch->specials.hunting)); do_say(ch, bufr, 0, 0); } else act("$n growls!", FALSE, ch, 0, 0, TO_ROOM); hit(ch, tmp, TYPE_UNDEFINED); return; } } if (CAN_SPEAK(ch)){ sprintf(bufr, "Damn! Lost %s!", HMHR(ch->specials.hunting)); do_say(ch, bufr, 0, 0); } else act("$n growls!", FALSE, ch, 0, 0, TO_ROOM); ch->specials.hunting = 0; return; } else { do_move(ch, "", dir+1, 0); if (ch->in_room == ch->specials.hunting->in_room && hates(ch, ch->specials.hunting)) { if (CAN_SEE(ch, ch->specials.hunting)){ if (CAN_SPEAK(ch)){ sprintf(bufr, "HA! Found you!", HMHR(ch->specials.hunting)); do_say(ch, bufr, 0, 0); sprintf(bufr, "Now die!", HMHR(ch->specials.hunting)); do_say(ch, bufr, 0, 0); } else act("$n growls!", FALSE, ch, 0, 0, TO_ROOM); hit(ch, tmp, TYPE_UNDEFINED); } } return; } }