/* File: borg9.c */ /* Purpose: Highest level functions 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" #include "borg7.h" #include "borg8.h" #include "borg9.h" /* * The "Angband Borg" is an automatic Angband player. * * Use of the Borg requires re-compilation with ALLOW_BORG defined, * and linking the various "borg source files" into the executable. * * The "borg source files" have been updated for use with Angband 2.8.2, * and will not work with any other version without (minor) modifications. * * Note that you can only use the Borg if your character has been marked * as a "Borg User". You can do this, if necessary, by responding "y" * when asked if you really want to use the Borg. This will (normally) * result in your character being inelligible for the high score list. * * The "do_cmd_borg()" function, called when the user hits "^Z", allows * the user to interact with the Borg, by entering a "borg-command" when * prompted. Any borg-command (except escape) will cause the Borg to be * initialized (if needed), and then the borg-command will be executed. * * The first time you enter any borg-command, the Borg is initialized. * This consists of three major steps, and requires at least 400K of * free memory. If enough memory is not available, the game may abort. * First, the various "borg" modules are initialized. Second, various * important "state" information is extracted, including the level and * race/class of the player, and this information is used to perform * more initialization. Third, some "historical" information (killed * uniques, maximum dungeon depth, etc) is "stolen" from the game. * * The simplest borg-command is "z", which "activates" the Borg, causing it * to take control of the character, and play the game, until you hit a key. * Other borg-commands, including "?", are described in "do_cmd_borg()". * * When the Borg is "activated", it uses the "Term_inkey_hook" to steal * control from the user. Later, if it detects any input from the user, * it gracefully relinquishes control by clearing the "Term_inkey_hook" * after any pending borg-key-sequences are complete. * * The Borg will abort if it detects any errors (such as bizarre screens), * strange situations (such as death), or panic situations (such as being * about to die), if the appropriate flags are set. * * The Borg is only supposed to "know" what a human playing the game could * know, that is, observe the information actually displayed on the screen, * and is only supposed to "do" the things that a human playing the game * could do, that is, send keypresses to the game. * * The Borg uses the "z-term.c" package to "look" at the screen, using * the screen access function "Term_what()", the cursor location function * "Term_locate()", and the cursor visibility function "Term_get_cursor()". * Actually, it uses inline versions of these functions (for efficiency). * This is very similar to what a human does, except that a human must * actually mentally convert visual impulses into colored symbols. * * The Borg uses a special hook in the "z-term.c" package to allow it to * "steal" control from the "Term_inkey()" and "Term_flush()" functions, * and to "send" keypress events as if it was a normal user. This is not * quite what a human does, in particular, by stealing control in such a * manner, the Borg is able to know exactly when the game is waiting for * a keypress, which allows it to know when the screen has finished * being refreshed. * * If the code wqs properly designed, that is, if the Borg actually acted * just like a real human player, then it could, in theory, be run as an * external process, which would actually examine a physical display and * send keypresses directly to a physical keyboard, or at least (to avoid * the need for a camera and a robotic arm), which would run the game in * a "pseudo-terminal", allowing it to examine the current screen image * and send keypress events to the underlying process (the Angband Game). * * Currently, the code is *not* properly designed, that is, there are a * few places in which it does things which a human would not be able to * do, such as examine (and even change) the values of certain variables * private to the game. However, there are very few of these places, so * with a little work, it should be possible for the Borg to actually * simulate a real human player. All known "problems" are listed below, * and note that almost all of them would disappear if the Borg was made * into a separate executable and taught to generate its own character, * and to determine when the game was ready for a keypress. * * A major problem is that the Borg "knows" when the game is waiting for * a keypress. There is no single solution for this problem, since even * humans sometimes get confused when playing over a very slow connection. * There are some heuristics which would probably be sufficient, including * waiting until the screen does not change for a full second, and assuming * that it is therefore waiting for input, or using clever choices of some * "free" commands, such as the "note" command, to allow the Borg to cause * special things to appear on the screen when the game is ready for the * next command. The hardest thing would actually be making sure that all * possible situations were accounted for. * * A minor problem is that the Borg must assume that a human has already * created a character, and set up various options and such so that the * Borg can function correctly. Currently, to avoid crashing if these * assumptions are incorrect, the code actually sets some options and * such to known values, and redraws the screen using the new options, * any time a human user asks the Borg to initialize or resume playing. * Since a human may, at any time, take over and perform arbitrary acts, * and since the Borg does not actually generate the character, currently, * when the Borg is initialized (or restarted) it "steals" some important * values directly from the game, including the deepest level which has * been explored, and which uniques are known to have been killed. Other * values which could be stolen are actually extracted from the screen, * such as the race and class of the character. All of these problems * would go away if the Borg generated the character and knew that nobody * else was going to use the character file. Note that certain options * cannot be changed by the Borg, including "preserve" mode and "maximize" * mode, which are determined when the character is created. If "preserve" * mode is not true, then the Borg may lose some artifacts. If "maximize" * mode is not false, then the Borg may break in unexpected ways. * * There are several things that the current Borg code is "able" to learn * without "cheating", such as the descriptions of all of the equipment * and inventory items, information about each available spell or prayer, * and the location of the current panel relative to the overall dungeon, * by actually sending various keypresses to the game and observing the * results, but this is *extremely* annoying to watch, so by default, this * code is not used, and the Borg uses special "cheat" code to obtain this * information directly from the game. * * There are several pieces of information about the game that are used * directly by the current Borg code, including some of the tables in the * "tables.c" file, and some of the static arrays of information which are * initialized in the "init1.c" and "init2.c" files, but note that this is * not cheating, since if the Borg was compiled into a separate executable, * it would simply use these files directly. * * Note that the Borg can play any race/class combination, that is, he can * make "effective" use of at least a few spells/prayers, and is not "broken" * by low strength, blind-ness, hallucination, etc. This does not, however, * mean that he plays all classes equally well, especially since he is so * dependant on a high strength for so many things. * * Note that the Borg understands the "cheat_live" flag, and if it is set, * he will automatically "cheat death" whenever he dies. If this flag is * not set, then the Borg will automatically stop playing as soon as the * special "You die." message appears, allowing the human user to observe * the tombstone screen if desired. Currently, the "cheat_live" flag is * pretty much required for low level mage characters, since they almost * always die once or twice before they become strong enough to survive. * * Note that the "c_t" parameter bears a close resemblance to the number of * "player turns" that have gone by. Except that occasionally, the Borg will * do something that he *thinks* will take time but which actually does not * (for example, attempting to open a hallucinatory door), and that sometimes, * the Borg performs a "repeated" command (rest, open, tunnel, or search), * which may actually take longer than a single turn. This has the effect * that the "c_t" variable is slightly lacking in "precision". Note that * we can store every time-stamp in a 's16b', since we reset the clock to * 1000 on each new level, and we refuse to stay on any level longer than * 30000 turns, unless we are totally stuck, in which case we abort. This * almost never happens unless the Borg is permanently paralyzed, or is in * a corner surrounded by lice with no means of escape, or runs out of light * in a dark hallway and cannot find the stairs. */ /* * Stat advantages: * High STR (attacks, to-dam, digging, weight limit) * High DEX (attacks, to-hit, armor class) * High CON (hitpoints, recovery) * High WIS (better prayers, saving throws) * High INT (better spells, disarming, device usage) * High CHR (better item costs) * * Class advantages: * Warrior (good fighting, sensing) * Mage (good spells) * Priest (good prayers, fighting) * Ranger (some spells, fighting) * Rogue (some spells, fighting, sensing) * Paladin (prayers, fighting, sensing) * * Race advantages: * Gnome (free action) * Dwarf (resist blindness) * High elf (see invisible) * Non-human (infravision) */ /* * We currently handle: * Level changing (intentionally or accidentally) * Embedded objects (gold) that must be extracted * Ignore embedded objects if too "weak" to extract * Traps (disarm), Doors (open/etc), Rubble (tunnel) * Stores (enter via movement, exit via escape) * Stores (limited commands, and different commands) * Always deal with objects one at a time, not in piles * Discard junk before trying to pick up more stuff * Use "identify" to obtain knowledge and/or money * Rely on "sensing" objects as much as possible * Do not sell junk or worthless items to any shop * Do not attempt to buy something without the cash * Use the non-optimal stairs if stuck on a level * Use "flow" code for all tasks for consistency * Cancel all goals when major world changes occur * Use the "danger" code to avoid potential death * Use the "danger" code to avoid inconvenience * Try to avoid danger (both actively and passively) * Handle "Mace of Disruption", "Scythe of Slicing", etc * Learn spells, and use them when appropriate * Remember that studying prayers is slightly random * Do not try to read scrolls when blind or confused * Do not study/use spells/prayers when blind/confused * Use spells/prayers at least once for the experience * Attempt to heal when "confused", "blind", etc * Attempt to fix "fear", "poison", "cuts", etc * Analyze potential equipment in proper context * Priests should avoid edged weapons (spell failure) * Mages should avoid most gloves (lose mana) * Non-warriors should avoid heavy armor (lose mana) * Keep "best" ring on "tight" right finger in stores * Remove items which do not contribute to total fitness * Wear/Remove/Sell/Buy items in most optimal order * Pursue optimal combination of available equipment * Notice "failure" when using rods/staffs/artifacts * Notice "failure" when attempting spells/prayers * Attempt to correctly track terrain, objects, monsters * Take account of "clear" and "multi-hued" monsters * Take account of "flavored" (randomly colored) objects * Handle similar objects/monsters (mushrooms, coins) * Multi-hued/Clear monsters, and flavored objects * Keep in mind that some monsters can move (quickly) * Do not fire at monsters that may not actually exist * Assume everything is an object until proven otherwise * Parse messages to correct incorrect assumptions * Search for secret doors after exploring the level * React intelligently to changes in the wall structure * Do not recalculate "flow" information unless required * Collect monsters/objects/terrain not currently in view * Keep in mind that word of recall is a delayed action * Keep track of charging items (rods and artifacts) * Be very careful not to access illegal locations! * Do not rest next to dangerous (or immobile) monsters * Recall into dungeon if prepared for resulting depth * Do not attempt to destroy cursed ("terrible") artifacts * Attempted destruction will yield "terrible" inscription * Use "maximum" level and depth to prevent "thrashing" * Use "maximum" hp's and sp's when checking "potentials" * Attempt to recognize large groups of "disguised" monsters * Beware of firing at a monster which is no longer present * Stockpile items in the Home, and use those stockpiles * Discounted spell-books (low level ones are ignored) * Take items out of the home to sell them when no longer needed * Trappers and Mimics (now treated as invisible monsters) * Invisible monsters (induce "fear" of nearby regions) * Fleeing monsters are "followed" down corridors and such * * We ignore: * Running out of light can (fatally) confuse the Borg * Running out of food can kill you, try not to starve * Long object descriptions may have clipped inscriptions * * We need to handle: * Better "fleeing" code from nasty monsters * Appearance of "similar" monsters (jackals + grip) * Hallucination (induces fake objects and monsters) * Special screens (including tombstone) with no "walls" * Appearance of the '@' symbol on "special" screens * Technically a room can have no exits, requiring digging * Try to use a shovel/pick to help with tunnelling * If wounded, must run away from monsters, then rest * When blind, monster and object info may be invalid * When totally surrounded by monsters, try to escape rooms * Conserve memory space (each grid byte costs about 15K) * Conserve computation time (especially with the flow code) * Note -- nutrition can come from food, scrolls, or spells * Note -- recall can come from scrolls, rods, or spells * Note -- identify can come from scrolls, rods, staffs, or spells * Becoming "afraid" (attacking takes a turn for no effect) * Beware of firing missiles at a food ration under a mushroom * Attempt to save possibly useful equipment in the home * * We need to handle "loading" saved games: * The "max_depth" value is lost if loaded in the town * If we track "dead uniques" then this information is lost * The "map" info, "flow" info, "tracking" info, etc is lost * The contents of the shops (and the home) are lost * We may be asked to "resume" a non-Borg character (icky) */ /* * Some variables */ static bool initialized; /* Hack -- Initialized */ /* * Mega-Hack -- extract some "hidden" variables * * XXX XXX XXX This step would not be necessary if more info * was available on the screen. Perhaps we should track the * maximal value of "my_stat_cur" so we have "my_stat_max". */ static void borg_hidden(void) { int i; int stat_add[6]; /* Clear "stat_add[]" */ for (i = 0; i < 6; i++) stat_add[i] = 0; /* Scan the equipment */ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) { auto_item *item = &auto_items[i]; /* Skip empty items */ if (!item->iqty) continue; /* Affect stats */ if (item->flags1 & TR1_STR) stat_add[A_STR] += item->pval; if (item->flags1 & TR1_INT) stat_add[A_INT] += item->pval; if (item->flags1 & TR1_WIS) stat_add[A_WIS] += item->pval; if (item->flags1 & TR1_DEX) stat_add[A_DEX] += item->pval; if (item->flags1 & TR1_CON) stat_add[A_CON] += item->pval; if (item->flags1 & TR1_CHR) stat_add[A_CHR] += item->pval; } /* Mega-Hack -- Guess at "my_stat_cur[]" */ for (i = 0; i < 6; i++) { int value; /* Hack -- reverse the known bonus */ value = modify_stat_value(auto_stat[i], -stat_add[i]); /* Hack -- save the maximum/current stats */ my_stat_max[i] = my_stat_cur[i] = value; } } /* * Think about the world and perform an action * * Check inventory/equipment/spells/panel once per "turn" * * Process "store" and other modes when necessary * * Note that the non-cheating "inventory" and "equipment" parsers * will get confused by a "weird" situation involving an ant ("a") * on line one of the screen, near the left, next to a shield, of * the same color, and using --(-- the ")" symbol, directly to the * right of the ant. This is very rare, but perhaps not completely * impossible. I ignore this situation. * * The handling of stores is a complete and total hack, but seems * to work remarkably well, considering... Note that while in a * store, time does not pass, and most actions are not available, * and a few new commands are available ("sell" and "purchase"). * * Note the use of "cheat" functions to extract the current inventory, * the current equipment, the current panel, and the current spellbook * information. These can be replaced by (very expensive) "parse" * functions, which cause an insane amount of "screen flashing". * * Technically, we should attempt to parse all the messages that * indicate that it is necessary to re-parse the equipment, the * inventory, or the books, and only set the appropriate flags at * that point. This would not only reduce the potential screen * flashing, but would also optimize the code a lot, since the * "cheat_inven()" and "cheat_equip()" functions are expensive. * For paranoia, we could always select items and spells using * capital letters, and keep a global verification buffer, and * induce failure and recheck the inventory/equipment any time we * get a mis-match. We could even do some of the state processing * by hand, for example, charge reduction and such. This might also * allow us to keep track of how long we have held objects, especially * if we attempt to do "item tracking" in the inventory extraction code. */ static bool borg_think(void) { int i; byte t_a; char buf[128]; /*** Process inventory/equipment ***/ /* Cheat */ if (auto_cheat_equip && auto_do_equip) { /* Only do it once */ auto_do_equip = FALSE; /* Cheat the "equip" screen */ borg_cheat_equip(); /* Done */ return (FALSE); } /* Cheat */ if (auto_cheat_inven && auto_do_inven) { /* Only do it once */ auto_do_inven = FALSE; /* Cheat the "inven" screen */ borg_cheat_inven(); /* Done */ return (FALSE); } /* Parse "equip" mode */ if ((0 == borg_what_text(0, 0, 10, &t_a, buf)) && (streq(buf, "Equipment "))) { /* Parse the "equip" screen */ borg_parse_equip(); /* Leave this mode */ borg_keypress(ESCAPE); /* Done */ return (TRUE); } /* Parse "inven" mode */ if ((0 == borg_what_text(0, 0, 10, &t_a, buf)) && (streq(buf, "Inventory "))) { /* Parse the "inven" screen */ borg_parse_inven(); /* Leave this mode */ borg_keypress(ESCAPE); /* Done */ return (TRUE); } /* Check "equip" */ if (auto_do_equip) { /* Only do it once */ auto_do_equip = FALSE; /* Enter "equip" mode */ borg_keypress('e'); /* Done */ return (TRUE); } /* Check "inven" */ if (auto_do_inven) { /* Only do it once */ auto_do_inven = FALSE; /* Enter "inven" mode */ borg_keypress('i'); /* Done */ return (TRUE); } /*** Find books ***/ /* Only if needed */ if (auto_do_spell && (auto_do_spell_aux == 0)) { /* Assume no books */ for (i = 0; i < 9; i++) auto_book[i] = -1; /* Scan the pack */ for (i = 0; i < INVEN_PACK; i++) { auto_item *item = &auto_items[i]; /* Skip non-books */ if (item->tval != mb_ptr->spell_book) continue; /* Note book locations */ auto_book[item->sval] = i; } } /*** Process books ***/ /* Hack -- Warriors never browse */ if (auto_class == 0) auto_do_spell = FALSE; /* Hack -- Blind or Confused prevents browsing */ if (do_blind || do_confused) auto_do_spell = FALSE; /* XXX XXX XXX Dark */ /* Hack -- Stop doing spells when done */ if (auto_do_spell_aux > 8) auto_do_spell = FALSE; /* Cheat */ if (auto_cheat_spell && auto_do_spell) { /* Look for the book */ i = auto_book[auto_do_spell_aux]; /* Cheat the "spell" screens (all of them) */ if (i >= 0) { /* Cheat that page */ borg_cheat_spell(auto_do_spell_aux); } /* Advance to the next book */ auto_do_spell_aux++; /* Done */ return (FALSE); } /* Check for "browse" mode */ if ((0 == borg_what_text(COL_SPELL, ROW_SPELL, -12, &t_a, buf)) && (streq(buf, "Lv Mana Fail"))) { /* Parse the "spell" screen */ borg_parse_spell(auto_do_spell_aux); /* Advance to the next book */ auto_do_spell_aux++; /* Leave that mode */ borg_keypress(ESCAPE); /* Done */ return (TRUE); } /* Check "spells" */ if (auto_do_spell) { /* Look for the book */ i = auto_book[auto_do_spell_aux]; /* Enter the "spell" screen */ if (i >= 0) { /* Enter "browse" mode */ borg_keypress('b'); /* Pick the next book */ borg_keypress(I2A(i)); /* Done */ return (TRUE); } /* Advance to the next book */ auto_do_spell_aux++; /* Done */ return (FALSE); } /*** Handle stores ***/ /* Hack -- Check for being in a store */ if ((0 == borg_what_text(3, 5, 16, &t_a, buf)) && (streq(buf, "Item Description"))) { /* Assume the Home */ shop_num = 7; /* Extract the "store" name */ if (0 == borg_what_text(50, 3, -20, &t_a, buf)) { int i; /* Check the store names */ for (i = 0; i < 7; i++) { cptr name = (f_name + f_info[0x08+i].name); if (prefix(buf, name)) shop_num = i; } } /* Hack -- reset page/more */ auto_shops[shop_num].page = 0; auto_shops[shop_num].more = 0; /* React to new stores */ if (auto_do_browse_what != shop_num) { /* Clear all the items */ for (i = 0; i < 24; i++) { /* XXX Wipe the ware */ WIPE(&auto_shops[shop_num].ware[i], auto_item); } /* Save the store */ auto_do_browse_what = shop_num; } /* Extract the "page", if any */ if ((0 == borg_what_text(20, 5, 8, &t_a, buf)) && (prefix(buf, "(Page "))) /* --)-- */ { /* Take note of the page */ auto_shops[shop_num].more = 1; auto_shops[shop_num].page = (buf[6] - '0') - 1; } /* React to disappearing pages */ if (auto_do_browse_more != auto_shops[shop_num].more) { /* Clear the second page */ for (i = 12; i < 24; i++) { /* XXX Wipe the ware */ WIPE(&auto_shops[shop_num].ware[i], auto_item); } /* Save the new one */ auto_do_browse_more = auto_shops[shop_num].more; } /* Extract the current gold (unless in home) */ if (0 == borg_what_text(68, 19, -9, &t_a, buf)) { /* Save the gold, if valid */ if (buf[0]) auto_gold = atol(buf); } /* Parse the store (or home) inventory */ for (i = 0; i < 12; i++) { int n; char desc[80]; char cost[10]; /* Default to "empty" */ desc[0] = '\0'; cost[0] = '\0'; /* Verify "intro" to the item */ if ((0 == borg_what_text(0, i + 6, 3, &t_a, buf)) && (buf[0] == I2A(i)) && (buf[1] == p2) && (buf[2] == ' ')) { int k; /* Extract the item description */ if (0 != borg_what_text(3, i + 6, -65, &t_a, desc)) { desc[0] = '\0'; } /* Strip trailing spaces */ for (k = strlen(desc); (k > 0) && (desc[k-1] == ' '); k--) /* loop */; desc[k] = '\0'; /* Extract the item cost in stores */ if (shop_num != 7) { if (0 != borg_what_text(68, i + 6, -9, &t_a, cost)) { cost[0] = '\0'; } } } /* Extract actual index */ n = auto_shops[shop_num].page * 12 + i; /* Ignore "unchanged" descriptions */ if (streq(desc, auto_shops[shop_num].ware[n].desc)) continue; /* Analyze the item */ borg_item_analyze(&auto_shops[shop_num].ware[n], desc); /* Hack -- Save the declared cost */ auto_shops[shop_num].ware[n].cost = atol(cost); } /* Hack -- browse as needed */ if (auto_shops[shop_num].more && auto_do_browse) { /* Check next page */ borg_keypress(' '); /* Done browsing */ auto_do_browse = FALSE; /* Done */ return (TRUE); } /* Recheck inventory */ auto_do_inven = TRUE; /* Recheck equipment */ auto_do_equip = TRUE; /* Recheck spells */ auto_do_spell = TRUE; /* Restart spells */ auto_do_spell_aux = 0; /* Hack -- browse again later */ auto_do_browse = TRUE; /* Examine the inventory */ borg_notice(); /* Evaluate the current world */ my_power = borg_power(); /* Hack -- allow user abort */ if (auto_cancel) return (TRUE); /* Think until done */ return (borg_think_store()); } /*** Determine panel ***/ /* Hack -- cheat */ if (auto_cheat_panel) { /* Cheat */ w_y = panel_row * (SCREEN_HGT / 2); w_x = panel_col * (SCREEN_WID / 2); /* Done */ auto_do_panel = FALSE; } /* Hack -- Check for "sector" mode */ if ((0 == borg_what_text(0, 0, 16, &t_a, buf)) && (prefix(buf, "Map sector "))) { /* Hack -- get the panel info */ w_y = (buf[12] - '0') * (SCREEN_HGT / 2); w_x = (buf[14] - '0') * (SCREEN_WID / 2); /* Leave panel mode */ borg_keypress(ESCAPE); /* Done */ return (TRUE); } /* Check equipment */ if (auto_do_panel) { /* Only do it once */ auto_do_panel = FALSE; /* Enter "panel" mode */ borg_keypress('L'); /* Done */ return (TRUE); } /*** Analyze the Frame ***/ /* Analyze the frame */ if (auto_do_frame) { /* Only once */ auto_do_frame = FALSE; /* Analyze the "frame" */ borg_update_frame(); } /*** Re-activate Tests ***/ /* Check equip again later */ auto_do_equip = TRUE; /* Check inven again later */ auto_do_inven = TRUE; /* Check panel again later */ auto_do_panel = TRUE; /* Check frame again later */ auto_do_frame = TRUE; /* Check spells again later */ auto_do_spell = TRUE; /* Hack -- Start the books over */ auto_do_spell_aux = 0; /*** Analyze status ***/ /* Track best level */ if (auto_level > auto_max_level) auto_max_level = auto_level; if (auto_depth > auto_max_depth) auto_max_depth = auto_depth; /*** Think about it ***/ /* Increment the clock */ c_t++; /* Examine the screen */ borg_update(); /* Extract some "hidden" variables */ borg_hidden(); /* Examine the equipment/inventory */ borg_notice(); /* Evaluate the current world */ my_power = borg_power(); /* Hack -- allow user abort */ if (auto_cancel) return (TRUE); /* Do something */ return (borg_think_dungeon()); } /* * Hack -- methods of hurting a monster (order not important). * * See "message_pain()" for details. */ static cptr suffix_pain[] = { " barely notices.", " flinches.", " squelches.", " quivers in pain.", " writhes about.", " writhes in agony.", " jerks limply.", " shrugs off the attack.", " snarls with pain.", " yelps in pain.", " howls in pain.", " howls in agony.", /* xxx */ " yelps feebly.", " ignores the attack.", " grunts with pain.", " squeals in pain.", " shrieks in pain.", " shrieks in agony.", /* xxx */ " cries out feebly.", /* xxx */ /* xxx */ " cries out in pain.", " screams in pain.", " screams in agony.", /* xxx */ /* xxx */ NULL }; /* * Hack -- methods of killing a monster (order not important). * * See "mon_take_hit()" for details. */ static cptr prefix_kill[] = { "You have killed ", "You have slain ", "You have destroyed ", NULL }; /* * Hack -- methods of monster death (order not important). * * See "project_m()", "do_cmd_fire()", "mon_take_hit()" for details. */ static cptr suffix_died[] = { " dies.", " is destroyed.", " dissolves!", " shrivels away in the light!", NULL }; /* * Hack -- methods of hitting the player (order not important). * * The "insult", "moan", and "begs you for money" messages are ignored. * * See "make_attack_normal()" for details. */ static cptr suffix_hit_by[] = { " hits you.", /* RBM_HIT */ " touches you.", /* RBM_TOUCH */ " punches you.", /* RBM_PUNCH */ " kicks you.", /* RBM_KICK */ " claws you.", /* RBM_CLAW */ " bites you.", /* RBM_BITE */ " stings you.", /* RBM_STING */ /* xxx */ /* RBM_XXX1 */ " butts you.", /* RBM_BUTT */ " crushes you.", /* RBM_CRUSH */ " engulfs you.", /* RBM_ENGULF */ /* xxx */ /* RBM_XXX2 */ " crawls on you.", /* RBM_CRAWL */ " drools on you.", /* RBM_DROOL */ " spits on you.", /* RBM_SPIT */ /* xxx */ /* RBM_XXX3 */ " gazes at you.", /* RBM_GAZE */ " wails at you.", /* RBM_WAIL */ " releases spores at you.", /* RBM_SPORE */ /* xxx */ /* RBM_XXX4 */ /* xxx */ /* RBM_BEG */ /* xxx */ /* RBM_INSULT */ /* xxx */ /* RBM_MOAN */ /* xxx */ /* RBM_XXX5 */ NULL }; /* * Hack -- methods of casting spells at the player (order important). * * See "make_attack_spell()" for details. */ static cptr suffix_spell[] = { " makes a high pitched shriek.", /* RF4_SHRIEK */ " does something.", /* RF4_XXX2X4 */ " does something.", /* RF4_XXX3X4 */ " does something.", /* RF4_XXX4X4 */ " fires an arrow.", /* RF4_ARROW_1 */ " fires an arrow!", /* RF4_ARROW_2 */ " fires a missile.", /* RF4_ARROW_3 */ " fires a missile!", /* RF4_ARROW_4 */ " breathes acid.", /* RF4_BR_ACID */ " breathes lightning.", /* RF4_BR_ELEC */ " breathes fire.", /* RF4_BR_FIRE */ " breathes frost.", /* RF4_BR_COLD */ " breathes gas.", /* RF4_BR_POIS */ " breathes nether.", /* RF4_BR_NETH */ " breathes light.", /* RF4_BR_LITE */ " breathes darkness.", /* RF4_BR_DARK */ " breathes confusion.", /* RF4_BR_CONF */ " breathes sound.", /* RF4_BR_SOUN */ " breathes chaos.", /* RF4_BR_CHAO */ " breathes disenchantment.", /* RF4_BR_DISE */ " breathes nexus.", /* RF4_BR_NEXU */ " breathes time.", /* RF4_BR_TIME */ " breathes inertia.", /* RF4_BR_INER */ " breathes gravity.", /* RF4_BR_GRAV */ " breathes shards.", /* RF4_BR_SHAR */ " breathes plasma.", /* RF4_BR_PLAS */ " breathes force.", /* RF4_BR_WALL */ " does something.", /* RF4_BR_MANA */ " does something.", /* RF4_XXX5X4 */ " does something.", /* RF4_XXX6X4 */ " does something.", /* RF4_XXX7X4 */ " does something.", /* RF4_XXX8X4 */ " casts an acid ball.", /* RF5_BA_ACID */ " casts a lightning ball.", /* RF5_BA_ELEC */ " casts a fire ball.", /* RF5_BA_FIRE */ " casts a frost ball.", /* RF5_BA_COLD */ " casts a stinking cloud.", /* RF5_BA_POIS */ " casts a nether ball.", /* RF5_BA_NETH */ " gestures fluidly.", /* RF5_BA_WATE */ " invokes a mana storm.", /* RF5_BA_MANA */ " invokes a darkness storm.", /* RF5_BA_DARK */ " draws psychic energy from you!", /* RF5_DRAIN_MANA */ " gazes deep into your eyes.", /* RF5_MIND_BLAST */ " looks deep into your eyes.", /* RF5_BRAIN_SMASH */ " points at you and curses.", /* RF5_CAUSE_1 */ " points at you and curses horribly.", /* RF5_CAUSE_2 */ " points at you, incanting terribly!", /* RF5_CAUSE_3 */ " points at you, screaming the word DIE!", /* RF5_CAUSE_4 */ " casts a acid bolt.", /* RF5_BO_ACID */ " casts a lightning bolt.", /* RF5_BO_ELEC */ " casts a fire bolt.", /* RF5_BO_FIRE */ " casts a frost bolt.", /* RF5_BO_COLD */ " does something.", /* RF5_BO_POIS */ " casts a nether bolt.", /* RF5_BO_NETH */ " casts a water bolt.", /* RF5_BO_WATE */ " casts a mana bolt.", /* RF5_BO_MANA */ " casts a plasma bolt.", /* RF5_BO_PLAS */ " casts an ice bolt.", /* RF5_BO_ICEE */ " casts a magic missile.", /* RF5_MISSILE */ " casts a fearful illusion.", /* RF5_SCARE */ " casts a spell, burning your eyes!", /* RF5_BLIND */ " creates a mesmerising illusion.", /* RF5_CONF */ " drains power from your muscles!", /* RF5_SLOW */ " stares deep into your eyes!", /* RF5_HOLD */ " concentrates on XXX body.", /* RF6_HASTE */ " does something.", /* RF6_XXX1X6 */ " concentrates on XXX wounds.", /* RF6_HEAL */ " does something.", /* RF6_XXX2X6 */ " blinks away.", /* RF6_BLINK */ " teleports away.", /* RF6_TPORT */ " does something.", /* RF6_XXX3X6 */ " does something.", /* RF6_XXX4X6 */ " commands you to return.", /* RF6_TELE_TO */ " teleports you away.", /* RF6_TELE_AWAY */ " gestures at your feet.", /* RF6_TELE_LEVEL */ " does something.", /* RF6_XXX5 */ " gestures in shadow.", /* RF6_DARKNESS */ " casts a spell and cackles evilly.", /* RF6_TRAPS */ " tries to blank your mind.", /* RF6_FORGET */ " does something.", /* RF6_XXX6X6 */ " does something.", /* RF6_XXX7X6 */ " does something.", /* RF6_XXX8X6 */ " magically summons help!", /* RF6_S_MONSTER */ " magically summons monsters!", /* RF6_S_MONSTERS */ " magically summons ants.", /* RF6_S_ANT */ " magically summons spiders.", /* RF6_S_SPIDER */ " magically summons hounds.", /* RF6_S_HOUND */ " magically summons hydras.", /* RF6_S_HYDRA */ " magically summons an angel!", /* RF6_S_ANGEL */ " magically summons a hellish adversary!", /* RF6_S_DEMON */ " magically summons an undead adversary!", /* RF6_S_UNDEAD */ " magically summons a dragon!", /* RF6_S_DRAGON */ " magically summons greater undead!", /* RF6_S_HI_UNDEAD */ " magically summons ancient dragons!", /* RF6_S_HI_DRAGON */ " magically summons mighty undead opponents!", /* RF6_S_WRAITH */ " magically summons special opponents!", /* RF6_S_UNIQUE */ NULL }; #if 0 /* * These messages are not currently matched */ static void some_extra_messages() { /* XXX XXX XXX */ msg_format("%^s looks healthier.", m_name); msg_format("%^s looks REALLY healthy!", m_name); } #endif /* * Hack -- Spontaneous level feelings (order important). * * See "do_cmd_feeling()" for details. */ static cptr prefix_feeling[] = { "Looks like any other level", "You feel there is something special", "You have a superb feeling", "You have an excellent feeling", "You have a very good feeling", "You have a good feeling", "You feel strangely lucky", "You feel your luck is turning", "You like the look of this place", "This level can't be all bad", "What a boring place", NULL }; /* * Hack -- Parse a message from the world * * Note that detecting "death" is EXTREMELY important, to prevent * all sorts of errors arising from attempting to parse the "tomb" * screen, and to allow the user to "observe" the "cause" of death. * * Note that detecting "failure" is EXTREMELY important, to prevent * bizarre situations after failing to use a staff of perceptions, * which would otherwise go ahead and send the "item index" which * might be a legal command (such as "a" for "aim"). This method * is necessary because the Borg cannot parse "prompts", and must * assume the success of the prompt-inducing command, unless told * otherwise by a failure message. Also, we need to detect failure * because some commands, such as detection spells, need to induce * furthur processing if they succeed, but messages are only given * if the command fails. * * Note that certain other messages may contain useful information, * and so they are "analyzed" and sent to "borg_react()", which just * queues the messages for later analysis in the proper context. * * Along with the actual message, we send a special formatted buffer, * containing a leading "opcode", which may contain extra information, * such as the index of a spell, and an "argument" (for example, the * capitalized name of a monster), with a "colon" to separate them. * * XXX XXX XXX Several message strings take a "possessive" of the form * "his" or "her" or "its". These strings are all represented by the * encoded form "XXX" in the various match strings. Unfortunately, * the encode form is never decoded, so the Borg currently ignores * messages about several spells (heal self and haste self). * * XXX XXX XXX We notice a few "terrain feature" messages here so * we can acquire knowledge about wall types and door types. */ static void borg_parse_aux(cptr msg, int len) { int i, tmp; char who[256]; char buf[256]; auto_grid *ag = &auto_grids[g_y][g_x]; /* Log (if needed) */ if (auto_fff) borg_info(format("& Msg <%s>", msg)); /* Hack -- Notice death */ if (prefix(msg, "You die.")) { /* Abort (unless cheating) */ if (!(wizard || cheat_live)) { /* Abort */ borg_oops("death"); /* Abort right now! */ auto_active = FALSE; /* Noise XXX XXX XXX */ Term_xtra(TERM_XTRA_NOISE, 1); } /* Done */ return; } /* Hack -- Notice "failure" */ if (prefix(msg, "You failed ")) { /* Hack -- store the keypress */ borg_note("# Normal failure."); /* Set the failure flag */ auto_failure = TRUE; /* Flush our key-buffer */ borg_flush(); return; } /* Ignore teleport trap */ if (prefix(msg, "You hit a teleport")) return; /* Ignore arrow traps */ if (prefix(msg, "An arrow ")) return; /* Ignore dart traps */ if (prefix(msg, "A small dart ")) return; /* Hit somebody */ if (prefix(msg, "You hit ")) { tmp = strlen("You hit "); strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp); strnfmt(buf, 256, "HIT:%^s", who); borg_react(msg, buf); return; } /* Miss somebody */ if (prefix(msg, "You miss ")) { tmp = strlen("You miss "); strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp); strnfmt(buf, 256, "MISS:%^s", who); borg_react(msg, buf); return; } /* Miss somebody (because of fear) */ if (prefix(msg, "You are too afraid to attack ")) { tmp = strlen("You are too afraid to attack "); strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp); strnfmt(buf, 256, "MISS:%^s", who); borg_react(msg, buf); return; } /* "It screams in pain." (etc) */ for (i = 0; suffix_pain[i]; i++) { /* "It screams in pain." (etc) */ if (suffix(msg, suffix_pain[i])) { tmp = strlen(suffix_pain[i]); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "PAIN:%^s", who); borg_react(msg, buf); return; } } /* "You have killed it." (etc) */ for (i = 0; prefix_kill[i]; i++) { /* "You have killed it." (etc) */ if (prefix(msg, prefix_kill[i])) { tmp = strlen(prefix_kill[i]); strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp); strnfmt(buf, 256, "KILL:%^s", who); borg_react(msg, buf); return; } } /* "It dies." (etc) */ for (i = 0; suffix_died[i]; i++) { /* "It dies." (etc) */ if (suffix(msg, suffix_died[i])) { tmp = strlen(suffix_died[i]); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "DIED:%^s", who); borg_react(msg, buf); return; } } /* "It misses you." */ if (suffix(msg, " misses you.")) { tmp = strlen(" misses you."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "MISS_BY:%^s", who); borg_react(msg, buf); return; } /* "It hits you." (etc) */ for (i = 0; suffix_hit_by[i]; i++) { /* "It hits you." (etc) */ if (suffix(msg, suffix_hit_by[i])) { tmp = strlen(suffix_hit_by[i]); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "HIT_BY:%^s", who); borg_react(msg, buf); return; } } /* "It casts a spell." (etc) */ for (i = 0; suffix_spell[i]; i++) { /* "It casts a spell." (etc) */ if (suffix(msg, suffix_spell[i])) { tmp = strlen(suffix_spell[i]); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "SPELL_%03d:%^s", 96+i, who); borg_react(msg, buf); return; } } /* State -- Asleep */ if (suffix(msg, " falls asleep!")) { tmp = strlen(" falls asleep!"); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE_SLEEP:%^s", who); borg_react(msg, buf); return; } /* State -- Not Asleep */ if (suffix(msg, " wakes up.")) { tmp = strlen(" wakes up."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE_AWAKE:%^s", who); borg_react(msg, buf); return; } /* State -- Afraid */ if (suffix(msg, " flees in terror!")) { tmp = strlen(" flees in terror!"); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE__FEAR:%^s", who); borg_react(msg, buf); return; } /* State -- Not Afraid */ if (suffix(msg, " recovers his courage.")) { tmp = strlen(" recovers his courage."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE__BOLD:%^s", who); borg_react(msg, buf); return; } /* State -- Not Afraid */ if (suffix(msg, " recovers her courage.")) { tmp = strlen(" recovers her courage."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE__BOLD:%^s", who); borg_react(msg, buf); return; } /* State -- Not Afraid */ if (suffix(msg, " recovers its courage.")) { tmp = strlen(" recovers its courage."); strnfmt(who, 1 + len - tmp, "%s", msg); strnfmt(buf, 256, "STATE__BOLD:%^s", who); borg_react(msg, buf); return; } /* Feature XXX XXX XXX */ if (streq(msg, "The door appears to be broken.")) { /* Only process open doors */ if (ag->feat == FEAT_OPEN) { /* Mark as broken */ ag->feat = FEAT_BROKEN; } return; } /* Feature XXX XXX XXX */ if (streq(msg, "The door appears to be stuck.")) { /* Only process non-jammed doors */ if ((ag->feat >= FEAT_DOOR_HEAD) && (ag->feat <= FEAT_DOOR_HEAD + 0x07)) { /* Mark the door as jammed */ ag->feat = FEAT_DOOR_HEAD + 0x08; /* Clear goals */ goal = 0; } return; } #if 0 /* Feature XXX XXX XXX */ if (streq(msg, "This seems to be permanent rock.")) { /* Skip granite walls */ if (ag->feat == FEAT_WALL_EXTRA) continue; /* Only process walls */ if ((ag->feat >= FEAT_WALL_EXTRA) && (ag->feat <= FEAT_PERM_SOLID)) { /* Mark the wall as permanent */ ag->feat = FEAT_PERM_SOLID; /* Clear goals */ goal = 0; } return; } /* Feature XXX XXX XXX */ if (streq(msg, "You tunnel into the granite wall.")) { /* Skip granite walls */ if (ag->feat == FEAT_WALL_EXTRA) continue; /* Only process walls */ if ((ag->feat >= FEAT_WALL_EXTRA) && (ag->feat <= FEAT_PERM_SOLID)) { /* Mark the wall as granite */ ag->feat = FEAT_WALL_EXTRA; /* Clear goals */ goal = 0; } return; } #endif /* Feature XXX XXX XXX */ if (streq(msg, "You tunnel into the quartz vein.")) { /* Process magma veins with treasure */ if (ag->feat == FEAT_MAGMA_K) { /* Mark the vein */ ag->feat = FEAT_QUARTZ_K; /* Clear goals */ goal = 0; } /* Process magma veins */ else if (ag->feat == FEAT_MAGMA) { /* Mark the vein */ ag->feat = FEAT_QUARTZ; /* Clear goals */ goal = 0; } return; } /* Feature XXX XXX XXX */ if (streq(msg, "You tunnel into the magma vein.")) { /* Process quartz veins with treasure */ if (ag->feat == FEAT_QUARTZ_K) { /* Mark the vein */ ag->feat = FEAT_MAGMA_K; /* Clear goals */ goal = 0; } /* Process quartz veins */ else if (ag->feat == FEAT_QUARTZ) { /* Mark the vein */ ag->feat = FEAT_MAGMA; /* Clear goals */ goal = 0; } return; } /* Word of Recall -- Ignition */ if (prefix(msg, "The air about you becomes ")) { /* Initiate recall */ goal_recalling = TRUE; return; } /* Word of Recall -- Lift off */ if (prefix(msg, "You feel yourself yanked ")) { /* Recall complete */ goal_recalling = FALSE; return; } /* Word of Recall -- Cancelled */ if (prefix(msg, "A tension leaves ")) { /* Hack -- Oops */ goal_recalling = FALSE; return; } /* Feelings about the level */ for (i = 0; prefix_feeling[i]; i++) { /* "You feel..." (etc) */ if (prefix(msg, prefix_feeling[i])) { strnfmt(buf, 256, "FEELING:%d", i); borg_react(msg, buf); return; } } } /* * Parse a message, piece of a message, or set of messages. * * We must handle long messages which are "split" into multiple * pieces, and also multiple messages which may be "combined" * into a single set of messages. */ static void borg_parse(cptr msg) { static char len = 0; static char buf[1024]; /* Flush messages */ if (len && (!msg || (msg[0] != ' '))) { int i, j; /* Split out punctuation */ for (j = i = 0; i < len-1; i++) { /* Check for punctuation */ if ((buf[i] == '.') || (buf[i] == '!') || (buf[i] == '?') || (buf[i] == '"')) { /* Require space */ if (buf[i+1] == ' ') { /* Terminate */ buf[i+1] = '\0'; /* Parse fragment */ borg_parse_aux(buf + j, (i + 1) - j); /* Restore */ buf[i+1] = ' '; /* Advance past spaces */ for (j = i + 2; buf[j] == ' '; j++) /* loop */; } } } /* Parse tail */ borg_parse_aux(buf + j, len - j); /* Forget */ len = 0; } /* No message */ if (!msg) { /* Start over */ len = 0; } /* Continued message */ else if (msg[0] == ' ') { /* Collect, verify, and grow */ len += strnfmt(buf+len, 1024-len, "%s", msg+1); } /* New message */ else { /* Collect, verify, and grow */ len = strnfmt(buf, 1024, "%s", msg); } } /* * XXX XXX XXX Mega-Hack -- keypress stealer from "z-term.c" */ extern errr (*Term_inkey_hook)(char *ch, bool wait, bool take); /* * This function lets the Borg "steal" control from the user. * * The "z-term.c" file provides a special hook which we use to * bypass the standard "Term_flush()" and "Term_inkey()" functions * and replace them with the function below. * * The only way that the Borg can be stopped once it is started, * unless it dies or encounters an error, is to press any key. * This function checks for user input on a regular basic, and * when any is encountered, it relinquishes control gracefully. * * We handle "broken" messages, in which long messages are "broken" * into pieces, and all but the first message are "indented" by one * space, by collecting all the pieces into a complete message and * then parsing the message once it is known to be complete. * * Note the complete hack that allows the Borg to run in "demo" * mode, which allows the Borg to cheat death 100 times, and stamps * the number of cheats in the player "age" field. XXX XXX XXX * * Note that this function hook automatically removes itself when * it realizes that it should no longer be active. Note that this * may take place after the game has asked for the next keypress, * but the various "keypress" routines should be able to handle this. * * XXX XXX XXX We do not correctly handle the "take" flag */ static errr Term_inkey_borg(char *ch, bool wait, bool take) { int i, x, y; int visible; byte t_a; char buf[128]; /* XXX XXX XXX */ if (!auto_active) { /* Message */ borg_note("# Removing keypress hook"); /* Remove hook */ Term_inkey_hook = NULL; /* Flush keys */ borg_flush(); /* Clear if needed */ if (ch) (*ch) = '\0'; /* Nothing ready */ return (1); } /* Hack -- flush keys */ if (!ch) { /* Flush keys */ borg_flush(); /* Success */ return (0); } /* Clear the key */ (*ch) = '\0'; /* Always wait for keys */ if (!wait) return (1); /* Check the "cursor state" XXX XXX XXX */ /* Note that the cursor visibility determines whether the */ /* game is asking us for a "command" or for some other key. */ /* This requires that "hilite_player" be FALSE. */ /* Hack -- Extract the cursor visibility */ (void)Term_get_cursor(&visible); /* XXX XXX XXX Mega-Hack -- Catch "Die? [y/n]" messages */ /* Hack -- cheat death */ if (visible && (0 == Term_locate(&x, &y)) && (y == 0) && (x >= 4) && (0 == borg_what_text(0, 0, 4, &t_a, buf)) && (prefix(buf, "Die?"))) { /* Take note */ borg_note("# Cheating death..."); /* Cheat death */ (*ch) = 'n'; /* Success */ return (0); } /* XXX XXX XXX Mega-Hack -- Catch "-more-" messages */ /* If the cursor is visible... */ /* And the cursor is on the top line... */ /* And there is text before the cursor... */ /* And that text is "-more-" */ if (visible && (0 == Term_locate(&x, &y)) && (y == 0) && (x >= 7) && (0 == borg_what_text(x-7, y, 7, &t_a, buf)) && (streq(buf, " -more-"))) { /* Get the message */ if (0 == borg_what_text(0, 0, x-7, &t_a, buf)) { /* Parse it */ borg_parse(buf); } /* Hack -- not active */ if (!auto_active) return (1); /* Clear the message */ (*ch) = ' '; /* Done */ return (0); } /* XXX XXX XXX Mega-Hack -- catch normal messages */ /* If the cursor is NOT visible... */ /* And there is text on the first line... */ if (!visible && (0 == borg_what_text(0, 0, 3, &t_a, buf)) && (t_a == TERM_WHITE) && buf[0] && ((buf[0] != ' ') || (buf[1] != ' ') || (buf[2] != ' '))) { /* Get the message */ if (0 == borg_what_text(0, 0, -80, &t_a, buf)) { int k = strlen(buf); /* Strip trailing spaces */ while ((k > 0) && (buf[k-1] == ' ')) k--; /* Terminate */ buf[k] = '\0'; /* Parse it */ borg_parse(buf); } /* Hack -- not active */ if (!auto_active) return (1); /* Clear the message */ (*ch) = ' '; /* Done */ return (0); } /* Flush messages */ borg_parse(NULL); /* Check for key */ i = borg_inkey(FALSE); /* Need more keys */ if (!i) { /* Hack -- Process events (do not wait) */ (void)Term_xtra(TERM_XTRA_EVENT, FALSE); /* Hack -- User Abort XXX XXX XXX */ if (Term->key_head != Term->key_tail) { /* Take note */ borg_note("# Preparing to abort"); /* Cancel later */ auto_cancel = TRUE; /* Hack -- Forget all keypresses */ Term->key_head = Term->key_tail = 0; /* Hack -- Flush all events */ (void)Term_xtra(TERM_XTRA_FLUSH, 0); } /* XXX XXX XXX */ if (death) { /* Oops */ borg_oops("unexpected death!"); /* Return */ return (1); } /* Save the system random info */ auto_rand_quick = Rand_quick; auto_rand_value = Rand_value; /* Use the local random info */ Rand_quick = TRUE; Rand_value = auto_rand_local; /* Update and Think */ while (!borg_think()) /* loop */; /* Save the local random info */ auto_rand_local = Rand_value; /* Restore the system random info */ Rand_quick = auto_rand_quick; Rand_value = auto_rand_value; /* Hack -- allow stepping to induce a clean cancel */ if (auto_step && (!--auto_step)) auto_cancel = TRUE; } /* Check for key */ i = borg_inkey(take); /* Use it */ if (!i) { /* Oops */ borg_oops("user abort"); /* Hack -- Escape! */ i = ESCAPE; } /* Enqueue the keypress */ (*ch) = i; /* Success */ return (0); } /* * Initialize the game */ static void borg_prepare(void) { int book; /*** Options/Redraw ***/ /* We use the original keypress codes */ rogue_like_commands = FALSE; /* We pick up items when we step on them */ always_pickup = TRUE; /* We specify targets by hand */ use_old_target = FALSE; /* We do NOT query any commands */ carry_query_flag = FALSE; other_query_flag = FALSE; /* We repeat by hand */ always_repeat = FALSE; /* We do not haggle */ auto_haggle = TRUE; /* We need space */ show_labels = FALSE; show_weights = FALSE; /* We need color */ use_color = TRUE; /* We need the dungeon level */ depth_in_feet = FALSE; /* Allow items to stack */ stack_allow_items = TRUE; /* Allow wands to stack */ stack_allow_wands = TRUE; /* Do not ignore discounts */ stack_force_costs = FALSE; /* Ignore inscriptions */ stack_force_notes = TRUE; /* Efficiency */ avoid_abort = TRUE; /* Efficiency */ hitpoint_warn = 0; /* Hack -- notice "command" mode */ hilite_player = FALSE; /* Reset the visuals */ reset_visuals(FALSE); /* Redraw everything */ do_cmd_redraw(); /*** Race/Class/Spells ***/ /* Extract the race */ auto_race = p_ptr->prace; /* Extract the class */ auto_class = p_ptr->pclass; /* Extract the race pointer */ rb_ptr = &race_info[auto_race]; /* Extract the class pointer */ cb_ptr = &class_info[auto_class]; /* Extract the magic pointer */ mb_ptr = &magic_info[auto_class]; /* Initialize the books */ for (book = 0; book < 9; book++) { borg_prepare_book(book); } } /* * Initialize the Borg * * We should verify "legal" values for various things, since, * for example, when the "wall" symbols have been redefined, * the Borg may crash the machine. XXX XXX XXX */ void borg_init_9(void) { byte *test; /*** Initialize (system) ***/ /* Message */ prt("Initializing the Borg (system)...", 0, 0); /* Refresh */ Term_fresh(); /* Mega-Hack -- verify memory */ C_MAKE(test, 300 * 1024L, byte); C_KILL(test, 300 * 1024L, byte); /*** Initialize (internal) ***/ /* Message */ prt("Initializing the Borg (internal)...", 0, 0); /* Refresh */ Term_fresh(); /* Mega-Hack -- Cheat a lot */ auto_cheat_inven = TRUE; auto_cheat_equip = TRUE; /* Mega-Hack -- Cheat a lot */ auto_cheat_spell = TRUE; /* Mega-Hack -- Cheat a lot */ auto_cheat_panel = TRUE; /* Initialize */ borg_init_1(); borg_init_2(); borg_init_3(); borg_init_4(); borg_init_5(); borg_init_6(); borg_init_7(); borg_init_8(); /* Clear line */ prt("", 0, 0); /* Prepare */ borg_prepare(); /* Refresh */ Term_fresh(); /* Official message */ borg_note("# Ready..."); /* Now it is ready */ initialized = TRUE; } /* * Hack -- forward declare */ extern void do_cmd_borg(void); /* * Hack -- interact with the Borg */ void do_cmd_borg(void) { char cmd; /* Get a "Borg command", or abort */ if (!get_com("Borg command: ", &cmd)) return; /* Simple help */ if (cmd == '?') { int i = 2; /* Save the screen */ Term_save(); /* Clear the screen */ Term_clear(); /* Dump commands */ Term_putstr(5, i++, -1, TERM_WHITE, "Command '?' displays this screen."); Term_putstr(5, i++, -1, TERM_WHITE, "Command '$' initializes the Borg."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 'z' activates the Borg."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 'u' updates the Borg."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 'x' steps the Borg."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 'f' modifies the normal flags."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 'c' modifies the cheat flags."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 'l' activates a log file."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 's' activates search mode."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 'i' displays grid info."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 'a' displays avoidances."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 'k' displays monster info."); Term_putstr(5, i++, -1, TERM_WHITE, "Command 't' displays object info."); /* Prompt for key */ msg_print("Available commands: ?,$,z,u,x,f,c,w,l,s,i,a,k,t."); msg_print(NULL); /* Restore the screen */ Term_load(); /* Done */ return; } /* Hack -- force initialization */ if (!initialized) borg_init_9(); /* Command: Nothing */ if (cmd == '$') { /* Nothing */ } /* Command: Activate */ else if (cmd == 'z') { /* Prepare */ borg_prepare(); /* Activate */ auto_active = TRUE; /* Reset cancel */ auto_cancel = FALSE; /* Step forever */ auto_step = 0; /* Allowable Cheat -- Obtain "recall" flag */ goal_recalling = (p_ptr->word_recall ? TRUE : FALSE); /* Message */ borg_note("# Installing keypress hook"); /* Activate the key stealer */ Term_inkey_hook = Term_inkey_borg; } /* Command: Update */ else if (cmd == 'u') { /* Prepare */ borg_prepare(); /* Activate */ auto_active = TRUE; /* Immediate cancel */ auto_cancel = TRUE; /* Step forever */ auto_step = 0; /* Allowable Cheat -- Obtain "recall" flag */ goal_recalling = (p_ptr->word_recall ? TRUE : FALSE); /* Message */ borg_note("# Installing keypress hook"); /* Activate the key stealer */ Term_inkey_hook = Term_inkey_borg; } /* Command: Step */ else if (cmd == 'x') { /* Prepare */ borg_prepare(); /* Activate */ auto_active = TRUE; /* Reset cancel */ auto_cancel = FALSE; /* Step for one (or more) turns */ auto_step = (command_arg > 0) ? command_arg : 1; /* Allowable Cheat -- Obtain "recall" flag */ goal_recalling = (p_ptr->word_recall ? TRUE : FALSE); /* Message */ borg_note("# Installing keypress hook"); /* Activate the key stealer */ Term_inkey_hook = Term_inkey_borg; } /* Command: toggle "flags" */ else if (cmd == 'f') { /* Get a "Borg command", or abort */ if (!get_com("Borg command: Toggle Flag: ", &cmd)) return; /* Dump savefile at each death */ if (cmd == 'd') { auto_flag_dump = !auto_flag_dump; msg_format("Borg -- auto_flag_dump is now %d.", auto_flag_dump); } /* Dump savefile at each level */ else if (cmd == 's') { auto_flag_save = !auto_flag_save; msg_format("Borg -- auto_flag_save is now %d.", auto_flag_save); } } /* Command: toggle "cheat" flags */ else if (cmd == 'c') { /* Get a "Borg command", or abort */ if (!get_com("Borg command: Toggle Cheat: ", &cmd)) return; /* Toggle */ if (cmd == 'i') { auto_cheat_inven = !auto_cheat_inven; msg_format("Borg -- auto_cheat_inven is now %d.", auto_cheat_inven); } else if (cmd == 'e') { auto_cheat_equip = !auto_cheat_equip; msg_format("Borg -- auto_cheat_equip is now %d.", auto_cheat_equip); } else if (cmd == 's') { auto_cheat_spell = !auto_cheat_spell; msg_format("Borg -- auto_cheat_spell is now %d.", auto_cheat_spell); } else if (cmd == 'p') { auto_cheat_panel = !auto_cheat_panel; msg_format("Borg -- auto_cheat_panel is now %d.", auto_cheat_panel); } } /* Start a new log file */ else if (cmd == 'l') { char buf[80]; /* Close the log file */ if (auto_fff) my_fclose(auto_fff); /* Hack -- drop permissions */ safe_setuid_drop(); /* Default */ strcpy(buf, "borg.log"); /* XXX XXX XXX Get the name and open the log file */ if (get_string("Borg Log File: ", buf, 70)) { /* Open a new file */ auto_fff = my_fopen(buf, "w"); /* Failure */ if (!auto_fff) msg_print("Cannot open that file."); } /* Hack -- grab permissions */ safe_setuid_grab(); } /* Activate a search string */ else if (cmd == 's') { /* Get the new search string (or cancel the matching) */ if (!get_string("Borg Match String: ", auto_match, 70)) { /* Cancel it */ strcpy(auto_match, ""); /* Message */ msg_print("Borg Match String de-activated."); } } /* Command: check "info" flags */ else if (cmd == 'i') { int x, y; u16b mask; /* Get a "Borg command", or abort */ if (!get_com("Borg command: Show grids: ", &cmd)) return; /* Extract a flag */ switch (cmd) { case '0': mask = 1 << 0; break; case '1': mask = 1 << 1; break; case '2': mask = 1 << 2; break; case '3': mask = 1 << 3; break; case '4': mask = 1 << 4; break; case '5': mask = 1 << 5; break; case '6': mask = 1 << 6; break; case '7': mask = 1 << 7; break; case 'm': mask = BORG_MARK; break; case 'g': mask = BORG_GLOW; break; case 'd': mask = BORG_DARK; break; case 'o': mask = BORG_OKAY; break; case 'l': mask = BORG_LITE; break; case 'v': mask = BORG_VIEW; break; case 't': mask = BORG_TEMP; break; case 'x': mask = BORG_XTRA; break; default: mask = 0x00; break; } /* Scan map */ for (y = w_y; y < w_y + SCREEN_HGT; y++) { for (x = w_x; x < w_x + SCREEN_WID; x++) { byte a = TERM_RED; auto_grid *ag = &auto_grids[y][x]; /* Given mask, show only those grids */ if (mask && !(ag->info & mask)) continue; /* Given no mask, show unknown grids */ if (!mask && (ag->info & BORG_MARK)) continue; /* Color */ if (borg_cave_floor_bold(y, x)) a = TERM_YELLOW; /* Display */ print_rel('*', a, y, x); } } /* Get keypress */ msg_print("Press any key."); msg_print(NULL); /* Redraw map */ prt_map(); } /* Command: check "avoidances" */ else if (cmd == 'a') { int x, y, p; /* Scan map */ for (y = w_y; y < w_y + SCREEN_HGT; y++) { for (x = w_x; x < w_x + SCREEN_WID; x++) { byte a = TERM_RED; /* Obtain danger */ p = borg_danger(y, x, 1); /* Skip non-avoidances */ if (p <= avoidance / 2) continue; /* Use yellow for less painful */ if (p <= avoidance) a = TERM_YELLOW; /* Display */ print_rel('*', a, y, x); } } /* Get keypress */ msg_format("Avoidance value %d.", avoidance); msg_print(NULL); /* Redraw map */ prt_map(); } /* Command: show "monsters" */ else if (cmd == 'k') { int i, n = 0; /* Scan the monsters */ for (i = 1; i < auto_kills_nxt; i++) { auto_kill *kill = &auto_kills[i]; /* Still alive */ if (kill->r_idx) { int x = kill->x; int y = kill->y; /* Display */ print_rel('*', TERM_RED, y, x); /* Count */ n++; } } /* Get keypress */ msg_format("There are %d known monsters.", n); msg_print(NULL); /* Redraw map */ prt_map(); } /* Command: show "objects" */ else if (cmd == 't') { int i, n = 0; /* Scan the objects */ for (i = 1; i < auto_takes_nxt; i++) { auto_take *take = &auto_takes[i]; /* Still alive */ if (take->k_idx) { int x = take->x; int y = take->y; /* Display */ print_rel('*', TERM_RED, y, x); /* Count */ n++; } } /* Get keypress */ msg_format("There are %d known objects.", n); msg_print(NULL); /* Redraw map */ prt_map(); } /* Command: debug -- current flow */ else if (cmd == '%') { int i, x, y; /* Flow */ for (i = 0; i < 250; i++) { int n = 0; /* Scan map */ for (y = w_y; y < w_y + SCREEN_HGT; y++) { for (x = w_x; x < w_x + SCREEN_WID; x++) { byte a = TERM_RED; /* Verify flow cost */ if (auto_data_flow->data[y][x] != i) continue; /* Display */ print_rel('*', a, y, x); /* Count */ n++; } } /* Nothing */ if (!n) break; /* Get keypress */ msg_format("Flow depth %d.", i); msg_print(NULL); /* Redraw map */ prt_map(); } } /* Command: debug -- danger of grid */ else if (cmd == '#') { int n; /* Turns */ n = (command_arg ? command_arg : 1); /* Danger of grid */ msg_format("Danger(%d,%d,%d) is %d", target_col, target_row, n, borg_danger(target_row, target_col, n)); } /* Command: debug -- fear of depth */ else if (cmd == '_') { int i; /* Max depth */ msg_format("Max depth %d:", auto_max_depth); /* Dump fear codes */ for (i = 0; i <= auto_max_depth; i++) { /* Dump fear code */ msg_format(" %d", auto_fear_depth[i]); } } /* Oops */ else { /* Message */ msg_print("That is not a legal Borg command."); } } #else #ifdef MACINTOSH static int HACK = 0; #endif /* MACINTOSH */ #endif /* ALLOW_BORG */