/* File: borg5.c */ /* Purpose: Medium level stuff for the Borg -BEN- */ #include "angband.h" #ifdef ALLOW_BORG #include "borg1.h" #include "borg2.h" #include "borg3.h" #include "borg4.h" #include "borg5.h" /* * This file is responsible for the "borg_update" routine, which is used * to notice changes in the world, and to keep track of terrain features, * objects, monsters, both from visual changes, and from world messages. * * One big thing this file does is "object/monster tracking", which * attempts to gather information about the objects and monsters in * the dungeon, including their identity, location, and state, and * to "follow" them if they "move", and to delete them when they die. * * Information about terrain is used to help plan "flow" paths. Info * about objects and monsters is used to optimize planning paths to * those objects and monsters. Info about monsters is also used for * the "danger" functions, which help avoid dangerous situations. * * Notes: * We assume that monsters/objects can never appear in walls/doors * We count the occurance of invisible or offscreen monsters * We treat "mimics" and "trappers" as "invisible" monsters * * To Do: * Analyze the probable danger of offscreen monsters * Track approximate monster hitpoints (min/max hitpoints?) * If so, factor in regeneration and various spell attacks * Take account of monster "fear" when "following" monsters * * Bugs: * Groups of monsters may induce faulty monster matching * Teleporting monsters may induce faulty monster matching * Monsters which appear singly and in groups are "weird" * The timestamps are not quite in sync properly (?) */ /* * Old values */ static s16b old_depth = -1; /* Previous depth */ static s16b old_chp = -1; /* Previous hit points */ static s16b old_csp = -1; /* Previous spell points */ static int o_w_x = -1; /* Old panel */ static int o_w_y = -1; /* Old panel */ static int o_c_x = -1; /* Old location */ static int o_c_y = -1; /* Old location */ /* * Hack -- message memory */ static s16b auto_msg_len; static s16b auto_msg_siz; static char *auto_msg_buf; static s16b auto_msg_num; static s16b auto_msg_max; static s16b *auto_msg_pos; static s16b *auto_msg_use; /* * Hack -- help identify "unique" monster names */ static int auto_unique_size; /* Number of uniques */ static s16b *auto_unique_what; /* Indexes of uniques */ static cptr *auto_unique_text; /* Names of uniques */ /* * Hack -- help identify "normal" monster names */ static int auto_normal_size; /* Number of normals */ static s16b *auto_normal_what; /* Indexes of normals */ static cptr *auto_normal_text; /* Names of normals */ /* * Hack -- monster/object tracking grids */ typedef struct auto_wank auto_wank; struct auto_wank { byte x; byte y; byte t_a; char t_c; bool is_take; bool is_kill; }; /* * Hack -- object/monster tracking array */ static int auto_wank_num = 0; static auto_wank *auto_wanks; /* * Attempt to guess what kind of object is at the given location. * * This routine should rarely, if ever, return "zero". * * Hack -- we use "base level" instead of "allocation levels". */ static int borg_guess_kind(byte a, char c) { int i, s; int b_i = 0, b_s = 0; /* Find an "acceptable" object */ for (i = 1; i < MAX_K_IDX; i++) { object_kind *k_ptr = &k_info[i]; /* Skip non-objects */ if (!k_ptr->name) continue; /* Base score */ s = 10000; /* Hack -- penalize "extremely" out of depth */ if (k_ptr->level > auto_depth + 50) s = s - 500; /* Hack -- penalize "very" out of depth */ if (k_ptr->level > auto_depth + 15) s = s - 100; /* Hack -- penalize "rather" out of depth */ if (k_ptr->level > auto_depth + 5) s = s - 50; /* Hack -- penalize "somewhat" out of depth */ if (k_ptr->level > auto_depth) s = s - 10; /* Hack -- Penalize "depth miss" */ s = s - ABS(k_ptr->level - auto_depth); /* Hack -- Penalize INSTA_ART items */ if (k_ptr->flags3 & TR3_INSTA_ART) s = s - 1000; /* Hack -- Penalize CURSED items */ if (k_ptr->flags3 & TR3_LIGHT_CURSE) s = s - 5000; /* Hack -- Penalize BROKEN items */ if (k_ptr->cost <= 0) s = s - 5000; /* Verify char */ if (c != k_ptr->x_char) continue; /* Flavored objects */ if (k_ptr->has_flavor) { /* Hack -- penalize "flavored" objects */ s = s - 20; } /* Normal objects */ else { /* Verify attr */ if (a != k_ptr->x_attr) continue; } /* Desire "best" possible score */ if (b_i && (s < b_s)) continue; /* Track it */ b_i = i; b_s = s; } /* Result */ return (b_i); } /* * Delete an old "object" record */ static void borg_delete_take(int i) { auto_grid *ag; auto_take *take = &auto_takes[i]; /* Paranoia -- Already wiped */ if (!take->k_idx) return; /* Note */ borg_note(format("# Forgetting an object '%s' at (%d,%d)", (k_name + k_info[take->k_idx].name), take->x, take->y)); /* Access the grid */ ag = &auto_grids[take->y][take->x]; /* Forget it */ ag->take = 0; /* Kill the object */ WIPE(take, auto_take); /* One less object */ auto_takes_cnt--; /* Wipe goals */ goal = 0; } /* * Determine if an object should be "viewable" */ static bool borg_follow_take_aux(int i, int y, int x) { auto_grid *ag; auto_take *take = &auto_takes[i]; /* Access the grid */ ag = &auto_grids[y][x]; /* Not on-screen */ if (!(ag->info & BORG_OKAY)) return (FALSE); /* Assume viewable */ return (TRUE); } /* * Attempt to "follow" a missing object * * This routine is not called when the player is blind or hallucinating. * * This function just deletes objects which have disappeared. * * We assume that a monster walking onto an object destroys the object. */ static void borg_follow_take(int i) { int ox, oy; auto_take *take = &auto_takes[i]; /* Paranoia */ if (!take->k_idx) return; /* Old location */ ox = take->x; oy = take->y; /* Out of sight */ if (!borg_follow_take_aux(i, oy, ox)) return; /* Note */ borg_note(format("# There was an object '%s' at (%d,%d)", (k_name + k_info[take->k_idx].name), ox, oy)); /* Kill the object */ borg_delete_take(i); } /* * Obtain a new "take" index */ static int borg_new_take(int k_idx, int y, int x) { int i, n = -1; auto_take *take; auto_grid *ag = &auto_grids[y][x]; /* Look for a "dead" object */ for (i = 1; (n < 0) && (i < auto_takes_nxt); i++) { /* Reuse "dead" objects */ if (!auto_takes[i].k_idx) n = i; } /* Allocate a new object */ if ((n < 0) && (auto_takes_nxt < 256)) { /* Acquire the entry, advance */ n = auto_takes_nxt++; } /* Hack -- steal an old object */ if (n < 0) { /* Note */ borg_note("# Too many objects"); /* Hack -- Pick a random object */ n = rand_int(auto_takes_nxt-1) + 1; /* Delete it */ borg_delete_take(n); } /* Count new object */ auto_takes_cnt++; /* Obtain the object */ take = &auto_takes[n]; /* Save the kind */ take->k_idx = k_idx; /* Save the location */ take->x = x; take->y = y; /* Save the index */ ag->take = n; /* Timestamp */ take->when = c_t; /* Note */ borg_note(format("# Creating an object '%s' at (%d,%d)", (k_name + k_info[take->k_idx].name), take->x, take->y)); /* Wipe goals */ goal = 0; /* Result */ return (n); } /* * Attempt to notice a changing "take" */ static bool observe_take_diff(int y, int x, byte a, char c) { int i, k_idx; auto_take *take; /* Guess the kind */ k_idx = borg_guess_kind(a, c); /* Oops */ if (!k_idx) return (FALSE); /* Make a new object */ i = borg_new_take(k_idx, y, x); /* Get the object */ take = &auto_takes[i]; /* Timestamp */ take->when = c_t; /* Okay */ return (TRUE); } /* * Attempt to "track" a "take" at the given location * Assume that the object has not moved more than "d" grids * Note that, of course, objects are never supposed to move, * but we may want to take account of "falling" missiles later. */ static bool observe_take_move(int y, int x, int d, byte a, char c) { int i, z, ox, oy; object_kind *k_ptr; /* Scan the objects */ for (i = 1; i < auto_takes_nxt; i++) { auto_take *take = &auto_takes[i]; /* Skip dead objects */ if (!take->k_idx) continue; /* Skip assigned objects */ if (take->seen) continue; /* Extract old location */ ox = take->x; oy = take->y; /* Calculate distance */ z = distance(oy, ox, y, x); /* Possible match */ if (z > d) continue; /* Access the kind */ k_ptr = &k_info[take->k_idx]; /* Require matching char */ if (c != k_ptr->x_char) continue; /* Require matching attr */ if (a != k_ptr->x_attr) { /* Ignore "flavored" colors */ if (!k_ptr->has_flavor) continue; } /* Actual movement (?) */ if (z) { /* Update the grids */ auto_grids[take->y][take->x].take = 0; /* Track it */ take->x = x; take->y = y; /* Update the grids */ auto_grids[take->y][take->x].take = i; /* Note */ borg_note(format("# Tracking an object '%s' at (%d,%d) from (%d,%d)", (k_name + k_ptr->name), take->x, take->y, ox, oy)); /* Clear goals */ goal = 0; } /* Timestamp */ take->when = c_t; /* Mark as seen */ take->seen = TRUE; /* Done */ return (TRUE); } /* Oops */ return (FALSE); } /* * Attempt to guess what type of monster is at the given location. * * If we are unable to think of any monster that could be at the * given location, we will assume the monster is a player ghost. * This is a total hack, but may prevent crashes. * * The guess can be improved by the judicious use of a specialized * "attr/char" mapping, especially for unique monsters. Currently, * the Borg does not stoop to such redefinitions. * * We will probably fail to identify "trapper" and "lurker" monsters, * since they look like whatever they are standing on. Now we will * probably just assume they are player ghosts. XXX XXX XXX * * Note that "town" monsters may only appear in town, and in "town", * only "town" monsters may appear, unless we summon or polymorph * a monster while in town, which should never happen. * * To guess which monster is at the given location, we consider every * possible race, keeping the race (if any) with the best "score". * * Certain monster races are "impossible", including town monsters * in the dungeon, dungeon monsters in the town, unique monsters * known to be dead, monsters more than 50 levels out of depth, * and monsters with an impossible char, or an impossible attr. * * Certain aspects of a monster race are penalized, including extreme * out of depth, minor out of depth, clear/multihued attrs. * * Certain aspects of a monster race are rewarded, including monsters * that appear in groups, monsters that reproduce, monsters that have * been seen on this level a lot. * * We are never called for "trapper", "lurker", or "mimic" monsters. * * The actual rewards and penalties probably need some tweaking. * * Hack -- try not to choose "unique" monsters, or we will flee a lot. */ static int borg_guess_race(byte a, char c, bool multi) { int i, s, n; int b_i = 0, b_s = 0; /* Find an "acceptable" monster */ for (i = 1; i < MAX_R_IDX-1; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-monsters */ if (!r_ptr->name) continue; /* Base score */ s = 10000; /* Verify char */ if (c != r_ptr->x_char) continue; /* Clear or multi-hued monsters */ if (r_ptr->flags1 & (RF1_ATTR_MULTI | RF1_ATTR_CLEAR)) { /* Penalize "weird" monsters */ if (!multi) s = s - 1000; } /* Normal monsters */ else { /* Verify multi */ if (multi) continue; /* Verify attr */ if (a != r_ptr->x_attr) continue; } /* Check uniques */ if (r_ptr->flags1 & RF1_UNIQUE) { /* Hack -- Dead uniques stay dead */ if (auto_race_death[i] > 0) continue; /* Prefer normals */ s = s - 10; } /* Hack -- penalize "extremely" out of depth */ if (r_ptr->level > auto_depth + 50) continue; /* Hack -- penalize "very" out of depth */ if (r_ptr->level > auto_depth + 15) s = s - 100; /* Hack -- penalize "rather" out of depth */ if (r_ptr->level > auto_depth + 5) s = s - 50; /* Hack -- penalize "somewhat" out of depth */ if (r_ptr->level > auto_depth) s = s - 10; /* Penalize "depth miss" */ s = s - ABS(r_ptr->level - auto_depth); /* Hack -- Reward group monsters */ if (r_ptr->flags1 & (RF1_FRIEND | RF1_FRIENDS)) s = s + 5; /* Hack -- Reward multiplying monsters */ if (r_ptr->flags2 & RF2_MULTIPLY) s = s + 10; /* Count occurances */ n = auto_race_count[i]; /* Mega-Hack -- Reward occurances XXX XXX XXX */ s = s + (n / 100) + (((n < 100) ? n : 100) / 10) + ((n < 10) ? n : 10); /* Desire "best" possible score */ if (b_i && (s < b_s)) continue; /* Track it */ b_i = i; b_s = s; } /* Success */ if (b_i) return (b_i); /* Message */ borg_note(format("# Assuming player ghost (char %d, attr %d)", c, a)); /* Assume player ghost */ return (MAX_R_IDX - 1); } /* * Attempt to convert a monster name into a race index * * First we check for all possible "unique" monsters, including * ones we have killed, and even if the monster name is "prefixed" * (as in "The Tarrasque" and "The Lernean Hydra"). Since we use * a fast binary search, this is acceptable. * * Otherwise, if the monster is NOT named "The xxx", we assume it * must be a "player ghost" (which is impossible). * * Then, we do a binary search on all "normal" monster names, using * a search which is known to find the last matching entry, if one * exists, and otherwise to find an entry which would follow the * matching entry if there was one, unless the matching entry would * follow all the existing entries, in which case it will find the * final entry in the list. Thus, we can search *backwards* from * the result of the search, and know that we will access all of * the matching entries, as long as we stop once we find an entry * which does not match, since this will catch all cases above. * * Finally, we assume the monster must be a "player ghost" (which * as noted above is impossible), which is a hack, but may prevent * crashes, even if it does induce strange behavior. */ static int borg_guess_race_name(cptr who) { int k, m, n; int i, b_i = 0; int s, b_s = 0; monster_race *r_ptr; /* Start the search */ m = 0; n = auto_unique_size; /* Binary search */ while (m < n - 1) { /* Pick a "middle" entry */ i = (m + n) / 2; /* Search to the right (or here) */ if (strcmp(auto_unique_text[i], who) <= 0) { m = i; } /* Search to the left */ else { n = i; } } /* Check for equality */ if (streq(who, auto_unique_text[m])) { /* Use this monster */ return (auto_unique_what[m]); } /* Assume player ghost */ if (!prefix(who, "The ")) { /* Message */ borg_note(format("# Assuming player ghost (%s)", who)); /* Oops */ return (MAX_R_IDX-1); } /* Skip the prefix */ who += 4; /* Start the search */ m = 0; n = auto_normal_size; /* Binary search */ while (m < n - 1) { /* Pick a "middle" entry */ i = (m + n) / 2; /* Search to the right (or here) */ if (strcmp(auto_normal_text[i], who) <= 0) { m = i; } /* Search to the left */ else { n = i; } } /* Scan possibilities */ for (k = m; k >= 0; k--) { /* Stop when done */ if (!streq(who, auto_normal_text[k])) break; /* Extract the monster */ i = auto_normal_what[k]; /* Access the monster */ r_ptr = &r_info[i]; /* Basic score */ s = 1000; /* Penalize "depth miss" */ s = s - ABS(r_ptr->level - auto_depth); /* Track best */ if (b_i && (s < b_s)) continue; /* Track it */ b_i = i; b_s = s; } /* Success */ if (b_i) return (b_i); /* Message */ borg_note(format("# Assuming player ghost (%s)", who)); /* Oops */ return (MAX_R_IDX-1); } /* * Hack -- Update a "new" monster */ static void borg_update_kill_new(int i) { auto_kill *kill = &auto_kills[i]; monster_race *r_ptr = &r_info[kill->r_idx]; /* Guess at the monster speed */ kill->speed = (r_ptr->speed); #if 0 /* Hack -- assume optimal racial variety */ if (!(r_ptr->flags1 & RF1_UNIQUE)) { /* Hack -- Assume full speed bonus */ kill->speed += (extract_energy[kill->speed] / 10); } #endif /* Extract max hitpoints */ kill->power = r_ptr->hdice * r_ptr->hside; /* Some monsters never sleep */ if (r_ptr->sleep == 0) kill->awake = TRUE; /* Some monsters never move */ if (r_ptr->flags1 & RF1_NEVER_MOVE) kill->awake = TRUE; } /* * Hack -- Update a "old" monster * * We round the player speed down, and the monster speed up, * and we assume maximum racial speed for each monster. */ static void borg_update_kill_old(int i) { int t, e; auto_kill *kill = &auto_kills[i]; /* Player energy per game turn */ e = extract_energy[auto_speed]; /* Game turns per player move */ t = (100 + (e - 1)) / e; /* Monster energy per game turn */ e = extract_energy[kill->speed]; /* Monster moves (times ten) */ kill->moves = (t * e) / 10; } /* * Delete an old "kill" record */ static void borg_delete_kill(int i) { auto_kill *kill = &auto_kills[i]; /* Paranoia -- Already wiped */ if (!kill->r_idx) return; /* Note */ borg_note(format("# Forgetting a monster '%s' at (%d,%d)", (r_name + r_info[kill->r_idx].name), kill->x, kill->y)); /* Update the grids */ auto_grids[kill->y][kill->x].kill = 0; /* Kill the monster */ WIPE(kill, auto_kill); /* One less monster */ auto_kills_cnt--; /* Recalculate danger */ auto_danger_wipe = TRUE; /* Wipe goals */ goal = 0; } /* * Determine if a monster should be "viewable" */ static bool borg_follow_kill_aux(int i, int y, int x) { int d; auto_grid *ag; auto_kill *kill = &auto_kills[i]; monster_race *r_ptr = &r_info[kill->r_idx]; /* Distance to player */ d = distance(c_y, c_x, y, x); /* Too far away */ if (d > MAX_SIGHT) return (FALSE); /* Access the grid */ ag = &auto_grids[y][x]; /* Not on-screen */ if (!(ag->info & BORG_OKAY)) return (FALSE); /* Line of sight */ if (ag->info & BORG_VIEW) { /* Use "illumination" */ if (ag->info & (BORG_LITE | BORG_GLOW)) { /* We can see invisible */ if (my_see_inv) return (TRUE); /* Monster is not invisible */ if (!(r_ptr->flags2 & RF2_INVISIBLE)) return (TRUE); } /* Use "infravision" */ if (d <= my_see_infra) { /* Infravision works on "warm" creatures */ if (!(r_ptr->flags2 & RF2_COLD_BLOOD)) return (TRUE); } } /* Telepathy requires "telepathy" */ if (my_telepathy) { /* Telepathy fails on "strange" monsters */ if (r_ptr->flags2 & RF2_EMPTY_MIND) return (FALSE); if (r_ptr->flags2 & RF2_WEIRD_MIND) return (FALSE); /* Success */ return (TRUE); } /* Nope */ return (FALSE); } /* * Attempt to "follow" a missing monster * * This routine is not called when the player is blind or hallucinating. * * Currently this function is a total hack, but handles the case of only * one possible location (taking it), two adjacent possible locations * (taking the diagonal one), and three adjacent locations with the * central one being a diagonal (taking the diagonal one). * * We should perhaps handle the case of three adjacent locations with * the central one being a non-diagonal (taking the non-diagonal one). * * We should perhaps attempt to take into account "last known direction", * which would allow us to "predict" motion up to walls, and we should * perhaps attempt to take into account the "standard" flee algorithm, * though that feels a little like cheating. */ static void borg_follow_kill(int i) { int j; int x, y; int ox, oy; int dx, b_dx = 0; int dy, b_dy = 0; auto_grid *ag; auto_kill *kill = &auto_kills[i]; /* Paranoia */ if (!kill->r_idx) return; /* Old location */ ox = kill->x; oy = kill->y; /* Out of sight */ if (!borg_follow_kill_aux(i, oy, ox)) return; /* Note */ borg_note(format("# There was a monster '%s' at (%d,%d)", (r_name + r_info[kill->r_idx].name), ox, oy)); /* Prevent silliness */ if (!borg_cave_floor_bold(oy, ox)) { /* Delete the monster */ borg_delete_kill(i); /* Done */ return; } /* Prevent loops */ if (rand_int(100) < 1) { /* Just delete the monster */ borg_delete_kill(i); /* Done */ return; } /* Scan locations */ for (j = 0; j < 8; j++) { /* Access offset */ dx = ddx_ddd[j]; dy = ddy_ddd[j]; /* Access location */ x = ox + dx; y = oy + dy; /* Access the grid */ ag = &auto_grids[y][x]; /* Skip known walls and doors */ if (!borg_cave_floor_grid(ag)) continue; /* Skip known monsters */ if (ag->kill) continue; /* Skip visible grids */ if (borg_follow_kill_aux(i, y, x)) continue; /* Collect the offsets */ b_dx += dx; b_dy += dy; } /* Don't go too far */ if (b_dx < -1) b_dx = -1; else if (b_dx > 1) b_dx = 1; /* Don't go too far */ if (b_dy < -1) b_dy = -1; else if (b_dy > 1) b_dy = 1; /* Access location */ x = ox + b_dx; y = oy + b_dy; /* Access the grid */ ag = &auto_grids[y][x]; /* Avoid walls and doors */ if (!borg_cave_floor_grid(ag)) { /* Just delete the monster */ borg_delete_kill(i); /* Done */ return; } /* Avoid monsters */ if (ag->kill) { /* Just delete the monster */ borg_delete_kill(i); /* Done */ return; } /* Assume awake */ kill->awake = TRUE; /* Update the grids */ auto_grids[kill->y][kill->x].kill = 0; /* Save the old Location */ kill->ox = ox; kill->oy = oy; /* Save the Location */ kill->x = ox + b_dx; kill->y = oy + b_dy; /* Update the grids */ auto_grids[kill->y][kill->x].kill = i; /* Note */ borg_note(format("# Following a monster '%s' to (%d,%d) from (%d,%d)", (r_name + r_info[kill->r_idx].name), kill->x, kill->y, ox, oy)); /* Recalculate danger */ auto_danger_wipe = TRUE; /* Clear goals */ goal = 0; } /* * Obtain a new "kill" index */ static int borg_new_kill(int r_idx, int y, int x) { int i, n = -1; auto_kill *kill; /* Look for a "dead" monster */ for (i = 1; (n < 0) && (i < auto_kills_nxt); i++) { /* Skip real entries */ if (!auto_kills[i].r_idx) n = i; } /* Allocate a new monster */ if ((n < 0) && (auto_kills_nxt < 256)) { /* Acquire the entry, advance */ n = auto_kills_nxt++; } /* Hack -- steal an old monster */ if (n < 0) { /* Note */ borg_note("# Too many monsters"); /* Hack -- Pick a random monster */ n = rand_int(auto_kills_nxt-1) + 1; /* Kill it */ borg_delete_kill(n); } /* Count the monsters */ auto_kills_cnt++; /* Access the monster */ kill = &auto_kills[n]; /* Save the race */ kill->r_idx = r_idx; /* Location */ kill->ox = kill->x = x; kill->oy = kill->y = y; /* Update the grids */ auto_grids[kill->y][kill->x].kill = n; /* Timestamp */ kill->when = c_t; /* Update the monster */ borg_update_kill_new(n); /* Update the monster */ borg_update_kill_old(n); /* Recalculate danger */ auto_danger_wipe = TRUE; /* Clear goals */ goal = 0; /* Note */ borg_note(format("# Creating a monster '%s' at (%d,%d)", (r_name + r_info[kill->r_idx].name), kill->x, kill->y)); /* Wipe goals */ goal = 0; /* Return the monster */ return (n); } /* * Attempt to notice a changing "kill" */ static bool observe_kill_diff(int y, int x, byte a, char c) { int i, r_idx; auto_kill *kill; /* Guess the race */ r_idx = borg_guess_race(a, c, FALSE); /* Oops */ /* if (!r_idx) return (FALSE); */ /* Create a new monster */ i = borg_new_kill(r_idx, y, x); /* Get the object */ kill = &auto_kills[i]; /* Timestamp */ kill->when = c_t; /* Done */ return (TRUE); } /* * Attempt to "track" a "kill" at the given location * Assume that the monster moved at most 'd' grids. * If "flag" is TRUE, allow monster "conversion" */ static bool observe_kill_move(int y, int x, int d, byte a, char c, bool flag) { int i, z, ox, oy; int r_idx; auto_kill *kill; monster_race *r_ptr; /* Look at the monsters */ for (i = 1; i < auto_kills_nxt; i++) { kill = &auto_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Skip assigned monsters */ if (kill->seen) continue; /* Old location */ ox = kill->x; oy = kill->y; /* Calculate distance */ z = distance(oy, ox, y, x); /* Verify distance */ if (z > d) continue; /* Verify "reasonable" motion, if allowed */ if (!flag && (z > (kill->moves / 10) + 1)) continue; /* Access the monster race */ r_ptr = &r_info[kill->r_idx]; /* Verify matching char */ if (c != r_ptr->x_char) continue; /* Verify matching attr */ if (a != r_ptr->x_attr) { /* Require matching attr (for normal monsters) */ if (!(r_ptr->flags1 & (RF1_ATTR_MULTI | RF1_ATTR_CLEAR))) { /* Require flag */ if (!flag) continue; /* Never flicker known monsters */ if (kill->known) continue; /* Find a multi-hued monster */ r_idx = borg_guess_race(a, c, TRUE); /* Handle failure */ if (r_idx == MAX_R_IDX - 1) continue; /* Note */ borg_note(format("# Flickering monster '%s' at (%d,%d)", (r_name + r_info[r_idx].name), x, y)); /* Note */ borg_note(format("# Converting a monster '%s' at (%d,%d)", (r_name + r_info[kill->r_idx].name), kill->x, kill->y)); /* Change the race */ kill->r_idx = r_idx; /* Update the monster */ borg_update_kill_new(i); /* Update the monster */ borg_update_kill_old(i); /* Recalculate danger */ auto_danger_wipe = TRUE; /* Clear goals */ goal = 0; } } /* Actual movement */ if (z) { /* Assume awake */ kill->awake = TRUE; /* Update the grids */ auto_grids[kill->y][kill->x].kill = 0; /* Save the old Location */ kill->ox = kill->x; kill->oy = kill->y; /* Save the Location */ kill->x = x; kill->y = y; /* Update the grids */ auto_grids[kill->y][kill->x].kill = i; /* Note */ borg_note(format("# Tracking a monster '%s' at (%d,%d) from (%d,%d)", (r_name + r_ptr->name), kill->x, kill->y, ox, oy)); /* Recalculate danger */ auto_danger_wipe = TRUE; /* Clear goals */ goal = 0; } /* Note when last seen */ kill->when = c_t; /* Update the monster */ borg_update_kill_old(i); /* Mark as seen */ kill->seen = TRUE; /* Done */ return (TRUE); } /* Oops */ return (FALSE); } /* * Calculate base danger from a spell attack by an invisible monster * * We attempt to take account of various resistances, both in * terms of actual damage, and special effects, as appropriate. */ static int borg_fear_spell(int i) { int z = 0; int p = 0; int ouch = 0; /* Damage taken */ if (old_chp > auto_chp) ouch = (old_chp - auto_chp) * 2; /* Check the spell */ switch (i) { case 96+0: /* RF4_SHRIEK */ p += 10; break; case 96+1: /* RF4_XXX2X4 */ break; case 96+2: /* RF4_XXX3X4 */ break; case 96+3: /* RF4_XXX4X4 */ break; case 96+4: /* RF4_ARROW_1 */ z = (1 * 6); break; case 96+5: /* RF4_ARROW_2 */ z = (3 * 6); break; case 96+6: /* RF4_ARROW_3 */ z = (5 * 6); break; case 96+7: /* RF4_ARROW_4 */ z = (7 * 6); break; case 96+8: /* RF4_BR_ACID */ if (my_immune_acid) break; z = ouch; p += 40; break; case 96+9: /* RF4_BR_ELEC */ if (my_immune_elec) break; z = ouch; p += 20; break; case 96+10: /* RF4_BR_FIRE */ if (my_immune_fire) break; z = ouch; p += 40; break; case 96+11: /* RF4_BR_COLD */ if (my_immune_cold) break; z = ouch; p += 20; break; case 96+12: /* RF4_BR_POIS */ z = ouch; if (my_resist_pois) break; p += 20; break; case 96+13: /* RF4_BR_NETH */ z = ouch; if (my_resist_nethr) break; p += 50; if (my_hold_life) break; p += 150; break; case 96+14: /* RF4_BR_LITE */ z = ouch; if (my_resist_lite) break; if (my_resist_blind) break; p += 20; break; case 96+15: /* RF4_BR_DARK */ z = ouch; if (my_resist_dark) break; if (my_resist_blind) break; p += 20; break; case 96+16: /* RF4_BR_CONF */ z = ouch; if (my_resist_confu) break; p += 100; break; case 96+17: /* RF4_BR_SOUN */ z = ouch; if (my_resist_sound) break; p += 50; break; case 96+18: /* RF4_BR_CHAO */ z = ouch; if (my_resist_chaos) break; p += 200; if (my_resist_nethr) break; if (my_hold_life) break; p += 100; if (my_resist_confu) break; p += 50; break; case 96+19: /* RF4_BR_DISE */ z = ouch; if (my_resist_disen) break; p += 500; break; case 96+20: /* RF4_BR_NEXU */ z = ouch; if (my_resist_nexus) break; p += 100; break; case 96+21: /* RF4_BR_TIME */ z = ouch; p += 200; break; case 96+22: /* RF4_BR_INER */ z = ouch; p += 50; break; case 96+23: /* RF4_BR_GRAV */ z = ouch; p += 50; if (my_resist_sound) break; p += 50; break; case 96+24: /* RF4_BR_SHAR */ z = ouch; if (my_resist_shard) break; p += 50; break; case 96+25: /* RF4_BR_PLAS */ z = ouch; if (my_resist_sound) break; p += 50; break; case 96+26: /* RF4_BR_WALL */ z = ouch; if (my_resist_sound) break; p += 50; break; case 96+27: /* RF4_BR_MANA */ /* XXX XXX XXX */ break; case 96+28: /* RF4_XXX5X4 */ break; case 96+29: /* RF4_XXX6X4 */ break; case 96+30: /* RF4_XXX7X4 */ break; case 96+31: /* RF4_XXX8X4 */ break; case 128+0: /* RF5_BA_ACID */ if (my_immune_acid) break; z = ouch; p += 40; break; case 128+1: /* RF5_BA_ELEC */ if (my_immune_elec) break; z = ouch; p += 20; break; case 128+2: /* RF5_BA_FIRE */ if (my_immune_fire) break; z = ouch; p += 40; break; case 128+3: /* RF5_BA_COLD */ if (my_immune_cold) break; z = ouch; p += 20; break; case 128+4: /* RF5_BA_POIS */ z = (12 * 2); if (my_resist_pois) z = (z + 2) / 3; if (my_resist_pois) break; p += 20; break; case 128+5: /* RF5_BA_NETH */ z = ouch; if (my_resist_nethr) break; p += 200; break; case 128+6: /* RF5_BA_WATE */ z = ouch; p += 50; break; case 128+7: /* RF5_BA_MANA */ z = ouch; break; case 128+8: /* RF5_BA_DARK */ z = ouch; if (my_resist_dark) break; if (my_resist_blind) break; p += 20; break; case 128+9: /* RF5_DRAIN_MANA */ if (auto_msp) p += 10; break; case 128+10: /* RF5_MIND_BLAST */ z = 20; break; case 128+11: /* RF5_BRAIN_SMASH */ z = (12 * 15); p += 100; break; case 128+12: /* RF5_CAUSE_1 */ z = (3 * 8); break; case 128+13: /* RF5_CAUSE_2 */ z = (8 * 8); break; case 128+14: /* RF5_CAUSE_3 */ z = (10 * 15); break; case 128+15: /* RF5_CAUSE_4 */ z = (15 * 15); p += 50; break; case 128+16: /* RF5_BO_ACID */ if (my_immune_acid) break; z = ouch; p += 40; break; case 128+17: /* RF5_BO_ELEC */ if (my_immune_elec) break; z = ouch; p += 20; break; case 128+18: /* RF5_BO_FIRE */ if (my_immune_fire) break; z = ouch; p += 40; break; case 128+19: /* RF5_BO_COLD */ if (my_immune_cold) break; z = ouch; p += 20; break; case 128+20: /* RF5_BO_POIS */ /* XXX XXX XXX */ break; case 128+21: /* RF5_BO_NETH */ z = ouch; if (my_resist_nethr) break; p += 200; break; case 128+22: /* RF5_BO_WATE */ z = ouch; p += 20; break; case 128+23: /* RF5_BO_MANA */ z = ouch; break; case 128+24: /* RF5_BO_PLAS */ z = ouch; p += 20; break; case 128+25: /* RF5_BO_ICEE */ z = ouch; p += 20; break; case 128+26: /* RF5_MISSILE */ z = ouch; break; case 128+27: /* RF5_SCARE */ p += 10; break; case 128+28: /* RF5_BLIND */ p += 10; break; case 128+29: /* RF5_CONF */ p += 10; break; case 128+30: /* RF5_SLOW */ p += 5; break; case 128+31: /* RF5_HOLD */ p += 20; break; case 160+0: /* RF6_HASTE */ p += 10; break; case 160+1: /* RF6_XXX1X6 */ break; case 160+2: /* RF6_HEAL */ p += 10; break; case 160+3: /* RF6_XXX2X6 */ break; case 160+4: /* RF6_BLINK */ break; case 160+5: /* RF6_TPORT */ break; case 160+6: /* RF6_XXX3X6 */ break; case 160+7: /* RF6_XXX4X6 */ break; case 160+8: /* RF6_TELE_TO */ p += 20; break; case 160+9: /* RF6_TELE_AWAY */ p += 10; break; case 160+10: /* RF6_TELE_LEVEL */ p += 50; break; case 160+11: /* RF6_XXX5 */ break; case 160+12: /* RF6_DARKNESS */ p += 5; break; case 160+13: /* RF6_TRAPS */ p += 50; break; case 160+14: /* RF6_FORGET */ p += 500; break; case 160+15: /* RF6_XXX6X6 */ break; case 160+16: /* RF6_XXX7X6 */ break; case 160+17: /* RF6_XXX8X6 */ break; case 160+18: /* RF6_S_MONSTER */ p += (auto_depth) * 10; break; case 160+19: /* RF6_S_MONSTERS */ p += (auto_depth) * 20; break; case 160+20: /* RF6_S_ANT */ p += (auto_depth) * 20; break; case 160+21: /* RF6_S_SPIDER */ p += (auto_depth) * 20; break; case 160+22: /* RF6_S_HOUND */ p += (auto_depth) * 20; break; case 160+23: /* RF6_S_HYDRA */ p += (auto_depth) * 20; break; case 160+24: /* RF6_S_ANGEL */ p += (auto_depth) * 30; break; case 160+25: /* RF6_S_DEMON */ p += (auto_depth) * 30; break; case 160+26: /* RF6_S_UNDEAD */ p += (auto_depth) * 30; break; case 160+27: /* RF6_S_DRAGON */ p += (auto_depth) * 30; break; case 160+28: /* RF6_S_HI_UNDEAD */ p += (auto_depth) * 50; break; case 160+29: /* RF6_S_HI_DRAGON */ p += (auto_depth) * 50; break; case 160+30: /* RF6_S_WRAITH */ p += (auto_depth) * 50; break; case 160+31: /* RF6_S_UNIQUE */ p += (auto_depth) * 50; break; } /* Notice damage */ return (p + z); } /* * Increase the "region danger" */ static void borg_fear_grid(int y, int x, int k) { int x0, y0, x1, x2, y1, y2; /* Message */ borg_note(format("# Fearing grid (%d,%d) value %d", x, y, k)); /* Current region */ y0 = (y/11); x0 = (x/11); /* Nearby regions */ y1 = (y0 > 0) ? (y0 - 1) : 0; x1 = (x0 > 0) ? (x0 - 1) : 0; y2 = (x0 < 5) ? (x0 + 1) : 5; x2 = (x0 < 17) ? (x0 + 1) : 17; /* Collect "fear", limit to 10000, spread around */ auto_fear_region[y0][x0] = MIN(auto_fear_region[y0][x0] + k * 3, 10000); auto_fear_region[y0][x1] = MIN(auto_fear_region[y0][x1] + k * 2, 10000); auto_fear_region[y0][x2] = MIN(auto_fear_region[y0][x2] + k * 2, 10000); auto_fear_region[y1][x0] = MIN(auto_fear_region[y1][x0] + k * 2, 10000); auto_fear_region[y2][x0] = MIN(auto_fear_region[y2][x0] + k * 2, 10000); auto_fear_region[y1][x1] = MIN(auto_fear_region[y1][x1] + k * 1, 10000); auto_fear_region[y1][x2] = MIN(auto_fear_region[y1][x2] + k * 1, 10000); auto_fear_region[y2][x1] = MIN(auto_fear_region[y2][x1] + k * 1, 10000); auto_fear_region[y2][x2] = MIN(auto_fear_region[y2][x1] + k * 1, 10000); } /* * Attempt to locate a monster which could explain a message involving * the given monster name, near the given location, up to the given * distance from the given location. * * Invisible monsters, bizarre monsters, and unexplainable monsters are * all treated the same way, and should eventually contribute some amount * of basic "fear" to the current region. * * First, we attempt to convert "similar" objects into the desired monster, * then we attempt to convert "similar" monsters into the desired monster, * then we attempt to match an existing monster, and finally, we give up. * * XXX XXX XXX Hack -- To prevent fatal situations, every time we think * there may be a monster nearby, we look for a nearby object which could * be the indicated monster, and convert it into that monster. This allows * us to correctly handle a room full of multiplying clear mushrooms. * * XXX XXX XXX When surrounded by multiple monsters of the same type, * we will ascribe most messages to one of those monsters, and ignore * the existance of all the other similar monsters. * * XXX XXX XXX Currently, confusion may cause messages to be ignored. */ static bool borg_locate_kill(cptr who, int y, int x, int r) { int i, d, r_idx; int b_i, b_d; auto_take *take; auto_kill *kill; object_kind *k_ptr; monster_race *r_ptr; /* Handle invisible monsters */ if (streq(who, "It") || streq(who, "Someone") || streq(who, "Something")) { /* Note */ borg_note("# Invisible monster nearby"); /* Ignore */ return (0); } /* Guess the monster race */ r_idx = borg_guess_race_name(who); /* Access the monster race */ r_ptr = &r_info[r_idx]; /* Note */ borg_note(format("# There is a monster '%s' within %d grids of %d,%d", (r_name + r_ptr->name), r, x, y)); /* Hack -- count racial appearances */ if (auto_race_count[r_idx] < MAX_SHORT) auto_race_count[r_idx]++; /* Handle trappers and lurkers and mimics */ if (r_ptr->flags1 & (RF1_CHAR_CLEAR | RF1_CHAR_MULTI)) { /* Note */ borg_note("# Bizarre monster nearby"); /* Ignore */ return (0); } /*** Hack -- Find a similar object ***/ /* Nothing yet */ b_i = -1; b_d = 999; /* Scan the objects */ for (i = 1; i < auto_takes_nxt; i++) { take = &auto_takes[i]; /* Skip "dead" objects */ if (!take->k_idx) continue; /* Access kind */ k_ptr = &k_info[take->k_idx]; /* Verify char */ if (k_ptr->x_char != r_ptr->x_char) continue; /* Verify attr (unless clear or multi-hued) */ if (!(r_ptr->flags1 & (RF1_ATTR_MULTI | RF1_ATTR_CLEAR))) { /* Verify attr (unless flavored) */ if (!(k_ptr->has_flavor)) { /* Verify attr */ if (k_ptr->x_attr != r_ptr->x_attr) continue; } } /* Calculate distance */ d = distance(take->y, take->x, y, x); /* Skip "wrong" objects */ if (d > r) continue; /* Track closest one */ if (d > b_d) continue; /* Track it */ b_i = i; b_d = d; } /* Found one */ if (b_i >= 0) { take = &auto_takes[b_i]; /* Access kind */ k_ptr = &k_info[take->k_idx]; /* Note */ borg_note(format("# Converting an object '%s' at (%d,%d)", (k_name + k_ptr->name), take->x, take->y)); /* Save location */ x = take->x; y = take->y; /* Delete the object */ borg_delete_take(b_i); /* Make a new monster */ b_i = borg_new_kill(r_idx, y, x); /* Get the monster */ kill = &auto_kills[b_i]; /* Timestamp */ kill->when = c_t; /* Known identity */ if (!r) kill->known = TRUE; /* Assume awake */ kill->awake = TRUE; /* Return the index */ return (b_i); } /*** Hack -- Find a similar monster ***/ /* Nothing yet */ b_i = -1; b_d = 999; /* Scan the monsters */ for (i = 1; i < auto_kills_nxt; i++) { kill = &auto_kills[i]; /* Skip "dead" monsters */ if (!kill->r_idx) continue; /* Skip "matching" monsters */ if (kill->r_idx == r_idx) continue; /* Verify char */ if (r_info[kill->r_idx].x_char != r_ptr->x_char) continue; /* Verify attr (unless clear or multi-hued) */ if (!(r_ptr->flags1 & (RF1_ATTR_MULTI | RF1_ATTR_CLEAR))) { /* Verify attr */ if (r_info[kill->r_idx].x_attr != r_ptr->x_attr) continue; } /* Distance away */ d = distance(kill->y, kill->x, y, x); /* Check distance */ if (d > r) continue; /* Track closest one */ if (d > b_d) continue; /* Track it */ b_i = i; b_d = d; } /* Found one */ if (b_i >= 0) { kill = &auto_kills[b_i]; /* Note */ borg_note(format("# Converting a monster '%s' at (%d,%d)", (r_name + r_info[kill->r_idx].name), kill->x, kill->y)); /* Change the race */ kill->r_idx = r_idx; /* Update the monster */ borg_update_kill_new(b_i); /* Update the monster */ borg_update_kill_old(b_i); /* Known identity */ if (!r) kill->known = TRUE; /* Wake the monster */ kill->awake = TRUE; /* Recalculate danger */ auto_danger_wipe = TRUE; /* Clear goals */ goal = 0; /* Index */ return (b_i); } /*** Hack -- Find an existing monster ***/ /* Nothing yet */ b_i = -1; b_d = 999; /* Scan the monsters */ for (i = 1; i < auto_kills_nxt; i++) { kill = &auto_kills[i]; /* Skip "dead" monsters */ if (!kill->r_idx) continue; /* Skip "different" monsters */ if (kill->r_idx != r_idx) continue; /* Distance away */ d = distance(kill->y, kill->x, y, x); /* Check distance */ if (d > r) continue; /* Track closest one */ if (d > b_d) continue; /* Track it */ b_i = i; b_d = d; } /* Found one */ if (b_i >= 0) { kill = &auto_kills[b_i]; /* Note */ borg_note(format("# Matched a monster '%s' at (%d,%d)", (r_name + r_info[kill->r_idx].name), kill->x, kill->y)); /* Known identity */ if (!r) kill->known = TRUE; /* Wake the monster */ kill->awake = TRUE; /* Index */ return (b_i); } /*** Oops ***/ /* Note */ borg_note(format("# Ignoring a monster '%s' near (%d,%d)", (r_name + r_ptr->name), x, y)); /* Oops */ return (0); } /* * Notice the "death" of a monster */ static void borg_count_death(int i) { int r_idx; auto_kill *kill = &auto_kills[i]; /* Access race */ r_idx = kill->r_idx; /* Hack -- count racial deaths */ if (auto_race_death[r_idx] < MAX_SHORT) auto_race_death[r_idx]++; } /* * Handle "detection" spells and "call lite" * * Note that we must use the "old" player location */ static bool borg_handle_self(cptr str) { int i; int q_x, q_y; /* Extract panel */ q_x = o_w_x / 33; q_y = o_w_y / 11; /* Handle failure */ if (auto_failure) { borg_note("# Something failed"); } /* Handle "call lite" */ else if (prefix(str, "lite")) { /* Message */ borg_note(format("# Called lite at (%d,%d)", o_c_x, o_c_y)); /* Hack -- convert torch-lit grids to perma-lit grids */ for (i = 0; i < auto_lite_n; i++) { int x = auto_lite_x[i]; int y = auto_lite_y[i]; /* Get the grid */ auto_grid *ag = &auto_grids[y][x]; /* Mark as perma-lit */ ag->info |= BORG_GLOW; /* Mark as not dark */ ag->info &= ~BORG_DARK; } } /* Handle "detect walls" */ else if (prefix(str, "wall")) { /* Message */ borg_note(format("# Detected walls (%d,%d to %d,%d)", q_x, q_y, q_x+1, q_y+1)); /* Mark detected walls */ auto_detect_wall[q_y+0][q_x+0] = TRUE; auto_detect_wall[q_y+0][q_x+1] = TRUE; auto_detect_wall[q_y+1][q_x+0] = TRUE; auto_detect_wall[q_y+1][q_x+1] = TRUE; } /* Handle "detect traps" */ else if (prefix(str, "trap")) { /* Message */ borg_note(format("# Detected traps (%d,%d to %d,%d)", q_x, q_y, q_x+1, q_y+1)); /* Mark detected traps */ auto_detect_trap[q_y+0][q_x+0] = TRUE; auto_detect_trap[q_y+0][q_x+1] = TRUE; auto_detect_trap[q_y+1][q_x+0] = TRUE; auto_detect_trap[q_y+1][q_x+1] = TRUE; } /* Handle "detect doors" */ else if (prefix(str, "door")) { /* Message */ borg_note(format("# Detected doors (%d,%d to %d,%d)", q_x, q_y, q_x+1, q_y+1)); /* Mark detected doors */ auto_detect_door[q_y+0][q_x+0] = TRUE; auto_detect_door[q_y+0][q_x+1] = TRUE; auto_detect_door[q_y+1][q_x+0] = TRUE; auto_detect_door[q_y+1][q_x+1] = TRUE; } /* Handle "detect traps and doors" */ else if (prefix(str, "both")) { /* Message */ borg_note(format("# Detected traps and doors (%d,%d to %d,%d)", q_x, q_y, q_x+1, q_y+1)); /* Mark detected traps */ auto_detect_trap[q_y+0][q_x+0] = TRUE; auto_detect_trap[q_y+0][q_x+1] = TRUE; auto_detect_trap[q_y+1][q_x+0] = TRUE; auto_detect_trap[q_y+1][q_x+1] = TRUE; /* Mark detected doors */ auto_detect_door[q_y+0][q_x+0] = TRUE; auto_detect_door[q_y+0][q_x+1] = TRUE; auto_detect_door[q_y+1][q_x+0] = TRUE; auto_detect_door[q_y+1][q_x+1] = TRUE; } /* Done */ return (TRUE); } /* * Update the Borg based on the current "map" */ static void borg_forget_map(void) { int x, y; auto_grid *ag; #ifdef BORG_ROOMS /* Wipe the "rooms" */ borg_wipe_rooms(); #endif /* Clean up the grids */ for (y = 0; y < AUTO_MAX_Y; y++) { for (x = 0; x < AUTO_MAX_X; x++) { /* Access the grid */ ag = &auto_grids[y][x]; /* Wipe it */ WIPE(ag, auto_grid); /* Lay down the outer walls */ ag->feat = FEAT_PERM_SOLID; } } /* Clean up the grids */ for (y = 1; y < AUTO_MAX_Y-1; y++) { for (x = 1; x < AUTO_MAX_X-1; x++) { /* Access the grid */ ag = &auto_grids[y][x]; /* Forget the contents */ ag->feat = FEAT_NONE; /* Hack -- prepare the town */ if (!auto_depth) ag->feat = FEAT_FLOOR; } } /* Reset "auto_data_cost" */ COPY(auto_data_cost, auto_data_hard, auto_data); /* Reset "auto_data_flow" */ COPY(auto_data_flow, auto_data_hard, auto_data); /* Clear "auto_data_know" */ WIPE(auto_data_know, auto_data); /* Clear "auto_data_icky" */ WIPE(auto_data_icky, auto_data); /* Forget the view */ borg_forget_view(); } /* * Update the "map" based on visual info on the screen * * Note that we make assumptions about the grid under the player, * to prevent painful situations such as seeming to be standing * in a wall, or on a trap, etc. * * In general, we use the same "feat" codes as the game itself, but * sometimes we are just guessing (as with "visible traps"), and we * use some special codes, explained below. * * Note that we use the "feat" code of "FEAT_NONE" for grids which * have never been seen, or which, when seen, have always contained * an object or monster. These grids are probably walls, unless * they contain a monster or object, in which case they are probably * floors, unless they contain a monster which passes through walls, * in which case they are probably walls. * * Note that we use the "feat" code of "FEAT_FLOOR" for grids which * were a normal floor last time we checked. These grids may have * changed into non-floor grids recently (via earthquake?), unless * the grid is on the current panel, and is currently "lit" in some * manner, and does not contain a monster. * * Note that we use the "feat" code of "FEAT_INVIS" for grids which * once contained a wall/door, but then contained a monster or object. * These grids are probably floors, unless the grid contains a monster * which can pass through walls, in which case note that missiles and * spells may not affect a monster in the grid. * * Note that we use the other "feat" codes for grids which probably * contain the given feature type, unless several feature types use * the same symbol, in which case we use some "default" code, changing * our guess when messages provide us with more information. This is * especially necessary for distinguishing magma from quartz, and for * distinguishing normal doors from locked doors from jammed doors. * Note that most "feat" codes, even if they are not "guesses", may * not be valid unless the grid is on the current panel. * * We use the "BORG_MARK" flag to mark a grid as having been "observed", * though this may or may not indicate that the "feature" code is known, * since observations of monsters or objects via telepathy and/or remote * detection may trigger this flag. * * We use the "BORG_OKAY" flag to mark a grid as being on the current * panel, which is used for various things, including verifying that * a grid can be used as the destination of a target, and to allow us * to assume that off-panel monsters are not "visible". * * Note the "interesting" code used to learn which floor grids are "dark" * and which are "perma-lit", by tracking those floor grids which appear * to be "lit", and then marking all of these grids which do not appear * to be lit by the torch as "known" to be illuminated, and by marking * any grids which "disappear" or which are displayed as "dark floors" * as "known" to be "dark". This leaves many grids, especially those * lit by the torch, as being neither lit nor dark. * * The basic problem is that, especially with no special options set, * the player has very little direct information about which grids * are perma-lit, since all non-floor grids are memorized when they * are seen, and torch-lit floor grids look just like perma-lit * floor grids. Also, monsters hide any object or feature in their * grid, and objects hide any feature in their grid, and objects are * memorized when they are seen, and monsters can be detected by a * variety of methods, including infravision and telepathy. * * So we ignore most non-floor grids, and we mark any floor grids which * are "known" to be perma-lit as "BORG_GLOW", and any which are "known" * to be dark as "BORG_DARK". These flags are used for many purposes, * most importantly, to determine when "call lite" would be useful, and * to help determine when a monster is standing in a viewable perma-lit * grid, and should thus be "visible", and to determine when the player * has "lite", even though his torch has gone out. * * When a "call lite" spell succeeds, we mark the grids around the * player as "BORG_GLOW" and not "BORG_DARK", but we do not attempt * to "spread" the lite to the entire room, since, in general, it is * not possible to know what grids are in the "room". * * Note that we assume that normally, when the player steps onto * something, it disappears, and turns into a normal floor, unless * the player is stepping onto a grid which is normally "permanent" * (floors, stairs, store doors), in which case it does not change. * * Note that when we encounter a grid which blocks motion, but which * was previously thought to not block motion, we must be sure to * remove it from any "flow" which might be in progress, to prevent * nasty situations in which we attempt to flow into a wall grid * which was thought to be something else, like an unknown grid. * * XXX XXX XXX Running out of lite can induce fatal confusion. */ static void borg_update_map(void) { int i, x, y, dx, dy; auto_grid *ag; byte t_a; byte t_c; /* Analyze the current map panel */ for (dy = 0; dy < SCREEN_HGT; dy++) { /* Direct access XXX XXX XXX */ byte *aa = &(Term->scr->a[dy+1][13]); char *cc = &(Term->scr->c[dy+1][13]); /* Scan the row */ for (dx = 0; dx < SCREEN_WID; dx++) { bool old_wall; bool new_wall; /* Obtain the map location */ x = w_x + dx; y = w_y + dy; /* Save contents */ t_a = *aa++; t_c = *cc++; /* Get the auto_grid */ ag = &auto_grids[y][x]; /* Notice "on-screen" */ ag->info |= BORG_OKAY; /* Notice "knowledge" */ if (t_c != ' ') ag->info |= BORG_MARK; /* Notice the player */ if (t_c == '@') { /* Memorize player location */ c_x = x; c_y = y; /* Hack -- white */ t_a = TERM_WHITE; /* Permanent stuff */ switch (ag->feat) { /* Open doors */ case FEAT_OPEN: case FEAT_BROKEN: t_c = '\''; break; /* Stairs */ case FEAT_LESS: t_c = '<'; break; /* Stairs */ case FEAT_MORE: t_c = '>'; break; /* Shops */ case FEAT_SHOP_HEAD + 0x00: t_c = '1'; break; case FEAT_SHOP_HEAD + 0x01: t_c = '2'; break; case FEAT_SHOP_HEAD + 0x02: t_c = '3'; break; case FEAT_SHOP_HEAD + 0x03: t_c = '4'; break; case FEAT_SHOP_HEAD + 0x04: t_c = '5'; break; case FEAT_SHOP_HEAD + 0x05: t_c = '6'; break; case FEAT_SHOP_HEAD + 0x06: t_c = '7'; break; case FEAT_SHOP_HEAD + 0x07: t_c = '8'; break; default: t_c = '.'; break; } } /* Save the old "wall" or "door" */ old_wall = !borg_cave_floor_grid(ag); /* Analyze symbol */ switch (t_c) { /* Darkness */ case ' ': { /* The grid is not lit */ ag->info &= ~BORG_GLOW; /* Known grids must be dark floors */ if (ag->feat != FEAT_NONE) ag->info |= BORG_DARK; /* Done */ break; } /* Floors */ case '.': { /* Handle "blind" */ if (do_blind) { /* Nothing */ } /* Handle "dark" floors */ else if (t_a == TERM_L_DARK) { /* Dark floor grid */ ag->info |= BORG_DARK; ag->info &= ~BORG_GLOW; } /* Handle "lit" floors */ else { /* Track "lit" floors */ auto_temp_y[auto_temp_n] = y; auto_temp_x[auto_temp_n] = x; auto_temp_n++; } /* Known floor */ ag->feat = FEAT_FLOOR; /* Done */ break; } /* Open doors */ case '\'': { /* Accept broken */ if (ag->feat == FEAT_BROKEN) break; /* Assume normal */ ag->feat = FEAT_OPEN; /* Done */ break; } /* Walls */ case '#': { /* Accept non-granite */ if (ag->feat >= FEAT_WALL_EXTRA) break; /* Assume granite */ ag->feat = FEAT_WALL_EXTRA; /* Done */ break; } /* Seams */ case '%': { /* Accept quartz */ if (ag->feat == FEAT_QUARTZ) break; /* Assume magma */ ag->feat = FEAT_MAGMA; /* Done */ break; } /* Hidden */ case '*': { /* Accept quartz */ if (ag->feat == FEAT_QUARTZ_K) break; /* Assume magma */ ag->feat = FEAT_MAGMA_K; /* Done */ break; } /* Rubble */ case ':': { /* Assume rubble */ ag->feat = FEAT_RUBBLE; /* Done */ break; } /* Doors */ case '+': { /* Accept closed and locked and jammed doors */ if ((ag->feat >= FEAT_DOOR_HEAD) && (ag->feat <= FEAT_DOOR_TAIL)) break; /* Assume easy */ ag->feat = FEAT_DOOR_HEAD + 0x00; /* Done */ break; } /* Traps */ case '^': { /* Assume trap door */ ag->feat = FEAT_TRAP_HEAD + 0x00; /* Done */ break; } /* Up stairs */ case '<': { /* Obvious */ ag->feat = FEAT_LESS; /* Check for an existing "up stairs" */ for (i = 0; i < track_less_num; i++) { /* Stop if we already new about these stairs */ if ((track_less_x[i] == x) && (track_less_y[i] == y)) break; } /* Track the newly discovered "up stairs" */ if ((i == track_less_num) && (i < track_less_size)) { track_less_x[i] = x; track_less_y[i] = y; track_less_num++; } /* Done */ break; } /* Down stairs */ case '>': { /* Obvious */ ag->feat = FEAT_MORE; /* Check for an existing "down stairs" */ for (i = 0; i < track_more_num; i++) { /* We already knew about that one */ if ((track_more_x[i] == x) && (track_more_y[i] == y)) break; } /* Track the newly discovered "down stairs" */ if ((i == track_more_num) && (i < track_more_size)) { track_more_x[i] = x; track_more_y[i] = y; track_more_num++; } /* Done */ break; } /* Store doors */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': { /* Shop type */ i = D2I(t_c) - 1; /* Obvious */ ag->feat = FEAT_SHOP_HEAD + i; /* Save new information */ track_shop_x[i] = x; track_shop_y[i] = y; /* Done */ break; } /* Monsters/Objects */ default: { auto_wank *wank; /* Access next wank, advance */ wank = &auto_wanks[auto_wank_num++]; /* Save some information */ wank->x = x; wank->y = y; wank->t_a = t_a; wank->t_c = t_c; wank->is_take = auto_is_take[(byte)(t_c)]; wank->is_kill = auto_is_kill[(byte)(t_c)]; /* Mark old wall/door grids as probable floor grids */ if (!borg_cave_floor_grid(ag)) ag->feat = FEAT_INVIS; /* Done */ break; } } /* Save the new "wall" or "door" */ new_wall = !borg_cave_floor_grid(ag); /* Notice wall changes */ if (old_wall != new_wall) { /* Remove this grid from any flow */ if (new_wall) auto_data_flow->data[y][x] = 255; /* Remove this grid from any flow */ auto_data_know->data[y][x] = FALSE; /* Remove this grid from any flow */ auto_data_icky->data[y][x] = FALSE; /* Recalculate the view (if needed) */ if (ag->info & BORG_VIEW) auto_update_view = TRUE; /* Recalculate the lite (if needed) */ if (ag->info & BORG_LITE) auto_update_lite = TRUE; } } } } /* * Look at the screen and update the borg * * Uses the "panel" info (w_x, w_y) obtained earlier * * Note that all the "important" messages that occured after our last * action have been "queued" in a usable form. We must attempt to use * these messages to update our knowledge about the world, keeping in * mind that the world may have changed in drastic ways. * * Note that the "c_t" variable corresponds *roughly* to player turns, * except that resting and "repeated" commands count as a single turn, * and "free" moves (including "illegal" moves, such as attempted moves * into walls, or tunneling into monsters) are counted as turns. * * Also note that "c_t" is not incremented until the Borg is about to * do something, so nothing ever has a time-stamp of the current time. * * We rely on the fact that all "perma-lit" grids are memorized when * they are seen, so any grid on the current panel that appears "dark" * must not be perma-lit. * * We rely on the fact that all "objects" are memorized when they are * seen, so any grid on the current panel that appears "dark" must not * have an object in it. But it could have a monster which looks like * an object, but this is very rare. XXX XXX XXX * * XXX XXX XXX The basic problem with timestamping the monsters * and objects is that we often get a message about a monster, and so * we want to timestamp it, but then we cannot use the timestamp to * indicate that the monster has not been "checked" yet. Perhaps * we need to do something like give each monster a "moved" flag, * and clear all the flags to FALSE each turn before tracking. (?) * * Note that when two monsters of the same race are standing next to * each other, and they both move, such that the second monster ends * up where the first one began, we will incorrectly assume that the * first monster stayed still, and either the second monster moved * two spaces, or the second monster disappeared and a third monster * appeared, which is technically possible, if the first monster ate * the second, and then cloned the third. * * There is a problem with monsters which look like objects, namely, * they are assumed to be objects, and then if they leave line of * sight, they disappear, and the Borg assumes that they are gone, * when really they should be identified as monsters. * * XXX XXX Hack -- note the fast direct access to the screen. */ void borg_update(void) { int i, k, x, y, dx, dy; cptr msg; cptr what; auto_grid *ag; bool reset = FALSE; /*** Process objects/monsters ***/ /* Scan monsters */ for (i = 1; i < auto_kills_nxt; i++) { auto_kill *kill = &auto_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Clear flags */ kill->seen = FALSE; kill->used = FALSE; /* Skip recently seen monsters */ if (c_t - kill->when < 2000) continue; /* Note */ borg_note(format("# Expiring a monster '%s' (%d) at (%d,%d)", (r_name + r_info[kill->r_idx].name), kill->r_idx, kill->x, kill->y)); /* Kill the monster */ borg_delete_kill(i); } /* Scan objects */ for (i = 1; i < auto_takes_nxt; i++) { auto_take *take = &auto_takes[i]; /* Skip dead objects */ if (!take->k_idx) continue; /* Clear flags */ take->seen = FALSE; /* Skip recently seen objects */ if (c_t - take->when < 2000) continue; /* Note */ borg_note(format("# Expiring an object '%s' (%d) at (%d,%d)", (k_name + k_info[take->k_idx].name), take->k_idx, take->x, take->y)); /* Kill the object */ borg_delete_take(i); } /*** Handle messages ***/ /* Process messages */ for (i = 0; i < auto_msg_num; i++) { /* Get the message */ msg = auto_msg_buf + auto_msg_pos[i]; /* Note the message */ borg_note(format("# %s (+)", msg)); } /* Process messages */ for (i = 0; i < auto_msg_num; i++) { /* Skip parsed messages */ if (auto_msg_use[i]) continue; /* Get the message */ msg = auto_msg_buf + auto_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Hack -- Handle "SELF" info */ if (prefix(msg, "SELF:")) { (void)borg_handle_self(what); auto_msg_use[i] = 1; } /* Handle "You feel..." */ else if (prefix(msg, "FEELING:")) { auto_feeling = atoi(what); auto_msg_use[i] = 1; } } /* Process messages */ for (i = 0; i < auto_msg_num; i++) { /* Skip parsed messages */ if (auto_msg_use[i]) continue; /* Get the message */ msg = auto_msg_buf + auto_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Handle "You hit xxx." */ if (prefix(msg, "HIT:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0) { auto_msg_use[i] = 2; } } /* Handle "You miss xxx." */ else if (prefix(msg, "MISS:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0) { auto_msg_use[i] = 2; } } /* Handle "You have killed xxx." */ else if (prefix(msg, "KILL:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0) { borg_count_death(k); borg_delete_kill(k); auto_msg_use[i] = 2; } } /* Handle "xxx dies." */ else if (prefix(msg, "DIED:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 3)) != 0) { borg_count_death(k); borg_delete_kill(k); auto_msg_use[i] = 2; } } /* Handle "xxx screams in pain." */ else if (prefix(msg, "PAIN:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 3)) != 0) { auto_msg_use[i] = 2; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__FEAR:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0) { auto_msg_use[i] = 2; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__BOLD:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 0)) != 0) { auto_msg_use[i] = 2; } } } /* Process messages */ for (i = 0; i < auto_msg_num; i++) { /* Skip parsed messages */ if (auto_msg_use[i]) continue; /* Get the message */ msg = auto_msg_buf + auto_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Handle "You hit xxx." */ if (prefix(msg, "HIT:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 1)) != 0) { auto_msg_use[i] = 3; } } /* Handle "You miss xxx." */ else if (prefix(msg, "MISS:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 1)) != 0) { auto_msg_use[i] = 3; } } /* Handle "You have killed xxx." */ else if (prefix(msg, "KILL:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, g_y, g_x, 1)) != 0) { borg_count_death(k); borg_delete_kill(k); auto_msg_use[i] = 3; } } /* Handle "xxx dies." */ else if (prefix(msg, "DIED:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0) { borg_count_death(k); borg_delete_kill(k); auto_msg_use[i] = 3; } } /* Handle "xxx screams in pain." */ else if (prefix(msg, "PAIN:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0) { auto_msg_use[i] = 3; } } /* Handle "xxx hits you." */ else if (prefix(msg, "HIT_BY:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, o_c_y, o_c_x, 1)) != 0) { auto_msg_use[i] = 3; } } /* Handle "xxx misses you." */ else if (prefix(msg, "MISS_BY:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, o_c_y, o_c_x, 1)) != 0) { auto_msg_use[i] = 3; } } /* Handle "sleep" */ else if (prefix(msg, "STATE_SLEEP:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0) { auto_msg_use[i] = 3; } } /* Handle "awake" */ else if (prefix(msg, "STATE_AWAKE:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0) { auto_msg_use[i] = 3; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__FEAR:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0) { auto_msg_use[i] = 3; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__BOLD:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0) { auto_msg_use[i] = 3; } } /* Hack -- Handle "spell" */ else if (prefix(msg, "SPELL_")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, o_c_y, o_c_x, 20)) != 0) { auto_msg_use[i] = 3; } } } /*** Handle new levels ***/ /* Hack -- note new levels */ if (old_depth != auto_depth) { /* Hack -- Restart the clock */ c_t = 1000; /* When level was begun */ auto_began = c_t; /* Not completed */ auto_completed = FALSE; /* New danger thresh-hold */ avoidance = auto_chp; /* Wipe the danger */ auto_danger_wipe = TRUE; /* Update some stuff */ auto_update_view = TRUE; auto_update_lite = TRUE; /* Examine the world */ auto_do_inven = TRUE; auto_do_equip = TRUE; auto_do_spell = TRUE; auto_do_panel = TRUE; auto_do_frame = TRUE; /* Enable some functions */ auto_do_crush_junk = TRUE; auto_do_crush_hole = TRUE; auto_do_crush_slow = TRUE; /* Mega-Hack -- Clear "call lite" stamp */ when_call_lite = 0; /* Mega-Hack -- Clear "wizard lite" stamp */ when_wizard_lite = 0; /* Mega-Hack -- Clear "detect traps" stamp */ when_detect_traps = 0; /* Mega-Hack -- Clear "detect doors" stamp */ when_detect_doors = 0; /* Mega-Hack -- Clear "detect walls" stamp */ when_detect_walls = 0; /* Hack -- Clear "panel" flags */ for (y = 0; y < 6; y++) { for (x = 0; x < 6; x++) { auto_detect_wall[y][x] = FALSE; auto_detect_trap[y][x] = FALSE; auto_detect_door[y][x] = FALSE; } } /* Hack -- Clear "fear" */ for (y = 0; y < 6; y++) { for (x = 0; x < 18; x++) { auto_fear_region[y][x] = 0; } } /* Hack -- Clear "shop visit" stamps */ for (i = 0; i < 8; i++) auto_shops[i].when = 0; /* No goal yet */ goal = 0; /* Hack -- Clear "shop" goals */ goal_shop = goal_ware = goal_item = -1; /* Do not use any stairs */ stair_less = stair_more = FALSE; /* Hack -- cannot rise past town */ if (!auto_depth) goal_rising = FALSE; /* Assume not leaving the level */ goal_leaving = FALSE; /* Assume not fleeing the level */ goal_fleeing = FALSE; /* Assume not ignoring monsters */ goal_ignoring = FALSE; /* No known stairs */ track_less_num = 0; track_more_num = 0; /* No objects here */ auto_takes_cnt = 0; auto_takes_nxt = 1; /* Forget old objects */ C_WIPE(auto_takes, 256, auto_take); /* No monsters here */ auto_kills_cnt = 0; auto_kills_nxt = 1; /* Forget old monsters */ C_WIPE(auto_kills, 256, auto_kill); /* Hack -- Forget race counters */ C_WIPE(auto_race_count, MAX_R_IDX, s16b); /* Forget the map */ borg_forget_map(); /* Reset */ reset = TRUE; /* Save new depth */ old_depth = auto_depth; } /* Handle old level */ else { /* Reduce fear over time */ if (!(c_t % 10)) { for (y = 0; y < 6; y++) { for (x = 0; x < 18; x++) { if (auto_fear_region[y][x]) auto_fear_region[y][x]--; } } } /* Handle changing map panel */ if ((o_w_x != w_x) || (o_w_y != w_y)) { /* Forget the previous map panel */ for (dy = 0; dy < SCREEN_HGT; dy++) { for (dx = 0; dx < SCREEN_WID; dx++) { /* Access the actual location */ x = o_w_x + dx; y = o_w_y + dy; /* Get the auto_grid */ ag = &auto_grids[y][x]; /* Clear the "okay" field */ ag->info &= ~BORG_OKAY; } } } } /*** Update the map ***/ /* Track floors */ auto_temp_n = 0; /* Update the map */ borg_update_map(); /* Reset */ if (reset) { /* Fake old panel */ o_w_x = w_x; o_w_y = w_y; /* Fake old location */ o_c_x = c_x; o_c_y = c_y; /* Fake goal location */ g_x = c_x; g_y = c_y; } /* Player moved */ if ((o_c_x != c_x) || (o_c_y != c_y)) { /* Update view */ auto_update_view = TRUE; /* Update lite */ auto_update_lite = TRUE; } /* Update the view */ if (auto_update_view) { /* Update the view */ borg_update_view(); /* Take note */ auto_update_view = FALSE; } /* Update the lite */ if (auto_update_lite) { /* Update the lite */ borg_update_lite(); /* Take note */ auto_update_lite = FALSE; } /* Examine "lit" grids */ for (i = 0; i < auto_temp_n; i++) { /* Get location */ x = auto_temp_x[i]; y = auto_temp_y[i]; /* Get the auto_grid */ ag = &auto_grids[y][x]; /* Skip torch-lit grids */ if (ag->info & BORG_LITE) continue; /* Assume not dark */ ag->info &= ~BORG_DARK; /* Assume perma-lit */ ag->info |= BORG_GLOW; } /*** Track objects and monsters ***/ /* Pass 1 -- stationary monsters */ for (i = auto_wank_num - 1; i >= 0; i--) { auto_wank *wank = &auto_wanks[i]; /* Track stationary monsters */ if (wank->is_kill && observe_kill_move(wank->y, wank->x, 0, wank->t_a, wank->t_c, FALSE)) { /* Hack -- excise the entry */ auto_wanks[i] = auto_wanks[--auto_wank_num]; } } /* Pass 2 -- stationary objects */ for (i = auto_wank_num - 1; i >= 0; i--) { auto_wank *wank = &auto_wanks[i]; /* Track stationary objects */ if (wank->is_take && observe_take_move(wank->y, wank->x, 0, wank->t_a, wank->t_c)) { /* Hack -- excise the entry */ auto_wanks[i] = auto_wanks[--auto_wank_num]; } } /* Pass 3a -- moving monsters (distance 1) */ for (i = auto_wank_num - 1; i >= 0; i--) { auto_wank *wank = &auto_wanks[i]; /* Track moving monsters */ if (wank->is_kill && observe_kill_move(wank->y, wank->x, 1, wank->t_a, wank->t_c, FALSE)) { /* Hack -- excise the entry */ auto_wanks[i] = auto_wanks[--auto_wank_num]; } } /* Pass 3b -- moving monsters (distance 2) */ for (i = auto_wank_num - 1; i >= 0; i--) { auto_wank *wank = &auto_wanks[i]; /* Track moving monsters */ if (wank->is_kill && observe_kill_move(wank->y, wank->x, 2, wank->t_a, wank->t_c, FALSE)) { /* Hack -- excise the entry */ auto_wanks[i] = auto_wanks[--auto_wank_num]; } } /* Pass 3c -- moving monsters (distance 3) */ for (i = auto_wank_num - 1; i >= 0; i--) { auto_wank *wank = &auto_wanks[i]; /* Track moving monsters */ if (wank->is_kill && observe_kill_move(wank->y, wank->x, 3, wank->t_a, wank->t_c, FALSE)) { /* Hack -- excise the entry */ auto_wanks[i] = auto_wanks[--auto_wank_num]; } } /* Pass 3d -- moving monsters (distance 3, allow changes) */ for (i = auto_wank_num - 1; i >= 0; i--) { auto_wank *wank = &auto_wanks[i]; /* Track moving monsters */ if (wank->is_kill && observe_kill_move(wank->y, wank->x, 3, wank->t_a, wank->t_c, TRUE)) { /* Hack -- excise the entry */ auto_wanks[i] = auto_wanks[--auto_wank_num]; } } /* Pass 4 -- new objects */ for (i = auto_wank_num - 1; i >= 0; i--) { auto_wank *wank = &auto_wanks[i]; /* Track new objects */ if (wank->is_take && observe_take_diff(wank->y, wank->x, wank->t_a, wank->t_c)) { /* Hack -- excise the entry */ auto_wanks[i] = auto_wanks[--auto_wank_num]; } } /* Pass 5 -- new monsters */ for (i = auto_wank_num - 1; i >= 0; i--) { auto_wank *wank = &auto_wanks[i]; /* Track new monsters */ if (wank->is_kill && observe_kill_diff(wank->y, wank->x, wank->t_a, wank->t_c)) { /* Hack -- excise the entry */ auto_wanks[i] = auto_wanks[--auto_wank_num]; } } /*** Handle messages ***/ /* Process messages */ for (i = 0; i < auto_msg_num; i++) { /* Skip parsed messages */ if (auto_msg_use[i]) continue; /* Get the message */ msg = auto_msg_buf + auto_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Handle "xxx dies." */ if (prefix(msg, "DIED:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0) { borg_count_death(k); borg_delete_kill(k); auto_msg_use[i] = 4; } } /* Handle "xxx screams in pain." */ else if (prefix(msg, "PAIN:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0) { auto_msg_use[i] = 4; } } /* Handle "xxx hits you." */ else if (prefix(msg, "HIT_BY:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_y, c_x, 1)) != 0) { auto_msg_use[i] = 4; } } /* Handle "xxx misses you." */ else if (prefix(msg, "MISS_BY:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_y, c_x, 1)) != 0) { auto_msg_use[i] = 4; } } /* Handle "sleep" */ else if (prefix(msg, "STATE_SLEEP:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0) { auto_msg_use[i] = 4; } } /* Handle "awake" */ else if (prefix(msg, "STATE_AWAKE:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0) { auto_msg_use[i] = 4; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__FEAR:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0) { auto_msg_use[i] = 4; } } /* Handle "sleep" */ else if (prefix(msg, "STATE__BOLD:")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0) { auto_msg_use[i] = 4; } } /* Hack -- Handle "spell" */ else if (prefix(msg, "SPELL_")) { /* Attempt to find the monster */ if ((k = borg_locate_kill(what, c_y, c_x, 20)) != 0) { auto_msg_use[i] = 4; } } } /* Process messages */ for (i = 0; i < auto_msg_num; i++) { /* Skip parsed messages */ if (auto_msg_use[i]) continue; /* Get the message */ msg = auto_msg_buf + auto_msg_pos[i]; /* Get the arguments */ what = strchr(msg, ':') + 1; /* Handle "xxx hits you." */ if (prefix(msg, "HIT_BY:")) { borg_fear_grid(c_y, c_x, 4 * ((auto_depth / 5) + 1)); auto_msg_use[i] = 5; } /* Handle "xxx misses you." */ else if (prefix(msg, "MISS_BY:")) { borg_fear_grid(c_y, c_x, 2 * ((auto_depth / 5) + 1)); auto_msg_use[i] = 5; } /* Hack -- Handle "spell" */ else if (prefix(msg, "SPELL_")) { borg_fear_grid(c_y, c_x, borg_fear_spell(atoi(msg+6))); auto_msg_use[i] = 5; } } /* Display messages */ for (i = 0; i < auto_msg_num; i++) { /* Get the message */ msg = auto_msg_buf + auto_msg_pos[i]; /* Final message */ borg_note(format("# %s (%d)", msg, auto_msg_use[i])); } /*** Notice missing monsters ***/ /* Scan the monster list */ for (i = 1; i < auto_kills_nxt; i++) { auto_kill *kill = &auto_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Skip seen monsters */ if (kill->when == c_t) continue; /* Hack -- blind or hallucinating */ if (do_blind || do_image) continue; /* Predict the monster */ borg_follow_kill(i); } /*** Notice missing objects ***/ /* Scan the object list */ for (i = 1; i < auto_takes_nxt; i++) { auto_take *take = &auto_takes[i]; /* Skip dead objects */ if (!take->k_idx) continue; /* Skip seen objects */ if (take->when == c_t) continue; /* Hack -- blind or hallucinating */ if (do_blind || do_image) continue; /* Follow the object */ borg_follow_take(i); } #ifdef BORG_ROOMS /* Analyze the current map panel */ for (dy = 0; dy < SCREEN_HGT; dy++) { for (dx = 0; dx < SCREEN_WID; dx++) { /* Obtain the map location */ x = w_x + dx; y = w_y + dy; /* Get the auto_grid */ ag = &auto_grids[y][x]; /* Clear all "broken" rooms */ if (!borg_cave_floor_grid(ag)) { /* Clear all rooms containing walls */ if (ag->room) { /* Clear all rooms containing walls */ if (borg_clear_room(y, x)) goal = 0; } } /* Create "fake" rooms as needed */ else { /* Mega-Hack -- super-fake rooms */ if (!ag->room) { /* Acquire a new room */ auto_room *ar = borg_free_room(); /* Initialize the room */ ar->x1 = ar->x2 = x; ar->y1 = ar->y2 = y; /* Save the room */ ag->room = ar->self; } } } } /*** Maintain room info ***/ /* Make "fake" rooms for all viewable unknown grids */ for (n = 0; n < auto_view_n; n++) { /* Access the location */ y = auto_view_y[n]; x = auto_view_x[n]; /* Access the grid */ ag = &auto_grids[y][x]; /* Skip walls/doors */ if (!borg_cave_floor_grid(ag)) continue; /* Skip "known" grids */ if (ag->feat != FEAT_NONE) continue; /* Make "fake" rooms as needed */ if (!ag->room) { auto_room *ar; /* Get a new room */ ar = borg_free_room(); /* Initialize the room */ ar->x1 = ar->x2 = x; ar->y1 = ar->y2 = y; /* Save the room */ ag->room = ar->self; } } /* Access the player grid */ ag = &auto_grids[c_y][c_x]; /* Paranoia -- require a self room */ if (!ag->room) { /* Acquire a new room */ auto_room *ar = borg_free_room(); /* Initialize the room */ ar->x1 = ar->x2 = c_x; ar->y1 = ar->y2 = c_y; /* Save the room */ ag->room = ar->self; } /* Build a "bigger" room around the player */ if (borg_build_room(c_y, c_x)) goal = 0; /* Hack */ if (TRUE) { auto_room *ar; /* Mark all the "containing rooms" as visited. */ for (ar = room(1, c_y, c_x); ar; ar = room(0, 0, 0)) { /* Note the visit */ ar->when = c_t; } } #endif /*** Various things ***/ /* Forget goals while "impaired" in any way */ if (do_blind || do_confused || do_afraid || do_image) goal = 0; /* Forget goals while "bleeding" in any way */ if (do_weak || do_poisoned || do_cut || do_stun) goal = 0; /* Forget goals when HP or SP changes */ if ((auto_chp != old_chp) || (auto_csp != old_csp)) goal = 0; /* Save the hit points */ old_chp = auto_chp; /* Save the spell points */ old_csp = auto_csp; /* Forget failure */ auto_failure = FALSE; /* Forget the messages */ auto_msg_len = 0; auto_msg_num = 0; /*** Save old info ***/ /* Save the old "location" */ o_c_x = c_x; o_c_y = c_y; /* Save the old "panel" */ o_w_x = w_x; o_w_y = w_y; /*** Defaults ***/ /* Default "goal" location */ g_x = c_x; g_y = c_y; } /* * Handle various "important" messages * * Actually, we simply "queue" them for later analysis */ void borg_react(cptr msg, cptr buf) { int len; /* Note actual message */ borg_note(format("> %s", msg)); /* Extract length of parsed message */ len = strlen(buf); /* Verify space */ if (auto_msg_num + 1 > auto_msg_max) { borg_oops("too many messages"); return; } /* Verify space */ if (auto_msg_len + len + 1 > auto_msg_siz) { borg_oops("too much messages"); return; } /* Assume not used yet */ auto_msg_use[auto_msg_num] = 0; /* Save the message position */ auto_msg_pos[auto_msg_num] = auto_msg_len; /* Save the message text */ strcpy(auto_msg_buf + auto_msg_len, buf); /* Advance the buf */ auto_msg_len += len + 1; /* Advance the pos */ auto_msg_num++; } /* * Sorting hook -- comp function -- see below * * We use "u" to point to an array of strings, and "v" to point to * an array of indexes, and we sort them together by the strings. */ static bool ang_sort_comp_hook(vptr u, vptr v, int a, int b) { cptr *text = (cptr*)(u); s16b *what = (s16b*)(v); int cmp; /* Compare the two strings */ cmp = (strcmp(text[a], text[b])); /* Strictly less */ if (cmp < 0) return (TRUE); /* Strictly more */ if (cmp > 0) return (FALSE); /* Enforce "stable" sort */ return (what[a] <= what[b]); } /* * Sorting hook -- swap function -- see below * * We use "u" to point to an array of strings, and "v" to point to * an array of indexes, and we sort them together by the strings. */ static void ang_sort_swap_hook(vptr u, vptr v, int a, int b) { cptr *text = (cptr*)(u); s16b *what = (s16b*)(v); cptr texttmp; s16b whattmp; /* Swap "text" */ texttmp = text[a]; text[a] = text[b]; text[b] = texttmp; /* Swap "what" */ whattmp = what[a]; what[a] = what[b]; what[b] = whattmp; } /* * Initialize this file */ void borg_init_5(void) { int i; int size; s16b what[512]; cptr text[512]; /*** Message tracking ***/ /* No chars saved yet */ auto_msg_len = 0; /* Maximum buffer size */ auto_msg_siz = 4096; /* Allocate a buffer */ C_MAKE(auto_msg_buf, auto_msg_siz, char); /* No msg's saved yet */ auto_msg_num = 0; /* Maximum number of messages */ auto_msg_max = 128; /* Allocate array of positions */ C_MAKE(auto_msg_pos, auto_msg_max, s16b); /* Allocate array of use-types */ C_MAKE(auto_msg_use, auto_msg_max, s16b); /*** Object/Monster tracking ***/ /* Array of "wanks" */ C_MAKE(auto_wanks, AUTO_VIEW_MAX, auto_wank); /*** Reset the map ***/ /* Forget the map */ borg_forget_map(); /*** Parse "unique" monster names ***/ /* Start over */ size = 0; /* Collect "unique" monsters */ for (i = 1; i < MAX_R_IDX-1; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-monsters */ if (!r_ptr->name) continue; /* Skip non-unique monsters */ if (!(r_ptr->flags1 & RF1_UNIQUE)) continue; /* Use it */ text[size] = r_name + r_ptr->name; what[size] = i; size++; } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort */ ang_sort(text, what, size); /* Save the size */ auto_unique_size = size; /* Allocate the arrays */ C_MAKE(auto_unique_text, auto_unique_size, cptr); C_MAKE(auto_unique_what, auto_unique_size, s16b); /* Save the entries */ for (i = 0; i < size; i++) auto_unique_text[i] = text[i]; for (i = 0; i < size; i++) auto_unique_what[i] = what[i]; /*** Parse "normal" monster names ***/ /* Start over */ size = 0; /* Collect "normal" monsters */ for (i = 1; i < MAX_R_IDX-1; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-monsters */ if (!r_ptr->name) continue; /* Skip unique monsters */ if (r_ptr->flags1 & RF1_UNIQUE) continue; /* Use it */ text[size] = r_name + r_ptr->name; what[size] = i; size++; } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort */ ang_sort(text, what, size); /* Save the size */ auto_normal_size = size; /* Allocate the arrays */ C_MAKE(auto_normal_text, auto_normal_size, cptr); C_MAKE(auto_normal_what, auto_normal_size, s16b); /* Save the entries */ for (i = 0; i < size; i++) auto_normal_text[i] = text[i]; for (i = 0; i < size; i++) auto_normal_what[i] = what[i]; } #else #ifdef MACINTOSH static int HACK = 0; #endif /* MACINTOSH */ #endif /* ALLOW_BORG */