/* File: borg6.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" #include "borg6.h" /* * This file is responsible for the low level dungeon goals. * * This includes calculating the danger from monsters, determining * how and when to attack monsters, and calculating "flow" paths * from place to place for various reasons. * * Notes: * We assume that invisible/offscreen monsters are dangerous * We consider physical attacks, missile attacks, spell attacks, * wand attacks, etc, as variations on a single theme. * We take account of monster resistances and susceptibilities * We try not to wake up sleeping monsters by throwing things * * To Do: * Consider various "special" attacks like sleep and slow * * Bugs: * We use missiles and bolt spells against ghosts in walls * Currently the "twitchy()" function is not very smart * We get "twitchy" when we are afraid of the monsters * Annoyance and Danger are very different things (!) */ /* * Given a "source" and "target" locations, extract a "direction", * which will move one step from the "source" towards the "target". * * Note that we use "diagonal" motion whenever possible. * * We return "5" if no motion is needed. */ static int borg_extract_dir(int y1, int x1, int y2, int x2) { /* No movement required */ if ((y1 == y2) && (x1 == x2)) return (5); /* South or North */ if (x1 == x2) return ((y1 < y2) ? 2 : 8); /* East or West */ if (y1 == y2) return ((x1 < x2) ? 6 : 4); /* South-east or South-west */ if (y1 < y2) return ((x1 < x2) ? 3 : 1); /* North-east or North-west */ if (y1 > y2) return ((x1 < x2) ? 9 : 7); /* Paranoia */ return (5); } /* * Given a "source" and "target" locations, extract a "direction", * which will move one step from the "source" towards the "target". * * We prefer "non-diagonal" motion, which allows us to save the * "diagonal" moves for avoiding pillars and other obstacles. * * If no "obvious" path is available, we use "borg_extract_dir()". * * We return "5" if no motion is needed. */ static int borg_goto_dir(int y1, int x1, int y2, int x2) { int d, e; int ay = (y2 > y1) ? (y2 - y1) : (y1 - y2); int ax = (x2 > x1) ? (x2 - x1) : (x1 - x2); /* Default direction */ e = borg_extract_dir(y1, x1, y2, x2); /* Adjacent location, use default */ if ((ay <= 1) && (ay <= 1)) return (e); /* Try south/north (primary) */ if (ay > ax) { d = (y1 < y2) ? 2 : 8; if (borg_cave_floor_bold(y1 + ddy[d], x1 + ddx[d])) return (d); } /* Try east/west (primary) */ if (ay < ax) { d = (x1 < x2) ? 6 : 4; if (borg_cave_floor_bold(y1 + ddy[d], x1 + ddx[d])) return (d); } /* Try diagonal */ d = borg_extract_dir(y1, x1, y2, x2); /* Check for walls */ if (borg_cave_floor_bold(y1 + ddy[d], x1 + ddx[d])) return (d); /* Try south/north (secondary) */ if (ay <= ax) { d = (y1 < y2) ? 2 : 8; if (borg_cave_floor_bold(y1 + ddy[d], x1 + ddx[d])) return (d); } /* Try east/west (secondary) */ if (ay >= ax) { d = (x1 < x2) ? 6 : 4; if (borg_cave_floor_bold(y1 + ddy[d], x1 + ddx[d])) return (d); } /* Circle obstacles */ if (!ay) { /* Circle to the south */ d = (x1 < x2) ? 3 : 1; if (borg_cave_floor_bold(y1 + ddy[d], x1 + ddx[d])) return (d); /* Circle to the north */ d = (x1 < x2) ? 9 : 7; if (borg_cave_floor_bold(y1 + ddy[d], x1 + ddx[d])) return (d); } /* Circle obstacles */ if (!ax) { /* Circle to the east */ d = (y1 < y2) ? 3 : 9; if (borg_cave_floor_bold(y1 + ddy[d], x1 + ddx[d])) return (d); /* Circle to the west */ d = (y1 < y2) ? 1 : 7; if (borg_cave_floor_bold(y1 + ddy[d], x1 + ddx[d])) return (d); } /* Oops */ return (e); } /* * Clear the "flow" information * * This function was once a major bottleneck, so we now use several * slightly bizarre, but highly optimized, memory copying methods. */ static void borg_flow_clear(void) { /* Reset the "cost" fields */ COPY(auto_data_cost, auto_data_hard, auto_data); /* Wipe costs and danger */ if (auto_danger_wipe) { /* Wipe the "know" flags */ WIPE(auto_data_know, auto_data); /* Wipe the "icky" flags */ WIPE(auto_data_icky, auto_data); /* Wipe complete */ auto_danger_wipe = FALSE; } /* Start over */ flow_head = 0; flow_tail = 0; } /* * Spread a "flow" from the "destination" grids outwards * * We fill in the "cost" field of every grid that the player can * "reach" with the number of steps needed to reach that grid, * if the grid is "reachable", and otherwise, with "255", which * is the largest possible value that can be stored in a byte. * * Thus, certain grids which are actually "reachable" but only by * a path which is at least 255 steps in length will thus appear * to be "unreachable", but this is not a major concern. * * We use the "flow" array as a "circular queue", and thus we must * be careful not to allow the "queue" to "overflow". This could * only happen with a large number of distinct destination points, * each several units away from every other destination point, and * in a dungeon with no walls and no dangerous monsters. But this * is technically possible, so we must check for it just in case. * * We do not need a "priority queue" because the cost from grid to * grid is always "one" and we process them in order. If we did * use a priority queue, this function might become unusably slow, * unless we reactivated the "room building" code. * * We handle both "walls" and "danger" by marking every grid which * is "impassible", due to either walls, or danger, as "ICKY", and * marking every grid which has been "checked" as "KNOW", allowing * us to only check the wall/danger status of any grid once. This * provides some important optimization, since many "flows" can be * done before the "ICKY" and "KNOW" flags must be reset. * * Note that the "borg_enqueue_grid()" function should refuse to * enqueue "dangeous" destination grids, but does not need to set * the "KNOW" or "ICKY" flags, since having a "cost" field of zero * means that these grids will never be queued again. In fact, * the "borg_enqueue_grid()" function can be used to enqueue grids * which are "walls", such as "doors" or "rubble". * * This function is extremely expensive, and is a major bottleneck * in the code, due more to internal processing than to the use of * the "borg_danger()" function, especially now that the use of the * "borg_danger()" function has been optimized several times. * * The "optimize" flag allows this function to stop as soon as it * finds any path which reaches the player, since in general we are * looking for paths to destination grids which the player can take, * and we can stop this function as soon as we find any usable path, * since it will always be as short a path as possible. * * We queue the "children" in reverse order, to allow any "diagonal" * neighbors to be processed first, since this may boost efficiency. * * Note that we should recalculate "danger", and reset all "flows" * if we notice that a wall has disappeared, and if one appears, we * must give it a maximal cost, and mark it as "icky", in case it * was currently included in any flow. * * If a "depth" is given, then the flow will only be spread to that * depth, note that the maximum legal value of "depth" is 250. */ static void borg_flow_spread(int depth, bool optimize, bool avoid) { int i; int n, o = 0; int x1, y1; int x, y; /* Now process the queue */ while (flow_head != flow_tail) { /* Extract the next entry */ x1 = auto_flow_x[flow_tail]; y1 = auto_flow_y[flow_tail]; /* Circular queue -- dequeue the next entry */ if (++flow_tail == AUTO_FLOW_MAX) flow_tail = 0; /* Cost (one per movement grid) */ n = auto_data_cost->data[y1][x1] + 1; /* New depth */ if (n > o) { /* Optimize (if requested) */ if (optimize && (n > auto_data_cost->data[c_y][c_x])) break; /* Limit depth */ if (n > depth) break; /* Save */ o = n; } /* Queue the "children" */ for (i = 0; i < 8; i++) { int old_head; auto_grid *ag; /* Neighbor grid */ x = x1 + ddx_ddd[i]; y = y1 + ddy_ddd[i]; /* Skip "reached" grids */ if (auto_data_cost->data[y][x] <= n) continue; /* Access the grid */ ag = &auto_grids[y][x]; /* Avoid "wall" grids (not doors) */ if (ag->feat >= FEAT_SECRET) continue; /* Avoid unknown grids (if requested) */ if (avoid && (ag->feat == FEAT_NONE)) continue; /* Ignore "icky" grids */ if (auto_data_icky->data[y][x]) continue; /* Analyze every grid once */ if (!auto_data_know->data[y][x]) { int p; /* Mark as known */ auto_data_know->data[y][x] = TRUE; /* Get the danger */ p = borg_danger(y, x, 1); /* Dangerous grid */ if (p > avoidance / 2) { /* Mark as icky */ auto_data_icky->data[y][x] = TRUE; /* Ignore this grid */ continue; } } /* Save the flow cost */ auto_data_cost->data[y][x] = n; /* Enqueue that entry */ auto_flow_x[flow_head] = x; auto_flow_y[flow_head] = y; /* Circular queue -- memorize head */ old_head = flow_head; /* Circular queue -- insert with wrap */ if (++flow_head == AUTO_FLOW_MAX) flow_head = 0; /* Circular queue -- handle overflow (badly) */ if (flow_head == flow_tail) flow_head = old_head; } } /* Forget the flow info */ flow_head = flow_tail = 0; } /* * Enqueue a fresh (legal) starting grid, if it is safe */ static void borg_flow_enqueue_grid(int y, int x) { int old_head; /* Avoid icky grids */ if (auto_data_icky->data[y][x]) return; /* Unknown */ if (!auto_data_know->data[y][x]) { /* Mark as known */ auto_data_know->data[y][x] = TRUE; /* Mark dangerous grids as icky */ if (borg_danger(y, x, 1) > avoidance / 2) { /* Icky */ auto_data_icky->data[y][x] = TRUE; /* Avoid */ return; } } /* Only enqueue a grid once */ if (!auto_data_cost->data[y][x]) return; /* Save the flow cost (zero) */ auto_data_cost->data[y][x] = 0; /* Enqueue that entry */ auto_flow_y[flow_head] = y; auto_flow_x[flow_head] = x; /* Circular queue -- memorize head */ old_head = flow_head; /* Circular queue -- insert with wrap */ if (++flow_head == AUTO_FLOW_MAX) flow_head = 0; /* Circular queue -- handle overflow */ if (flow_head == flow_tail) flow_head = old_head; } /* * Do a "reverse" flow from the player outwards */ static void borg_flow_reverse(void) { /* Clear the flow codes */ borg_flow_clear(); /* Enqueue the player's grid */ borg_flow_enqueue_grid(c_y, c_x); /* Spread, but do NOT optimize */ borg_flow_spread(250, FALSE, FALSE); } /* * Attempt to induce "word of recall" */ bool borg_recall(void) { /* Multiple "recall" fails */ if (!goal_recalling) { /* Try to "recall" */ if (borg_spell(5, 4) || borg_prayer(4, 4) || borg_zap_rod(SV_ROD_RECALL) || borg_read_scroll(SV_SCROLL_WORD_OF_RECALL)) { /* Success */ return (TRUE); } } /* Nothing */ return (FALSE); } /* * Prevent starvation by any means possible */ static bool borg_eat_food_any(void) { int i; /* Scan the inventory for "normal" food */ for (i = 0; i < INVEN_PACK; i++) { auto_item *item = &auto_items[i]; /* Skip empty items */ if (!item->iqty) continue; /* Skip unknown food */ if (!item->kind) continue; /* Skip non-food */ if (item->tval != TV_FOOD) continue; /* Skip "flavored" food */ if (item->sval < SV_FOOD_MIN_FOOD) continue; /* Eat something of that type */ if (borg_eat_food(item->sval)) return (TRUE); } /* Scan the inventory for "okay" food */ for (i = 0; i < INVEN_PACK; i++) { auto_item *item = &auto_items[i]; /* Skip empty items */ if (!item->iqty) continue; /* Skip unknown food */ if (!item->kind) continue; /* Skip non-food */ if (item->tval != TV_FOOD) continue; /* Skip "icky" food */ if (item->sval < SV_FOOD_MIN_OKAY) continue; /* Eat something of that type */ if (borg_eat_food(item->sval)) return (TRUE); } /* Nothing */ return (FALSE); } /* * Mega-Hack -- evaluate the "freedom" of the given location * * The theory is that often, two grids will have equal "danger", * but one will be "safer" than the other, perhaps because it * is closer to stairs, or because it is in a corridor, or has * some other characteristic that makes it "safer". * * Then, if the Borg is in danger, say, from a normal speed monster * which is standing next to him, he will know that walking away from * the monster is "pointless", because the monster will follow him, * but if the resulting location is "safer" for some reason, then * he will consider it. This will allow him to flee towards stairs * in the town, and perhaps towards corridors in the dungeon. * * This method is used in town to chase the stairs. * * XXX XXX XXX We should attempt to walk "around" buildings. */ static int borg_freedom(int y, int x) { int d, f = 0; /* Hack -- chase stairs in town */ if (!auto_depth && track_more_num) { /* Love the stairs! */ d = double_distance(y, x, track_more_y[0], track_more_x[0]); /* Proximity is good */ f += (1000 - d); /* Close proximity is great */ if (d < 4) f += (2000 - (d * 500)); } /* Freedom */ return (f); } /* * Check a floor grid for "happy" status * * These grids are floor grids which contain stairs, or which * are non-corners in corridors, or which are directly adjacent * to pillars. Stairs are good because they can be used to leave * the level. Corridors are good because you can back into them * to avoid groups of monsters and because they can be used for * escaping. Pillars are good because while standing next to a * pillar, you can walk "around" it in two different directions, * allowing you to retreat from a single normal monster forever. */ static bool borg_happy_grid_bold(int y, int x) { auto_grid *ag = &auto_grids[y][x]; /* Accept stairs */ if (ag->feat == FEAT_LESS) return (TRUE); if (ag->feat == FEAT_MORE) return (TRUE); /* Hack -- weak/dark is very unhappy */ if (do_weak || !my_cur_lite) return (FALSE); /* Case 1a: north-south corridor */ if (borg_cave_floor_bold(y-1, x) && borg_cave_floor_bold(y+1, x) && !borg_cave_floor_bold(y, x-1) && !borg_cave_floor_bold(y, x+1)) { /* Happy */ return (TRUE); } /* Case 1b: east-west corridor */ if (borg_cave_floor_bold(y, x-1) && borg_cave_floor_bold(y, x+1) && !borg_cave_floor_bold(y-1, x) && !borg_cave_floor_bold(y+1, x)) { /* Happy */ return (TRUE); } /* Case 2a: north pillar */ if (!borg_cave_floor_bold(y-1, x) && borg_cave_floor_bold(y-1, x-1) && borg_cave_floor_bold(y-1, x+1) && borg_cave_floor_bold(y-2, x)) { /* Happy */ return (TRUE); } /* Case 2b: south pillar */ if (!borg_cave_floor_bold(y+1, x) && borg_cave_floor_bold(y+1, x-1) && borg_cave_floor_bold(y+1, x+1) && borg_cave_floor_bold(y+2, x)) { /* Happy */ return (TRUE); } /* Case 2c: east pillar */ if (!borg_cave_floor_bold(y, x+1) && borg_cave_floor_bold(y-1, x+1) && borg_cave_floor_bold(y+1, x+1) && borg_cave_floor_bold(y, x+2)) { /* Happy */ return (TRUE); } /* Case 2d: west pillar */ if (!borg_cave_floor_bold(y, x-1) && borg_cave_floor_bold(y-1, x-1) && borg_cave_floor_bold(y+1, x-1) && borg_cave_floor_bold(y, x-2)) { /* Happy */ return (TRUE); } /* Not happy */ return (FALSE); } /* * is there a unique nearby? (check auto_kills) * This is used to keep us in fights and make us use all * our 'spare' equiptment to kill uniques * This only works for uniques we know about. If one of the * monsters around is misidentified then it may be a unique * and we wouldn't know. */ static bool borg_near_unique(int dist) { auto_kill *kill; monster_race *r_ptr; int x9, y9, ax, ay, d; int i; /* make sure there is a unique around */ for (i = 1; i < auto_kills_nxt; i++) { kill = &auto_kills[i]; r_ptr = &r_info[kill->r_idx]; /* Skip dead monsters */ if (!kill->r_idx) continue; x9 = kill->x; y9 = kill->y; /* Distance components */ ax = (x9 > c_x) ? (x9 - c_x) : (c_x - x9); ay = (y9 > c_y) ? (y9 - c_y) : (c_y - y9); /* Distance */ d = MAX(ax, ay); /* if the unique is too far then skip it. */ if (d > dist) continue; /* found one. Done. */ if (r_ptr->flags1 & RF1_UNIQUE) return (TRUE); } /* no nearby uniques */ return FALSE; } /* * Help determine if "phase door" seems like a good idea */ bool borg_caution_phase(bool emergency) { int n, k, i, d, x, y, p; int dis = 10; int min = dis / 2; auto_grid *ag; /* Simulate 100 attempts */ for (n = k = 0; k < 100; k++) { /* Pick a location */ for (i = 0; i < 100; i++) { /* Pick a (possibly illegal) location */ while (1) { y = rand_spread(c_y, dis); x = rand_spread(c_x, dis); d = distance(c_y, c_x, y, x); if ((d >= min) && (d <= dis)) break; } /* Ignore illegal locations */ if ((y <= 0) || (y >= AUTO_MAX_Y - 1)) continue; if ((x <= 0) || (x >= AUTO_MAX_X - 1)) continue; /* Access */ ag = &auto_grids[y][x]; /* Skip unknown grids */ if (ag->feat == FEAT_NONE) continue; /* Skip weird grids */ if (ag->feat == FEAT_INVIS) continue; /* Skip walls */ if (!borg_cave_floor_bold(y, x)) continue; /* Skip monsters */ if (ag->kill) continue; /* Stop looking */ break; } /* No location */ /* in the real code it would keep trying but here we should */ /* assume that there is unknown spots that you would be able */ /* to go but may be dangerious. */ if (i >= 100) { n++; continue; } /* Examine */ p = borg_danger(y, x, 2); /* if *very* scary, do not allow jumps at all */ if (p > auto_chp) n++; } /* Too much danger */ /* in an emergency try with extra danger allowed */ if (n > (emergency ? 20 : 5)) { borg_note(format("# No Phase. scary squares: %d", n)); return (FALSE); } /* note how scary it was */ if (emergency) borg_note(format("# Emergency Phase. scary squares: %d", n)); else borg_note(format("# Safe to Phase. scary squares: %d", n)); /* Okay */ return (TRUE); } /* * Try to phase door or teleport * b_q is the danger of the least dangerious square around us. */ bool borg_escape(int b_q) { /* also run if stunned or it is scary here */ if ((b_q > avoidance) || do_heavy_stun || (b_q > ((auto_mhp*2)/5) && !borg_fighting_unique)) { /* only escape with spell if fail is low */ int allow_fail = 2; /* if very healthy, allow extra fail */ if ((auto_chp*100)/auto_mhp > 90) allow_fail = 20; /* very scary, do not allow fail */ if ((b_q > avoidance) || do_heavy_stun) allow_fail = 0; /* Phase door, if useful */ if (amt_phase && borg_caution_phase(FALSE) && (borg_spell_fail(0, 2, allow_fail) || borg_prayer_fail(4, 0, allow_fail) || borg_read_scroll(SV_SCROLL_PHASE_DOOR))) { /* Success */ return (TRUE); } /* Teleport via spell */ if ( borg_spell_fail(1, 5, allow_fail) || borg_prayer_fail(1, 1, allow_fail) || borg_prayer_fail(4, 1, allow_fail)) { /* Success */ return (TRUE); } /* if low failure allowed, use scroll before staff */ /* !FIX: should check for % fail use of staff AJG */ if ( ((allow_fail > 1) && (borg_use_staff(SV_STAFF_TELEPORTATION) || borg_read_scroll(SV_SCROLL_TELEPORT))) || ((allow_fail <= 1) && (borg_read_scroll(SV_SCROLL_TELEPORT) || borg_use_staff(SV_STAFF_TELEPORTATION)))) { /* Success */ return (TRUE); } /* if I fail to teleport, try phase again (better to */ /* take a chance and phase out than stick around and die) */ if ( amt_phase && borg_caution_phase(TRUE) && (borg_spell_fail(0, 2, allow_fail) || borg_prayer_fail(4, 0, allow_fail) || borg_read_scroll(SV_SCROLL_PHASE_DOOR))) { /* Success */ return (TRUE); } /* if we got this far we tried to escape but couldn't... */ /* time to flee */ if (!goal_fleeing) { /* Note */ borg_note("# Fleeing (failed to teleport)"); /* Start fleeing */ goal_fleeing = TRUE; } /* Flee now */ if (!goal_leaving) { /* Flee! */ borg_note("# Leaving (failed to teleport)"); /* Start leaving */ goal_leaving = TRUE; } } /* Attempt to teleport (usually) */ /* do not escape from uniques so quick */ if (((b_q > avoidance / 2) && !borg_fighting_unique ) || (b_q > avoidance) || do_heavy_stun) { /* allow any fail beacuse this round is 'safe' */ /* (next round may not be) */ /* XXX XXX XXX Count close calls */ /* Phase door, if useful */ if (amt_phase && borg_caution_phase(FALSE) && (borg_spell(0, 2) || borg_prayer(4, 0) || borg_read_scroll(SV_SCROLL_PHASE_DOOR))) { /* Success */ return (TRUE); } /* Try teleportation */ if ((rand_int(100) < 50) && (borg_spell(1, 5) || borg_prayer(4, 1) || borg_prayer(1, 1) || borg_use_staff(SV_STAFF_TELEPORTATION) || borg_read_scroll(SV_SCROLL_TELEPORT))) { /* Success */ return (TRUE); } } return (FALSE); } /* * ** Try healing ** * this function tries to heal the borg both before trying to flee (pre_flight) * and after. Before the flight check, try to heal up enough to stay in the fight. * after the flight check, we are no longer in immediate danger so heal up as a * precaution. */ static bool borg_heal( bool pre_flight, int danger ) { int hp_down ; int allow_fail = 20; int chance; hp_down = auto_mhp - auto_chp; /* before the fight only do sure fire cures */ if (pre_flight) allow_fail = 0; /* if unhurt no healing needed */ if (hp_down == 0) return FALSE; /* Hack -- heal when wounded (prayers) */ /* 4/5 hp 0% */ /* 3/4 hp 5% */ /* 2/3 hp 20% */ /* 1/2 hp 50% */ /* 1/3 hp 80% */ /* 1/4 hp 100% */ chance = rand_int(100); /* BIG HACK. get max damage that can be done. */ if (!pre_flight) { if ( danger ) { s16b save_avoid; save_avoid = avoidance; avoidance = 1; danger = borg_danger(c_y, c_x, 1); avoidance = save_avoid; } /* if the next hit may make us run, heal now */ if (((auto_chp - danger) / 2) < danger) { if (mb_ptr->spell_book == TV_PRAYER_BOOK) chance -= 50; else chance -= 20; } if (!(((auto_chp <= ((auto_mhp * 4) / 5)) && (chance < 0)) || ((auto_chp <= ((auto_mhp * 3) / 4)) && (chance < 5)) || ((auto_chp <= ((auto_mhp * 2) / 3)) && (chance < 20)) || ((auto_chp <= (auto_mhp / 2)) && (chance < 50)) || ((auto_chp <= (auto_mhp / 3)) && (chance < 80)) || (auto_chp <= (auto_mhp / 4)) || do_heavy_stun || do_stun)) return FALSE; } /* only heal before attempting to flee if the danger is */ /* more than the avoidance and being healed will help */ if ( pre_flight ) { if (danger < avoidance) return (FALSE); if (danger > auto_mhp) return (FALSE); } /* Cure light Wounds (2d10) */ if ( hp_down < 10 && (danger - (hp_down < 6 ? hp_down : 6) < avoidance) && borg_prayer_fail(0, 1, allow_fail)) { return (TRUE); } /* Cure Serious Wounds (4d10) */ if ( hp_down < 20 && (danger - (hp_down < 12 ? hp_down : 12) < avoidance) && borg_prayer_fail(1, 2, allow_fail)) { return (TRUE); } /* Cure Critical Wounds (6d10) */ if ( hp_down < 50 && (danger - (hp_down < 18 ? hp_down : 18) < avoidance) && (borg_prayer_fail(2, 2, allow_fail) || borg_prayer_fail(6, 0, allow_fail))) { return (TRUE); } /* Cure Mortal Wounds (8d10) */ if ( hp_down < 120 && (danger - (hp_down < 24 ? hp_down : 24) < avoidance) && (borg_prayer_fail(2, 7, allow_fail) || borg_prayer_fail(6, 1, allow_fail))) { return (TRUE); } /* Heal (300hp) */ if (hp_down < 350 && (danger - (hp_down < 300 ? hp_down : 300) < avoidance) && borg_prayer_fail(3, 2, allow_fail) ) { return (TRUE); } /* Healing (2000hp) */ if ((danger - (hp_down < 2000 ? hp_down : 2000) < avoidance) && borg_prayer_fail(6, 2, allow_fail)) { return (TRUE); } /* Try a lesser prayer again, just incase we tried a */ /* higher spell and failed */ /* (didn't have/not enough mana...) */ if ( !pre_flight && (borg_prayer_fail(3, 2, allow_fail) || borg_prayer_fail(2, 7, allow_fail) || borg_prayer_fail(2, 2, allow_fail) || borg_prayer_fail(1, 2, allow_fail) || borg_prayer_fail(0, 1, allow_fail))) { return (TRUE); } /* very hurt, Big heal - reusable */ if (hp_down > 150) { if (((danger - (hp_down < 500 ? hp_down : 500) < avoidance) || !pre_flight) && borg_zap_rod(SV_ROD_HEALING)) /* 500hp */ { return TRUE; } if (((danger - (hp_down < 300 ? hp_down : 300) < avoidance) || !pre_flight) && borg_use_staff(SV_STAFF_HEALING)) /* 300hp */ { return TRUE; } } /* very hurt, use big potions. */ if ((hp_down > 300) || ((hp_down > 150) && borg_fighting_unique)) { /* use life and *healing* before healing because they are less */ /* common and clutter inventory. */ if (((danger - (hp_down < 1200 ? hp_down : 1200) < avoidance) || !pre_flight) && (borg_quaff_potion(SV_POTION_LIFE) || borg_quaff_potion(SV_POTION_STAR_HEALING)) ) { return TRUE; } if ((((danger - (hp_down < 300 ? hp_down : 300)) < avoidance) || !pre_flight) && borg_quaff_potion(SV_POTION_HEALING)) /* 300hp */ { return TRUE; } } /* do not drink cure potions during a fight to heal. */ if (!pre_flight) return (FALSE); /* use staff more librally, it will not last long. */ if (auto_level > 25) { if (((danger - (hp_down < 300 ? hp_down : 300)) < avoidance) && borg_use_staff(SV_STAFF_HEALING)) /* 300hp */ { return TRUE; } } /* Do not use up CSW/CCW on healing if high level */ if (auto_level > 15) return (FALSE); /* not so hurt, use smaller potions. */ /* (only use up cure crits if we have lots) */ if (amt_cure_critical > 8) { if ((danger - (hp_down < 12 ? hp_down : 12) < avoidance) && borg_quaff_potion(SV_POTION_CURE_CRITICAL)) { return (TRUE); } } if ((danger - (hp_down < 8 ? hp_down : 8) < avoidance) && borg_quaff_potion(SV_POTION_CURE_SERIOUS)) { return (TRUE); } return (FALSE); } /* * Be "cautious" and attempt to prevent death or dishonor. * * Strategy: * * (1) Caution * (1a) Analyze the situation * (1a1) try to heal * (1a2) try a defence * (1b) Teleport from danger * (1c) Handle critical stuff * (1d) Retreat to happy grids * (1e) Back away from danger * (1f) Heal various conditions * * (2) Attack * (2a) Simulate possible attacks * (2b) Perform optimal attack * * (3) Recover * (3a) Recover by spells/prayers * (3b) Recover by items/etc * (3c) Recover by resting * * XXX XXX XXX * In certain situations, the "proper" course of action is to simply * attack a nearby monster, since often most of the danger is due to * a single monster which can sometimes be killed in a single blow. * * Actually, both "borg_caution()" and "borg_recover()" need to * be more intelligent, and should probably take into account * such things as nearby monsters, and/or the relative advantage * of simply pummeling nearby monsters instead of recovering. * * Note that invisible/offscreen monsters contribute to the danger * of an extended "region" surrounding the observation, so we will * no longer rest near invisible monsters if they are dangerous. * * We sometimes try to "rest" to restore mana near "wimpy" monsters * which happen to drain mana, which is counter-productive. * * XXX XXX XXX * We should perhaps reduce the "fear" values of each region over * time, to take account of obsolete invisible monsters. * * Note that walking away from a fast monster is counter-productive, * since the monster will often just follow us, so we use a special * method which allows us to factor in the speed of the monster and * predict the state of the world after we move one step. Of course, * walking away from a spell casting monster is even worse, since the * monster will just get to use the spell attack multiple times. But, * if we are trying to get to known safety, then fleeing in such a way * might make sense. Actually, this has been done too well, note that * it makes sense to flee some monsters, if they "stumble", or if we * are trying to get to stairs. XXX XXX XXX * * Note that the "flow" routines attempt to avoid entering into * situations that are dangerous, but sometimes we do not see the * danger coming, and then we must attempt to survive by any means. * * We will attempt to "teleport" if the danger in the current situation, * as well as that resulting from attempting to "back away" from danger, * are sufficient to kill us in one or two blows. This allows us to * avoid teleportation in situations where simply backing away is the * proper course of action, for example, when standing next to a nasty * stationary monster, but also to teleport when backing away will not * reduce the danger sufficiently. * * But note that in "nasty" situations (when we are running out of light, * or when we are starving, blind, confused, or hallucinating), we will * ignore the possibility of "backing away" from danger, when considering * the possibility of using "teleport" to escape. But if the teleport * fails, we will still attempt to "retreat" or "back away" if possible. * * XXX XXX XXX Note that it should be possible to do some kind of nasty * "flow" algorithm which would use a priority queue, or some reasonably * efficient normal queue stuff, to determine the path which incurs the * smallest "cumulative danger", and minimizes the total path length. * It may even be sufficient to treat each step as having a cost equal * to the danger of the destination grid, plus one for the actual step. * This would allow the Borg to prefer a ten step path passing through * one grid with danger 10, to a five step path, where each step has * danger 9. Currently, he often chooses paths of constant danger over * paths with small amounts of high danger. However, the current method * is very fast, which is certainly a point in its favor... * * When in danger, attempt to "flee" by "teleport" or "recall", and if * this is not possible, attempt to "heal" damage, if needed, and else * attempt to "flee" by "running". * * XXX XXX XXX Both "borg_caution()" and "borg_recover()" should only * perform the "healing" tasks if they will cure more "damage"/"stuff" * than may be re-applied in the next turn, this should prevent using * wimpy healing spells next to dangerous monsters, and resting to regain * mana near a mana-drainer. * * Whenever we are in a situation in which, even when fully healed, we * could die in a single round, we set the "goal_fleeing" flag, and if * we could die in two rounds, we set the "goal_leaving" flag. * * In town, whenever we could die in two rounds if we were to stay still, * we set the "goal_leaving" flag. In combination with the "retreat" and * the "back away" code, this should allow us to leave town before getting * into situations which might be fatal. * * Flag "goal_fleeing" means get off this level right now, using recall * if possible when we get a chance, and otherwise, take stairs, even if * it is very dangerous to do so. * * Flag "goal_leaving" means get off this level when possible, using * stairs if possible when we get a chance. * * We will also take stairs if we happen to be standing on them, and we * could die in two rounds. This is often "safer" than teleportation, * and allows the "retreat" code to retreat towards stairs, knowing that * once there, we will leave the level. */ bool borg_caution(void) { int j, p; int q, b_q = -1; bool nasty = FALSE; /*** Notice "nasty" situations ***/ /* About to run out of light is extremely nasty */ if (!my_lite && auto_items[INVEN_LITE].pval < 250) nasty = TRUE; /* Starvation is nasty */ if (do_weak) nasty = TRUE; /* Blind-ness is nasty */ if (do_blind) nasty = TRUE; /* Confusion is nasty */ if (do_confused) nasty = TRUE; /* Hallucination is nasty */ if (do_image) nasty = TRUE; /* if on level 100 and not ready for Morgoth, run */ if (auto_depth == 100) { if (borg_ready_morgoth == 0) { /* Start leaving */ if (!goal_leaving) { /* Note */ borg_note("# Leaving (No Morgoth Yet)"); /* Start leaving */ goal_leaving = TRUE; } } } /*** Evaluate local danger ***/ /* am I fighting a unique? */ borg_fighting_unique = borg_near_unique(6); /* Look around */ p = borg_danger(c_y, c_x, 1); /* Unless "nasty"... */ if (!nasty || !auto_depth) { int i; /* Attempt to find a better grid */ for (i = 0; i < 8; i++) { int x = c_x + ddx_ddd[i]; int y = c_y + ddy_ddd[i]; /* Access the grid */ auto_grid *ag = &auto_grids[y][x]; /* Skip walls/doors */ if (!borg_cave_floor_grid(ag)) continue; /* Skip unknown grids */ if (ag->feat == FEAT_NONE) continue; /* Skip monster grids */ if (ag->kill) continue; /* Mega-Hack -- skip stores XXX XXX XXX */ if ((ag->feat >= FEAT_SHOP_HEAD) && (ag->feat <= FEAT_SHOP_TAIL)) continue; /* Mega-Hack -- skip traps XXX XXX XXX */ if ((ag->feat >= FEAT_TRAP_HEAD) && (ag->feat <= FEAT_TRAP_TAIL)) continue; /* Extract the danger there */ q = borg_danger(y, x, 2); /* Skip larger danger */ if ((b_q >= 0) && (b_q < q)) continue; /* Track */ b_q = q; } } /* Danger (ignore stupid "fear" danger) */ if ((p > avoidance / 2) || (p > auto_fear_region[c_y/11][c_x/11])) { /* Describe (briefly) the current situation */ borg_note(format("# Loc:%d,%d Dep:%d Lev:%d HP:%d/%d SP:%d/%d Danger:b_q=%d/p=%d", c_x, c_y, auto_depth, auto_level, auto_chp, auto_mhp, auto_csp, auto_msp, b_q, p)); } else { if (borg_goi) borg_note(format("# Loc:%d,%d goi!", c_x, c_y)); } /* No (good) retreat */ if ((b_q < 0) || (b_q > p)) b_q = p; /* try healing before running away */ if (borg_heal( TRUE, p )) return TRUE; /* do some defence before running away! */ if (borg_defend()) return TRUE; /*** Danger ***/ /* Impending doom */ /* don't take off in the middle of a fight */ /* just to restock and it is useless to restock */ /* if you have just left town. */ if (borg_restock(auto_depth) && !borg_fighting_unique && (auto_time_town + (c_t - auto_began)) > 200) { /* Start leaving */ if (!goal_leaving) { /* Note */ borg_note(format("# Leaving (restock) %s", borg_restock(auto_depth))); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Note */ borg_note(format("# Fleeing (restock) %s", borg_restock(auto_depth))); /* Start fleeing */ goal_fleeing = TRUE; } } /* Excessive danger */ else if ((b_q > auto_mhp) && (rand_int(100) > (auto_level+20))) { /* Start fleeing */ /* do not flee level if going after Morgoth or fighting a unique */ if (!goal_fleeing && !borg_fighting_unique && !((auto_depth == 100) && (borg_ready_morgoth == 1))) { /* Note */ borg_note("# Fleeing (excessive danger)"); /* Start fleeing */ goal_fleeing = TRUE; } } /* Potential danger (near death) in town */ else if (!auto_depth && (p > auto_chp)) { /* Flee now */ if (!goal_leaving) { /* Flee! */ borg_note("# Leaving (potential danger)"); /* Start leaving */ goal_leaving = TRUE; } } /*** Stairs ***/ /* Leaving or Fleeing, take stairs */ if (goal_leaving || goal_fleeing) { /* Take next stairs */ stair_less = goal_fleeing; if (borg_ready_morgoth == 0) stair_less = TRUE; /* Only go down if fleeing or prepared. */ stair_more = goal_fleeing; if ((cptr)NULL == borg_prepared(auto_depth+1)) stair_more = TRUE; } /* Take stairs up */ if (stair_less || (b_q > auto_chp / 2)) { /* Current grid */ auto_grid *ag = &auto_grids[c_y][c_x]; /* Usable stairs */ if (ag->feat == FEAT_LESS) { /* Take the stairs */ borg_keypress('<'); /* Success */ return (TRUE); } } /* Take stairs down */ if ((stair_more || (b_q > auto_chp / 2)) && !goal_recalling) { /* Current grid */ auto_grid *ag = &auto_grids[c_y][c_x]; /* Usable stairs */ if (ag->feat == FEAT_MORE) { /* Take the stairs */ borg_keypress('>'); /* Success */ return (TRUE); } } /*** Escape if possible ***/ /* Attempt to escape */ if (borg_escape(b_q)) { /* Hack -- reset the "goal" location */ g_x = g_y = 0; /* Success */ return (TRUE); } /*** Deal with critical situations ***/ /* Hack -- require light */ if (!my_lite) { auto_item *item = &auto_items[INVEN_LITE]; /* Must have light -- Refuel current torch */ if ((item->tval == TV_LITE) && (item->sval == SV_LITE_TORCH)) { /* Try to refuel the torch */ if ((item->pval < 500) && borg_refuel_torch()) return (TRUE); } /* Must have light -- Refuel current lantern */ if ((item->tval == TV_LITE) && (item->sval == SV_LITE_LANTERN)) { /* Try to refill the lantern */ if ((item->pval < 1000) && borg_refuel_lantern()) return (TRUE); } /* Flee for fuel */ if (auto_depth && (item->pval < 250)) { /* Start leaving */ if (!goal_leaving) { /* Flee */ borg_note("# Leaving (need fuel)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Flee */ borg_note("# Fleeing (need fuel)"); /* Start fleeing */ goal_fleeing = TRUE; } } } /* Hack -- prevent starvation */ if (do_weak) { /* Attempt to satisfy hunger */ if (borg_eat_food_any() || borg_spell(2, 0) || borg_prayer(1, 5)) { /* Success */ return (TRUE); } /* Flee for food */ if (auto_depth) { /* Start leaving */ if (!goal_leaving) { /* Flee */ borg_note("# Leaving (need food)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Flee */ borg_note("# Fleeing (need food)"); /* Start fleeing */ goal_fleeing = TRUE; } } } /*** Flee on foot ***/ /* Strategic retreat */ if (p > avoidance / 2) { int d, b_d = -1; int r, b_r = -1; int b_x = c_x; int b_y = c_y; /* Scan the useful viewable grids */ for (j = 1; j < auto_view_n; j++) { int x1 = c_x; int y1 = c_y; int x2 = auto_view_x[j]; int y2 = auto_view_y[j]; /* Require "floor" grids */ if (!borg_cave_floor_bold(y2, x2)) continue; /* Require "happy" grids */ if (!borg_happy_grid_bold(y2, x2)) continue; /* Track "nearest" grid */ if (b_r >= 0) { int ay = ((y2 > y1) ? (y2 - y1) : (y1 - y2)); int ax = ((x2 > x1) ? (x2 - x1) : (x1 - x2)); /* Ignore "distant" locations */ if ((ax > b_r) || (ay > b_r)) continue; } /* Reset */ r = 0; /* Simulate movement */ while (1) { auto_grid *ag; /* Obtain direction */ d = borg_goto_dir(y1, x1, y2, x2); /* Verify direction */ if ((d == 0) || (d == 5)) break; /* Track distance */ r++; /* Simulate the step */ y1 += ddy[d]; x1 += ddx[d]; /* Obtain the grid */ ag = &auto_grids[y1][x1]; /* Require floor */ if (!borg_cave_floor_grid(ag)) break; /* Require line of sight */ if (!borg_los(y1, x1, y2, x2)) break; /* Check danger (over time) */ if (borg_danger(y1, x1, r+1) > p) break; /* !FIX AJG keep dying by colossus. Maybe this will help... */ /* make sure it is not dangerous to take the first step. */ if (r == 1 && borg_danger(y1, x1, 1) >= p) break; /* Skip monsters */ if (ag->kill) break; /* Skip traps */ if ((ag->feat >= FEAT_TRAP_HEAD) && (ag->feat <= FEAT_TRAP_TAIL)) break; /* Safe arrival */ if ((x1 == x2) && (y1 == y2)) { /* Save distance */ b_r = r; /* Save location */ b_x = x2; b_y = y2; /* Done */ break; } } } /* Retreat */ if (b_r >= 0) { /* Save direction */ b_d = borg_goto_dir(c_y, c_x, b_y, b_x); /* Hack -- set goal */ g_x = c_x + ddx[b_d]; g_y = c_y + ddy[b_d]; /* Note */ borg_note(format("# Retreating to %d,%d (distance %d) via %d,%d (%d >= %d)", b_x, b_y, b_r, g_x, g_y, p, borg_danger(g_y, g_x, 2))); /* Strategic retreat */ borg_keypress('0' + b_d); /* Success */ return (TRUE); } } /* Want to back away */ if (p > avoidance / 2) { int i, b_i = -1; int k, b_k = -1; int f, b_f = -1; /* Current danger */ b_k = p; /* Check the freedom */ b_f = borg_freedom(c_y, c_x); /* Attempt to find a better grid */ for (i = 0; i < 8; i++) { int x = c_x + ddx_ddd[i]; int y = c_y + ddy_ddd[i]; /* Access the grid */ auto_grid *ag = &auto_grids[y][x]; /* Skip walls/doors */ if (!borg_cave_floor_grid(ag)) continue; /* Skip monster grids */ if (ag->kill) continue; /* Mega-Hack -- skip stores XXX XXX XXX */ if ((ag->feat >= FEAT_SHOP_HEAD) && (ag->feat <= FEAT_SHOP_TAIL)) continue; /* Mega-Hack -- skip traps XXX XXX XXX */ if ((ag->feat >= FEAT_TRAP_HEAD) && (ag->feat <= FEAT_TRAP_TAIL)) continue; /* Extract the danger there */ k = borg_danger(y, x, 2); /* Skip higher danger */ if (b_k < k) continue; /* Check the freedom */ f = borg_freedom(y, x); /* Skip bad locations */ if ((b_k == k) && (b_f > f)) continue; /* Save the info */ b_i = i; b_k = k; b_f = f; } /* Back away */ if (b_i >= 0) { /* Hack -- set goal */ g_x = c_x + ddx_ddd[b_i]; g_y = c_y + ddy_ddd[b_i]; /* Note */ borg_note(format("# Backing up to %d,%d (%d >= %d)", g_x, g_y, p, borg_danger(g_y, g_x, 2))); /* Back away from danger */ borg_keypress('0' + ddd[b_i]); /* Success */ return (TRUE); } /* Note */ borg_note(format("# Cornered (danger %d)", p)); } /*** Try healing ***/ /* heal when wounded */ if (borg_heal(FALSE, p)) return TRUE; /* Hack -- heal when blind/confused */ if ((do_blind || do_confused) && (rand_int(100) < 70)) { if (borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_potion(SV_POTION_CURE_CRITICAL)) { return (TRUE); } } /* Hack -- cure wounds when bleeding */ if (do_cut && (auto_chp < 2 || (rand_int(100) < 10))) { if (borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_potion(SV_POTION_CURE_CRITICAL)) { return (TRUE); } } /* Hack -- cure poison when poisoned */ if (do_poisoned && (auto_chp < 2 || rand_int(100) < 10)) { if (borg_spell(1, 4) || borg_prayer(2, 0) || borg_quaff_potion(SV_POTION_CURE_POISON) || borg_quaff_potion(SV_POTION_CURE_CRITICAL)) { return (TRUE); } } /* Hack -- cure fear when afraid */ if (do_afraid && (rand_int(100) < 70)) { if (borg_prayer(0, 3) || borg_quaff_potion(SV_POTION_BOLDNESS) || borg_quaff_potion(SV_POTION_HEROISM) || borg_quaff_potion(SV_POTION_BESERK_STRENGTH) || borg_spell(7, 2) || borg_spell(7, 0)) { return (TRUE); } } /* restore Mana */ /* note, blow the staff charges easy because the staff will not last. */ if (auto_csp < (auto_msp / 5) && (rand_int(100) < 50)) { if (borg_use_staff(SV_STAFF_THE_MAGI)) { borg_note("# Use Magi Staff"); return (TRUE); } } /* blowing potions is harder */ /* NOTE: must have enough mana to keep up GOI or do a HEAL */ if (auto_csp < (auto_msp / 10) || ((auto_depth > 95) && (auto_csp < 70) && (auto_msp > 0))) { /* If deep do not check, just use staff */ if ((auto_depth > 95) || (rand_int(100) < 30)) { int num_mana = 5; if (auto_depth > 95) { /* stock up on mana to take out MORGOTH */ num_mana = 20; } /* only use the potions if we have lots or */ /* are battling a unique */ if (amt_mana > num_mana || borg_fighting_unique) { if (borg_use_staff(SV_STAFF_THE_MAGI) || borg_quaff_potion(SV_POTION_RESTORE_MANA)) { borg_note("# Restored My Mana"); return (TRUE); } } } } /*** Note impending death XXX XXX XXX ***/ /* Flee from low hit-points */ if (((auto_chp < auto_mhp / 3) || ((auto_chp < auto_mhp / 2) && auto_chp < 100)) && (amt_cure_critical < 3) && (amt_heal < 1)) { /* Flee from low hit-points */ if (auto_depth && (rand_int(100) < 25)) { /* Start leaving */ if (!goal_leaving) { /* Flee */ borg_note("# Leaving (low hit-points)"); /* Start leaving */ goal_leaving = TRUE; } /* Start fleeing */ if (!goal_fleeing) { /* Flee */ borg_note("# Fleeing (low hit-points)"); /* Start fleeing */ goal_fleeing = TRUE; } } } /* Try an emergency teleport (This will take us to negative mana but, */ /* what the hell, I am about to die anyway. */ if ((((p*2)/ 3) > avoidance) && (auto_msp > 1)) { int sv_mana = auto_csp; auto_csp = auto_msp; if ( borg_spell_fail(1, 5, 15) || borg_prayer_fail(1, 1, 15) || borg_prayer_fail(4, 1, 15) || borg_spell_fail(0, 2, 15) || borg_prayer_fail(4, 0, 15) || borg_spell_fail(5, 2, 15)) { /* verify use of spell */ borg_keypress('y'); /* Flee! */ borg_note("# Emergency Teleport! AHHHhhhh!!!...."); /* Hack -- reset the "goal" location */ g_x = g_y = 0; goal_fleeing = TRUE; return (TRUE); } auto_csp = sv_mana; } /* Hack -- use "recall" to flee if possible */ if (goal_fleeing && auto_depth && (borg_recall())) { /* Note */ borg_note("# Fleeing the level (recall)"); /* Success */ return (TRUE); } /* Nothing */ return (FALSE); } /* * New method for handling attacks, missiles, and spells * * Every turn, we evaluate every known method of causing damage * to monsters, and evaluate the "reward" inherent in each of * the known methods which is usable at that time, and then * we actually use whichever method, if any, scores highest. * * For each attack, we need a function which will determine the best * possible result of using that attack, and return its value. Also, * if requested, the function should actually perform the action. * * Note that the functions should return zero if the action is not * usable, or if the action is not useful. * * These functions need to apply some form of "cost" evaluation, to * prevent the use of expensive spells with minimal reward. Also, * we should always prefer attacking by hand to using spells if the * damage difference is "small", since there is no "cost" in making * a physical attack. * * We should take account of "spell failure", as well as "missile * missing" and "blow missing" probabilities. * * Note that the functions may store local state information when * doing a "simulation" and then they can use this information if * they are asked to implement their strategy. * * XXX XXX XXX * * We should "reward" doing damage to monsters which are "dangerous", * unless they are sleeping, in which case we should penalize waking * them up, unless we can kill them instantly. Note that this means * that killing a single "gnome mage" with 10 hitpoints should be * considered "better" than doing 30 damage to a "white jelly", * since the "gnome mage" is much more "dangerous". We should * check the danger over several turns, to take account of nasty * monsters which are not right next to us. * * We should attempt to apply any "brand" effects of the current * "weapon" and/or "ammo". * * We should "attempt" to keep track of each monsters "hitpoints", * since this will make the attack code "smarter", but we should * not be too optimistic, since mistakes could be fatal. * * We should try to avoid damaging objects on the ground, that is, * we should not use "frost ball" near potions, etc. * * Note that all "fire" commands have a minimum range of 20, but the * various "throw" commands have a range which is limited by strength * and weight, and is limited to a total distance of ten. We should * attempt to take into to account the "effective" range. XXX XXX XXX * * There are several types of damage inducers: * * Attacking physically * Launching missiles * Throwing objects * Casting spells * Praying prayers * Using wands * Using rods * Using staffs * Using scrolls */ enum { BF_THRUST, BF_LAUNCH, BF_OBJECT, BF_SPELL_MAGIC_MISSILE, BF_SPELL_ELEC_BOLT, BF_SPELL_COLD_BOLT, BF_SPELL_FIRE_BOLT, BF_SPELL_ACID_BOLT, BF_SPELL_LITE_BEAM, BF_SPELL_POISON_BALL, BF_SPELL_COLD_BALL, BF_SPELL_ACID_BALL, BF_SPELL_FIRE_BALL, BF_SPELL_POISON_STORM, BF_SPELL_COLD_STORM, BF_SPELL_METEOR_STORM, BF_SPELL_MANA_STORM, BF_PRAYER_HOLY_ORB_BALL, BF_ROD_ELEC_BOLT, BF_ROD_COLD_BOLT, BF_ROD_ACID_BOLT, BF_ROD_FIRE_BOLT, BF_ROD_LITE_BEAM, BF_ROD_ELEC_BALL, BF_ROD_COLD_BALL, BF_ROD_ACID_BALL, BF_ROD_FIRE_BALL, BF_WAND_MAGIC_MISSILE, BF_WAND_ELEC_BOLT, BF_WAND_COLD_BOLT, BF_WAND_ACID_BOLT, BF_WAND_FIRE_BOLT, BF_WAND_LITE_BEAM, BF_WAND_STINKING_CLOUD, BF_WAND_ELEC_BALL, BF_WAND_COLD_BALL, BF_WAND_ACID_BALL, BF_WAND_FIRE_BALL, BF_WAND_DRAGON_COLD, BF_WAND_DRAGON_FIRE, BF_MAX }; /* * Guess how much damage a physical attack will do to a monster */ static int borg_thrust_damage_one(int i) { int dam; int mult; auto_kill *kill; monster_race *r_ptr; auto_item *item; int chance; /* Examine current weapon */ item = &auto_items[INVEN_WIELD]; /* Monster record */ kill = &auto_kills[i]; /* Monster race */ r_ptr = &r_info[kill->r_idx]; /* Damage */ dam = (item->dd * (item->ds + 1) / 2); /* here is the place for slays and such */ mult = 1; if (((my_slay_animal) && (r_ptr->flags3 & RF3_ANIMAL)) || ((my_slay_evil) && (r_ptr->flags3 & RF3_EVIL))) mult = 2; if (((my_slay_undead) && (r_ptr->flags3 & RF3_ANIMAL)) || ((my_slay_demon) && (r_ptr->flags3 & RF3_DEMON)) || ((my_slay_orc) && (r_ptr->flags3 & RF3_ORC)) || ((my_slay_troll) && (r_ptr->flags3 & RF3_TROLL)) || ((my_slay_giant) && (r_ptr->flags3 & RF3_GIANT)) || ((my_slay_dragon) && (r_ptr->flags3 & RF3_DRAGON)) || ((my_brand_acid) && !(r_ptr->flags3 & RF3_IM_ACID)) || ((my_brand_fire) && !(r_ptr->flags3 & RF3_IM_FIRE)) || ((my_brand_cold) && !(r_ptr->flags3 & RF3_IM_COLD)) || ((my_brand_elec) && !(r_ptr->flags3 & RF3_IM_ELEC))) mult = 3; if ((my_kill_dragon) && (r_ptr->flags3 & RF3_DRAGON)) mult = 5; dam *= mult; dam = dam + item->to_d + my_to_dam; dam = dam * my_num_blow; /* reduce for % chance to hit (AC) */ chance = (my_skill_thn + ((my_to_hit + item->to_h) * 3)); if ((r_ptr->ac * 3 / 4) > 0) chance = (chance * 100) / (r_ptr->ac * 3 / 4); /* 5% automatic success/fail */ if (chance > 95) chance = 95; if (chance < 5) chance = 5; /* add 20% to chance to give a bit more wieght to weapons */ chance += 20; dam = (dam * chance) / 100; /* Limit damage to twice maximal hitpoints */ if (dam > kill->power * 2) dam = kill->power * 2; /* Damage */ return (dam); } /* * Simulate/Apply the optimal result of making a physical attack */ static int borg_attack_aux_thrust(void) { int p, dir; int i, b_i = -1; int d, b_d = -1; auto_grid *ag; auto_kill *kill; /* Too afraid to attack */ if (do_afraid) return (0); /* Examine possible destinations */ for (i = 0; i < auto_temp_n; i++) { int x = auto_temp_x[i]; int y = auto_temp_y[i]; /* Require "adjacent" */ if (distance(c_y, c_x, y, x) > 1) continue; /* Acquire grid */ ag = &auto_grids[y][x]; /* Calculate "average" damage */ d = borg_thrust_damage_one(ag->kill); /* No damage */ if (d <= 0) continue; /* Obtain the monster */ kill = &auto_kills[ag->kill]; /* Hack -- avoid waking most "hard" sleeping monsters */ if (!kill->awake && (d <= kill->power)) { /* Calculate danger */ p = borg_danger_aux(y, x, 1, ag->kill); if (p > avoidance / 2) continue; } /* Calculate "danger" to player */ p = borg_danger_aux(c_y, c_x, 4, ag->kill); /* Reduce "bonus" of partial kills */ if (d <= kill->power) p = p / 10; /* Add the danger to the damage */ d += p; /* Ignore lower damage */ if ((b_i >= 0) && (d < b_d)) continue; /* Save the info */ b_i = i; b_d = d; } /* Nothing to attack */ if (b_i < 0) return (0); /* Simulation */ if (auto_simulate) return (b_d); /* Save the location */ g_x = auto_temp_x[b_i]; g_y = auto_temp_y[b_i]; /* Note */ borg_note(format("# Facing location (%d,%d)", g_x, g_y)); /* Note */ borg_note(format("# Attacking with weapon '%s'", auto_items[INVEN_WIELD].desc)); /* Get a direction for attacking */ dir = borg_extract_dir(c_y, c_x, g_y, g_x); /* Attack the grid */ borg_keypress('0' + dir); /* Success */ return (b_d); } /* * Target a location. Can be used alone or at "Direction?" prompt. * * Warning -- This will only work for locations on the current panel */ static bool borg_target(int y, int x) { int x1, y1, x2, y2; /* Log */ borg_note(format("# Targetting location (%d,%d)", x, y)); /* Target mode */ borg_keypress('*'); /* Target a location */ borg_keypress('p'); /* Determine "path" */ x1 = c_x; y1 = c_y; x2 = x; y2 = y; /* Move to the location (diagonals) */ for (; (y1 < y2) && (x1 < x2); y1++, x1++) borg_keypress('3'); for (; (y1 < y2) && (x1 > x2); y1++, x1--) borg_keypress('1'); for (; (y1 > y2) && (x1 < x2); y1--, x1++) borg_keypress('9'); for (; (y1 > y2) && (x1 > x2); y1--, x1--) borg_keypress('7'); /* Move to the location */ for (; y1 < y2; y1++) borg_keypress('2'); for (; y1 > y2; y1--) borg_keypress('8'); for (; x1 < x2; x1++) borg_keypress('6'); for (; x1 > x2; x1--) borg_keypress('4'); /* Select the target */ borg_keypress('5'); /* Success */ return (TRUE); } /* * Guess how much damage a spell attack will do to a monster * * We only handle the "standard" damage types. * * We are paranoid about monster resistances * * We ignore "special" effects for now */ static int borg_launch_damage_one(int i, int dam, int typ) { auto_kill *kill; monster_race *r_ptr; /* Monster record */ kill = &auto_kills[i]; /* Monster race */ r_ptr = &r_info[kill->r_idx]; /* Analyze the damage type */ switch (typ) { /* Magic Missile */ case GF_MISSILE: break; /* Arrow */ case GF_ARROW: break; /* Pure damage */ case GF_MANA: /* only use mana storm against uniques... this */ /* should cut down on some mana use. */ if (!borg_fighting_unique) dam = 0; break; /* Meteor -- powerful magic missile */ case GF_METEOR: break; /* Acid */ case GF_ACID: if (r_ptr->flags3 & RF3_IM_ACID) dam /= 9; break; /* Electricity */ case GF_ELEC: if (r_ptr->flags3 & RF3_IM_ELEC) dam /= 9; break; /* Fire damage */ case GF_FIRE: if (r_ptr->flags3 & RF3_IM_FIRE) dam /= 9; break; /* Cold */ case GF_COLD: if (r_ptr->flags3 & RF3_IM_COLD) dam /= 9; break; /* Poison */ case GF_POIS: if (r_ptr->flags3 & RF3_IM_POIS) dam /= 9; break; /* Ice */ case GF_ICE: if (r_ptr->flags3 & RF3_IM_COLD) dam /= 9; break; /* Holy Orb */ case GF_HOLY_ORB: if (r_ptr->flags3 & RF3_EVIL) dam *= 2; break; /* Weak Lite */ case GF_LITE_WEAK: if (!(r_ptr->flags3 & RF3_HURT_LITE)) dam = 0; break; /* Drain Life */ case GF_OLD_DRAIN: if ((r_ptr->flags3 & RF3_UNDEAD) || (r_ptr->flags3 & RF3_DEMON) || (strchr("Egv", r_ptr->d_char))) { dam = 0; } break; /* Weird attacks */ case GF_PLASMA: case GF_NETHER: case GF_WATER: case GF_CHAOS: case GF_SHARDS: case GF_SOUND: case GF_CONFUSION: case GF_DISENCHANT: case GF_NEXUS: case GF_FORCE: case GF_INERTIA: case GF_TIME: case GF_GRAVITY: case GF_LITE: case GF_DARK: dam /= 2; break; /* Various */ case GF_OLD_SLOW: case GF_OLD_CONF: case GF_OLD_SLEEP: case GF_OLD_POLY: case GF_OLD_HEAL: case GF_OLD_CLONE: case GF_OLD_SPEED: case GF_DARK_WEAK: case GF_KILL_WALL: case GF_KILL_DOOR: case GF_KILL_TRAP: case GF_MAKE_WALL: case GF_MAKE_DOOR: case GF_MAKE_TRAP: case GF_AWAY_UNDEAD: case GF_AWAY_EVIL: case GF_TURN_UNDEAD: case GF_TURN_EVIL: case GF_TURN_ALL: case GF_DISP_UNDEAD: case GF_DISP_EVIL: case GF_DISP_ALL: dam = 0; break; /* pretend the monster just gets trashed when it is telleported away */ case GF_AWAY_ALL: dam = 999999; /* never teleport away uniques. These are the guys you are trying */ /* to kill! */ if (r_ptr->flags1 & RF1_UNIQUE) dam = -5000000; break; } /* Limit damage to twice maximal hitpoints */ if (dam > kill->power * 2) dam = kill->power * 2; /* give a small bonus for whacking a unique */ /* this should be just enough to give prefrence to wacking uniques */ if (r_ptr->flags1 & RF1_UNIQUE) dam++; /* Damage */ return (dam); } /* * Simulate / Invoke the launching of a bolt at a monster */ static int borg_launch_bolt_aux_hack(int i, int dam, int typ) { int d, p, x, y; auto_grid *ag; auto_kill *kill; /* Monster */ kill = &auto_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) return (0); /* Require current knowledge */ if (kill->when < c_t) return (0); /* Acquire location */ x = kill->x; y = kill->y; /* Acquire the grid */ ag = &auto_grids[y][x]; /* Never shoot walls/doors */ if (!borg_cave_floor_grid(ag)) return (0); /* Hack -- Unknown grids should be avoided some of the time */ if ((ag->feat == FEAT_NONE) && ((c_t % 8) == 0)) return (0); /* Hack -- Weird grids should be avoided some of the time */ if ((ag->feat == FEAT_INVIS) && ((c_t % 8) == 0)) return (0); /* Calculate damage */ d = borg_launch_damage_one(i, dam, typ); /* No damage */ if (d <= 0 && typ != GF_AWAY_ALL) return (0); /* Calculate danger */ p = borg_danger_aux(y, x, 1, i); /* Hack -- avoid waking most "hard" sleeping monsters */ if (!kill->awake && (p > avoidance / 2) && (d < kill->power) && typ != GF_AWAY_ALL) { return (-999); } /* Hack -- ignore "easy" / "sleeping" town monsters */ if (!auto_depth && !kill->awake) { return (0); } /* Calculate "danger" to player */ p = borg_danger_aux(c_y, c_x, 4, i); /* Reduce "bonus" of partial kills */ if (d < kill->power) p = p / 10; /* for telleport away, danger is the only factor */ if (typ == GF_AWAY_ALL && d > 0) d = 0; /* Add in power */ d += p; /* Result */ return (d); } /* * Determine the "reward" of launching a beam/bolt/ball at a location * * An "unreachable" location always has zero reward. * * Basically, we sum the "rewards" of doing the appropriate amount of * damage to each of the "affected" monsters. */ static int borg_launch_bolt_aux(int y, int x, int rad, int dam, int typ, int max) { int i; int x1, y1; int x2, y2; int dist; int r, n; auto_grid *ag; /* Reset damage */ n = 0; /* Initial location */ x1 = c_x; y1 = c_y; /* Final location */ x2 = x; y2 = y; /* Start over */ x = x1; y = y1; /* Simulate the spell/missile path */ for (dist = 0; dist < max; dist++) { /* Get the grid */ ag = &auto_grids[y][x]; /* Stop at walls */ /* note: beams end at walls. */ if (dist) { /* Stop at walls */ /* note if beam, this is the end of the beam */ if (!borg_cave_floor_grid(ag)) { if (rad != -1) return (0); else return (n); } } /* Collect damage (bolts/beams) */ if (rad <= 0) n += borg_launch_bolt_aux_hack(ag->kill, dam, typ); /* Check for arrival at "final target" */ /* except beams, which keep going. */ if ((rad != -1) && ((x == x2) && (y == y2))) break; /* Stop bolts at monsters */ if (!rad && ag->kill) return (0); if (dist) { /* Stop at unknown grids (see above) */ /* note if beam, this is the end of the beam */ if (ag->feat == FEAT_NONE) { if (rad != -1) return (0); else return (n); } /* Stop at weird grids (see above) */ /* note if beam, this is the end of the beam */ if (ag->feat == FEAT_INVIS) { if (rad != -1) return (0); else return (n); } } /* Calculate the new location */ mmove2(&y, &x, y1, x1, y2, x2); } /* Bolt/Beam attack */ if (rad <= 0) return (n); /* Excessive distance */ if (dist >= MAX_RANGE) return (0); /* Check monsters in blast radius */ for (i = 0; i < auto_temp_n; i++) { /* Acquire location */ x = auto_temp_x[i]; y = auto_temp_y[i]; /* Get the grid */ ag = &auto_grids[y][x]; /* Check distance */ r = distance(y2, x2, y, x); /* Maximal distance */ if (r > rad) continue; /* Never pass through walls */ if (!borg_los(y2, x2, y, x)) continue; /* Collect damage, lowered by distance */ n += borg_launch_bolt_aux_hack(ag->kill, dam / (r + 1), typ); /* check destroyed stuff. */ if (ag->take) { auto_take *take = &auto_takes[ag->take]; object_kind *k_ptr = &k_info[take->k_idx]; switch (typ) { case GF_ACID: { /* rings/boots cost extra (might be speed!) */ if (k_ptr->tval == TV_BOOTS) { n -= 2000; } break; } case GF_ELEC: { /* rings/boots cost extra (might be speed!) */ if (k_ptr->tval == TV_RING) { n -= 2000; } break; } case GF_FIRE: /* rings/boots cost extra (might be speed!) */ if (k_ptr->tval == TV_BOOTS) { n -= 2000; } break; case GF_COLD: { if (k_ptr->tval == TV_POTION) { n -= 2000; } break; } case GF_MANA: { /* rings/boots cost extra (might be speed!) */ if (k_ptr->tval == TV_RING || k_ptr->tval == TV_BOOTS || k_ptr->tval == TV_POTION) { n -= 2000; } break; } } } } /* Result */ return (n); } /* * Simulate/Apply the optimal result of launching a beam/bolt/ball * * Note that "beams" have a "rad" of "-1", "bolts" have a "rad" of "0", * and "balls" have a "rad" of "2" or "3", depending on "blast radius". */ static int borg_launch_bolt(int rad, int dam, int typ, int max) { int num = 0; int i, b_i = -1; int n, b_n = 0; /* Examine possible destinations */ for (i = 0; i < auto_temp_n; i++) { int x = auto_temp_x[i]; int y = auto_temp_y[i]; /* Consider it */ n = borg_launch_bolt_aux(y, x, rad, dam, typ, max); /* Skip useless attacks */ if (n <= 0) continue; /* Collect best attack */ if ((b_i >= 0) && (n < b_n)) continue; /* Hack -- reset chooser */ if ((b_i >= 0) && (n > b_n)) num = 0; /* Apply the randomizer */ if ((num > 1) && (rand_int(num) != 0)) continue; /* Track it */ b_i = i; b_n = n; } /* Simulation */ if (auto_simulate) return (b_n); /* Save the location */ g_x = auto_temp_x[b_i]; g_y = auto_temp_y[b_i]; /* Target the location */ (void)borg_target(g_y, g_x); /* Result */ return (b_n); } /* * Simulate/Apply the optimal result of launching a normal missile * * First, pick the "optimal" ammo, then pick the optimal target */ static int borg_attack_aux_launch(void) { int b_n; int k, b_k = -1; int d, b_d = -1; auto_item *bow = &auto_items[INVEN_BOW]; /* Scan the pack */ for (k = 0; k < INVEN_PACK; k++) { auto_item *item = &auto_items[k]; /* Skip empty items */ if (!item->iqty) continue; /* Skip bad missiles */ if (item->tval != my_ammo_tval) continue; /* Skip worthless missiles */ if (item->value <= 0) continue; /* Skip un-identified, non-average, missiles */ if (!item->able && !streq(item->note, "{average}")) continue; /* Determine average damage */ d = (item->dd * (item->ds + 1) / 2); d = d + item->to_d + bow->to_d; d = d * my_ammo_power * my_num_fire; /* !FIX should do several things here; branded ammo, chance to hit, saving*/ /* good ammo for big baddies. oh well... later. AJG */ /* Paranoia */ if (d <= 0) continue; /* Ignore worse damage */ if ((b_k >= 0) && (d <= b_d)) continue; /* Track */ b_k = k; b_d = d; } /* Nothing to use */ if (b_k < 0) return (0); /* No firing while blind, confused, or hallucinating */ if (do_blind || do_confused || do_image) return (0); /* Choose optimal location */ b_n = borg_launch_bolt(0, b_d, GF_ARROW, MAX_RANGE); /* Cost one point */ b_n = b_n - 1; /* Simulation */ if (auto_simulate) return (b_n); /* Do it */ borg_note(format("# Firing standard missile '%s'", auto_items[b_k].desc)); /* Fire */ borg_keypress('f'); /* Use the missile */ borg_keypress(I2A(b_k)); /* Use target */ borg_keypress('5'); /* Value */ return (b_n); } /* * Simulate/Apply the optimal result of throwing an object * * First choose the "best" object to throw, then check targets. */ static int borg_attack_aux_object(void) { int b_n; int b_r = 0; int k, b_k = -1; int d, b_d = -1; int div, mul; /* Scan the pack */ for (k = 0; k < INVEN_PACK; k++) { auto_item *item = &auto_items[k]; /* Skip empty items */ if (!item->iqty) continue; /* Skip un-identified, non-average, objects */ if (!item->able && !streq(item->note, "{average}")) continue; /* Skip "equipment" items (not ammo) */ if (borg_wield_slot(item) >= 0) continue; /* Determine average damage from object */ d = (k_info[item->kind].dd * (k_info[item->kind].ds + 1) / 2); /* Skip useless stuff */ if (d <= 0) continue; /* Skip "expensive" stuff */ if (d < item->value) continue; /* Hack -- Save last five flasks for fuel, if needed */ if ((item->tval == TV_FLASK) && (amt_fuel <= 5)) continue; /* Ignore worse damage */ if ((b_k >= 0) && (d <= b_d)) continue; /* Track */ b_k = k; b_d = d; /* Extract a "distance multiplier" */ mul = 10; /* Enforce a minimum "weight" of one pound */ div = ((item->weight > 10) ? item->weight : 10); /* Hack -- Distance -- Reward strength, penalize weight */ b_r = (adj_str_blow[my_stat_ind[A_STR]] + 20) * mul / div; /* Max distance of 10 */ if (b_r > 10) b_r = 10; } /* Nothing to use */ if (b_k < 0) return (0); /* No firing while blind, confused, or hallucinating */ if (do_blind || do_confused || do_image) return (0); /* Choose optimal location */ b_n = borg_launch_bolt(0, b_d, GF_ARROW, b_r); /* Cost one point */ b_n = b_n - 1; /* Simulation */ if (auto_simulate) return (b_n); /* Do it */ borg_note(format("# Throwing painful object '%s'", auto_items[b_k].desc)); /* Fire */ borg_keypress('v'); /* Use the object */ borg_keypress(I2A(b_k)); /* Use target */ borg_keypress('5'); /* Value */ return (b_n); } /* * Simulate/Apply the optimal result of using a "normal" attack spell * * Take into account the failure rate of spells/objects/etc. XXX XXX XXX */ static int borg_attack_aux_spell_bolt(int book, int what, int rad, int dam, int typ) { int b_n; auto_magic *as = &auto_magics[book][what]; /* No firing while blind, confused, or hallucinating */ if (do_blind || do_confused || do_image) return (0); /* make sure I am powerfull enough to do another goi if this one falls */ if (borg_goi && ((auto_csp - as->power) < 70)) return (0); /* Paranoia */ if (auto_simulate && (rand_int(100) < 10)) return (0); /* Require ability (right now) */ /* !FIX AJG should check for %fail.... but what fail rate to use? */ /* 40% for now...*/ if (!borg_spell_okay_fail(book, what, 40)) return (0); /* Choose optimal location */ b_n = borg_launch_bolt(rad, dam, typ, MAX_RANGE); /* Penalize mana usage */ b_n = b_n - as->power; /* Penalize use of reserve mana */ if (auto_csp - as->power < auto_msp / 2) b_n = b_n - (as->power * 10); /* Really penalize use of mana needed for final teleport */ /* (6 pts for mage) */ if ((auto_msp > 30) && (auto_csp - as->power) < 6) b_n = b_n - (as->power * 50); /* Simulation */ if (auto_simulate) return (b_n); /* Cast the spell */ (void)borg_spell(book, what); /* Use target */ borg_keypress('5'); /* Value */ return (b_n); } /* * Simulate/Apply the optimal result of using a "normal" attack prayer */ static int borg_attack_aux_prayer_bolt(int book, int what, int rad, int dam, int typ) { int b_n; auto_magic *as = &auto_magics[book][what]; /* No firing while blind, confused, or hallucinating */ if (do_blind || do_confused || do_image) return (0); /* Paranoia */ if (auto_simulate && (rand_int(100) < 10)) return (0); /* Require ability */ if (!borg_prayer_okay(book, what)) return (0); /* Choose optimal location */ b_n = borg_launch_bolt(rad, dam, typ, MAX_RANGE); /* Penalize mana usage */ b_n = b_n - as->power; /* Penalize use of reserve mana */ if (auto_csp - as->power < auto_msp / 2) b_n = b_n - as->power * 10; /* Simulation */ if (auto_simulate) return (b_n); /* Cast the prayer */ (void)borg_prayer(book, what); /* Use target */ borg_keypress('5'); /* Value */ return (b_n); } /* * Simulate/Apply the optimal result of using a "normal" attack rod */ static int borg_attack_aux_rod_bolt(int sval, int rad, int dam, int typ) { int i; int b_n; /* No firing while blind, confused, or hallucinating */ if (do_blind || do_confused || do_image) return (0); /* Paranoia */ if (auto_simulate && (rand_int(100) < 10)) return (0); /* Look for that rod */ i = borg_slot(TV_ROD, sval); /* None available */ if (i < 0) return (0); /* Still "charging" */ if (!auto_items[i].pval) return (0); /* Choose optimal location */ b_n = borg_launch_bolt(rad, dam, typ, MAX_RANGE); /* Simulation */ if (auto_simulate) return (b_n); /* Zap the rod */ (void)borg_zap_rod(sval); /* Use target */ borg_keypress('5'); /* Value */ return (b_n); } /* * Simulate/Apply the optimal result of using a "normal" attack wand */ static int borg_attack_aux_wand_bolt(int sval, int rad, int dam, int typ) { int i; int b_n; /* No firing while blind, confused, or hallucinating */ if (do_blind || do_confused || do_image) return (0); /* Paranoia */ if (auto_simulate && (rand_int(100) < 10)) return (0); /* Look for that wand */ i = borg_slot(TV_WAND, sval); /* None available */ if (i < 0) return (0); /* No charges */ if (!auto_items[i].pval) return (0); /* Choose optimal location */ b_n = borg_launch_bolt(rad, dam, typ, MAX_RANGE); /* Penalize charge usage */ b_n = b_n - 5; /* Simulation */ if (auto_simulate) return (b_n); /* Aim the wand */ (void)borg_aim_wand(sval); /* Use target */ borg_keypress('5'); /* Value */ return (b_n); } /* * Simulate/Apply the optimal result of using the given "type" of attack */ static int borg_attack_aux(int what) { int dam = 0, rad = 0; /* Analyze */ switch (what) { /* Physical attack */ case BF_THRUST: return (borg_attack_aux_thrust()); /* Missile attack */ case BF_LAUNCH: return (borg_attack_aux_launch()); /* Object attack */ case BF_OBJECT: return (borg_attack_aux_object()); /* Spell -- magic missile */ case BF_SPELL_MAGIC_MISSILE: dam = (3+((auto_level-1)/5))*(4+1)/2; return (borg_attack_aux_spell_bolt(0, 0, rad, dam, GF_MISSILE)); /* Spell -- electric bolt */ case BF_SPELL_ELEC_BOLT: dam = (3+((auto_level-5)/4))*(8+1)/2; return (borg_attack_aux_spell_bolt(1, 1, rad, dam, GF_ELEC)); /* Spell -- cold bolt */ case BF_SPELL_COLD_BOLT: dam = (5+((auto_level-5)/4))*(8+1)/2; return (borg_attack_aux_spell_bolt(1, 7, rad, dam, GF_COLD)); /* Spell -- fire bolt */ case BF_SPELL_FIRE_BOLT: dam = (8+((auto_level-5)/4))*(8+1)/2; return (borg_attack_aux_spell_bolt(2, 6, rad, dam, GF_FIRE)); /* Spell -- acid bolt */ case BF_SPELL_ACID_BOLT: dam = (6+((auto_level-5)/4))*(8+1)/2; return (borg_attack_aux_spell_bolt(8, 0, rad, dam, GF_ACID)); /* Spell -- light beam */ case BF_SPELL_LITE_BEAM: rad = -1; dam = (6*(8+1)/2); return (borg_attack_aux_spell_bolt(8, 0, rad, dam, GF_LITE_WEAK)); /* Spell -- stinking cloud */ case BF_SPELL_POISON_BALL: rad = 2; dam = (10 + (auto_level/2)); return (borg_attack_aux_spell_bolt(0, 8, rad, dam, GF_POIS)); /* Spell -- cold ball */ case BF_SPELL_COLD_BALL: rad = 2; dam = (30 + auto_level); return (borg_attack_aux_spell_bolt(3, 0, rad, dam, GF_COLD)); /* Spell -- acid ball */ case BF_SPELL_ACID_BALL: rad = 2; dam = (40 + (auto_level/2)); return (borg_attack_aux_spell_bolt(8, 2, rad, dam, GF_ACID)); /* Spell -- fire ball */ case BF_SPELL_FIRE_BALL: rad = 2; dam = (55 + auto_level); return (borg_attack_aux_spell_bolt(3, 4, rad, dam, GF_FIRE)); /* Spell -- poison storm */ case BF_SPELL_POISON_STORM: rad = 3; dam = (20 + (auto_level/2)); return (borg_attack_aux_spell_bolt(8, 1, rad, dam, GF_POIS)); /* Spell -- cold storm */ case BF_SPELL_COLD_STORM: rad = 3; dam = (70 + auto_level); return (borg_attack_aux_spell_bolt(8, 3, rad, dam, GF_COLD)); /* Spell -- meteor storm */ case BF_SPELL_METEOR_STORM: rad = 3; dam = (65 + auto_level); return (borg_attack_aux_spell_bolt(8, 4, rad, dam, GF_METEOR)); /* Spell -- mana storm */ case BF_SPELL_MANA_STORM: rad = 3; dam = (300 + (auto_level * 2)); return (borg_attack_aux_spell_bolt(8, 5, rad, dam, GF_MANA)); /* Prayer -- orb of draining */ case BF_PRAYER_HOLY_ORB_BALL: rad = ((auto_level >= 30) ? 3 : 2); dam = ((auto_class == 2) ? 2 : 4); dam = (3*(8+1)/2 + auto_level + (auto_level/dam)); return (borg_attack_aux_prayer_bolt(2, 1, rad, dam, GF_HOLY_ORB)); /* Wand -- elec bolt */ case BF_ROD_ELEC_BOLT: dam = 3*(8+1)/2; return (borg_attack_aux_rod_bolt(SV_ROD_ELEC_BOLT, rad, dam, GF_ELEC)); /* Wand -- cold bolt */ case BF_ROD_COLD_BOLT: dam = 5*(8+1)/2; return (borg_attack_aux_rod_bolt(SV_ROD_COLD_BOLT, rad, dam, GF_COLD)); /* Wand -- acid bolt */ case BF_ROD_ACID_BOLT: dam = 6*(8+1)/2; return (borg_attack_aux_rod_bolt(SV_ROD_ACID_BOLT, rad, dam, GF_ACID)); /* Wand -- fire bolt */ case BF_ROD_FIRE_BOLT: dam = 8*(8+1)/2; return (borg_attack_aux_rod_bolt(SV_ROD_FIRE_BOLT, rad, dam, GF_FIRE)); /* Spell -- light beam */ case BF_ROD_LITE_BEAM: rad = -1; dam = (6*(8+1)/2); return (borg_attack_aux_rod_bolt(SV_ROD_LITE, rad, dam, GF_LITE_WEAK)); /* Wand -- elec ball */ case BF_ROD_ELEC_BALL: rad = 2; dam = 32; return (borg_attack_aux_rod_bolt(SV_ROD_ELEC_BALL, rad, dam, GF_ELEC)); /* Wand -- acid ball */ case BF_ROD_COLD_BALL: rad = 2; dam = 48; return (borg_attack_aux_rod_bolt(SV_ROD_COLD_BALL, rad, dam, GF_COLD)); /* Wand -- acid ball */ case BF_ROD_ACID_BALL: rad = 2; dam = 60; return (borg_attack_aux_rod_bolt(SV_ROD_ACID_BALL, rad, dam, GF_ACID)); /* Wand -- fire ball */ case BF_ROD_FIRE_BALL: rad = 2; dam = 72; return (borg_attack_aux_rod_bolt(SV_ROD_FIRE_BALL, rad, dam, GF_FIRE)); /* Wand -- magic missile */ case BF_WAND_MAGIC_MISSILE: dam = 2*(6+1)/2; return (borg_attack_aux_wand_bolt(SV_WAND_MAGIC_MISSILE, rad, dam, GF_MISSILE)); /* Wand -- elec bolt */ case BF_WAND_ELEC_BOLT: dam = 3*(8+1)/2; return (borg_attack_aux_wand_bolt(SV_WAND_ELEC_BOLT, rad, dam, GF_ELEC)); /* Wand -- cold bolt */ case BF_WAND_COLD_BOLT: dam = 3*(8+1)/2; return (borg_attack_aux_wand_bolt(SV_WAND_COLD_BOLT, rad, dam, GF_COLD)); /* Wand -- acid bolt */ case BF_WAND_ACID_BOLT: dam = 5*(8+1)/2; return (borg_attack_aux_wand_bolt(SV_WAND_ACID_BOLT, rad, dam, GF_ACID)); /* Wand -- fire bolt */ case BF_WAND_FIRE_BOLT: dam = 6*(8+1)/2; return (borg_attack_aux_wand_bolt(SV_WAND_FIRE_BOLT, rad, dam, GF_FIRE)); /* Spell -- light beam */ case BF_WAND_LITE_BEAM: rad = -1; dam = (6*(8+1)/2); return (borg_attack_aux_wand_bolt(SV_WAND_LITE, rad, dam, GF_LITE_WEAK)); /* Wand -- stinking cloud */ case BF_WAND_STINKING_CLOUD: rad = 2; dam = 12; return (borg_attack_aux_wand_bolt(SV_WAND_STINKING_CLOUD, rad, dam, GF_POIS)); /* Wand -- elec ball */ case BF_WAND_ELEC_BALL: rad = 2; dam = 32; return (borg_attack_aux_wand_bolt(SV_WAND_ELEC_BALL, rad, dam, GF_ELEC)); /* Wand -- acid ball */ case BF_WAND_COLD_BALL: rad = 2; dam = 48; return (borg_attack_aux_wand_bolt(SV_WAND_COLD_BALL, rad, dam, GF_COLD)); /* Wand -- acid ball */ case BF_WAND_ACID_BALL: rad = 2; dam = 60; return (borg_attack_aux_wand_bolt(SV_WAND_ACID_BALL, rad, dam, GF_ACID)); /* Wand -- fire ball */ case BF_WAND_FIRE_BALL: rad = 2; dam = 72; return (borg_attack_aux_wand_bolt(SV_WAND_FIRE_BALL, rad, dam, GF_FIRE)); /* Wand -- dragon cold */ case BF_WAND_DRAGON_COLD: rad = 3; dam = 80; return (borg_attack_aux_wand_bolt(SV_WAND_DRAGON_COLD, rad, dam, GF_COLD)); /* Wand -- dragon fire */ case BF_WAND_DRAGON_FIRE: rad = 3; dam = 100; return (borg_attack_aux_wand_bolt(SV_WAND_DRAGON_FIRE, rad, dam, GF_FIRE)); } /* Oops */ return (0); } /* * Attack nearby monsters, in the best possible way, if any. * * We consider a variety of possible attacks, including physical attacks * on adjacent monsters, missile attacks on nearby monsters, spell/prayer * attacks on nearby monsters, and wand/rod attacks on nearby monsters. * * Basically, for each of the known "types" of attack, we "simulate" the * "optimal" result of using that attack, and then we "apply" the "type" * of attack which appears to have the "optimal" result. * * When calculating the "result" of using an attack, we only consider the * effect of the attack on visible, on-screen, known monsters, which are * within 16 grids of the player. This prevents most "spurious" attacks, * but we can still be fooled by situations like creeping coins which die * while out of sight, leaving behind a pile of coins, which we then find * again, and attack with distance attacks, which have no effect. Perhaps * we should "expect" certain results, and take note of failure to observe * those effects. XXX XXX XXX * * See above for the "semantics" of each "type" of attack. */ bool borg_attack(void) { int i, x, y; int n, b_n = 0; int g, b_g = -1; auto_grid *ag; /* Nobody around */ if (!auto_kills_cnt) return (FALSE); /* Set the attacking flag so that danger is boosted for monsters */ /* we want to attack first. */ borg_attacking = TRUE; /* Reset list */ auto_temp_n = 0; /* Find "nearby" monsters */ for (i = 1; i < auto_kills_nxt; i++) { auto_kill *kill; /* Monster */ kill = &auto_kills[i]; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Require current knowledge */ if (kill->when < c_t) continue; /* Ignore multiplying monsters */ if (goal_ignoring && !do_afraid && (r_info[kill->r_idx].flags2 & RF2_MULTIPLY)) continue; /* Acquire location */ x = kill->x; y = kill->y; /* Get grid */ ag = &auto_grids[y][x]; /* Never shoot off-screen */ if (!(ag->info & BORG_OKAY)) continue; /* Never shoot through walls */ if (!(ag->info & BORG_VIEW)) continue; /* Check the distance XXX XXX XXX */ if (distance(c_y, c_x, y, x) > 16) continue; /* Save the location (careful) */ auto_temp_x[auto_temp_n] = x; auto_temp_y[auto_temp_n] = y; auto_temp_n++; } /* No destinations */ if (!auto_temp_n) { borg_attacking = FALSE; return (FALSE); } /* Simulate */ auto_simulate = TRUE; /* Analyze the possible attacks */ for (g = 0; g < BF_MAX; g++) { /* Simulate */ n = borg_attack_aux(g); /* Track "best" attack */ if (n <= b_n) continue; /* Track best */ b_g = g; b_n = n; } /* Nothing good */ if (b_n <= 0) { borg_attacking = FALSE; return (FALSE); } /* Note */ borg_note(format("# Performing attack type %d with value %d", b_g, b_n)); /* Instantiate */ auto_simulate = FALSE; /* Instantiate */ (void)borg_attack_aux(b_g); borg_attacking = FALSE; /* Success */ return (TRUE); } /* * try to make this look like borg_attack stuff * * There are several types of seup moves: * * Temporary speed * Protect From Evil * Bless\Prayer * Berserk\Heroism * Temp Resist (either all or just cold/fire?) * Shield * teleport away * * * * see inviso? * * Glyph of Warding * * * some day! * */ enum { BD_SPEED, BD_PROT_FROM_EVIL, BD_BLESS, BD_BERSERK, BD_HERO, BD_RESIST1, BD_RESIST2, BD_RESIST3, BD_RESIST4, BD_RESIST5, BD_RESIST6, BD_SHIELD, BD_GOI, BD_TELL_AWAY, BD_CREATE_DOOR, BD_MAX }; /* * Bless/Prayer to prepare for battle */ static int borg_defend_aux_bless( int p1 ) { int fail_allowed = 10; /* already blessed */ if (borg_bless) return 0; if ( !borg_prayer_okay_fail(0, 2, fail_allowed) && !borg_prayer_okay_fail(3, 0, fail_allowed ) && !borg_prayer_okay_fail(1, 3, fail_allowed )) return 0; /* if we are in some danger but not much, go for a quick bless */ if (p1 > avoidance/7 && p1 < avoidance/3) { /* Simulation */ /* bless is a low priority */ if (auto_simulate) return (1); /* do it! */ if ( borg_prayer_fail( 3, 0, fail_allowed ) || borg_prayer_fail( 1, 3, fail_allowed ) || borg_prayer_fail( 0, 2, fail_allowed )) return 1; } return 0; } /* * Speed to prepare for battle */ static int borg_defend_aux_speed( int p1 ) { int p2 = 0; /* int i = 0; */ bool good_speed = FALSE; bool speed_spell = FALSE; bool speed_staff = FALSE; bool speed_rod = FALSE; int fail_allowed = 20; /* already fast */ if (borg_speed) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 10; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/3) fail_allowed += 20; /* only cast defence spells if fail rate is not too high */ if ( borg_spell_okay_fail( 3, 3, fail_allowed ) || borg_spell_okay_fail( 7, 3, fail_allowed )) speed_spell = TRUE; /* staff must have charges */ if ( -1 != borg_slot(TV_STAFF, SV_STAFF_SPEED) && auto_items[borg_slot(TV_STAFF, SV_STAFF_SPEED)].pval) speed_staff = TRUE; /* rod can't be charging */ if ( -1 != borg_slot(TV_ROD, SV_ROD_SPEED) && auto_items[borg_slot(TV_ROD, SV_ROD_SPEED)].pval) speed_rod = TRUE; if (0 > borg_slot(TV_POTION, SV_POTION_SPEED) && !speed_staff && !speed_rod && !speed_spell) return 0; /* if we have an infinite/large suppy of speed we can */ /* be generious with our use */ if (speed_rod || speed_spell || speed_staff) good_speed = TRUE; /* pretend we are protected and look again */ borg_speed = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_speed = FALSE; /* if we just did GOI do a speed right after. */ if (good_speed && borg_goi) { /* HACK pretend that it was scary and will be very safe */ /* This is done because GOI messes up our calculations */ p1 = 10000; p2 = 1; } /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if ( ((p1 > p2) && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && (p1 > (avoidance/8)) && good_speed) || ((p1 > p2) && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/3)) && (p1 > (avoidance/2)))) { /* Simulation */ if (auto_simulate) return (p1-p2 + borg_goi * 50); /* do it! */ if (borg_spell_fail( 7, 3, fail_allowed)) return (p1-p2); if (borg_spell_fail( 3, 3, fail_allowed)) return (p1-p2); if ( borg_zap_rod( SV_ROD_SPEED ) || borg_use_staff( SV_STAFF_SPEED) || borg_quaff_potion(SV_POTION_SPEED) ) /* Value */ return (p1-p2 + borg_goi * 50); } /* default to can't do it. */ return 0; } /* * Globe of Invulnurability */ static int borg_defend_aux_goi(int p1) { int p2 = 0; /* int i = 0; */ int fail_allowed = 20; if (borg_goi) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 10; else /* a bit scary */ if ( p1 > (avoidance/2)) fail_allowed -= 5; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/4) fail_allowed += 10; if (!borg_spell_okay_fail(7, 4, fail_allowed)) return 0; /* pretend we are protected and look again */ borg_goi = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_goi = FALSE; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && p1 > (avoidance/8)) { /* Simulation */ if (auto_simulate) return (p1-p2); /* do it! */ borg_spell(7, 4); /* Value */ return (p1-p2); } /* default to can't do it. */ return 0; } /* cold/fire */ static int borg_defend_aux_resist1( p1 ) { int p2 = 0; /* int i = 0; */ int fail_allowed = 20; bool save_fire, save_cold; if (borg_temp_fire && borg_temp_cold) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 10; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/3) fail_allowed += 20; if (!borg_prayer_okay_fail(1, 7, fail_allowed)) return 0; /* pretend we are protected and look again */ save_fire = borg_temp_fire; save_cold = borg_temp_cold; borg_temp_fire = TRUE; borg_temp_cold = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_temp_fire = save_fire; borg_temp_cold = save_cold; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && p1 > (avoidance/8)) { /* Simulation */ if (auto_simulate) return (p1-p2); /* do it! */ borg_prayer(1, 7); /* Value */ return (p1-p2); } /* default to can't do it. */ return 0; } /* all resists */ static int borg_defend_aux_resist2( int p1) { int p2 = 0; /* int i = 0; */ int fail_allowed = 20; bool save_fire, save_acid, save_poison, save_elec, save_cold; if (borg_temp_fire && borg_temp_acid && borg_temp_poison && borg_temp_elec && borg_temp_cold) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 10; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/3) fail_allowed += 20; if (!borg_spell_okay_fail(4, 4, fail_allowed)) return 0; /* pretend we are protected and look again */ save_fire = borg_temp_fire; save_acid = borg_temp_acid; save_poison = borg_temp_poison; save_elec = borg_temp_elec; save_cold = borg_temp_cold; borg_temp_fire = TRUE; borg_temp_cold = TRUE; borg_temp_acid = TRUE; borg_temp_poison = TRUE; borg_temp_elec = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_temp_fire = save_fire; borg_temp_cold = save_cold; borg_temp_acid = save_acid; borg_temp_poison = save_poison; borg_temp_elec = save_elec; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && p1 > (avoidance/8)) { /* Simulation */ if (auto_simulate) return ((p1-p2) -1); /* do it! */ borg_spell(4, 4); /* Value */ return ((p1-p2)-1); } /* default to can't do it. */ return 0; } /* fire */ static int borg_defend_aux_resist3( p1 ) { int p2 = 0; /* int i = 0; */ int fail_allowed = 20; bool save_fire; if ( borg_temp_fire ) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 10; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/3) fail_allowed += 20; if (!borg_spell_okay_fail(4, 0, fail_allowed)) return 0; save_fire = borg_temp_fire; /* pretend we are protected and look again */ borg_temp_fire = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_temp_fire = save_fire; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && p1 > (avoidance/8)) { /* Simulation */ if (auto_simulate) return (p1-p2); /* do it! */ borg_spell(4, 0); /* Value */ return (p1-p2); } /* default to can't do it. */ return 0; } /* cold */ static int borg_defend_aux_resist4( p1 ) { int p2 = 0; /* int i = 0; */ int fail_allowed = 20; bool save_cold; if ( borg_temp_cold ) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 10; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/3) fail_allowed += 20; if (!borg_spell_okay_fail(4, 1, fail_allowed)) return 0; save_cold = borg_temp_cold; /* pretend we are protected and look again */ borg_temp_cold = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_temp_cold = save_cold; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && p1 > (avoidance/8)) { /* Simulation */ if (auto_simulate) return (p1-p2); /* do it! */ borg_spell(4, 1); /* Value */ return (p1-p2); } /* default to can't do it. */ return 0; } /* acid */ static int borg_defend_aux_resist5( p1 ) { int p2 = 0; /* int i = 0; */ int fail_allowed = 20; bool save_acid; if ( borg_temp_acid ) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 10; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/3) fail_allowed += 20; if (!borg_spell_okay_fail(4, 2, fail_allowed)) return 0; save_acid = borg_temp_acid; /* pretend we are protected and look again */ borg_temp_acid = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_temp_acid = save_acid; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && p1 > (avoidance/8)) { /* Simulation */ if (auto_simulate) return (p1-p2); /* do it! */ borg_spell(4, 2); /* Value */ return (p1-p2); } /* default to can't do it. */ return 0; } /* poison */ static int borg_defend_aux_resist6( p1 ) { int p2 = 0; /* int i = 0; */ int fail_allowed = 20; bool save_poison; if ( borg_temp_poison ) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 10; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/3) fail_allowed += 20; if (!borg_spell_okay_fail(4, 3, fail_allowed)) return 0; save_poison = borg_temp_poison; /* pretend we are protected and look again */ borg_temp_poison = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_temp_poison = save_poison; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && p1 > (avoidance/8)) { /* Simulation */ if (auto_simulate) return (p1-p2); /* do it! */ borg_spell(4, 3); /* Value */ return (p1-p2); } /* default to can't do it. */ return 0; } static int borg_defend_aux_prot_evil( int p1) { int p2 = 0; int fail_allowed = 20; /* if already protected */ if (borg_prot_from_evil) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 5; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/3) fail_allowed += 20; if ((!borg_prayer_okay_fail(2, 4, fail_allowed)) && (0 > borg_slot(TV_SCROLL, SV_SCROLL_PROTECTION_FROM_EVIL)) ) return 0; /* pretend we are protected and look again */ borg_prot_from_evil = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_prot_from_evil = FALSE; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && p1 > (avoidance/8)) { /* Simulation */ if (auto_simulate) return (p1-p2); /* do it! */ if (borg_prayer_fail(2, 4, fail_allowed)) return (p1-p2); borg_read_scroll(SV_SCROLL_PROTECTION_FROM_EVIL); /* Value */ return (p1-p2); } /* default to can't do it. */ return 0; } static int borg_defend_aux_shield( int p1) { int p2 = 0; int fail_allowed = 20; /* if already protected */ if (borg_shield) return 0; /* if very scary, do not allow for much chance of fail */ if ( p1 > avoidance) fail_allowed -= 19; else /* a little scary */ if ( p1 > (avoidance*2)/3) fail_allowed -= 5; else /* not very scary, allow lots of fail */ if ( p1 < avoidance/3) fail_allowed += 20; if (!borg_spell_okay_fail(7, 1, fail_allowed)) return 0; /* pretend we are protected and look again */ borg_shield = TRUE; p2 = borg_danger(c_y, c_x, 1); borg_shield = FALSE; /* if this is an improvement and we may not avoid monster now and */ /* we may have before */ if (p1 > p2 && p2 <= (borg_fighting_unique?((avoidance*2)/3): (avoidance/2)) && p1 > (avoidance/8)) { /* Simulation */ if (auto_simulate) return (p1-p2); /* do it! */ borg_spell_fail(7, 1, fail_allowed); return (p1-p2); } /* default to can't do it. */ return 0; } /* * Try to get rid of all of the non-uniques around so you can go at it * 'mano-e-mano' with the unique. */ static int borg_defend_aux_tell_away( int p1) { int b_n = 0; int fail_allowed = 1; /* This is not working. It keeps teleporting away the unique! */ /* I will debug it later... */ return -1; /* Only tell away if scared and there is a local unique */ if ( p1 < avoidance/2) return 0; if (!borg_spell_okay_fail(3, 2, fail_allowed)) return 0; if (!borg_fighting_unique) return 0; /* Choose optimal location */ b_n = borg_launch_bolt(-1, 0, GF_AWAY_ALL, MAX_RANGE); /* if this doesn't help enough, don't bother. */ if ((p1 - b_n) > (avoidance*2)/3) return 0; /* Simulation */ if (auto_simulate) return (b_n); /* Cast the spell */ if (borg_spell(3, 2)) { /* Use target */ borg_keypress('5'); /* Value */ return (b_n); } return 0; } /* * Simulate/Apply the optimal result of using the given "type" of defence * p1 is the current danger level (passed in for effiency) */ static int borg_defend_aux(int what, int p1) { int dam = 0, rad = 0; /* Analyze */ switch (what) { case BD_SPEED: { return (borg_defend_aux_speed(p1)); } case BD_PROT_FROM_EVIL: { return (borg_defend_aux_prot_evil(p1)); } case BD_RESIST1: { return (borg_defend_aux_resist1(p1)); } case BD_RESIST2: { return (borg_defend_aux_resist2(p1)); } case BD_RESIST3: { return (borg_defend_aux_resist3(p1)); } case BD_RESIST4: { return (borg_defend_aux_resist4(p1)); } case BD_RESIST5: { return (borg_defend_aux_resist5(p1)); } case BD_RESIST6: { return (borg_defend_aux_resist6(p1)); } case BD_BLESS: { return (borg_defend_aux_bless(p1)); } case BD_HERO: { /*"You feel like a hero!"*/ /*"The heroism wears off." */ /* return (borg_defend_aux_hero(p1)); */ break; } case BD_BERSERK: { /*"You feel like a killing machine!"*/ /*"You feel less Berserk."*/ /* return (borg_defend_aux_berserk(p1));*/ break; } case BD_SHIELD: { return (borg_defend_aux_shield(p1)); } case BD_GOI: { return (borg_defend_aux_goi(p1)); } case BD_TELL_AWAY: { return (borg_defend_aux_tell_away(p1)); } case BD_CREATE_DOOR: { /* to avoid summoners */ /* again, I have no formula... */ /* return (borg_defend_aux_create_door(p1)); */ break; } } return 0; } /* * prepare to attack... this is setup for a battle. */ bool borg_defend(void) { int p1, n, b_n = 0; int g, b_g = -1; /* Simulate */ auto_simulate = TRUE; /* check danger level. (this is passed in for efficency) */ p1 = borg_danger(c_y, c_x, 1); /* if you have a globe up and it is about to drop, */ /* refresh it (if you can) */ if (borg_goi && borg_goi < 2) { int p; /* check 'true' danger. This will make sure we do not */ /* refresh our GOI if no-one is around */ borg_attacking = TRUE; p = borg_danger(c_y, c_x, 1); borg_attacking = FALSE; if (p > auto_fear_region[c_y/11][c_x/11]) { if (borg_spell(7, 4)) { borg_note("# refreshing GOI "); borg_goi += 13; return (TRUE); } } } /* if no danger, no defence needed */ /* HACK EXCEPT... if goi we will also do speed */ if ((p1 <= auto_fear_region[c_y/11][c_x/11] && !borg_goi) || (borg_goi && borg_speed)) return FALSE; /* Analyze the possible setup moves */ for (g = 0; g < BD_MAX; g++) { /* Simulate */ n = borg_defend_aux(g, p1); /* Track "best" attack */ if (n <= b_n) continue; /* Track best */ b_g = g; b_n = n; } /* Nothing good */ if (b_n <= 0) { return (FALSE); } /* Note */ borg_note(format("# Performing defence type %d with value %d", b_g, b_n)); /* Instantiate */ auto_simulate = FALSE; /* Instantiate */ (void)borg_defend_aux(b_g, p1); /* Success */ return (TRUE); } /* * check to make sure there are no monsters around * that should prevent resting */ static bool borg_check_rest(void) { int i; /* Examine all the monsters */ for (i = 1; i < auto_kills_nxt; i++) { auto_kill *kill = &auto_kills[i]; monster_race *r_ptr = &r_info[kill->r_idx]; int x9 = kill->x; int y9 = kill->y; int ax, ay, d; /* Skip dead monsters */ if (!kill->r_idx) continue; /* Distance components */ ax = (x9 > c_x) ? (x9 - c_x) : (c_x - x9); ay = (y9 > c_y) ? (y9 - c_y) : (c_y - y9); /* Distance */ d = MAX(ax, ay); /* Minimal distance */ if (d > 20) continue; /* if breeder, not safe */ if (r_ptr->flags2 & RF2_MULTIPLY) return FALSE; /* if absorbs mana, not safe */ /* should check LOS... oh well. AJG !FIX */ if ((r_ptr->flags5 & RF5_DRAIN_MANA) && (auto_msp > 1)) return FALSE; } return TRUE; } /* * Attempt to recover from damage and such after a battle * * Note that resting while in danger is counter-productive, unless * the danger is low, in which case it may induce "farming". * * Note that resting while recall is active will often cause you * to lose hard-won treasure from nasty monsters, so we disable * resting when waiting for recall in the dungeon near objects. * * First we try spells/prayers, which are "free", and then we * try food, potions, scrolls, staffs, rods, artifacts, etc. * * XXX XXX XXX * Currently, we use healing spells as if they were "free", but really, * this is only true if the "danger" is less than the "reward" of doing * the healing spell, and if there are no monsters which might soon step * around the corner and attack. */ bool borg_recover(void) { int p, q, i; /*** Handle annoying situations ***/ /* Refuel current torch */ if ((auto_items[INVEN_LITE].tval == TV_LITE) && (auto_items[INVEN_LITE].sval == SV_LITE_TORCH)) { /* Refuel the torch if needed */ if (auto_items[INVEN_LITE].pval < 2500) { if (borg_refuel_torch()) return (TRUE); /* Take note */ borg_note(format("# Need to refuel but cant!", p)); } } /* Refuel current lantern */ if ((auto_items[INVEN_LITE].tval == TV_LITE) && (auto_items[INVEN_LITE].sval == SV_LITE_LANTERN)) { /* Refuel the lantern if needed */ if (auto_items[INVEN_LITE].pval < 5000) { if (borg_refuel_lantern()) return (TRUE); /* Take note */ borg_note(format("# Need to refuel but cant!", p)); } } /*** Do not recover when in danger ***/ /* Look around for danger */ p = borg_danger(c_y, c_x, 1); /* Never recover in dangerous situations */ if (p > avoidance / 4) return (FALSE); /*** Roll for "paranoia" ***/ /* Base roll */ q = rand_int(100); /* Half dead */ if (auto_chp < auto_mhp / 2) q = q - 10; /* Almost dead */ if (auto_chp < auto_mhp / 4) q = q - 10; /*** Use "cheap" cures ***/ /* Hack -- cure stun */ if (do_stun && (q < 75)) { if (borg_prayer(2, 7) || borg_prayer(3, 2) || borg_prayer(6, 1) || borg_prayer(6, 2)) { /* Take note */ borg_note(format("# Cure Stun", p)); return (TRUE); } } /* Hack -- cure stun */ if (do_heavy_stun) { if (borg_prayer(2, 7) || borg_prayer(3, 2) || borg_prayer(6, 1) || borg_prayer(6, 2)) { /* Take note */ borg_note(format("# Cure Heavy Stun", p)); return (TRUE); } } /* Hack -- cure cuts */ if (do_cut && (q < 75)) { if (borg_prayer(2, 2) || borg_prayer(2, 7) || borg_prayer(3, 2) || borg_prayer(6, 0) || borg_prayer(6, 1) || borg_prayer(6, 2)) { /* Take note */ borg_note(format("# Cure Cuts", p)); return (TRUE); } } /* Hack -- cure poison */ if (do_poisoned && (q < 75)) { if (borg_spell(1, 4) || borg_prayer(2, 0)) { /* Take note */ borg_note(format("# Cure poison", p)); return (TRUE); } } /* Hack -- cure fear */ if (do_afraid && (q < 75)) { if (borg_spell(7, 2) || borg_spell(7, 0) || borg_prayer(0, 3)) { /* Take note */ borg_note(format("# Cure fear", p)); return (TRUE); } } /* Hack -- satisfy hunger */ if (do_hungry && (q < 75)) { if (borg_spell(2, 0) || borg_prayer(1, 5)) { return (TRUE); } } /* Hack -- heal damage */ if ((auto_chp < auto_mhp / 2) && (q < 75) && p == 0 && (auto_csp > auto_msp /4)) { if (borg_prayer(3, 2) || borg_prayer(6, 2) || borg_prayer(2, 7) || borg_prayer(6, 1) ) { /* Take note */ borg_note(format("# heal damage (recovering)")); return (TRUE); } } /* cure experience loss with prayer */ if (do_fix_exp && borg_prayer(6, 4)) { return (TRUE); } /* cure stat drain with prayer */ for (i = 0; i < 6; i++) { if ( do_fix_stat[i] && borg_prayer(6, 3) ) { return (TRUE); } } /*** Use "expensive" cures ***/ /* Hack -- cure stun */ if (do_stun && (q < 25)) { if (borg_quaff_potion(SV_POTION_CURE_CRITICAL) || borg_use_staff(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING) || borg_zap_rod(SV_ROD_HEALING)) { return (TRUE); } } /* Hack -- cure heavy stun */ if (do_heavy_stun && (q < 95)) { if (borg_quaff_potion(SV_POTION_CURE_CRITICAL) || borg_use_staff(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING) || borg_zap_rod(SV_ROD_HEALING)) { return (TRUE); } } /* Hack -- cure cuts */ if (do_cut && (q < 25)) { if (borg_quaff_potion(SV_POTION_CURE_CRITICAL) || borg_use_staff(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING) || borg_zap_rod(SV_ROD_HEALING)) { return (TRUE); } } /* Hack -- cure poison */ if (do_poisoned && (q < 25)) { if (borg_quaff_potion(SV_POTION_CURE_POISON) || borg_quaff_potion(SV_POTION_SLOW_POISON) || borg_quaff_potion(SV_POTION_CURE_CRITICAL) || borg_eat_food(SV_FOOD_CURE_POISON) || borg_eat_food(SV_FOOD_WAYBREAD) || borg_use_staff(SV_STAFF_CURING) || borg_zap_rod(SV_ROD_CURING) || borg_activate_artifact(ART_DAL)) { return (TRUE); } } /* Hack -- cure blindness */ if (do_blind && (q < 25)) { if (borg_eat_food(SV_FOOD_CURE_BLINDNESS) || borg_quaff_potion(SV_POTION_CURE_LIGHT) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_potion(SV_POTION_CURE_CRITICAL) || borg_use_staff(SV_STAFF_CURING) || borg_use_staff(SV_ROD_CURING)) { return (TRUE); } } /* Hack -- cure confusion */ if (do_confused && (q < 25)) { if (borg_eat_food(SV_FOOD_CURE_CONFUSION) || borg_quaff_potion(SV_POTION_CURE_SERIOUS) || borg_quaff_potion(SV_POTION_CURE_CRITICAL) || borg_use_staff(SV_STAFF_CURING) || borg_use_staff(SV_ROD_CURING)) { return (TRUE); } } /* Hack -- cure fear */ if (do_afraid && (q < 25)) { if (borg_eat_food(SV_FOOD_CURE_PARANOIA) || borg_quaff_potion(SV_POTION_BOLDNESS) || borg_quaff_potion(SV_POTION_HEROISM) || borg_quaff_potion(SV_POTION_BESERK_STRENGTH) || borg_activate_artifact(ART_DAL)) { return (TRUE); } } /* Hack -- satisfy hunger */ if (do_hungry && (q < 25)) { if (borg_read_scroll(SV_SCROLL_SATISFY_HUNGER)) { return (TRUE); } } /* Hack -- heal damage */ if ((auto_chp < auto_mhp / 2) && (q < 25)) { if (borg_zap_rod(SV_ROD_HEALING) || borg_quaff_potion(SV_POTION_CURE_CRITICAL) || borg_quaff_potion(SV_POTION_CURE_SERIOUS)) { return (TRUE); } } /*** Just Rest ***/ /* Hack -- rest until healed */ if ((do_blind || do_confused || do_image || do_poisoned || do_afraid || do_cut || do_stun || do_heavy_stun || (auto_chp < auto_mhp) || (auto_csp < auto_msp)) && (!auto_takes_cnt || !goal_recalling) && !borg_goi && (rand_int(100) < 90) && borg_check_rest( ) && p <= auto_fear_region[c_y/11][c_x/11]) { /* XXX XXX XXX */ if (!do_weak && !do_hungry) { /* Take note */ borg_note(format("# Resting (danger %d)...", p)); /* Rest until done */ borg_keypress('R'); borg_keypress('&'); borg_keypress('\n'); /* Done */ return (TRUE); } } /* Nope */ return (FALSE); } /* * Take one "step" towards the given location, return TRUE if possible */ static bool borg_play_step(int y2, int x2) { auto_grid *ag; int dir, x, y; /* Get a direction, if possible */ dir = borg_goto_dir(c_y, c_x, y2, x2); /* We have arrived */ if (dir == 5) return (FALSE); /* Obtain the destination */ x = c_x + ddx[dir]; y = c_y + ddy[dir]; /* Access the grid we are stepping on */ ag = &auto_grids[y][x]; /* Hack -- set goal */ g_x = x; g_y = y; /* Monsters -- Attack */ if (ag->kill) { auto_kill *kill = &auto_kills[ag->kill]; /* can't attack someone if afraid! */ if (do_afraid) return (FALSE); /* Message */ borg_note(format("# Walking into a '%s' at (%d,%d)", r_name + r_info[kill->r_idx].name, kill->x, kill->y)); /* Walk into it */ borg_keypress('0' + dir); return (TRUE); } /* Objects -- Take */ if (ag->take) { auto_take *take = &auto_takes[ag->take]; /* Message */ borg_note(format("# Walking onto a '%s' at (%d,%d)", k_name + k_info[take->k_idx].name, take->x, take->y)); /* Walk onto it */ borg_keypress('0' + dir); return (TRUE); } /* Traps -- disarm */ if ((ag->feat >= FEAT_TRAP_HEAD) && (ag->feat <= FEAT_TRAP_TAIL)) { /* Disarm */ borg_note("# Disarming a trap"); borg_keypress('D'); borg_keypress('0' + dir); return (TRUE); } /* Closed Doors -- Open */ if ((ag->feat >= FEAT_DOOR_HEAD) && (ag->feat <= FEAT_DOOR_HEAD + 0x07)) { /* Paranoia XXX XXX XXX */ if (!rand_int(100)) return (FALSE); /* Open */ borg_note("# Opening a door"); borg_keypress('0'); borg_keypress('9'); borg_keypress('o'); borg_keypress('0' + dir); return (TRUE); } /* Jammed Doors -- Bash or destroy */ if ((ag->feat >= FEAT_DOOR_HEAD + 0x08) && (ag->feat <= FEAT_DOOR_TAIL)) { /* Paranoia XXX XXX XXX */ if (!rand_int(100)) return (FALSE); /* Mega-Hack -- allow "destroy doors" */ if (borg_prayer(7, 0)) { borg_note("# Unbarring ways"); return (TRUE); } /* Mega-Hack -- allow "destroy doors" */ if (borg_spell(1, 2)) { borg_note("# Destroying doors"); return (TRUE); } /* Mega-Hack -- allow "stone to mud" */ if (borg_spell(1, 8)) { borg_note("# Melting a door"); borg_keypress('0' + dir); return (TRUE); } /* Bash */ borg_note("# Bashing a door"); borg_keypress('B'); borg_keypress('0' + dir); return (TRUE); } /* Rubble, Treasure, Seams, Walls -- Tunnel or Melt */ if (ag->feat >= FEAT_SECRET) { /* Mega-Hack -- prevent infinite loops */ if (rand_int(100) < 10) return (FALSE); /* Mega-Hack -- allow "stone to mud" */ if (borg_spell(1, 8)) { borg_note("# Melting a wall/etc"); borg_keypress('0' + dir); return (TRUE); } /* Tunnel */ borg_note("# Digging through wall/etc"); borg_keypress('0'); borg_keypress('9'); borg_keypress('9'); borg_keypress('T'); borg_keypress('0' + dir); return (TRUE); } /* Shops -- Enter */ if ((ag->feat >= FEAT_SHOP_HEAD) && (ag->feat <= FEAT_SHOP_TAIL)) { /* Message */ borg_note(format("# Entering a '%d' shop", (ag->feat - FEAT_SHOP_HEAD) + 1)); /* Enter the shop */ borg_keypress('0' + dir); return (TRUE); } /* Walk in that direction */ borg_keypress('0' + dir); /* Did something */ return (TRUE); } /* * Act twitchy */ bool borg_twitchy(void) { int dir; /* This is a bad thing */ borg_note("# Twitchy!"); /* Pick a random direction */ dir = randint(9); /* Hack -- set goal */ g_x = c_x + ddx[dir]; g_y = c_y + ddy[dir]; /* Move */ borg_keypress('0' + dir); /* We did something */ return (TRUE); } /* * Commit the current "flow" */ static bool borg_flow_commit(cptr who, int why) { int cost; /* Cost of current grid */ cost = auto_data_cost->data[c_y][c_x]; /* Verify the total "cost" */ if (cost >= 250) return (FALSE); /* Message */ if (who) borg_note(format("# Flowing toward %s at cost %d", who, cost)); /* Obtain the "flow" information */ COPY(auto_data_flow, auto_data_cost, auto_data); /* Save the goal type */ goal = why; /* Success */ return (TRUE); } /* * Attempt to take an optimal step towards the current goal location * * Note that the "borg_update()" routine notices new monsters and objects, * and movement of monsters and objects, and cancels any flow in progress. * * Note that the "borg_update()" routine notices when a grid which was * not thought to block motion is discovered to in fact be a grid which * blocks motion, and removes that grid from any flow in progress. * * When given multiple alternative steps, this function attempts to choose * the "safest" path, by penalizing grids containing embedded gold, monsters, * rubble, doors, traps, store doors, and even floors. This allows the Borg * to "step around" dangerous grids, even if this means extending the path by * a step or two, and encourages him to prefer grids such as objects and stairs * which are not only interesting but which are known not to be invisible traps. * * XXX XXX XXX XXX This function needs some work. It should attempt to * analyze the "local region" around the player and determine the optimal * choice of locations based on some useful computations. * * If it works, return TRUE, otherwise, cancel the goal and return FALSE. */ bool borg_flow_old(int why) { int x, y; auto_grid *ag; /* Continue */ if (goal == why) { int b_n = 0; int i, b_i = -1; int c, b_c; /* Flow cost of current grid */ b_c = auto_data_flow->data[c_y][c_x] * 10; /* Prevent loops */ b_c = b_c - 5; /* Look around */ for (i = 0; i < 8; i++) { /* Grid in that direction */ x = c_x + ddx_ddd[i]; y = c_y + ddy_ddd[i]; /* Access the grid */ ag = &auto_grids[y][x]; /* Flow cost at that grid */ c = auto_data_flow->data[y][x] * 10; /* Never backtrack */ if (c > b_c) continue; /* Notice new best value */ if (c < b_c) b_n = 0; /* Apply the randomizer to equivalent values */ if ((++b_n >= 2) && (rand_int(b_n) != 0)) continue; /* Track it */ b_i = i; b_c = c; } /* Try it */ if (b_i >= 0) { /* Access the location */ x = c_x + ddx_ddd[b_i]; y = c_y + ddy_ddd[b_i]; /* Attempt motion */ if (borg_play_step(y, x)) return (TRUE); } /* Cancel goal */ goal = 0; } /* Nothing to do */ return (FALSE); } /* * Prepare to flee the level via stairs */ bool borg_flow_stair_both(int why)