#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <ctype.h> #include <math.h> #include "global.h" #include "bug.h" #include "utils.h" #include "act_move.h" #include "comm.h" #include "constants.h" #include "db.h" #include "handler.h" #include "hash.h" #include "interpreter.h" #include "multiclass.h" #include "opinion.h" #include "spells.h" #include "fight.h" #include "modify.h" #define _TRACKING_C #include "tracking.h" /* predicates for find_path function */ int is_target_room_p(int room, void *tgt_room) { if (DEBUG > 3) log_info("called %s with %d, %08zx", __PRETTY_FUNCTION__, room, (size_t) tgt_room); // return room == (int)tgt_room; return room == (int)(size_t) tgt_room; } int named_object_on_ground(int room, void *c_data) { char *name = c_data; if (DEBUG > 3) log_info("called %s with %d, %08zx", __PRETTY_FUNCTION__, room, (size_t) c_data); return NULL != get_obj_in_list(name, real_roomp(room)->contents); } int named_mobile_in_room(int room, struct hunting_data *c_data) { struct char_data *scan = NULL; if (DEBUG > 3) log_info("called %s with %d, %08zx", __PRETTY_FUNCTION__, room, (size_t) c_data); for (scan = real_roomp(room)->people; scan; scan = scan->next_in_room) if (isname(c_data->name, scan->player.name)) { *(c_data->victim) = scan; return 1; } return 0; } /* Perform breadth first search on rooms from start (in_room) * until end (tgt_room) is reached. Then return the correct * direction to take from start to reach end. * thoth@manatee.cis.ufl.edu * if dvar<0 then search THROUGH closed but not locked doors, * for mobiles that know how to open doors. */ static void donothing(void) { if (DEBUG > 3) log_info("called %s with no arguments", __PRETTY_FUNCTION__); return; } int choose_exit(int in_room, int tgt_room, int depth) { if (DEBUG > 3) log_info("called %s with %d, %d, %d", __PRETTY_FUNCTION__, in_room, tgt_room, depth); // return find_path(in_room, is_target_room_p, (const void *)tgt_room, depth); return find_path(in_room, is_target_room_p, (const void *)(size_t) tgt_room, depth); } int go_direction(struct char_data *ch, int dir) { if (DEBUG > 3) log_info("called %s with %s, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), dir); if (ch->specials.fighting) return 0; if (!IS_SET(EXIT(ch, dir)->exit_info, EX_CLOSED)) { return do_move(ch, "", dir + 1); } else if (IsHumanoid(ch) && !IS_SET(EXIT(ch, dir)->exit_info, EX_LOCKED)) { open_door(ch, dir); } return 0; } int find_path(int in_room, ifuncp predicate, const void *c_data, int depth) { struct room_q *tmp_q = NULL; struct room_q *q_head = NULL; struct room_q *q_tail = NULL; int i = 0; int tmp_room = 0; int count = 0; int thru_doors = 0; struct room_data *herep = NULL; struct room_data *therep = NULL; struct room_direction_data *exitp = NULL; static struct hash_header x_room; if (DEBUG > 2) log_info("called %s with %d, %08zx, %d", __PRETTY_FUNCTION__, in_room, (size_t) c_data, depth); /* * If start = destination we are done */ if ((predicate) (in_room, c_data)) return -1; if (depth < 0) { thru_doors = TRUE; depth = -depth; } else { thru_doors = FALSE; } if (x_room.buckets) /* junk left over from a previous track */ destroy_hash_table(&x_room, donothing); init_hash_table(&x_room, sizeof(int), 2048); hash_enter(&x_room, in_room, (void *)-1); /* * initialize queue */ CREATE(q_head, struct room_q, 1); q_tail = q_head; q_tail->room_nr = in_room; q_tail->next_q = 0; while (q_head) { herep = real_roomp(q_head->room_nr); /* * for each room test all directions */ for (i = 0; i < MAX_NUM_EXITS; i++) { exitp = herep->dir_option[i]; if (exit_ok(exitp, &therep) && (thru_doors ? GO_OK_SMARTER : GO_OK)) { /* * next room */ tmp_room = herep->dir_option[i]->to_room; if (!((predicate) (tmp_room, c_data))) { /* * shall we add room to queue ? */ /* * count determines total breadth and depth */ if (!hash_find(&x_room, tmp_room) && (count < depth) && !IS_SET(RM_FLAGS(tmp_room), DEATH)) { count++; /* * mark room as visted and put on queue */ CREATE(tmp_q, struct room_q, 1); tmp_q->room_nr = tmp_room; tmp_q->next_q = 0; q_tail->next_q = tmp_q; q_tail = tmp_q; /* * ancestor for first layer is the direction */ /* * hash_enter(&x_room, tmp_room, ((int)hash_find(&x_room, q_head->room_nr) == -1) ? (void *)(i * + 1) : hash_find(&x_room, q_head->room_nr)); */ hash_enter(&x_room, tmp_room, ((int)(size_t) hash_find(&x_room, q_head->room_nr) == -1) ? (void *)(size_t) (i + 1) : hash_find(&x_room, q_head->room_nr)); } } else { /* * have reached our goal so free queue */ tmp_room = q_head->room_nr; for (; q_head; q_head = tmp_q) { tmp_q = q_head->next_q; DESTROY(q_head); } /* * return direction if first layer */ // if ((int)hash_find(&x_room, tmp_room) == -1) if ((int)(size_t) hash_find(&x_room, tmp_room) == -1) return (i); else /* else return the ancestor */ // return (-1 + (int)hash_find(&x_room, tmp_room)); return (-1 + (int)(size_t) hash_find(&x_room, tmp_room)); } } } /* * free queue head and point to next entry */ tmp_q = q_head->next_q; DESTROY(q_head); q_head = tmp_q; } /* * couldn't find path */ return (-1); } void MobHunt(struct char_data *ch) { int res = 0; int k = 0; if (DEBUG > 2) log_info("called %s with %s", __PRETTY_FUNCTION__, SAFE_NAME(ch)); if (ch->persist <= 0) { res = choose_exit(ch->in_room, ch->old_room, 2000); if (res > -1) { go_direction(ch, res); } else { if (ch->specials.hunting) { if (ch->specials.hunting->in_room == ch->in_room) { if (CanHate(ch, ch->specials.hunting) && (!IS_AFFECTED(ch->specials.hunting, AFF_HIDE))) { if (check_peaceful(ch, "You CAN'T fight here!\r\n")) { act("$n fumes at $N", TRUE, ch, 0, ch->specials.hunting, TO_ROOM); } else { if (IsHumanoid(ch)) { act("$n screams 'Time to die, $N'", TRUE, ch, 0, ch->specials.hunting, TO_ROOM); } else if (IsAnimal(ch)) { act("$n growls.", TRUE, ch, 0, 0, TO_ROOM); } hit(ch, ch->specials.hunting, 0); return; } } } } REMOVE_BIT(ch->specials.act, ACT_HUNTING); ch->specials.hunting = 0; ch->hunt_dist = 0; } } else if (ch->specials.hunting) { if (ch->hunt_dist <= 50) ch->hunt_dist = 50; for (k = 1; k <= 2 && ch->specials.hunting; k++) { ch->persist -= 1; res = dir_track(ch, ch->specials.hunting); if (res != -1) { go_direction(ch, res); } else { ch->persist = 0; ch->specials.hunting = 0; ch->hunt_dist = 0; } } } else { ch->persist = 0; } } int dir_track(struct char_data *ch, struct char_data *vict) { int code = 0; if (DEBUG > 2) log_info("called %s with %s, %s", __PRETTY_FUNCTION__, SAFE_NAME(ch), SAFE_NAME(vict)); if ((!ch) || (!vict)) return (-1); code = choose_exit(ch->in_room, vict->in_room, ch->hunt_dist); if ((!ch) || (!vict)) return (-1); if (code == -1) { if (ch->in_room == vict->in_room) { cprintf(ch, "\r\nTrack -> You have found your target!\r\n"); } else { cprintf(ch, "\r\nTrack -> You have lost the trail.\r\n"); } return (-1); /* false to continue the hunt */ } else { cprintf(ch, "\r\nTrack -> You see a faint trail %sward\r\n", dirs[code]); return (code); } } int track(struct char_data *ch, struct char_data *vict) { int code = 0; if (DEBUG > 2) log_info("called %s with %s, %s", __PRETTY_FUNCTION__, SAFE_NAME(ch), SAFE_NAME(vict)); if ((!ch) || (!vict)) return (-1); code = choose_exit(ch->in_room, vict->in_room, ch->hunt_dist); if ((!ch) || (!vict)) return (-1); if (ch->in_room == vict->in_room) { cprintf(ch, "\r\nTrack -> You have found your target!\r\n"); return (FALSE); /* false to continue the hunt */ } if (code == -1) { cprintf(ch, "\r\nTrack -> You have lost the trail.\r\n"); return (FALSE); } else { cprintf(ch, "\r\nTrack -> You see a faint trail %sward\r\n", dirs[code]); return (TRUE); } } void do_track(struct char_data *ch, const char *argument, int cmd) { int dist = 0; int code = 0; int cost = 0; char name[MAX_INPUT_LENGTH] = "\0"; struct hunting_data huntd; if (DEBUG) log_info("called %s with %s, %s, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), VNULL(argument), cmd); only_argument(argument, name); dist = ch->skills[SKILL_TRACK].learned; if (IS_SET(ch->player.class, CLASS_RANGER)) { dist *= 2; cost = 15 - (GET_LEVEL(ch, RANGER_LEVEL_IND) / 10); } else if (IS_SET(ch->player.class, CLASS_THIEF)) { cost = 50 - GET_LEVEL(ch, THIEF_LEVEL_IND); dist = dist; } else { dist = dist / 2; cost = 50 - (GET_LEVEL(ch, BestThiefClass(ch)) / 2); } if (!dist) { cprintf(ch, "You do not know of this skill yet!\r\n"); return; } if (GET_MANA(ch) < cost) { cprintf(ch, "You can not seem to concentrate on the trail...\r\n"); return; } GET_MANA(ch) -= cost; switch (GET_RACE(ch)) { case RACE_ELVEN: dist += 10; /* even better */ break; case RACE_DEVIL: case RACE_DEMON: dist = MAX_ROOMS; /* as good as can be */ break; default: break; } if (GetMaxLevel(ch) >= IMMORTAL) dist = MAX_ROOMS; ch->hunt_dist = dist; ch->specials.hunting = 0; huntd.name = name; huntd.victim = &ch->specials.hunting; code = find_path(ch->in_room, (ifuncp)named_mobile_in_room, &huntd, -dist); WAIT_STATE(ch, PULSE_VIOLENCE * 1); if (code == -1) { cprintf(ch, "You are unable to find traces of one.\r\n"); return; } else { if (IS_LIGHT(ch->in_room)) { SET_BIT(ch->specials.act, PLR_HUNTING); cprintf(ch, "You see traces of your quarry to the %s\r\n", dirs[code]); if (ch->skills[SKILL_TRACK].learned < 50) ch->skills[SKILL_TRACK].learned += 2; } else { ch->specials.hunting = 0; cprintf(ch, "It's too dark in here to track...\r\n"); return; } } } char *track_distance(struct char_data *ch, char *mob_name) { static char buf[MAX_STRING_LENGTH] = "No route"; if (!ch) return buf; int this_room = ch->in_room; struct char_data *target_mob = get_char_vis_world(ch, mob_name, NULL); if (!target_mob) return buf; int dest_room = target_mob->in_room; int next_room; int dir = -1; int dx = 0, dy = 0, dz = 0;; if ((dir = choose_exit(this_room, dest_room, -MAX_ROOMS)) >= 0) { while ((dir = choose_exit(this_room, dest_room, -MAX_ROOMS)) > -1) { struct room_data *from_here = NULL; struct room_data *to_here = NULL; from_here = real_roomp(this_room); next_room = from_here->dir_option[dir]->to_room; to_here = real_roomp(next_room); if (!to_here) return buf; switch (dir) { case 0: dy++; break; case 1: dx++; break; case 2: dy--; break; case 3: dx--; break; case 4: dz++; break; case 5: dz--; break; } this_room = next_room; } sprintf(buf, "(%3d%s, %3d%s, %3d%s)", abs(dx), dx < 0 ? "w" : "e", abs(dy), dy < 0 ? "s" : "n", abs(dz), dz < 0 ? "d" : "u"); } return buf; } double heading(double x, double y) { double val = 0.0; if (y == 0.0) { if (x > 0.0) return 90.0; else if (x == 0.0) return 0.0; else return 270.0; } val = atan2(y, x) * 180.0 / 3.14159; if (val > 90.0) return 450.0 - val; else if (val > 0.0) return 90.0 - val; else if (val < -90.0) return (0.0 - val) + 90.0; else return 90.0 - val; } void do_immtrack(struct char_data *ch, const char *argument, int cmd) { char buf[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0"; int loc_nr = 0; int location = 0; struct room_data *target_room = NULL; struct char_data *target_mob = NULL; struct obj_data *target_obj = NULL; int type_of_thing = 0; if (DEBUG) log_info("called %s with %s, %s, %d", __PRETTY_FUNCTION__, SAFE_NAME(ch), VNULL(argument), cmd); if (IS_NPC(ch)) { cprintf(ch, "You cannot cheat like this.\r\n"); return; } only_argument(argument, buf); if (!*buf) { cprintf(ch, "You must supply a room number or a name.\r\n"); return; } if (isdigit(*buf) && NULL == index(buf, '.')) { loc_nr = atoi(buf); if ((target_room = real_roomp(loc_nr))) { location = loc_nr; type_of_thing = 1; } else { cprintf(ch, "No room exists with that number.\r\n"); return; } } else if ((target_mob = get_char_vis_world(ch, buf, NULL))) { if (target_mob->in_room != NOWHERE) { location = target_mob->in_room; type_of_thing = 2; } else { cprintf(ch, "The mob is not available.\r\n"); cprintf(ch, "Try where #.mob to nail its room number.\r\n"); return; } } else if ((target_obj = get_obj_vis_world(ch, buf, NULL))) { if (target_obj->in_room != NOWHERE) { location = target_obj->in_room; type_of_thing = 3; } else { cprintf(ch, "The object is not available.\r\n"); cprintf(ch, "Try where #.object to nail its room number.\r\n"); return; } } else { cprintf(ch, "No such creature or object around.\r\n"); return; } /* * a location has been found. */ if (!real_roomp(location)) { log_error("Massive error in do_goto. Everyone Off NOW."); return; } switch (type_of_thing) { case 1: page_printf(ch, "Route to [#%d] %s:\r\n", target_room->number, target_room->name); break; case 2: page_printf(ch, "Route to [#%d] %s:\r\n", MobVnum(target_mob), SAFE_NAME(target_mob)); break; case 3: page_printf(ch, "Route to [#%d] %s:\r\n", ObjVnum(target_obj), SAFE_ONAME(target_obj)); break; } int this_room = ch->in_room; int dest_room = location; int next_room; int dir = -1; int dx = 0, dy = 0, dz = 0;; int i = 0; int count = 0; if ((dir = choose_exit(this_room, dest_room, -MAX_ROOMS)) < 0) { page_printf(ch, "Rats! Can't find a route!\r\n"); return; } else { count = 0; page_printf(ch, "Starting in [#%5d] %s...\r\n", this_room, real_roomp(this_room)->name); while ((dir = choose_exit(this_room, dest_room, -MAX_ROOMS)) > -1) { struct room_data *from_here = NULL; struct room_data *to_here = NULL; from_here = real_roomp(this_room); next_room = from_here->dir_option[dir]->to_room; to_here = real_roomp(next_room); if (!to_here) { page_printf(ch, "OOPS!\r\n"); return; } count++; page_printf(ch, " %2d %-8s -> [#%5d] %s\r\n", ++i, dirs[dir], next_room, to_here->name); switch (dir) { case 0: dy++; break; case 1: dx++; break; case 2: dy--; break; case 3: dx--; break; case 4: dz++; break; case 5: dz--; break; } this_room = next_room; } page_printf(ch, "Path is %d rooms long, with a distance of (%d%s, %d%s, %d%s),\r\nwhich is roughly %d room lengths away on a heading of %d degrees.\r\n", count, abs(dx), dx < 0 ? "w" : "e", abs(dy), dy < 0 ? "s" : "n", abs(dz), dz < 0 ? "d" : "u", (int)sqrt((double)dx * (double)dx + (double)dy * (double)dy), (int)heading(dx, dy) ); } }