/* File: borg9.c */ /* Purpose: Top level interface to the Borg -BEN- */ #include "angband.h" #include "borg.h" #ifdef ALLOW_BORG /* * 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 concept of "Cheating Death". If this * is available (see "wizard mode" and the "cheat_live" flag), then he will * automatically "cheat death" whenever he is "about to die". Otherwise, a * special hack will cause him to stop playing when his player dies. * * 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) */ /* * 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 (!borg_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 (!borg_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 (!borg_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 */ borg_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 (p_ptr->is_dead) { /* Oops */ borg_oops("unexpected death!"); /* Return */ return (1); } /* Save the system random info */ borg_rand_quick = Rand_quick; borg_rand_value = Rand_value; /* Use the local random info */ Rand_quick = TRUE; Rand_value = borg_rand_local; /* Update and Think */ while (!borg_think()) /* loop */; /* Save the local random info */ borg_rand_local = Rand_value; /* Restore the system random info */ Rand_quick = borg_rand_quick; Rand_value = borg_rand_value; /* Hack -- allow stepping to induce a clean cancel */ if (borg_step && (!--borg_step)) borg_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 the dungeon level */ depth_in_feet = FALSE; /* Allow items to stack */ testing_stack = TRUE; /* Allow monsters to carry */ testing_carry = TRUE; /* Do not ignore discounts */ stack_force_costs = FALSE; /* Ignore inscriptions */ stack_force_notes = TRUE; /* Efficiency */ avoid_abort = TRUE; /* Efficiency */ op_ptr->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 */ b_ptr->prace = p_ptr->prace; /* Extract the class */ b_ptr->pclass = p_ptr->pclass; /* Extract the race pointer */ rb_ptr = &race_info[b_ptr->prace]; /* Extract the class pointer */ cb_ptr = &class_info[b_ptr->pclass]; /* Extract the magic pointer */ mb_ptr = &magic_info[b_ptr->pclass]; /* Initialize the books */ for (book = 0; book < 9; book++) { borg_prepare_book(book); } } /* * Initialize some stuff */ static void borg_init_1(void) { int i; /* Allocate the "keypress queue" */ C_MAKE(borg_key_queue, BORG_KEY_SIZE, char); /* Prapare a local random number seed */ borg_rand_local = rand_int(0x10000000); /*** Dungeon ***/ /* Allocate */ MAKE(db_ptr, auto_dungeon); /*** Very special "tracking" array ***/ /* Track the shop locations */ C_MAKE(borg_track_shop_x, BORG_MAX_SHOP, byte); C_MAKE(borg_track_shop_y, BORG_MAX_SHOP, byte); /*** Special "tracking" arrays ***/ /* Track "up" stairs */ borg_track_less_num = 0; C_MAKE(borg_track_less_x, BORG_MAX_LESS, byte); C_MAKE(borg_track_less_y, BORG_MAX_LESS, byte); /* Track "down" stairs */ borg_track_more_num = 0; C_MAKE(borg_track_more_x, BORG_MAX_MORE, byte); C_MAKE(borg_track_more_y, BORG_MAX_MORE, byte); /*** Object tracking ***/ /* No objects yet */ borg_takes_cnt = 0; borg_takes_nxt = 1; /* Scan the objects */ for (i = 1; i < MAX_K_IDX; i++) { object_kind *k_ptr = &k_info[i]; /* Skip non-items */ if (!k_ptr->name) continue; /* Notice this object */ borg_char_is_take[(byte)(k_ptr->x_char)] = TRUE; } /*** Monster tracking ***/ /* No monsters yet */ borg_kills_cnt = 0; borg_kills_nxt = 1; /* Scan the monsters */ for (i = 1; i < MAX_R_IDX-1; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-monsters */ if (!r_ptr->name) continue; /* Hack -- Skip "clear" monsters XXX XXX XXX */ if (r_ptr->flags1 & RF1_CHAR_CLEAR) continue; /* Hack -- Skip "multi" monsters XXX XXX XXX */ if (r_ptr->flags1 & RF1_CHAR_MULTI) continue; /* Notice this monster */ borg_char_is_kill[(byte)(r_ptr->x_char)] = TRUE; } /*** Special counters ***/ /* Count racial appearances */ C_MAKE(borg_race_count, MAX_R_IDX, s16b); /* Count racial deaths */ C_MAKE(borg_race_death, MAX_R_IDX, s16b); /*** Hack -- Cheat XXX XXX XXX ***/ /* Hack -- Extract dead uniques */ for (i = 1; i < MAX_R_IDX-1; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-monsters */ if (!r_ptr->name) continue; /* Skip non-uniques */ if (!(r_ptr->flags1 & RF1_UNIQUE)) continue; /* Mega-Hack -- Access "dead unique" list */ if (r_ptr->max_num == 0) borg_race_death[i] = 1; } /* Hack -- Access max depth */ b_ptr->max_depth = p_ptr->max_depth; /* Hack -- Assume happy depth */ borg_happy_depth = b_ptr->max_depth; } /* * Sorting hook -- comp function -- see below * * We use "u" to point to an array of strings, and "v" to point to * an array of indexes, and we sort them together by the strings. */ static bool ang_sort_comp_hook(vptr u, vptr v, int a, int b) { cptr *text = (cptr*)(u); s16b *what = (s16b*)(v); int cmp; /* Compare the two strings */ cmp = (strcmp(text[a], text[b])); /* Strictly less */ if (cmp < 0) return (TRUE); /* Strictly more */ if (cmp > 0) return (FALSE); /* Enforce "stable" sort */ return (what[a] <= what[b]); } /* * Sorting hook -- swap function -- see below * * We use "u" to point to an array of strings, and "v" to point to * an array of indexes, and we sort them together by the strings. */ static void ang_sort_swap_hook(vptr u, vptr v, int a, int b) { cptr *text = (cptr*)(u); s16b *what = (s16b*)(v); cptr texttmp; s16b whattmp; /* Swap "text" */ texttmp = text[a]; text[a] = text[b]; text[b] = texttmp; /* Swap "what" */ whattmp = what[a]; what[a] = what[b]; what[b] = whattmp; } /* * Initialize some stuff * * Note that the Borg will never find Grond/Morgoth, but we * prepare the item parsers for them anyway. Actually, the * Borg might get lucky and find some of the special artifacts, * so it is always best to prepare for a situation if it does * not cost any effort. * * Note that all six artifact "Rings" will parse as "kind 506" * (the first artifact ring) and both artifact "Amulets" will * parse as "kind 503" (the first of the two artifact amulets), * but as long as we use the "name1" field (and not the "kind" * or "sval" fields) we should be okay. * * We sort the two arrays of items names in reverse order, so that * we will catch "mace of disruption" before "mace", "Scythe of * Slicing" before "Scythe", and for "Ring of XXX" before "Ring". * * Note that we do not have to parse "plural artifacts" (!) * * Hack -- This entire routine is a giant hack, but it works */ static void borg_init_3(void) { int i, k, n; int size; s16b what[512]; cptr text[512]; char buf[256]; /*** Item/Ware arrays ***/ /* Make the inventory array */ C_MAKE(borg_items, INVEN_TOTAL, auto_item); /* Make the stores in the town */ C_MAKE(borg_shops, BORG_MAX_SHOP, auto_shop); /*** Item/Ware arrays (simulation) ***/ /* Make the "safe" inventory array */ C_MAKE(borg_safe_items, INVEN_TOTAL, auto_item); /* Make the "safe" stores in the town */ C_MAKE(borg_safe_shops, BORG_MAX_SHOP, auto_shop); /*** Analysis info ***/ /* Allocate "ab_ptr" */ MAKE(ab_ptr, player_anal); /*** Plural Object Templates ***/ /* Start with no objects */ size = 0; /* Analyze some "item kinds" */ for (k = 1; k < MAX_K_IDX; k++) { object_type hack; /* Get the kind */ object_kind *k_ptr = &k_info[k]; /* Skip "empty" items */ if (!k_ptr->name) continue; /* Skip "gold" objects */ if (k_ptr->tval == TV_GOLD) continue; /* Skip "artifacts" */ if (k_ptr->flags3 & TR3_INSTA_ART) continue; /* Hack -- make an item */ object_prep(&hack, k); /* Describe a "plural" object */ hack.number = 2; object_desc_store(buf, &hack, FALSE, 0); /* Save an entry */ text[size] = string_make(buf); what[size] = k; size++; } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort */ ang_sort(text, what, size); /* Save the size */ ab_ptr->plural_size = size; /* Allocate the "item parsing arrays" (plurals) */ C_MAKE(ab_ptr->plural_text, ab_ptr->plural_size, cptr); C_MAKE(ab_ptr->plural_what, ab_ptr->plural_size, s16b); /* Save the entries */ for (i = 0; i < size; i++) ab_ptr->plural_text[i] = text[i]; for (i = 0; i < size; i++) ab_ptr->plural_what[i] = what[i]; /*** Singular Object Templates ***/ /* Start with no objects */ size = 0; /* Analyze some "item kinds" */ for (k = 1; k < MAX_K_IDX; k++) { object_type hack; /* Get the kind */ object_kind *k_ptr = &k_info[k]; /* Skip "empty" items */ if (!k_ptr->name) continue; /* Skip "dungeon terrain" objects */ if (k_ptr->tval == TV_GOLD) continue; /* Skip "artifacts" */ if (k_ptr->flags3 & TR3_INSTA_ART) continue; /* Hack -- make an item */ object_prep(&hack, k); /* Describe a "singular" object */ hack.number = 1; object_desc_store(buf, &hack, FALSE, 0); /* Save an entry */ text[size] = string_make(buf); what[size] = k; size++; } /* Analyze the "INSTA_ART" items */ for (i = 1; i < MAX_A_IDX; i++) { object_type hack; artifact_type *a_ptr = &a_info[i]; cptr name = (a_name + a_ptr->name); /* Skip "empty" items */ if (!a_ptr->name) continue; /* Skip non INSTA_ART things */ if (!(a_ptr->flags3 & TR3_INSTA_ART)) continue; /* Extract the "kind" */ k = lookup_kind(a_ptr->tval, a_ptr->sval); /* Hack -- make an item */ object_prep(&hack, k); /* Save the index */ hack.name1 = i; /* Describe a "singular" object */ hack.number = 1; object_desc_store(buf, &hack, FALSE, 0); /* Extract the "suffix" length */ n = strlen(name) + 1; /* Remove the "suffix" */ buf[strlen(buf) - n] = '\0'; /* Save an entry */ text[size] = string_make(buf); what[size] = k; size++; } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort */ ang_sort(text, what, size); /* Save the size */ ab_ptr->single_size = size; /* Allocate the "item parsing arrays" (plurals) */ C_MAKE(ab_ptr->single_text, ab_ptr->single_size, cptr); C_MAKE(ab_ptr->single_what, ab_ptr->single_size, s16b); /* Save the entries */ for (i = 0; i < size; i++) ab_ptr->single_text[i] = text[i]; for (i = 0; i < size; i++) ab_ptr->single_what[i] = what[i]; /*** Artifact and Ego-Item Parsers ***/ /* No entries yet */ size = 0; /* Collect the "artifact names" */ for (k = 1; k < MAX_A_IDX; k++) { artifact_type *a_ptr = &a_info[k]; /* Skip non-items */ if (!a_ptr->name) continue; /* Extract a string */ sprintf(buf, " %s", (a_name + a_ptr->name)); /* Save an entry */ text[size] = string_make(buf); what[size] = k; size++; } /* Collect the "ego-item names" */ for (k = 1; k < MAX_E_IDX; k++) { ego_item_type *e_ptr = &e_info[k]; /* Skip non-items */ if (!e_ptr->name) continue; /* Extract a string */ sprintf(buf, " %s", (e_name + e_ptr->name)); /* Save an entry */ text[size] = string_make(buf); what[size] = k + 256; size++; } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort */ ang_sort(text, what, size); /* Save the size */ ab_ptr->artego_size = size; /* Allocate the "item parsing arrays" (plurals) */ C_MAKE(ab_ptr->artego_text, ab_ptr->artego_size, cptr); C_MAKE(ab_ptr->artego_what, ab_ptr->artego_size, s16b); /* Save the entries */ for (i = 0; i < size; i++) ab_ptr->artego_text[i] = text[i]; for (i = 0; i < size; i++) ab_ptr->artego_what[i] = what[i]; } /* * Initialize some stuff */ static void borg_init_5(void) { int i; int size; s16b what[512]; cptr text[512]; /*** Old Info ***/ /* Old depth */ xb_ptr->old_depth = -1; /* Old stuff */ xb_ptr->old_chp = -1; xb_ptr->old_csp = -1; /* Old panel */ xb_ptr->old_wx = -1; xb_ptr->old_wy = -1; /* Old location */ xb_ptr->old_px = -1; xb_ptr->old_py = -1; /*** Message tracking ***/ /* No chars saved yet */ borg_msg_len = 0; /* Maximum buffer size */ borg_msg_siz = 4096; /* Allocate a buffer */ C_MAKE(borg_msg_buf, borg_msg_siz, char); /* No msg's saved yet */ borg_msg_num = 0; /* Maximum number of messages */ borg_msg_max = 128; /* Allocate array of positions */ C_MAKE(borg_msg_pos, borg_msg_max, s16b); /* Allocate array of use-types */ C_MAKE(borg_msg_use, borg_msg_max, s16b); /*** Object/Monster tracking ***/ /* Array of "wanks" */ C_MAKE(borg_wanks, AUTO_VIEW_MAX, auto_wank); /*** Reset the map ***/ /* Forget the map */ borg_forget_map(); /*** Parse "unique" monster names ***/ /* Start over */ size = 0; /* Collect "unique" monsters */ for (i = 1; i < MAX_R_IDX-1; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-monsters */ if (!r_ptr->name) continue; /* Skip non-unique monsters */ if (!(r_ptr->flags1 & RF1_UNIQUE)) continue; /* Use it */ text[size] = r_name + r_ptr->name; what[size] = i; size++; } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort */ ang_sort(text, what, size); /* Save the size */ ab_ptr->unique_size = size; /* Allocate the arrays */ C_MAKE(ab_ptr->unique_text, ab_ptr->unique_size, cptr); C_MAKE(ab_ptr->unique_what, ab_ptr->unique_size, s16b); /* Save the entries */ for (i = 0; i < size; i++) ab_ptr->unique_text[i] = text[i]; for (i = 0; i < size; i++) ab_ptr->unique_what[i] = what[i]; /*** Parse "normal" monster names ***/ /* Start over */ size = 0; /* Collect "normal" monsters */ for (i = 1; i < MAX_R_IDX-1; i++) { monster_race *r_ptr = &r_info[i]; /* Skip non-monsters */ if (!r_ptr->name) continue; /* Skip unique monsters */ if (r_ptr->flags1 & RF1_UNIQUE) continue; /* Use it */ text[size] = r_name + r_ptr->name; what[size] = i; size++; } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_hook; ang_sort_swap = ang_sort_swap_hook; /* Sort */ ang_sort(text, what, size); /* Save the size */ ab_ptr->normal_size = size; /* Allocate the arrays */ C_MAKE(ab_ptr->normal_text, ab_ptr->normal_size, cptr); C_MAKE(ab_ptr->normal_what, ab_ptr->normal_size, s16b); /* Save the entries */ for (i = 0; i < size; i++) ab_ptr->normal_text[i] = text[i]; for (i = 0; i < size; i++) ab_ptr->normal_what[i] = what[i]; } /* * Initialize the Borg */ static errr borg_init(void) { /* Prepare */ borg_prepare(); /* Initialize */ borg_init_1(); borg_init_3(); borg_init_5(); /* Success */ return (0); } /* * Hack -- interact with the Borg */ void do_cmd_borg(void) { char cmd; static bool initialized = FALSE; /* 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 borg_avoids."); 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; } /* Initialize (once) */ if (!initialized) { byte *test; /* 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); /* Message */ prt("Initializing the Borg (internal)...", 0, 0); /* Refresh */ Term_fresh(); /* Mega-Hack -- Cheat a lot */ borg_flag_cheat_inven = TRUE; borg_flag_cheat_equip = TRUE; /* Mega-Hack -- Cheat a lot */ borg_flag_cheat_spell = TRUE; /* Mega-Hack -- Cheat a lot */ borg_flag_cheat_panel = TRUE; /* Initialize */ (void)borg_init(); /* Clear line */ prt("", 0, 0); /* Prepare */ borg_prepare(); /* Refresh */ Term_fresh(); /* Official message */ borg_note("# Ready..."); /* Now it is ready */ initialized = TRUE; } /* Command: Nothing */ if (cmd == '$') { /* Nothing */ } /* Command: Activate */ else if (cmd == 'z') { /* Prepare */ borg_prepare(); /* Activate */ borg_active = TRUE; /* Reset cancel */ borg_cancel = FALSE; /* Step forever */ borg_step = 0; /* Allowable Cheat -- Obtain "recall" flag */ borg_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 */ borg_active = TRUE; /* Immediate cancel */ borg_cancel = TRUE; /* Step forever */ borg_step = 0; /* Allowable Cheat -- Obtain "recall" flag */ borg_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 */ borg_active = TRUE; /* Reset cancel */ borg_cancel = FALSE; /* Step for one (or more) turns */ borg_step = (p_ptr->command_arg > 0) ? p_ptr->command_arg : 1; /* Allowable Cheat -- Obtain "recall" flag */ borg_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 "strategy" flags */ else if (cmd == 'f') { /* Get a "Borg command", or abort */ if (!get_com("Borg command: Toggle Strategy Flag (d/s): ", &cmd)) return; /* Dump savefile at each death */ if (cmd == 'd') { borg_flag_dump = !borg_flag_dump; msg_format("Borg -- borg_flag_dump is now %d.", borg_flag_dump); } /* Dump savefile at each level */ else if (cmd == 's') { borg_flag_save = !borg_flag_save; msg_format("Borg -- borg_flag_save is now %d.", borg_flag_save); } } /* Command: toggle "cheat" flags */ else if (cmd == 'c') { /* Get a "Borg command", or abort */ if (!get_com("Borg command: Toggle Cheat Flag (i/e/s/p): ", &cmd)) return; /* Toggle */ if (cmd == 'i') { borg_flag_cheat_inven = !borg_flag_cheat_inven; msg_format("Borg -- borg_flag_cheat_inven is now %d.", borg_flag_cheat_inven); } else if (cmd == 'e') { borg_flag_cheat_equip = !borg_flag_cheat_equip; msg_format("Borg -- borg_flag_cheat_equip is now %d.", borg_flag_cheat_equip); } else if (cmd == 's') { borg_flag_cheat_spell = !borg_flag_cheat_spell; msg_format("Borg -- borg_flag_cheat_spell is now %d.", borg_flag_cheat_spell); } else if (cmd == 'p') { borg_flag_cheat_panel = !borg_flag_cheat_panel; msg_format("Borg -- borg_flag_cheat_panel is now %d.", borg_flag_cheat_panel); } } /* Start a new log file */ else if (cmd == 'l') { char buf[80]; /* Close the log file */ if (borg_fff) my_fclose(borg_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 */ borg_fff = my_fopen(buf, "w"); /* Failure */ if (!borg_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: ", borg_match_string, 70)) { /* Cancel it */ strcpy(borg_match_string, ""); /* Message */ msg_print("Borg Match String de-activated."); } } /* Command: check "info" flags */ else if (cmd == 'i') { int x, y; u16b mask = 0x00; /* 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; } /* Scan map */ for (y = xb_ptr->wy; y < xb_ptr->wy + SCREEN_HGT; y++) { for (x = xb_ptr->wx; x < xb_ptr->wx + SCREEN_WID; x++) { byte a = TERM_RED; /* Given mask, show only those grids */ if (mask && !(db_ptr->cave_info[y][x] & mask)) continue; /* Given no mask, show unknown grids */ if (!mask && (db_ptr->cave_info[y][x] & (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 actual danger */ else if (cmd == 'a') { int x, y, p; int limit = (p_ptr->command_arg ? p_ptr->command_arg : borg_avoid/2); /* Scan map */ for (y = xb_ptr->wy; y < xb_ptr->wy + SCREEN_HGT; y++) { for (x = xb_ptr->wx; x < xb_ptr->wx + SCREEN_WID; x++) { /* Obtain danger */ p = borg_danger(y, x, 1); /* High danger */ if (p > limit) { print_rel('*', TERM_RED, y, x); } } } /* Get keypress */ msg_format("Danger %d and above.", limit); msg_print(NULL); /* Redraw map */ prt_map(); } /* Command: check memorized danger */ else if (cmd == 'd') { int x, y, p; int limit = (p_ptr->command_arg ? p_ptr->command_arg : borg_avoid/2); /* Scan map */ for (y = xb_ptr->wy; y < xb_ptr->wy + SCREEN_HGT; y++) { for (x = xb_ptr->wx; x < xb_ptr->wx + SCREEN_WID; x++) { /* Obtain danger */ p = db_ptr->danger[y][x]; /* Unknown danger */ if (p < 0) { print_rel('*', TERM_YELLOW, y, x); } /* Irrelevant danger */ else if (borg_do_wipe_danger) { print_rel('*', TERM_VIOLET, y, x); } /* High danger */ else if (p > limit) { print_rel('*', TERM_RED, y, x); } } } /* Get keypress */ msg_format("Danger %d and above.", limit); 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 < borg_kills_nxt; i++) { auto_kill *kill = &borg_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 < borg_takes_nxt; i++) { auto_take *take = &borg_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; char ch; /* Flow */ for (i = 0; i < 250; i++) { int n = 0; /* Scan map */ for (y = xb_ptr->wy; y < xb_ptr->wy + SCREEN_HGT; y++) { for (x = xb_ptr->wx; x < xb_ptr->wx + SCREEN_WID; x++) { byte a = TERM_RED; /* Verify flow cost */ if (db_ptr->cost[y][x] != i) continue; /* Display */ print_rel('*', a, y, x); /* Count */ n++; } } /* Get keypress */ (void)get_com(format("Flow depth %d (SPC/ESC): ", i), &ch); /* Redraw map */ prt_map(); /* Stop */ if (ch == ESCAPE) break; } } /* Command: debug -- danger of grid */ else if (cmd == '#') { int n; /* Turns */ n = (p_ptr->command_arg ? p_ptr->command_arg : 1); /* Danger of grid */ msg_format("Danger(%d,%d,%d) is %d", p_ptr->target_row, p_ptr->target_col, n, borg_danger(p_ptr->target_row, p_ptr->target_col, n)); } /* Command: debug -- fear of depth */ else if (cmd == '_') { /* Max depth */ msg_format("Max depth %d:", b_ptr->max_depth); /* Happy depth */ msg_format("Happy depth %d:", borg_happy_depth); /* Happy count */ msg_format("Happy count %d:", borg_happy_count); } /* Oops */ else { /* Message */ msg_print("That is not a legal Borg command."); } } #else #ifdef MACINTOSH static int HACK = 0; #endif /* MACINTOSH */ #endif /* ALLOW_BORG */