diff options
author | Manoj Srivastava <srivasta@debian.org> | 2020-05-22 19:57:41 -0700 |
---|---|---|
committer | Manoj Srivastava <srivasta@debian.org> | 2020-05-22 20:02:19 -0700 |
commit | c3d2579ad8d7eb33059aa8fdbaf5b564411a57f2 (patch) | |
tree | 1570cda0676fdcf4171a69a7fe313c1b89a52b0c /src/object1.cc | |
parent | 986b7742bf244b4073ecca0723615f70be8a1ab6 (diff) | |
parent | 4e9b9c402ed95bf9a17fd6d795bc49bb4128a6fa (diff) |
Merge branch 'upstream' into debian-cmake-fixes
Diffstat (limited to 'src/object1.cc')
-rw-r--r-- | src/object1.cc | 6234 |
1 files changed, 6234 insertions, 0 deletions
diff --git a/src/object1.cc b/src/object1.cc new file mode 100644 index 00000000..3fb2ef26 --- /dev/null +++ b/src/object1.cc @@ -0,0 +1,6234 @@ +/* + * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke + * + * This software may be copied and distributed for educational, research, and + * not for profit purposes provided that this copyright and statement are + * included in all such copies. + */ + +#include "object1.hpp" + +#include "artifact_type.hpp" +#include "cave.hpp" +#include "cave_type.hpp" +#include "cmd2.hpp" +#include "cmd6.hpp" +#include "dungeon.hpp" +#include "dungeon_info_type.hpp" +#include "ego_item_type.hpp" +#include "feature_type.hpp" +#include "files.hpp" +#include "game.hpp" +#include "hook_get_in.hpp" +#include "hooks.hpp" +#include "lua_bind.hpp" +#include "mimic.hpp" +#include "monster1.hpp" +#include "monster2.hpp" +#include "monster_ego.hpp" +#include "monster_race.hpp" +#include "monster_race_flag.hpp" +#include "monster_type.hpp" +#include "object2.hpp" +#include "object_flag.hpp" +#include "object_flag_meta.hpp" +#include "object_flag_set.hpp" +#include "object_kind.hpp" +#include "object_type.hpp" +#include "options.hpp" +#include "player_race.hpp" +#include "player_race_mod.hpp" +#include "player_type.hpp" +#include "set_type.hpp" +#include "skills.hpp" +#include "spell_type.hpp" +#include "spells5.hpp" +#include "squeltch.hpp" +#include "stats.hpp" +#include "store_info_type.hpp" +#include "tables.hpp" +#include "util.hpp" +#include "util.h" +#include "variable.h" +#include "variable.hpp" +#include "wilderness_type_info.hpp" +#include "xtra1.hpp" +#include "z-rand.hpp" + +#include <boost/algorithm/string/join.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <cassert> +#include <fmt/format.h> +#include <utility> + +using boost::starts_with; + +static void apply_flags_set(s16b a_idx, s16b set_idx, object_flag_set *f); + +/* + * Hack -- note that "TERM_MULTI" is now just "TERM_VIOLET". + * We will have to find a cleaner method for "MULTI_HUED" later. + * There were only two multi-hued "flavors" (one potion, one food). + * Plus five multi-hued "base-objects" (3 dragon scales, one blade + * of chaos, and one something else). See the SHIMMER_OBJECTS code + * in "dungeon.c" and the object color extractor in "cave.c". + */ +#define TERM_MULTI TERM_VIOLET + + +/** + * Show all equipment/inventory slots, even when empty? + */ +static bool item_tester_full; + + +/* + * Max sizes of the following arrays + */ +#define MAX_ROCKS 62 /* Used with rings (min 58) */ +#define MAX_AMULETS 34 /* Used with amulets (min 30) */ +#define MAX_WOODS 35 /* Used with staffs (min 32) */ +#define MAX_METALS 39 /* Used with wands/rods (min 32/30) */ +#define MAX_COLORS 66 /* Used with potions (min 62) */ +#define MAX_SHROOM 20 /* Used with mushrooms (min 20) */ +#define MAX_TITLES 55 /* Used with scrolls (min 55) */ +#define MAX_SYLLABLES 164 /* Used with scrolls (see below) */ + + +/* + * Rings (adjectives and colors) + */ + +static cptr ring_adj[MAX_ROCKS] = +{ + "Alexandrite", "Amethyst", "Aquamarine", "Azurite", "Beryl", + "Bloodstone", "Calcite", "Carnelian", "Corundum", "Diamond", + "Emerald", "Fluorite", "Garnet", "Granite", "Jade", + "Jasper", "Lapis Lazuli", "Malachite", "Marble", "Moonstone", + "Onyx", "Opal", "Pearl", "Quartz", "Quartzite", + "Rhodonite", "Ruby", "Sapphire", "Tiger Eye", "Topaz", + "Turquoise", "Zircon", "Platinum", "Bronze", "Gold", + "Obsidian", "Silver", "Tortoise Shell", "Mithril", "Jet", + "Engagement", "Adamantite", + "Wire", "Dilithium", "Bone", "Wooden", + "Spikard", "Serpent", "Wedding", "Double", + "Plain", "Brass", "Scarab", "Shining", + "Rusty", "Transparent", "Copper", "Black Opal", "Nickel", + "Glass", "Fluorspar", "Agate", +}; + +static byte ring_col[MAX_ROCKS] = +{ + TERM_GREEN, TERM_VIOLET, TERM_L_BLUE, TERM_L_BLUE, TERM_L_GREEN, + TERM_RED, TERM_WHITE, TERM_RED, TERM_SLATE, TERM_WHITE, + TERM_GREEN, TERM_L_GREEN, TERM_RED, TERM_L_DARK, TERM_L_GREEN, + TERM_UMBER, TERM_BLUE, TERM_GREEN, TERM_WHITE, TERM_L_WHITE, + TERM_L_RED, TERM_L_WHITE, TERM_WHITE, TERM_L_WHITE, TERM_L_WHITE, + TERM_L_RED, TERM_RED, TERM_BLUE, TERM_YELLOW, TERM_YELLOW, + TERM_L_BLUE, TERM_L_UMBER, TERM_WHITE, TERM_L_UMBER, TERM_YELLOW, + TERM_L_DARK, TERM_L_WHITE, TERM_GREEN, TERM_L_BLUE, TERM_L_DARK, + TERM_YELLOW, TERM_VIOLET, + TERM_UMBER, TERM_L_WHITE, TERM_WHITE, TERM_UMBER, + TERM_BLUE, TERM_GREEN, TERM_YELLOW, TERM_ORANGE, + TERM_YELLOW, TERM_ORANGE, TERM_L_GREEN, TERM_YELLOW, + TERM_RED, TERM_WHITE, TERM_UMBER, TERM_L_DARK, TERM_L_WHITE, + TERM_WHITE, TERM_BLUE, TERM_L_WHITE +}; + + +/* + * Amulets (adjectives and colors) + */ + +static cptr amulet_adj[MAX_AMULETS] = +{ + "Amber", "Driftwood", "Coral", "Agate", "Ivory", + "Obsidian", "Bone", "Brass", "Bronze", "Pewter", + "Tortoise Shell", "Golden", "Azure", "Crystal", "Silver", + "Copper", "Amethyst", "Mithril", "Sapphire", "Dragon Tooth", + "Carved Oak", "Sea Shell", "Flint Stone", "Ruby", "Scarab", + "Origami Paper", "Meteoric Iron", "Platinum", "Glass", "Beryl", + "Malachite", "Adamantite", "Mother-of-pearl", "Runed" +}; + +static byte amulet_col[MAX_AMULETS] = +{ + TERM_YELLOW, TERM_L_UMBER, TERM_WHITE, TERM_L_WHITE, TERM_WHITE, + TERM_L_DARK, TERM_WHITE, TERM_ORANGE, TERM_L_UMBER, TERM_SLATE, + TERM_GREEN, TERM_YELLOW, TERM_L_BLUE, TERM_L_BLUE, TERM_L_WHITE, + TERM_L_UMBER, TERM_VIOLET, TERM_L_BLUE, TERM_BLUE, TERM_L_WHITE, + TERM_UMBER, TERM_L_BLUE, TERM_SLATE, TERM_RED, TERM_L_GREEN, + TERM_WHITE, TERM_L_DARK, TERM_L_WHITE, TERM_WHITE, TERM_L_GREEN, + TERM_GREEN, TERM_VIOLET, TERM_L_WHITE, TERM_UMBER +}; + + +/* + * Staffs (adjectives and colors) + */ + +static cptr staff_adj[MAX_WOODS] = +{ + "Aspen", "Balsa", "Banyan", "Birch", "Cedar", + "Cottonwood", "Cypress", "Dogwood", "Elm", "Eucalyptus", + "Hemlock", "Hickory", "Ironwood", "Locust", "Mahogany", + "Maple", "Mulberry", "Oak", "Pine", "Redwood", + "Rosewood", "Spruce", "Sycamore", "Teak", "Walnut", + "Mistletoe", "Hawthorn", "Bamboo", "Silver", "Runed", + "Golden", "Ashen", "Gnarled", "Ivory", "Willow" +}; + +static byte staff_col[MAX_WOODS] = +{ + TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, + TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, + TERM_L_UMBER, TERM_L_UMBER, TERM_UMBER, TERM_L_UMBER, TERM_UMBER, + TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_RED, + TERM_RED, TERM_L_UMBER, TERM_L_UMBER, TERM_L_UMBER, TERM_UMBER, + TERM_GREEN, TERM_L_UMBER, TERM_L_UMBER, TERM_L_WHITE, TERM_UMBER, + TERM_YELLOW, TERM_SLATE, TERM_UMBER, TERM_L_WHITE, TERM_L_UMBER +}; + + +/* + * Wands (adjectives and colors) + */ + +static cptr wand_adj[MAX_METALS] = +{ + "Aluminium", "Cast Iron", "Chromium", "Copper", "Gold", + "Iron", "Magnesium", "Molybdenum", "Nickel", "Rusty", + "Silver", "Steel", "Tin", "Titanium", "Tungsten", + "Zirconium", "Zinc", "Aluminium-Plated", "Copper-Plated", "Gold-Plated", + "Nickel-Plated", "Silver-Plated", "Steel-Plated", "Tin-Plated", "Zinc-Plated", + "Mithril-Plated", "Mithril", "Runed", "Bronze", "Brass", + "Platinum", "Lead", "Lead-Plated", "Ivory" , "Adamantite", + "Uridium", "Long", "Short", "Hexagonal" +}; + +static byte wand_col[MAX_METALS] = +{ + TERM_L_BLUE, TERM_L_DARK, TERM_WHITE, TERM_UMBER, TERM_YELLOW, + TERM_SLATE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_RED, + TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_WHITE, TERM_WHITE, + TERM_L_WHITE, TERM_L_WHITE, TERM_L_BLUE, TERM_L_UMBER, TERM_YELLOW, + TERM_L_UMBER, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, TERM_L_WHITE, + TERM_L_BLUE, TERM_L_BLUE, TERM_UMBER, TERM_L_UMBER, TERM_L_UMBER, + TERM_WHITE, TERM_SLATE, TERM_SLATE, TERM_WHITE, TERM_VIOLET, + TERM_L_RED, TERM_L_BLUE, TERM_BLUE, TERM_RED +}; + + +/* + * Rods (adjectives and colors). + * Efficiency -- copied from wand arrays + */ + +static cptr rod_adj[MAX_METALS]; + +static byte rod_col[MAX_METALS]; + + +/* + * Mushrooms (adjectives and colors) + */ + +static cptr food_adj[MAX_SHROOM] = +{ + "Blue", "Black", "Black Spotted", "Brown", "Dark Blue", + "Dark Green", "Dark Red", "Yellow", "Furry", "Green", + "Grey", "Light Blue", "Light Green", "Violet", "Red", + "Slimy", "Tan", "White", "White Spotted", "Wrinkled", +}; + +static byte food_col[MAX_SHROOM] = +{ + TERM_BLUE, TERM_L_DARK, TERM_L_DARK, TERM_UMBER, TERM_BLUE, + TERM_GREEN, TERM_RED, TERM_YELLOW, TERM_L_WHITE, TERM_GREEN, + TERM_SLATE, TERM_L_BLUE, TERM_L_GREEN, TERM_VIOLET, TERM_RED, + TERM_SLATE, TERM_L_UMBER, TERM_WHITE, TERM_WHITE, TERM_UMBER +}; + + +/* + * Color adjectives and colors, for potions. + * Hack -- The first four entries are hard-coded. + * (water, apple juice, slime mold juice, something) + */ + +static cptr potion_adj[MAX_COLORS] = +{ + "Clear", "Light Brown", "Icky Green", "Strangely Phosphorescent", + "Azure", "Blue", "Blue Speckled", "Black", "Brown", "Brown Speckled", + "Bubbling", "Chartreuse", "Cloudy", "Copper Speckled", "Crimson", "Cyan", + "Dark Blue", "Dark Green", "Dark Red", "Gold Speckled", "Green", + "Green Speckled", "Grey", "Grey Speckled", "Hazy", "Indigo", + "Light Blue", "Light Green", "Magenta", "Metallic Blue", "Metallic Red", + "Metallic Green", "Metallic Purple", "Misty", "Orange", "Orange Speckled", + "Pink", "Pink Speckled", "Puce", "Purple", "Purple Speckled", + "Red", "Red Speckled", "Silver Speckled", "Smoky", "Tangerine", + "Violet", "Vermilion", "White", "Yellow", "Violet Speckled", + "Pungent", "Clotted Red", "Viscous Pink", "Oily Yellow", "Gloopy Green", + "Shimmering", "Coagulated Crimson", "Yellow Speckled", "Gold", + "Manly", "Stinking", "Oily Black", "Ichor", "Ivory White", "Sky Blue", +}; + +static byte potion_col[MAX_COLORS] = +{ + TERM_WHITE, TERM_L_UMBER, TERM_GREEN, TERM_MULTI, + TERM_L_BLUE, TERM_BLUE, TERM_BLUE, TERM_L_DARK, TERM_UMBER, TERM_UMBER, + TERM_L_WHITE, TERM_L_GREEN, TERM_WHITE, TERM_L_UMBER, TERM_RED, TERM_L_BLUE, + TERM_BLUE, TERM_GREEN, TERM_RED, TERM_YELLOW, TERM_GREEN, + TERM_GREEN, TERM_SLATE, TERM_SLATE, TERM_L_WHITE, TERM_VIOLET, + TERM_L_BLUE, TERM_L_GREEN, TERM_RED, TERM_BLUE, TERM_RED, + TERM_GREEN, TERM_VIOLET, TERM_L_WHITE, TERM_ORANGE, TERM_ORANGE, + TERM_L_RED, TERM_L_RED, TERM_VIOLET, TERM_VIOLET, TERM_VIOLET, + TERM_RED, TERM_RED, TERM_L_WHITE, TERM_L_DARK, TERM_ORANGE, + TERM_VIOLET, TERM_RED, TERM_WHITE, TERM_YELLOW, TERM_VIOLET, + TERM_L_RED, TERM_RED, TERM_L_RED, TERM_YELLOW, TERM_GREEN, + TERM_MULTI, TERM_RED, TERM_YELLOW, TERM_YELLOW, + TERM_L_UMBER, TERM_UMBER, TERM_L_DARK, TERM_RED, TERM_WHITE, TERM_L_BLUE +}; + + +/* + * Syllables for scrolls (must be 1-4 letters each) + */ + +static cptr syllables[MAX_SYLLABLES] = +{ + "a", "ab", "ag", "aks", "ala", "an", "ankh", "app", + "arg", "arze", "ash", "aus", "ban", "bar", "bat", "bek", + "bie", "bin", "bit", "bjor", "blu", "bot", "bu", + "byt", "comp", "con", "cos", "cre", "dalf", "dan", + "den", "der", "doe", "dok", "eep", "el", "eng", "er", "ere", "erk", + "esh", "evs", "fa", "fid", "flit", "for", "fri", "fu", "gan", + "gar", "glen", "gop", "gre", "ha", "he", "hyd", "i", + "ing", "ion", "ip", "ish", "it", "ite", "iv", "jo", + "kho", "kli", "klis", "la", "lech", "man", "mar", + "me", "mi", "mic", "mik", "mon", "mung", "mur", "nag", "nej", + "nelg", "nep", "ner", "nes", "nis", "nih", "nin", "o", + "od", "ood", "org", "orn", "ox", "oxy", "pay", "pet", + "ple", "plu", "po", "pot", "prok", "re", "rea", "rhov", + "ri", "ro", "rog", "rok", "rol", "sa", "san", "sat", + "see", "sef", "seh", "shu", "ski", "sna", "sne", "snik", + "sno", "so", "sol", "sri", "sta", "sun", "ta", "tab", + "tem", "ther", "ti", "tox", "trol", "tue", "turs", "u", + "ulk", "um", "un", "uni", "ur", "val", "viv", "vly", + "vom", "wah", "wed", "werg", "wex", "whon", "wun", "x", + "yerg", "yp", "zun", "tri", "blaa", "jah", "bul", "on", + "foo", "ju", "xuxu" +}; + +/* + * Hold the titles of scrolls, 6 to 14 characters each + * Also keep an array of scroll colors (always WHITE for now) + */ + +static char scroll_adj[MAX_TITLES][16]; + +static byte scroll_col[MAX_TITLES]; + + +static byte object_flavor(object_kind const *k_ptr) +{ + /* Analyze the item */ + switch (k_ptr->tval) + { + case TV_AMULET: + { + return (0x80 + amulet_col[k_ptr->sval]); + } + + case TV_RING: + { + return (0x90 + ring_col[k_ptr->sval]); + } + + case TV_STAFF: + { + return (0xA0 + staff_col[k_ptr->sval]); + } + + case TV_WAND: + { + return (0xB0 + wand_col[k_ptr->sval]); + } + + case TV_ROD: + { + return (0xC0 + rod_col[k_ptr->sval]); + } + + case TV_SCROLL: + { + return (0xD0 + scroll_col[k_ptr->sval]); + } + + case TV_POTION: + case TV_POTION2: + { + return (0xE0 + potion_col[k_ptr->sval]); + } + + case TV_FOOD: + { + if (k_ptr->sval < SV_FOOD_MIN_FOOD) + { + return (0xF0 + food_col[k_ptr->sval]); + } + + break; + } + } + + /* No flavor */ + return 0; +} + + +/* + * Certain items, if aware, are known instantly + * This function is used only by "flavor_init()" + * + * XXX XXX XXX Add "EASY_KNOW" flag to "k_info.txt" file + */ +static bool_ object_easy_know(object_kind const *k_ptr) +{ + /* Analyze the "tval" */ + switch (k_ptr->tval) + { + /* Spellbooks */ + case TV_DRUID_BOOK: + case TV_MUSIC_BOOK: + case TV_SYMBIOTIC_BOOK: + { + return (TRUE); + } + + /* Simple items */ + case TV_FLASK: + case TV_EGG: + case TV_BOTTLE: + case TV_SKELETON: + case TV_CORPSE: + case TV_HYPNOS: + case TV_SPIKE: + case TV_JUNK: + { + return (TRUE); + } + + /* All Food, Potions, Scrolls, Rods */ + case TV_FOOD: + case TV_POTION: + case TV_POTION2: + case TV_SCROLL: + case TV_ROD: + case TV_ROD_MAIN: + { + if (k_ptr->flags & TR_NORM_ART) + return ( FALSE ); + return (TRUE); + } + + /* Some Rings, Amulets, Lites */ + case TV_RING: + case TV_AMULET: + case TV_LITE: + { + if (k_ptr->flags & TR_EASY_KNOW) return (TRUE); + return (FALSE); + } + } + + /* Nope */ + return (FALSE); +} + + + +/** + * Shuffle flavor arrays into a random permutation + */ +template <std::size_t N> +static void shuffle_flavors(cptr adj[], byte col[]) +{ + // The classic Fisher-Yates shuffle + for (std::size_t i = N - 1; i > 0; i--) + { + int j = rand_int(i + 1); + std::swap(adj[i], adj[j]); + std::swap(col[i], col[j]); + } +} + + + +/* + * Prepare the "variable" part of the "k_info" array. + * + * The "color"/"metal"/"type" of an item is its "flavor". + * For the most part, flavors are assigned randomly each game. + * + * Initialize descriptions for the "colored" objects, including: + * Rings, Amulets, Staffs, Wands, Rods, Food, Potions, Scrolls. + * + * The first 4 entries for potions are fixed (Water, Apple Juice, + * Slime Mold Juice, Unused Potion). + * + * Scroll titles are always between 6 and 14 letters long. This is + * ensured because every title is composed of whole words, where every + * word is from 1 to 8 letters long (one or two syllables of 1 to 4 + * letters each), and that no scroll is finished until it attempts to + * grow beyond 15 letters. The first time this can happen is when the + * current title has 6 letters and the new word has 8 letters, which + * would result in a 6 letter scroll title. + * + * Duplicate titles are avoided by requiring that no two scrolls share + * the same first four letters (not the most efficient method, and not + * the least efficient method, but it will always work). + * + * Hack -- make sure everything stays the same for each saved game + * This is accomplished by the use of a saved "random seed", as in + * "town_gen()". Since no other functions are called while the special + * seed is in effect, so this function is pretty "safe". + * + * Note that the "hacked seed" may provide an RNG with alternating parity! + */ +void flavor_init() +{ + auto &k_info = game->edit_data.k_info; + + /* Hack -- Induce consistant flavors */ + set_quick_rng(seed_flavor()); + + /* Efficiency -- Rods/Wands share initial array */ + for (std::size_t i = 0; i < MAX_METALS; i++) + { + rod_adj[i] = wand_adj[i]; + rod_col[i] = wand_col[i]; + } + + + /* Object flavors */ + shuffle_flavors<MAX_ROCKS>(ring_adj, ring_col); + shuffle_flavors<MAX_AMULETS>(amulet_adj, amulet_col); + shuffle_flavors<MAX_WOODS>(staff_adj, staff_col); + shuffle_flavors<MAX_METALS>(wand_adj, wand_col); + shuffle_flavors<MAX_METALS>(rod_adj, rod_col); + shuffle_flavors<MAX_SHROOM>(food_adj, food_col); + shuffle_flavors<MAX_COLORS - 4>(potion_adj + 4, potion_col + 4); + + /* Scrolls (random titles, always white) */ + for (std::size_t i = 0; i < MAX_TITLES; i++) + { + /* Get a new title */ + while (TRUE) + { + std::string buf; + + /* Collect words until done */ + while (1) + { + /* Choose one or two syllables */ + int s = ((rand_int(100) < 30) ? 1 : 2); + + /* Add a one or two syllable word */ + std::string tmp; + for (int q = 0; q < s; q++) + { + tmp += syllables[rand_int(MAX_SYLLABLES)]; + } + + /* Stop before getting too long */ + if (buf.size() + tmp.size() + 1 > 15) + { + break; + } + + /* Add the word with separator */ + if (buf.size() > 0) + { + buf += " "; + } + buf += tmp; + } + + /* Save the title */ + strcpy(scroll_adj[i], buf.c_str()); + + /* Assume okay */ + bool_ okay = TRUE; + + /* Check for "duplicate" scroll titles */ + for (std::size_t j = 0; j < i; j++) + { + cptr hack1 = scroll_adj[j]; + cptr hack2 = scroll_adj[i]; + + /* Compare first four characters */ + if (*hack1++ != *hack2++) continue; + if (*hack1++ != *hack2++) continue; + if (*hack1++ != *hack2++) continue; + if (*hack1++ != *hack2++) continue; + + /* Not okay */ + okay = FALSE; + + /* Stop looking */ + break; + } + + /* Break when done */ + if (okay) break; + } + + /* All scrolls are white */ + scroll_col[i] = TERM_WHITE; + } + + /* Hack -- Use the "complex" RNG */ + set_complex_rng(); + + /* Analyze every object */ + for (auto &k_ref: k_info) + { + auto k_ptr = &k_ref; + + /* Skip "empty" objects */ + if (!k_ptr->name) continue; + + /* Extract "flavor" (if any) */ + k_ptr->flavor = object_flavor(k_ptr); + + /* No flavor yields aware */ + if ((!k_ptr->flavor) && (k_ptr->tval != TV_ROD_MAIN)) + { + k_ptr->aware = TRUE; + } + + /* Check for "easily known" */ + k_ptr->easy_know = object_easy_know(k_ptr); + } +} + +/* + * Reset the "visual" lists + * + * This involves resetting various things to their "default" state. + * + * If the "prefs" flag is TRUE, then we will also load the appropriate + * "user pref file" based on the current setting of the "use_graphics" + * flag. This is useful for switching "graphics" on/off. + * + * The features, objects, and monsters, should all be encoded in the + * relevant "font.pref". XXX XXX XXX + * + * The "prefs" parameter is no longer meaningful. XXX XXX XXX + */ +void reset_visuals() +{ + auto &st_info = game->edit_data.st_info; + auto &race_mod_info = game->edit_data.race_mod_info; + auto &re_info = game->edit_data.re_info; + auto &r_info = game->edit_data.r_info; + auto &f_info = game->edit_data.f_info; + auto &k_info = game->edit_data.k_info; + + /* Extract some info about terrain features */ + for (auto &f_ref: f_info) + { + f_ref.x_attr = f_ref.d_attr; + f_ref.x_char = f_ref.d_char; + } + + /* Extract default attr/char code for stores */ + for (auto &st_ref: st_info) + { + /* Default attr/char */ + st_ref.x_attr = st_ref.d_attr; + st_ref.x_char = st_ref.d_char; + } + + /* Extract default attr/char code for objects */ + for (auto &k_ref: k_info) + { + /* Default attr/char */ + k_ref.x_attr = k_ref.d_attr; + k_ref.x_char = k_ref.d_char; + } + + /* Extract default attr/char code for monsters */ + for (auto &r_ref: r_info) + { + /* Default attr/char */ + r_ref.x_attr = r_ref.d_attr; + r_ref.x_char = r_ref.d_char; + } + + /* Reset attr/char code for ego monster overlay graphics */ + for (auto &re_ref: re_info) + { + /* Default attr/char */ + re_ref.g_attr = 0; + re_ref.g_char = 0; + } + + /* Reset attr/char code for race modifier overlay graphics */ + for (auto &rmp: race_mod_info) + { + /* Default attr/char */ + rmp.g_attr = 0; + rmp.g_char = 0; + } + + /* Normal symbols */ + process_pref_file("font.prf"); +} + + +/* + * Extract "xtra" flags from object. + */ +static void object_flags_xtra(object_type const *o_ptr, object_flag_set *f) +{ + // Artifacts don't get *ego* extra powers. + if (!o_ptr->artifact_name.empty()) + { + return; + } + + // Add sustain or power. + switch (o_ptr->xtra1) + { + case EGO_XTRA_SUSTAIN: + { + /* Choose a sustain */ + switch (o_ptr->xtra2 % 6) + { + case 0: + (*f) |= TR_SUST_STR; + break; + case 1: + (*f) |= TR_SUST_INT; + break; + case 2: + (*f) |= TR_SUST_WIS; + break; + case 3: + (*f) |= TR_SUST_DEX; + break; + case 4: + (*f) |= TR_SUST_CON; + break; + case 5: + (*f) |= TR_SUST_CHR; + break; + } + + break; + } + + case EGO_XTRA_POWER: + { + /* Choose a power */ + switch (o_ptr->xtra2 % 11) + { + case 0: + (*f) |= TR_RES_BLIND; + break; + case 1: + (*f) |= TR_RES_CONF; + break; + case 2: + (*f) |= TR_RES_SOUND; + break; + case 3: + (*f) |= TR_RES_SHARDS; + break; + case 4: + (*f) |= TR_RES_NETHER; + break; + case 5: + (*f) |= TR_RES_NEXUS; + break; + case 6: + (*f) |= TR_RES_CHAOS; + break; + case 7: + (*f) |= TR_RES_DISEN; + break; + case 8: + (*f) |= TR_RES_POIS; + break; + case 9: + (*f) |= TR_RES_DARK; + break; + case 10: + (*f) |= TR_RES_LITE; + break; + } + + break; + } + + } +} + +/* + * Disregard sets when calculating flags? + */ +bool_ object_flags_no_set = FALSE; + +/* + * Obtain the "flags" for an item + */ +object_flag_set object_flags(object_type const *o_ptr) +{ + auto const &k_info = game->edit_data.k_info; + auto const &a_info = game->edit_data.a_info; + + auto k_ptr = &k_info[o_ptr->k_idx]; + + /* Base object */ + auto f = k_ptr->flags; + + /* Artifact */ + if (o_ptr->name1) + { + auto a_ptr = &a_info[o_ptr->name1]; + + f = a_ptr->flags; + + if ((!object_flags_no_set) && (a_ptr->set != -1)) + { + apply_flags_set(o_ptr->name1, a_ptr->set, &f); + } + } + + /* Mix in art_{flags,esp} */ + f |= o_ptr->art_flags; + + /* Extra powers */ + object_flags_xtra(o_ptr, &f); + + return f; +} + +/* Return object granted power */ +int object_power(object_type *o_ptr) +{ + auto const &k_info = game->edit_data.k_info; + auto const &a_info = game->edit_data.a_info; + auto const &e_info = game->edit_data.e_info; + + auto k_ptr = &k_info[o_ptr->k_idx]; + int power = -1; + + /* Base object */ + power = k_ptr->power; + + /* Ego-item */ + if (o_ptr->name2) + { + auto e_ptr = &e_info[o_ptr->name2]; + + if (power == -1) + { + power = e_ptr->power; + } + + if (o_ptr->name2b) + { + auto e_ptr = &e_info[o_ptr->name2b]; + + if (power == -1) + { + power = e_ptr->power; + } + } + } + + /* Artifact */ + if (o_ptr->name1) + { + auto a_ptr = &a_info[o_ptr->name1]; + + if (power == -1) + { + power = a_ptr->power; + } + } + + return (power); +} + + + +/* + * Obtain the "flags" for an item which are known to the player + */ +object_flag_set object_flags_known(object_type const *o_ptr) +{ + auto const &k_info = game->edit_data.k_info; + auto const &a_info = game->edit_data.a_info; + + auto k_ptr = &k_info[o_ptr->k_idx]; + + /* Must be identified */ + if (!object_known_p(o_ptr)) + { + return object_flag_set(); + } + + /* Base object */ + auto flags = k_ptr->flags; + + /* Obvious flags */ + flags |= k_ptr->oflags; + + /* Artifact */ + if (o_ptr->name1) + { + auto a_ptr = &a_info[o_ptr->name1]; + + /* Need full knowledge or spoilers */ + if ((o_ptr->ident & IDENT_MENTAL)) + { + flags = a_ptr->flags; + + if ((!object_flags_no_set) && (a_ptr->set != -1)) + { + apply_flags_set(o_ptr->name1, a_ptr->set, &flags); + } + } + else + { + flags = object_flag_set(); + } + + flags |= a_ptr->oflags; + } + + /* Random artifact or ego item! */ + if (o_ptr->art_flags) + { + /* Need full knowledge or spoilers */ + if ((o_ptr->ident & IDENT_MENTAL)) + { + flags |= o_ptr->art_flags; + } + + flags |= o_ptr->art_oflags; + } + + /* Full knowledge for *identified* objects */ + if (!(o_ptr->ident & IDENT_MENTAL)) + { + return flags; + } + + /* Extra powers */ + object_flags_xtra(o_ptr, &flags); + + /* Hack - Res Chaos -> Res Confusion */ + if (flags & TR_RES_CHAOS) + { + flags |= TR_RES_CONF; + } + + // Done + return flags; +} + + +/** + * Calculate amount of EXP needed for the given object to + * level, assuming it's a sentient object. + */ +s32b calc_object_need_exp(object_type const *o_ptr) +{ + return (player_exp[o_ptr->elevel - 1] * 5 / 2); +} + + +/** + * Calculate the PVAL mask. + */ +static object_flag_set compute_pval_mask() +{ + object_flag_set f; + for (auto const object_flag_meta: object_flags_meta()) + { + if (object_flag_meta->is_pval) + { + f |= object_flag_meta->flag_set; + } + } + return f; +} + + + +/* + * Creates a description of the item "o_ptr", and stores it in "out_val". + * + * One can choose the "verbosity" of the description, including whether + * or not the "number" of items should be described, and how much detail + * should be used when describing the item. + * + * The given "buf" must be 80 chars long to hold the longest possible + * description, which can get pretty long, including incriptions, such as: + * "no more Maces of Disruption (Defender) (+10,+10) [+5] (+3 to stealth)". + * Note that the inscription will be clipped to keep the total description + * under 79 chars (plus a terminator). + * + * Note the use of "object_desc_num()" and "object_desc_int()" as hyper-efficient, + * portable, versions of some common "sprintf()" commands. + * + * Note that all ego-items (when known) append an "Ego-Item Name", unless + * the item is also an artifact, which should NEVER happen. + * + * Note that all artifacts (when known) append an "Artifact Name", so we + * have special processing for "Specials" (artifact Lites, Rings, Amulets). + * The "Specials" never use "modifiers" if they are "known", since they + * have special "descriptions", such as "The Necklace of the Dwarves". + * + * Special Lite's use the "k_info" base-name (Phial, Star, or Arkenstone), + * plus the artifact name, just like any other artifact, if known. + * + * Special Ring's and Amulet's, if not "aware", use the same code as normal + * rings and amulets, and if "aware", use the "k_info" base-name (Ring or + * Amulet or Necklace). They will NEVER "append" the "k_info" name. But, + * they will append the artifact name, just like any artifact, if known. + * + * None of the Special Rings/Amulets are "EASY_KNOW", though they could be, + * at least, those which have no "pluses", such as the three artifact lites. + * + * Hack -- Display "The One Ring" as "a Plain Gold Ring" until aware. + * + * If "pref" then a "numeric" prefix will be pre-pended. + * + * Mode: + * 0 -- The Cloak of Death + * 1 -- The Cloak of Death [1,+3] + * 2 -- The Cloak of Death [1,+3] (+2 to Stealth) + * 3 -- The Cloak of Death [1,+3] (+2 to Stealth) {nifty} + */ +static std::string object_desc_aux(object_type const *o_ptr, int pref, int mode) +{ + auto const &r_info = game->edit_data.r_info; + auto const &k_info = game->edit_data.k_info; + auto const &a_info = game->edit_data.a_info; + auto const &e_info = game->edit_data.e_info; + auto const &random_artifacts = game->random_artifacts; + static auto const TR_PVAL_MASK = compute_pval_mask(); + + bool_ hack_name = FALSE; + + bool_ append_name = FALSE; + + bool_ show_weapon = FALSE; + bool_ show_armour = FALSE; + + auto k_ptr = &k_info[o_ptr->k_idx]; + + /* Extract some flags */ + auto const flags = object_flags(o_ptr); + + /* See if the object is "aware" */ + bool_ aware = object_aware_p(o_ptr); + + /* See if the object is "known" */ + bool_ known = object_known_p(o_ptr); + + /* Hack -- Extract the sub-type "indexx" */ + auto const indexx = o_ptr->sval; + + /* Extract default "base" string */ + std::string basenm(k_ptr->name); + + /* Assume no "modifier" string */ + std::string modstr; + + /* Analyze the object */ + switch (o_ptr->tval) + { + /* Some objects are easy to describe */ + case TV_SKELETON: + case TV_BOTTLE: + case TV_JUNK: + case TV_SPIKE: + case TV_FLASK: + case TV_CHEST: + case TV_INSTRUMENT: + case TV_TOOL: + case TV_DIGGING: + { + break; + } + + + /* Missiles/ Bows/ Weapons */ + case TV_SHOT: + case TV_BOLT: + case TV_ARROW: + case TV_BOOMERANG: + case TV_BOW: + case TV_HAFTED: + case TV_POLEARM: + case TV_MSTAFF: + case TV_SWORD: + case TV_AXE: + { + show_weapon = TRUE; + break; + } + + /* Armour */ + case TV_BOOTS: + case TV_GLOVES: + case TV_CROWN: + case TV_HELM: + case TV_SHIELD: + case TV_SOFT_ARMOR: + case TV_HARD_ARMOR: + case TV_DRAG_ARMOR: + { + show_armour = TRUE; + break; + } + + + /* Lites (including a few "Specials") */ + case TV_LITE: + { + break; + } + + /* Amulets (including a few "Specials") */ + case TV_AMULET: + { + /* Color the object */ + modstr = amulet_adj[indexx]; + if (aware) append_name = TRUE; + + if (aware || o_ptr->ident & IDENT_STOREB) + basenm = "& Amulet~"; + else + basenm = aware ? "& # Amulet~" : "& # Amulet~"; + + if (known && o_ptr->artifact_name.empty() && artifact_p(o_ptr)) + { + basenm = k_ptr->name; + } + + break; + } + + /* Rings (including a few "Specials") */ + case TV_RING: + { + /* Color the object */ + modstr = ring_adj[indexx]; + if (aware) append_name = TRUE; + + if (aware || o_ptr->ident & IDENT_STOREB) + basenm = "& Ring~"; + else + basenm = aware ? "& # Ring~" : "& # Ring~"; + + /* Hack -- The One Ring */ + if (!aware && (o_ptr->sval == SV_RING_POWER)) modstr = "Plain Gold"; + + if (known && o_ptr->artifact_name.empty() && artifact_p(o_ptr)) + { + basenm = k_ptr->name; + } + + break; + } + + case TV_STAFF: + { + /* Color the object */ + modstr = staff_adj[o_ptr->pval2 % MAX_WOODS]; + if (aware) append_name = TRUE; + if (aware || o_ptr->ident & IDENT_STOREB) + basenm = "& Staff~"; + else + basenm = "& # Staff~"; + break; + } + + case TV_WAND: + { + /* Color the object */ + modstr = wand_adj[o_ptr->pval2 % MAX_METALS]; + if (aware) append_name = TRUE; + if (aware || o_ptr->ident & IDENT_STOREB) + basenm = "& Wand~"; + else + basenm = "& # Wand~"; + break; + } + + case TV_ROD: + { + /* Color the object */ + modstr = rod_adj[indexx]; + if (aware) append_name = TRUE; + if (aware || o_ptr->ident & IDENT_STOREB) + basenm = "& Rod Tip~"; + else + basenm = aware ? "& # Rod Tip~" : "& # Rod Tip~"; + if (o_ptr->sval == SV_ROD_HOME) + { + basenm = "& Great Rod Tip~ of Home Summoning"; + hack_name = TRUE; + } + break; + } + + case TV_ROD_MAIN: + { + modstr = k_info[lookup_kind(TV_ROD, o_ptr->pval)].name; + break; + } + + case TV_SCROLL: + { + /* Color the object */ + modstr = scroll_adj[indexx]; + if (aware) append_name = TRUE; + if (aware || o_ptr->ident & IDENT_STOREB) + basenm = "& Scroll~"; + else + basenm = aware ? "& Scroll~ titled \"#\"" : "& Scroll~ titled \"#\""; + break; + } + + case TV_POTION: + case TV_POTION2: + { + /* Color the object */ + if ((o_ptr->tval != TV_POTION2) || (o_ptr->sval != SV_POTION2_MIMIC) || (!aware)) + { + modstr = potion_adj[indexx]; + if (aware) append_name = TRUE; + } + else + { + modstr = get_mimic_name(o_ptr->pval2); + } + if (aware || o_ptr->ident & IDENT_STOREB) + basenm = "& Potion~"; + else + basenm = aware ? "& # Potion~" : "& # Potion~"; + break; + } + + case TV_FOOD: + { + /* Ordinary food is "boring" */ + if (o_ptr->sval >= SV_FOOD_MIN_FOOD) break; + + /* Color the object */ + modstr = food_adj[indexx]; + if (aware) append_name = TRUE; + if (aware || o_ptr->ident & IDENT_STOREB) + basenm = "& Mushroom~"; + else + basenm = aware ? "& # Mushroom~" : "& # Mushroom~"; + break; + } + + + /* Cloak of Mimicry */ + case TV_CLOAK: + { + show_armour = TRUE; + if (o_ptr->sval == SV_MIMIC_CLOAK) + { + modstr = get_mimic_object_name(o_ptr->pval2); + } + break; + } + + + case TV_SYMBIOTIC_BOOK: + { + modstr = basenm; + basenm = "& Symbiotic Spellbook~ #"; + break; + } + + case TV_MUSIC_BOOK: + { + modstr = basenm; + basenm = "& Songbook~ #"; + break; + } + + /* Druid Books */ + case TV_DRUID_BOOK: + { + modstr = basenm; + basenm = "& Elemental Stone~ #"; + break; + } + + case TV_PARCHMENT: + { + modstr = basenm; + basenm = "& Parchment~ - #"; + break; + } + + + /* Hack -- Gold/Gems */ + case TV_GOLD: + { + return basenm; + } + + case TV_CORPSE: + { + auto r_ptr = &r_info[o_ptr->pval2]; + + modstr = basenm; + if (r_ptr->flags & RF_UNIQUE) + { + basenm = fmt::format("& {}'s #~", r_ptr->name); + } + else + { + basenm = fmt::format("& {} #~", r_ptr->name); + } + break; + } + + case TV_EGG: + { + auto r_ptr = &r_info[o_ptr->pval2]; + + modstr = basenm; + basenm = fmt::format("& {} #~", r_ptr->name); + break; + } + + case TV_HYPNOS: + { + /* We print hit points further down. --dsb */ + auto r_ptr = &r_info[o_ptr->pval]; + + modstr = basenm; + basenm = fmt::format("& {}~", r_ptr->name); + break; + } + + case TV_TOTEM: + { + monster_type monster; + monster.r_idx = o_ptr->pval; + monster.ego = o_ptr->pval2; + monster.ml = TRUE; + monster.status = MSTATUS_ENEMY; + + char name[80]; + monster_desc(name, &monster, 0x188); + + modstr = basenm; + basenm = fmt::format("& #~ of {}", name); + break; + } + + case TV_RANDART: + { + modstr = basenm; + + if (known) + { + basenm = random_artifacts[indexx].name_full; + } + else + { + basenm = random_artifacts[indexx].name_short; + } + break; + } + + case TV_DAEMON_BOOK: + case TV_BOOK: + { + basenm = k_ptr->name; + if (o_ptr->sval == 255) + { + modstr = spell_type_name(spell_at(o_ptr->pval)); + } + break; + } + + /* Used in the "inventory" routine */ + default: + return "(nothing)"; + } + + /* Mega Hack */ + if ((!hack_name) && known && (k_ptr->flags & TR_FULL_NAME)) + { + basenm = k_ptr->name; + } + + /* Copy of the base string _without_ a prefix */ + std::string s; + + /* Start dumping the result */ + std::string t; + + /* The object "expects" a "number" */ + if (starts_with(basenm, "&")) + { + cptr ego = NULL; + + /* Grab any ego-item name */ + if (known && (o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN)) + { + auto e_ptr = &e_info[o_ptr->name2]; + auto e2_ptr = &e_info[o_ptr->name2b]; + + if (e_ptr->before) + { + ego = e_ptr->name; + } + else if (e2_ptr->before) + { + ego = e2_ptr->name; + } + } + + /* Skip the ampersand (and space) */ + s = basenm.substr(2); + + /* No prefix */ + if (pref <= 0) + { + /* Nothing */ + } + + /* Hack -- None left */ + else if (o_ptr->number <= 0) + { + t += "no more "; + } + + /* Extract the number */ + else if (o_ptr->number > 1) + { + t += std::to_string(o_ptr->number); + t += ' '; + } + + else if ((o_ptr->tval == TV_CORPSE) && (r_info[o_ptr->pval2].flags & RF_UNIQUE)) + { + /* Nothing */ + } + + else if ((o_ptr->tval == TV_HYPNOS) && (r_info[o_ptr->pval].flags & RF_UNIQUE)) + { + /* Nothing */ + } + + /* Hack -- The only one of its kind */ + else if (known && artifact_p(o_ptr)) + { + t += "The "; + } + + else if (ego != NULL) + { + if (is_a_vowel(ego[0])) + { + t += "an "; + } + else + { + t += "a "; + } + } + + /* A single one, with a vowel in the modifier */ + else if ((s[0] == '#') && (is_a_vowel(modstr[0]))) + { + t += "an "; + } + + /* A single one, with a vowel */ + else if (is_a_vowel(s[0])) + { + t += "an "; + } + + /* A single one, without a vowel */ + else + { + t += "a "; + } + + /* Grab any ego-item name */ + if (known && (o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN)) + { + auto e_ptr = &e_info[o_ptr->name2]; + auto e2_ptr = &e_info[o_ptr->name2b]; + + if (e_ptr->before) + { + t += e_ptr->name; + t += ' '; + } + if (e2_ptr->before) + { + t += e2_ptr->name; + t += ' '; + } + } + + /* -TM- Hack -- Add false-artifact names */ + /* Dagger inscribed {@w0%Smelly} will be named + * Smelly Dagger {@w0} */ + + if (auto str = strchr(o_ptr->inscription.c_str(), '%')) + { + t += &str[1]; + t += ' '; + } + + } + + /* Hack -- objects that "never" take an article */ + else + { + /* No ampersand */ + s = basenm; + + /* No pref */ + if (!pref) + { + /* Nothing */ + } + + /* Hack -- all gone */ + else if (o_ptr->number <= 0) + { + t += "no more "; + } + + /* Prefix a number if required */ + else if (o_ptr->number > 1) + { + t += std::to_string(o_ptr->number); + t += ' '; + } + + else if (o_ptr->tval == TV_RANDART) + { + /* Do nothing, since randarts have their prefix already included */ + } + + /* Hack -- The only one of its kind */ + else if (known && artifact_p(o_ptr)) + { + t += "The "; + } + + /* Hack -- single items get no prefix */ + else + { + /* Nothing */ + } + + /* Grab any ego-item name */ + if (known && (o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN)) + { + auto e_ptr = &e_info[o_ptr->name2]; + auto e2_ptr = &e_info[o_ptr->name2b]; + + if (e_ptr->before) + { + t += e_ptr->name; + t += ' '; + } + if (e2_ptr->before) + { + t += e2_ptr->name; + t += ' '; + } + } + } + + /* Copy the string */ + for (auto const c: s) + { + /* Pluralizer */ + if (c == '~') + { + /* Add a plural if needed */ + if ((o_ptr->number != 1) && (pref >= 0)) + { + assert(t.size() > 0); + char k = t[t.size() - 1]; + + /* XXX XXX XXX Mega-Hack */ + + /* Hack -- "Cutlass-es" and "Torch-es" */ + if ((k == 's') || (k == 'h')) { + t += "e"; + } + + /* Add an 's' */ + t += "s"; + } + } + + /* Modifier */ + else if (c == '#') + { + /* Grab any ego-item name */ + if (o_ptr->tval == TV_ROD_MAIN) + { + t += ' '; + + if (known && o_ptr->name2) + { + auto e_ptr = &e_info[o_ptr->name2]; + t += e_ptr->name; + } + } + + /* Insert the modifier */ + t += modstr; + } + + /* Normal */ + else + { + /* Copy */ + t += c; + } + } + + /* Append the "kind name" to the "base name" */ + if ((append_name) && (!artifact_p(o_ptr))) + { + t += " of "; + + if (((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))) + { + t += spell_type_name(spell_at(o_ptr->pval2)); + if (mode >= 1) + { + s32b bonus = o_ptr->pval3 & 0xFFFF; + s32b max = o_ptr->pval3 >> 16; + t += fmt::format("[{:d}|{:d}]", bonus, max); + } + } + else + { + t += k_ptr->name; + } + } + + /* Hack -- Append "Artifact" or "Special" names */ + if (known) + { + + /* -TM- Hack -- Add false-artifact names */ + /* Dagger inscribed {@w0#of Smell} will be named + * Dagger of Smell {@w0} */ + if (auto str = strchr(o_ptr->inscription.c_str(), '#')) + { + t += ' '; + t += &str[1]; + } + + /* Is it a new random artifact ? */ + if (!o_ptr->artifact_name.empty()) + { + t += ' '; + t += o_ptr->artifact_name; + } + + + /* Grab any artifact name */ + else if (o_ptr->name1) + { + auto a_ptr = &a_info[o_ptr->name1]; + + /* Unique corpses don't require another name */ + if (o_ptr->tval != TV_CORPSE) + { + t += ' '; + t += a_ptr->name; + } + } + + /* Grab any ego-item name */ + else if ((o_ptr->name2 || o_ptr->name2b) && (o_ptr->tval != TV_ROD_MAIN)) + { + auto e_ptr = &e_info[o_ptr->name2]; + auto e2_ptr = &e_info[o_ptr->name2b]; + + if (o_ptr->name2 && !e_ptr->before) + { + t += ' '; + t += e_ptr->name; + } + if (o_ptr->name2b && !e2_ptr->before) + { + t += ' '; + t += e2_ptr->name; + } + } + } + + /* It contains a spell */ + if (known && (flags & TR_SPELL_CONTAIN) && (o_ptr->pval2 != -1)) + { + t += fmt::format(" [{}]", spell_type_name(spell_at(o_ptr->pval2))); + } + + /* Add symbiote hp here, after the "fake-artifact" name. --dsb */ + if (o_ptr->tval == TV_HYPNOS) + { + t += fmt::format(" ({:d} hp)", o_ptr->pval2); + } + + /* No more details wanted */ + if (mode < 1) + { + return t; + } + + /* Hack -- Some objects can have an exp level */ + if ((flags & TR_LEVELS) && known) + { + auto need_exp = (o_ptr->elevel < PY_MAX_LEVEL) + ? std::to_string(calc_object_need_exp(o_ptr) - o_ptr->exp) + : "*****"; + t += fmt::format(" (E:{}, L:{})", need_exp, o_ptr->elevel); + } + + /* Hack -- Chests must be described in detail */ + if (o_ptr->tval == TV_CHEST) + { + /* Not searched yet */ + if (!known) + { + /* Nothing */ + } + + /* May be "empty" */ + else if (!o_ptr->pval) + { + t += " (empty)"; + } + } + + + /* Display the item like a weapon */ + if (flags & TR_SHOW_MODS) show_weapon = TRUE; + + /* Display the item like a weapon */ + if (o_ptr->to_h && o_ptr->to_d) show_weapon = TRUE; + + /* Display the item like armour */ + if (o_ptr->ac) show_armour = TRUE; + + /* Dump base weapon info */ + switch (o_ptr->tval) + { + /* Missiles and Weapons */ + case TV_SHOT: + case TV_BOLT: + case TV_ARROW: + /* Exploding arrow? */ + if (o_ptr->pval2 != 0) + t += " (exploding)"; + /* No break, we want to continue the description */ + + case TV_BOOMERANG: + case TV_HAFTED: + case TV_POLEARM: + case TV_MSTAFF: + case TV_AXE: + case TV_SWORD: + case TV_DAEMON_BOOK: + if ((o_ptr->tval == TV_DAEMON_BOOK) && (o_ptr->sval != SV_DEMONBLADE)) + break; + + /* Append a "damage" string */ + t += fmt::format(" ({:d}d{:d})", o_ptr->dd, o_ptr->ds); + + /* All done */ + break; + + + /* Bows get a special "damage string" */ + case TV_BOW: + /* Mega-Hack -- Extract the "base power" */ + s32b power = (o_ptr->sval % 10); + + /* Apply the "Extra Might" flag */ + if (flags & TR_XTRA_MIGHT) power += o_ptr->pval; + + /* Append a special "damage" string */ + t += fmt::format(" (x{:d})", power); + + /* All done */ + break; + } + + + /* Add the weapon bonuses */ + if (known) + { + /* Show the tohit/todam on request */ + if (show_weapon) + { + t += fmt::format(" ({:+d},{:+d})", o_ptr->to_h, o_ptr->to_d); + } + + /* Show the tohit if needed */ + else if (o_ptr->to_h) + { + t += fmt::format(" ({:+d}", o_ptr->to_h); + if (!(flags & TR_HIDE_TYPE) || (!o_ptr->artifact_name.empty())) + { + t += " to accuracy"; + } + t += ')'; + } + + /* Show the todam if needed */ + else if (o_ptr->to_d) + { + t += fmt::format(" ({:+d}", o_ptr->to_d); + if (!(flags & TR_HIDE_TYPE) || (!o_ptr->artifact_name.empty())) + { + t += " to damage"; + } + t += ')'; + } + } + + + /* Add the armor bonuses */ + if (known) + { + /* Show the armor class info */ + if (show_armour) + { + t += fmt::format(" [{:d},{:+d}]", o_ptr->ac, o_ptr->to_a); + } + + /* No base armor, but does increase armor */ + else if (o_ptr->to_a) + { + t += fmt::format(" [{:+d}]", o_ptr->to_a); + } + } + + /* Hack -- always show base armor */ + else if (show_armour) + { + t += fmt::format(" [{:d}]", o_ptr->ac); + } + + if ((flags & TR_MANA) && known && (o_ptr->pval > 0)) + { + t += fmt::format("({:d}%)", 100 * o_ptr->pval / 5); + } + + if (known && (flags & TR_LIFE) ) /* Can disp neg now -- Improv */ + { + t += fmt::format("({:d}%)", 100 * o_ptr->pval / 5); + } + + /* No more details wanted */ + if (mode < 2) + { + return t; + } + + + /* Hack -- Wands and Staffs have charges */ + if (known && + ((o_ptr->tval == TV_STAFF) || + (o_ptr->tval == TV_WAND))) + { + auto plural = (o_ptr->pval != 1) ? "s" : ""; + t += fmt::format(" ({:d} charge{})", o_ptr->pval, plural); + } + + /* + * Hack -- Rods have a "charging" indicator. + */ + else if (known && (o_ptr->tval == TV_ROD_MAIN)) + { + t += fmt::format(" ({:d}/{:d})", o_ptr->timeout, o_ptr->pval2); + } + + /* + * Hack -- Rods have a "charging" indicator. + */ + else if (known && (o_ptr->tval == TV_ROD)) + { + t += fmt::format(" ({:d} Mana to cast)", o_ptr->pval); + } + + /* Hack -- Process Lanterns/Torches */ + else if ((o_ptr->tval == TV_LITE) && (flags & TR_FUEL_LITE)) + { + t += fmt::format(" (with {:d} turns of light)", o_ptr->timeout); + } + + + /* Dump "pval" flags for wearable items */ + if (known && (flags & TR_PVAL_MASK)) + { + /* Start the display */ + t += fmt::format(" ({:+d}", o_ptr->pval); + + /* Do not display the "pval" flags */ + if (flags & TR_HIDE_TYPE) + { + /* Nothing */ + } + + /* Speed */ + else if (flags & TR_SPEED) + { + t += " to speed"; + } + + /* Attack speed */ + else if (flags & TR_BLOWS) + { + t += " attack"; + if (ABS(o_ptr->pval) != 1) + { + t += 's'; + } + } + + /* Critical chance */ + else if (flags & TR_CRIT) + { + t += "% of critical hits"; + } + + /* Stealth */ + else if (flags & TR_STEALTH) + { + t += " to stealth"; + } + + /* Infravision */ + else if (flags & TR_INFRA) + { + t += " to infravision"; + } + + /* Tunneling */ + else if (flags & TR_TUNNEL) + { + /* Nothing */ + } + + /* Finish the display */ + t += ')'; + } + + + /* Indicate "charging" artifacts XXX XXX XXX */ + if (known && (flags & TR_ACTIVATE) && o_ptr->timeout) + { + if (o_ptr->tval == TV_EGG) + { + t += " (stopped)"; + } + else + { + t += " (charging)"; + } + } + + + /* No more details wanted */ + if (mode < 3) + { + return t; + } + + + /* Inscribe */ + { + std::vector<std::string> inscrip; + + /* Sensed stuff */ + if ((o_ptr->ident & (IDENT_SENSE)) && sense_desc[o_ptr->sense] && sense_desc[o_ptr->sense][0] != '\0') + { + inscrip.push_back(sense_desc[o_ptr->sense]); + } + + /* Hack - Note "cursed" if the item is 'known' and cursed */ + if (cursed_p(o_ptr) && known && inscrip.empty()) + { + inscrip.push_back("cursed"); + } + + /* Use the standard inscription if available; + Chop at '#' or '%' if present. The suffix of the + '%' or '#' is handled elsewhere in this function. + */ + if (auto const pos = o_ptr->inscription.find_first_of("%#") != std::string::npos) + { + inscrip.push_back(o_ptr->inscription.substr(0, pos)); + } + + /* Mega-Hack -- note empty wands/staffs */ + if (!known && (o_ptr->ident & (IDENT_EMPTY))) + { + inscrip.push_back("empty"); + } + + /* Note "tried" if the object has been tested unsuccessfully */ + if (!aware && object_tried_p(o_ptr)) + { + inscrip.push_back("tried"); + } + + /* Note the discount, if any */ + if ((o_ptr->discount) && o_ptr->inscription.empty()) + { + inscrip.push_back(fmt::format("{:d}% off", o_ptr->discount)); + } + + /* Append the user's inscription */ + if (!o_ptr->inscription.empty()) + { + inscrip.push_back(o_ptr->inscription); + } + + /* Append the inscription, if any */ + if (!inscrip.empty()) + { + auto inscrip_str = boost::algorithm::join(inscrip, ", "); + + /* Make sure we don't exceed 75 characters */ + t.resize(std::min<std::size_t>(t.size(), 75)); + + /* Append the inscription */ + t += fmt::format(" {{{}}}", inscrip_str); + } + } + + return t; +} + +void object_desc(char *buf, object_type const *o_ptr, int pref, int mode) +{ + auto s = object_desc_aux(o_ptr, pref, mode); + auto n = std::min<std::size_t>(s.size(), 79); + s.copy(buf, n); + buf[n] = '\0'; +} + +/* + * Hack -- describe an item currently in a store's inventory + * This allows an item to *look* like the player is "aware" of it + */ +void object_desc_store(char *buf, object_type *o_ptr, int pref, int mode) +{ + auto &k_info = game->edit_data.k_info; + + /* Save the "aware" flag */ + bool_ hack_aware = k_info[o_ptr->k_idx].aware; + + /* Save the "known" flag */ + bool_ hack_known = (o_ptr->ident & (IDENT_KNOWN)) ? TRUE : FALSE; + + + /* Set the "known" flag */ + o_ptr->ident |= (IDENT_KNOWN); + + /* Force "aware" for description */ + k_info[o_ptr->k_idx].aware = TRUE; + + + /* Describe the object */ + object_desc(buf, o_ptr, pref, mode); + + + /* Restore "aware" flag */ + k_info[o_ptr->k_idx].aware = hack_aware; + + /* Clear the known flag */ + if (!hack_known) o_ptr->ident &= ~(IDENT_KNOWN); +} + + + + +/* + * Determine the "Activation" (if any) for an artifact + * Return a string, or NULL for "no activation" + */ +cptr item_activation(object_type *o_ptr) +{ + auto const &a_info = game->edit_data.a_info; + auto const &e_info = game->edit_data.e_info; + + /* Extract the flags */ + auto const flags = object_flags(o_ptr); + + /* Require activation ability */ + if (!(flags & TR_ACTIVATE)) return (NULL); + + + /* + * We need to deduce somehow that it is a random artifact -- one + * problem: It could be a random artifact which has NOT YET received + * a name. Thus we eliminate other possibilities instead of checking + * for art_name + */ + + if (o_ptr->tval == TV_EGG) + { + return "stop or resume the egg development"; + } + + if (o_ptr->tval == TV_INSTRUMENT) + { + if (!((o_ptr->name1 && a_info[o_ptr->name1].activate) || + (o_ptr->name2 && e_info[o_ptr->name2].activate) || + (o_ptr->name2b && e_info[o_ptr->name2b].activate))) + { + if (o_ptr->sval == SV_HORN) + { + return "aggravate monster every 100 turns"; + } + } + } + + return activation_aux(o_ptr, FALSE, 0); +} + +/* Grab the tval desc */ +static bool_ grab_tval_desc(int tval) +{ + int tv = 0; + + while (tval_descs[tv].tval && (tval_descs[tv].tval != tval)) + { + tv++; + } + + if (!tval_descs[tv].tval) return FALSE; + + text_out_c(TERM_L_BLUE, tval_descs[tv].desc); + text_out("\n"); + + return TRUE; +} + +static void check_first(bool_ *first) +{ + if (*first) { + *first = FALSE; + } + else + { + text_out(", "); + } +} + +/* + * Display the damage done with a multiplier + */ +void output_dam(object_type *o_ptr, int mult, int mult2, cptr against, cptr against2, bool_ *first) +{ + int dam; + + dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5 * mult; + dam += (o_ptr->to_d + p_ptr->to_d + p_ptr->to_d_melee) * 10; + dam *= p_ptr->num_blow; + check_first(first); + if (dam > 0) + { + if (dam % 10) + text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10)); + else + text_out_c(TERM_L_GREEN, format("%d", dam / 10)); + } + else + text_out_c(TERM_L_RED, "0"); + text_out(format(" against %s", against)); + + if (mult2) + { + dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5 * mult2; + dam += (o_ptr->to_d + p_ptr->to_d + p_ptr->to_d_melee) * 10; + dam *= p_ptr->num_blow; + check_first(first); + if (dam > 0) + { + if (dam % 10) + text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10)); + else + text_out_c(TERM_L_GREEN, format("%d", dam / 10)); + } + else + text_out_c(TERM_L_RED, "0"); + text_out(format(" against %s", against2)); + } +} + +/* + * Outputs the damage we do/would do with the weapon + */ +void display_weapon_damage(object_type *o_ptr) +{ + object_type forge, *old_ptr = &forge; + bool_ first = TRUE; + bool_ full = o_ptr->ident & (IDENT_MENTAL); + + /* Extract the flags */ + auto const flags = object_flags(o_ptr); + + /* Ok now the hackish stuff, we replace the current weapon with this one */ + object_copy(old_ptr, &p_ptr->inventory[INVEN_WIELD]); + object_copy(&p_ptr->inventory[INVEN_WIELD], o_ptr); + calc_bonuses(TRUE); + + text_out("\nUsing it you would have "); + text_out_c(TERM_L_GREEN, format("%d ", p_ptr->num_blow)); + text_out(format("blow%s and do an average damage per turn of ", (p_ptr->num_blow) ? "s" : "")); + + if (full && (flags & TR_SLAY_ANIMAL)) output_dam(o_ptr, 2, 0, "animals", NULL, &first); + if (full && (flags & TR_SLAY_EVIL)) output_dam(o_ptr, 2, 0, "evil creatures", NULL, &first); + if (full && (flags & TR_SLAY_ORC)) output_dam(o_ptr, 3, 0, "orcs", NULL, &first); + if (full && (flags & TR_SLAY_TROLL)) output_dam(o_ptr, 3, 0, "trolls", NULL, &first); + if (full && (flags & TR_SLAY_GIANT)) output_dam(o_ptr, 3, 0, "giants", NULL, &first); + if (full && (flags & TR_KILL_DRAGON)) output_dam(o_ptr, 5, 0, "dragons", NULL, &first); + else if (full && (flags & TR_SLAY_DRAGON)) output_dam(o_ptr, 3, 0, "dragons", NULL, &first); + if (full && (flags & TR_KILL_UNDEAD)) output_dam(o_ptr, 5, 0, "undead", NULL, &first); + else if (full && (flags & TR_SLAY_UNDEAD)) output_dam(o_ptr, 3, 0, "undead", NULL, &first); + if (full && (flags & TR_KILL_DEMON)) output_dam(o_ptr, 5, 0, "demons", NULL, &first); + else if (full && (flags & TR_SLAY_DEMON)) output_dam(o_ptr, 3, 0, "demons", NULL, &first); + + if (full && (flags & TR_BRAND_FIRE)) output_dam(o_ptr, 3, 6, "non fire resistant creatures", "fire susceptible creatures", &first); + if (full && (flags & TR_BRAND_COLD)) output_dam(o_ptr, 3, 6, "non cold resistant creatures", "cold susceptible creatures", &first); + if (full && (flags & TR_BRAND_ELEC)) output_dam(o_ptr, 3, 6, "non lightning resistant creatures", "lightning susceptible creatures", &first); + if (full && (flags & TR_BRAND_ACID)) output_dam(o_ptr, 3, 6, "non acid resistant creatures", "acid susceptible creatures", &first); + if (full && (flags & TR_BRAND_POIS)) output_dam(o_ptr, 3, 6, "non poison resistant creatures", "poison susceptible creatures", &first); + + output_dam(o_ptr, 1, 0, (first) ? "all monsters" : "other monsters", NULL, &first); + + text_out("."); + + /* get our weapon back */ + object_copy(&p_ptr->inventory[INVEN_WIELD], old_ptr); + calc_bonuses(TRUE); +} + +/* + * Display the ammo damage done with a multiplier + */ +void output_ammo_dam(object_type *o_ptr, int mult, int mult2, cptr against, cptr against2, bool_ *first) +{ + int dam; + object_type *b_ptr = &p_ptr->inventory[INVEN_BOW]; + int is_boomerang = (o_ptr->tval == TV_BOOMERANG); + int tmul = get_shooter_mult(b_ptr) + p_ptr->xtra_might; + if (is_boomerang) tmul = p_ptr->throw_mult; + + dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5; + dam += o_ptr->to_d * 10; + if (!is_boomerang) dam += b_ptr->to_d * 10; + dam *= tmul; + if (!is_boomerang) dam += (p_ptr->to_d_ranged) * 10; + dam *= mult; + check_first(first); + if (dam > 0) + { + if (dam % 10) + text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10)); + else + text_out_c(TERM_L_GREEN, format("%d", dam / 10)); + } + else + text_out_c(TERM_L_RED, "0"); + text_out(format(" against %s", against)); + + if (mult2) + { + dam = (o_ptr->dd + (o_ptr->dd * o_ptr->ds)) * 5; + dam += o_ptr->to_d * 10; + if (!is_boomerang) dam += b_ptr->to_d * 10; + dam *= tmul; + if (!is_boomerang) dam += (p_ptr->to_d_ranged) * 10; + dam *= mult2; + check_first(first); + if (dam > 0) + { + if (dam % 10) + text_out_c(TERM_L_GREEN, format("%d.%d", dam / 10, dam % 10)); + else + text_out_c(TERM_L_GREEN, format("%d", dam / 10)); + } + else + text_out_c(TERM_L_RED, "0"); + text_out(format(" against %s", against2)); + } +} + +/* + * Outputs the damage we do/would do with the current bow and this ammo + */ +void display_ammo_damage(object_type *o_ptr) +{ + bool_ first = TRUE; + int i; + bool_ full = o_ptr->ident & (IDENT_MENTAL); + + /* Extract the flags */ + auto const flags = object_flags(o_ptr); + + if (o_ptr->tval == TV_BOOMERANG) + text_out("\nUsing it you would do an average damage per throw of "); + else + text_out("\nUsing it with your current shooter you would do an average damage per shot of "); + if (full && (flags & TR_SLAY_ANIMAL)) output_ammo_dam(o_ptr, 2, 0, "animals", NULL, &first); + if (full && (flags & TR_SLAY_EVIL)) output_ammo_dam(o_ptr, 2, 0, "evil creatures", NULL, &first); + if (full && (flags & TR_SLAY_ORC)) output_ammo_dam(o_ptr, 3, 0, "orcs", NULL, &first); + if (full && (flags & TR_SLAY_TROLL)) output_ammo_dam(o_ptr, 3, 0, "trolls", NULL, &first); + if (full && (flags & TR_SLAY_GIANT)) output_ammo_dam(o_ptr, 3, 0, "giants", NULL, &first); + if (full && (flags & TR_KILL_DRAGON)) output_ammo_dam(o_ptr, 5, 0, "dragons", NULL, &first); + else if (full && (flags & TR_SLAY_DRAGON)) output_ammo_dam(o_ptr, 3, 0, "dragons", NULL, &first); + if (full && (flags & TR_KILL_UNDEAD)) output_ammo_dam(o_ptr, 5, 0, "undeads", NULL, &first); + else if (full && (flags & TR_SLAY_UNDEAD)) output_ammo_dam(o_ptr, 3, 0, "undeads", NULL, &first); + if (full && (flags & TR_KILL_DEMON)) output_ammo_dam(o_ptr, 5, 0, "demons", NULL, &first); + else if (full && (flags & TR_SLAY_DEMON)) output_ammo_dam(o_ptr, 3, 0, "demons", NULL, &first); + + if (full && (flags & TR_BRAND_FIRE)) output_ammo_dam(o_ptr, 3, 6, "non fire resistant creatures", "fire susceptible creatures", &first); + if (full && (flags & TR_BRAND_COLD)) output_ammo_dam(o_ptr, 3, 6, "non cold resistant creatures", "cold susceptible creatures", &first); + if (full && (flags & TR_BRAND_ELEC)) output_ammo_dam(o_ptr, 3, 6, "non lightning resistant creatures", "lightning susceptible creatures", &first); + if (full && (flags & TR_BRAND_ACID)) output_ammo_dam(o_ptr, 3, 6, "non acid resistant creatures", "acid susceptible creatures", &first); + if (full && (flags & TR_BRAND_POIS)) output_ammo_dam(o_ptr, 3, 6, "non poison resistant creatures", "poison susceptible creatures", &first); + + output_ammo_dam(o_ptr, 1, 0, (first) ? "all monsters" : "other monsters", NULL, &first); + text_out(". "); + + if (o_ptr->pval2) + { + text_out("The explosion will be "); + i = 0; + while (gf_names[i].gf != -1) + { + if (gf_names[i].gf == o_ptr->pval2) + break; + i++; + } + text_out_c(TERM_L_GREEN, (gf_names[i].gf != -1) ? gf_names[i].name : "something weird"); + text_out("."); + } +} + +/* + * Describe a magic stick powers + */ +static void describe_device(object_type *o_ptr) +{ + char buf[128]; + + /* Wands/... of shcool spell */ + if (((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF)) && object_known_p(o_ptr)) + { + /* Enter device mode */ + set_stick_mode(o_ptr); + + // Spell reference + auto spell = spell_at(o_ptr->pval2); + + text_out("\nSpell description:\n"); + spell_type_description_foreach(spell, + [] (std::string const &text) -> void { + text_out("\n"); + text_out(text.c_str()); + }); + + text_out("\nSpell level: "); + sprintf(buf, FMTs32b, get_level(o_ptr->pval2, 50)); + text_out_c(TERM_L_BLUE, buf); + + text_out("\nMinimum Magic Device level to increase spell level: "); + text_out_c(TERM_L_BLUE, format("%d", spell_type_skill_level(spell))); + + text_out("\nSpell fail: "); + sprintf(buf, FMTs32b, spell_chance_device(spell)); + text_out_c(TERM_GREEN, buf); + + text_out("\nSpell info: "); + text_out_c(TERM_YELLOW, spell_type_info(spell)); + + /* Leave device mode */ + unset_stick_mode(); + + text_out("\n"); + } +} + + +/* + * Helper for object_out_desc + * + * Print the level something was found on + * + */ +static cptr object_out_desc_where_found(s16b level, s16b dungeon) +{ + auto const &d_info = game->edit_data.d_info; + auto const &wf_info = game->edit_data.wf_info; + + static char str[80]; + + if (dungeon == DUNGEON_WILDERNESS) + { + /* Taking care of older objects */ + if (level == 0) + { + sprintf(str, "in the wilderness or in a town"); + } + else if (wf_info[level].terrain_idx == TERRAIN_TOWN) + { + sprintf(str, "in the town of %s", wf_info[level].name); + } + else + { + sprintf(str, "in %s", wf_info[level].text); + } + } + else + { + sprintf(str, "on level %d of %s", level, d_info[dungeon].name.c_str()); + } + + return str; +} + +/* + * Describe an item + */ +bool_ object_out_desc(object_type *o_ptr, FILE *fff, bool_ trim_down, bool_ wait_for_it) +{ + auto const &set_info = game->edit_data.set_info; + auto const &st_info = game->edit_data.st_info; + auto const &k_info = game->edit_data.k_info; + auto const &a_info = game->edit_data.a_info; + + cptr vp[64]; + byte vc[64]; + int vn; + + object_flag_set flags; + + /* Extract the flags */ + if ((!(o_ptr->ident & (IDENT_MENTAL))) && (!fff)) + { + flags = o_ptr->art_oflags; + } + else + { + flags = object_flags(o_ptr); + } + + if (fff) + { + /* Set up stuff for text_out */ + text_out_file = fff; + text_out_hook = text_out_to_file; + } + else + { + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Set up stuff for text_out */ + text_out_hook = text_out_to_screen; + text_out("\n"); + } + + /* No need to dump that */ + if (!fff) + { + if (!trim_down) grab_tval_desc(o_ptr->tval); + } + + if (object_known_p(o_ptr)) + { + if (o_ptr->k_idx && (!trim_down)) + { + auto k_ptr = &k_info[o_ptr->k_idx]; + + text_out_c(TERM_ORANGE, k_ptr->text); + text_out("\n"); + } + + if (o_ptr->name1 && (!trim_down)) + { + auto a_ptr = &a_info[o_ptr->name1]; + + text_out_c(TERM_YELLOW, a_ptr->text); + text_out("\n"); + + if (a_ptr->set != -1) + { + text_out_c(TERM_GREEN, set_info[a_ptr->set].desc.c_str()); + text_out("\n"); + } + } + + if ((flags & TR_LEVELS) && (!trim_down)) + { + if (o_ptr->pval3 == 0) text_out("It is sentient"); + else if (count_bits(o_ptr->pval3) > 1) text_out("It is sentient and can have access to the realms of "); + else text_out("It is sentient and can have access to the realm of "); + + bool_ first = TRUE; + for (std::size_t j = 0; j < flags_groups().size(); j++) + { + if (BIT(j) & o_ptr->pval3) + { + check_first(&first); + text_out_c(flags_groups()[j].color, flags_groups()[j].name); + } + } + + text_out(". "); + } + + if (flags & TR_ULTIMATE) + { + if ((wield_slot(o_ptr) == INVEN_WIELD) || + (wield_slot(o_ptr) == INVEN_BOW)) + text_out_c(TERM_VIOLET, "It is part of the trinity of the ultimate weapons. "); + else + text_out_c(TERM_VIOLET, "It is the ultimate armor. "); + } + + if (flags & TR_COULD2H) text_out("It can be wielded two-handed. "); + if (flags & TR_MUST2H) text_out("It must be wielded two-handed. "); + + /* Mega-Hack -- describe activation */ + if (flags & TR_ACTIVATE) + { + text_out("It can be activated for "); + text_out(item_activation(o_ptr)); + + /* Mega-hack -- get rid of useless line for e.g. randarts */ + if (flags & TR_ACTIVATE_NO_WIELD) + text_out(". "); + else + text_out(" if it is being worn. "); + } + /* Granted power */ + if (object_power(o_ptr) != -1) + { + text_out("It grants you the power of "); + text_out(powers_type[object_power(o_ptr)].name); + text_out(" if it is being worn. "); + } + + /* Describe light sources */ + { + int radius = 0; + + if (flags & TR_LITE1) radius += 1; + if (flags & TR_LITE2) radius += 2; + if (flags & TR_LITE3) radius += 3; + if (radius > 5) radius = 5; + + if (radius > 0) + { + if (flags & TR_FUEL_LITE) + { + text_out(format("It provides light (radius %d) when fueled. ", radius)); + } + else + { + text_out(format("It provides light (radius %d). ", radius)); + } + } + } + + /* Mega Hack^3 -- describe the Anchor of Space-time */ + if (o_ptr->name1 == ART_ANCHOR) + { + text_out("It prevents the space-time continuum from being disrupted. "); + } + + if (flags & TR_ANTIMAGIC_50) + { + text_out("It generates an antimagic field. "); + } + + if (flags & TR_SPELL_CONTAIN) + { + if (o_ptr->pval2 == -1) + text_out("It can be used to store a spell. "); + else + text_out("It has a spell stored inside. "); + } + + /* Pick up stat bonuses */ + vn = 0; + if (flags & TR_STR) vp[vn++] = "strength"; + if (flags & TR_INT) vp[vn++] = "intelligence"; + if (flags & TR_WIS) vp[vn++] = "wisdom"; + if (flags & TR_DEX) vp[vn++] = "dexterity"; + if (flags & TR_CON) vp[vn++] = "constitution"; + if (flags & TR_CHR) vp[vn++] = "charisma"; + if (flags & TR_INFRA) vp[vn++] = "infravision"; + if (flags & TR_TUNNEL) vp[vn++] = "ability to tunnel"; + if (flags & TR_SPEED) vp[vn++] = "speed"; + if (flags & TR_BLOWS) vp[vn++] = "attack speed"; + if (flags & TR_CRIT) vp[vn++] = "ability to score critical hits"; + if (flags & TR_LUCK) vp[vn++] = "luck"; + if (flags & TR_SPELL) vp[vn++] = "spell power"; + + /* Describe */ + if (vn) + { + int i; + + /* Intro */ + text_out("It "); + + /* What it does */ + if (o_ptr->pval > 0) text_out("increases "); + else text_out("decreases "); + + /* List */ + for (i = 0; i < vn; i++) + { + /* Connectives */ + if (i == 0) text_out("your "); + else if (i < (vn - 1)) text_out(", "); + else text_out(" and "); + + /* Dump the stat */ + text_out(vp[i]); + } + + text_out(" by "); + if (o_ptr->pval > 0) + text_out_c(TERM_L_GREEN, format("%i", o_ptr->pval)); + else + text_out_c(TERM_L_RED, format("%i", -o_ptr->pval)); + text_out(". "); + } + + + vn = 0; + if (flags & TR_MANA) vp[vn++] = "mana capacity"; + if (flags & TR_LIFE) vp[vn++] = "hit points"; + + /* Describe with percentuals */ + if (vn) + { + int percent; + + /* What it does */ + if (o_ptr->pval > 0) + text_out("It increases"); + else + text_out("It decreases"); + + text_out(" your "); + text_out(vp[0]); + if (vn == 2) + { + text_out(" and "); + text_out(vp[1]); + } + + text_out(" by "); + percent = 100 * o_ptr->pval / 5; + + + if (o_ptr->pval > 0) + text_out_c(TERM_L_GREEN, format("%i%%", percent)); + else + text_out_c(TERM_L_RED, format("%i%%", -percent)); + text_out(". "); + } + + vn = 0; + if (flags & TR_BRAND_ACID) + { + vc[vn] = TERM_GREEN; + vp[vn++] = "acid"; + } + if (flags & TR_BRAND_ELEC) + { + vc[vn] = TERM_L_BLUE; + vp[vn++] = "electricity"; + } + if (flags & TR_BRAND_FIRE) + { + vc[vn] = TERM_RED; + vp[vn++] = "fire"; + } + if (flags & TR_BRAND_COLD) + { + vc[vn] = TERM_L_WHITE; + vp[vn++] = "frost"; + } + /* Describe */ + if (vn) + { + int i; + + /* Intro */ + text_out("It does extra damage "); + + /* List */ + for (i = 0; i < vn; i++) + { + /* Connectives */ + if (i == 0) text_out("from "); + else if (i < (vn - 1)) text_out(", "); + else text_out(" and "); + + /* Dump the stat */ + text_out_c(vc[i], vp[i]); + } + text_out(". "); + } + + + if (flags & TR_BRAND_POIS) + { + text_out("It "); + text_out_c(TERM_L_GREEN, "poisons your foes"); + text_out(". "); + } + + if (flags & TR_CHAOTIC) + { + text_out("It produces chaotic effects. "); + } + + if (flags & TR_VAMPIRIC) + { + text_out("It drains life from your foes. "); + } + + if (flags & TR_IMPACT) + { + text_out("It can cause earthquakes. "); + } + + if (flags & TR_VORPAL) + { + text_out("It is very sharp and can cut your foes. "); + } + + if (flags & TR_WOUNDING) + { + text_out("It is very sharp and can make your foes bleed. "); + } + + if (flags & TR_KILL_DRAGON) + { + text_out("It is a great bane of dragons. "); + } + else if (flags & TR_SLAY_DRAGON) + { + text_out("It is especially deadly against dragons. "); + } + + if (flags & TR_SLAY_ORC) + { + text_out("It is especially deadly against orcs. "); + } + + if (flags & TR_SLAY_TROLL) + { + text_out("It is especially deadly against trolls. "); + } + + if (flags & TR_SLAY_GIANT) + { + text_out("It is especially deadly against giants. "); + } + + if (flags & TR_KILL_DEMON) + { + text_out("It is a great bane of demons. "); + } + else if (flags & TR_SLAY_DEMON) + { + text_out("It strikes at demons with holy wrath. "); + } + + if (flags & TR_KILL_UNDEAD) + { + text_out("It is a great bane of undead. "); + } + else if (flags & TR_SLAY_UNDEAD) + { + text_out("It strikes at undead with holy wrath. "); + } + + if (flags & TR_SLAY_EVIL) + { + text_out("It fights against evil with holy fury. "); + } + + if (flags & TR_SLAY_ANIMAL) + { + text_out("It is especially deadly against natural creatures. "); + } + + if (flags & TR_INVIS) + { + text_out("It makes you invisible. "); + } + + vn = 0; + if (flags & TR_SUST_STR) + { + vp[vn++] = "strength"; + } + if (flags & TR_SUST_INT) + { + vp[vn++] = "intelligence"; + } + if (flags & TR_SUST_WIS) + { + vp[vn++] = "wisdom"; + } + if (flags & TR_SUST_DEX) + { + vp[vn++] = "dexterity"; + } + if (flags & TR_SUST_CON) + { + vp[vn++] = "constitution"; + } + if (flags & TR_SUST_CHR) + { + vp[vn++] = "charisma"; + } + /* Describe */ + if (vn) + { + int i; + + /* Intro */ + text_out("It sustains "); + + /* List */ + for (i = 0; i < vn; i++) + { + /* Connectives */ + if (i == 0) text_out("your "); + else if (i < (vn - 1)) text_out(", "); + else text_out(" and "); + + /* Dump the stat */ + text_out(vp[i]); + } + text_out(". "); + } + + vn = 0; + if (flags & TR_IM_ACID) + { + vc[vn] = TERM_GREEN; + vp[vn++] = "acid"; + } + if (flags & TR_IM_ELEC) + { + vc[vn] = TERM_L_BLUE; + vp[vn++] = "electricity"; + } + if (flags & TR_IM_FIRE) + { + vc[vn] = TERM_RED; + vp[vn++] = "fire"; + } + if (flags & TR_IM_COLD) + { + vc[vn] = TERM_L_WHITE; + vp[vn++] = "cold"; + } + if (flags & TR_IM_NETHER) + { + vc[vn] = TERM_L_GREEN; + vp[vn++] = "nether"; + } + /* Describe */ + if (vn) + { + int i; + + /* Intro */ + text_out("It provides immunity "); + + /* List */ + for (i = 0; i < vn; i++) + { + /* Connectives */ + if (i == 0) text_out("to "); + else if (i < (vn - 1)) text_out(", "); + else text_out(" and "); + + /* Dump the stat */ + text_out_c(vc[i], vp[i]); + } + text_out(". "); + } + + if (flags & TR_FREE_ACT) + { + text_out("It provides immunity to paralysis. "); + } + if (flags & TR_RES_FEAR) + { + text_out("It makes you completely fearless. "); + } + + vn = 0; + if (flags & TR_HOLD_LIFE) + { + vp[vn++] = "life draining"; + } + if ((flags & TR_RES_ACID) && !(flags & TR_IM_ACID)) + { + vp[vn++] = "acid"; + } + if ((flags & TR_RES_ELEC) && !(flags & TR_IM_ELEC)) + { + vp[vn++] = "electricity"; + } + if ((flags & TR_RES_FIRE) && !(flags & TR_IM_FIRE)) + { + vp[vn++] = "fire"; + } + if ((flags & TR_RES_COLD) && !(flags & TR_IM_COLD)) + { + vp[vn++] = "cold"; + } + if (flags & TR_RES_POIS) + { + vp[vn++] = "poison"; + } + if (flags & TR_RES_LITE) + { + vp[vn++] = "light"; + } + if (flags & TR_RES_DARK) + { + vp[vn++] = "dark"; + } + if (flags & TR_RES_BLIND) + { + vp[vn++] = "blindness"; + } + if (flags & TR_RES_CONF) + { + vp[vn++] = "confusion"; + } + if (flags & TR_RES_SOUND) + { + vp[vn++] = "sound"; + } + if (flags & TR_RES_SHARDS) + { + vp[vn++] = "shards"; + } + if ((flags & TR_RES_NETHER) && !(flags & TR_IM_NETHER)) + { + vp[vn++] = "nether"; + } + if (flags & TR_RES_NEXUS) + { + vp[vn++] = "nexus"; + } + if (flags & TR_RES_CHAOS) + { + vp[vn++] = "chaos"; + } + if (flags & TR_RES_DISEN) + { + vp[vn++] = "disenchantment"; + } + /* Describe */ + if (vn) + { + int i; + + /* Intro */ + text_out("It provides resistance "); + + /* List */ + for (i = 0; i < vn; i++) + { + /* Connectives */ + if (i == 0) text_out("to "); + else if (i < (vn - 1)) text_out(", "); + else text_out(" and "); + + /* Dump the stat */ + text_out(vp[i]); + } + text_out(". "); + } + + if (flags & TR_SENS_FIRE) + { + text_out("It renders you especially vulnerable to fire. "); + } + if (flags & TR_WRAITH) + { + text_out("It renders you incorporeal. "); + } + if (flags & TR_WATER_BREATH) + { + text_out("It allows you to breathe underwater. "); + } + if (flags & TR_MAGIC_BREATH) + { + text_out("It allows you to breathe without air. "); + } + if (flags & TR_FEATHER) + { + text_out("It allows you to levitate. "); + } + if (flags & TR_FLY) + { + text_out("It allows you to fly. "); + } + if (flags & TR_CLIMB) + { + text_out("It allows you to climb mountains. "); + } + if (flags & TR_IMMOVABLE) + { + text_out("It renders you immovable. "); + } + if (flags & TR_SEE_INVIS) + { + text_out("It allows you to see invisible monsters. "); + } + + // ESP_* + { + if (flags & ESP_ALL) + { + text_out("It gives telepathic powers. "); + } + else + { + vn = 0; + if (flags & ESP_ORC) vp[vn++] = "orcs"; + if (flags & ESP_TROLL) vp[vn++] = "trolls"; + if (flags & ESP_DRAGON) vp[vn++] = "dragons"; + if (flags & ESP_SPIDER) vp[vn++] = "spiders"; + if (flags & ESP_GIANT) vp[vn++] = "giants"; + if (flags & ESP_DEMON) vp[vn++] = "demons"; + if (flags & ESP_UNDEAD) vp[vn++] = "undead"; + if (flags & ESP_EVIL) vp[vn++] = "evil beings"; + if (flags & ESP_ANIMAL) vp[vn++] = "animals"; + if (flags & ESP_THUNDERLORD) vp[vn++] = "thunderlords"; + if (flags & ESP_GOOD) vp[vn++] = "good beings"; + if (flags & ESP_NONLIVING) vp[vn++] = "non-living things"; + if (flags & ESP_UNIQUE) vp[vn++] = "unique beings"; + /* Describe */ + if (vn) + { + int i; + + /* Intro */ + text_out("It allows you to sense the presence "); + + /* List */ + for (i = 0; i < vn; i++) + { + /* Connectives */ + if (i == 0) text_out("of "); + else if (i < (vn - 1)) text_out(", "); + else text_out(" and "); + + /* Dump the stat */ + text_out(vp[i]); + } + text_out(". "); + } + } + } + + if (flags & TR_SLOW_DIGEST) + { + text_out("It slows your metabolism. "); + } + if (flags & TR_REGEN) + { + text_out("It speeds your regenerative powers. "); + } + if (flags & TR_REFLECT) + { + text_out("It reflects bolts and arrows. "); + } + if (flags & TR_SH_FIRE) + { + text_out("It produces a fiery sheath. "); + } + if (flags & TR_SH_ELEC) + { + text_out("It produces an electric sheath. "); + } + if (flags & TR_NO_MAGIC) + { + text_out("It produces an anti-magic shell. "); + } + if (flags & TR_NO_TELE) + { + text_out("It prevents teleportation. "); + } + if (flags & TR_XTRA_MIGHT) + { + text_out("It fires missiles with extra might. "); + } + if (flags & TR_XTRA_SHOTS) + { + text_out("It fires missiles excessively fast. "); + } + + vn = 0; + if (flags & TR_DRAIN_MANA) + { + vc[vn] = TERM_BLUE; + vp[vn++] = "mana"; + } + if (flags & TR_DRAIN_HP) + { + vc[vn] = TERM_RED; + vp[vn++] = "life"; + } + if (flags & TR_DRAIN_EXP) + { + vc[vn] = TERM_L_DARK; + vp[vn++] = "experience"; + } + /* Describe */ + if (vn) + { + int i; + + /* Intro */ + text_out("It "); + + /* List */ + for (i = 0; i < vn; i++) + { + /* Connectives */ + if (i == 0) text_out("drains "); + else if (i < (vn - 1)) text_out(", "); + else text_out(" and "); + + /* Dump the stat */ + text_out_c(vc[i], vp[i]); + } + text_out(". "); + } + + if (flags & TR_BLESSED) + { + text_out("It has been blessed by the gods. "); + } + if (flags & TR_AUTO_ID) + { + text_out("It identifies all items for you. "); + } + + if (flags & TR_TELEPORT) + { + text_out("It induces random teleportation. "); + } + if (flags & TR_AGGRAVATE) + { + text_out("It aggravates nearby creatures. "); + } + if (flags & TR_NEVER_BLOW) + { + text_out("It can't attack. "); + } + if (flags & TR_BLACK_BREATH) + { + text_out("It fills you with the Black Breath. "); + } + if (cursed_p(o_ptr)) + { + if (flags & TR_PERMA_CURSE) + { + text_out("It is permanently cursed. "); + } + else if (flags & TR_HEAVY_CURSE) + { + text_out("It is heavily cursed. "); + } + else + { + text_out("It is cursed. "); + } + } + if (flags & TR_TY_CURSE) + { + text_out("It carries an ancient foul curse. "); + } + + if (flags & TR_DG_CURSE) + { + text_out("It carries an ancient Morgothian curse. "); + } + if (flags & TR_CLONE) + { + text_out("It can clone monsters. "); + } + if (flags & TR_CURSE_NO_DROP) + { + text_out("It cannot be dropped while cursed. "); + } + if (flags & TR_AUTO_CURSE) + { + text_out("It can re-curse itself. "); + } + + if (flags & TR_CAPACITY) + { + text_out("It can hold more mana. "); + } + if (flags & TR_CHEAPNESS) + { + text_out("It can cast spells for a lesser mana cost. "); + } + if (flags & TR_FAST_CAST) + { + text_out("It can cast spells faster. "); + } + if (flags & TR_CHARGING) + { + text_out("It regenerates its mana faster. "); + } + + if (flags & TR_RES_MORGUL) + { + text_out("It can resist being shattered by morgul beings. "); + } + if ((flags & TR_IGNORE_ACID) && (flags & TR_IGNORE_FIRE) && (flags & TR_IGNORE_COLD) && (flags & TR_IGNORE_ELEC)) + { + text_out("It cannot be harmed by acid, cold, lightning or fire. "); + } + else + { + if (flags & TR_IGNORE_ACID) + { + text_out("It cannot be harmed by acid. "); + } + if (flags & TR_IGNORE_ELEC) + { + text_out("It cannot be harmed by electricity. "); + } + if (flags & TR_IGNORE_FIRE) + { + text_out("It cannot be harmed by fire. "); + } + if (flags & TR_IGNORE_COLD) + { + text_out("It cannot be harmed by cold. "); + } + } + } + + + if (!trim_down && !fff) + { + describe_device(o_ptr); + + if (object_known_p(o_ptr)) + { + /* Damage display for weapons */ + if (wield_slot(o_ptr) == INVEN_WIELD) + display_weapon_damage(o_ptr); + + /* Breakage/Damage display for boomerangs */ + if (o_ptr->tval == TV_BOOMERANG) + { + if (artifact_p(o_ptr)) + text_out("\nIt can never be broken."); + else + text_out("\nIt has 1% chance to break upon hit."); + display_ammo_damage(o_ptr); + } + + /* Breakage/Damage display for ammo */ + if (wield_slot(o_ptr) == INVEN_AMMO) + { + if (artifact_p(o_ptr)) + { + text_out("\nIt can never be broken."); + } + /* Exclude exploding arrows */ + else if (o_ptr->pval2 == 0) + { + text_out("\nIt has "); + text_out_c(TERM_L_RED, format("%d", breakage_chance(o_ptr))); + text_out("% chance to break upon hit."); + } + display_ammo_damage(o_ptr); + } + + /* Monster recall for totems and corpses */ + if (o_ptr->tval == TV_TOTEM) + { + monster_description_out(o_ptr->pval, 0); + } + if (o_ptr->tval == TV_CORPSE) + { + monster_description_out(o_ptr->pval2, 0); + } + } + + if (!object_known_p(o_ptr)) + text_out("\nYou might need to identify the item to know some more about it..."); + else if (!(o_ptr->ident & (IDENT_MENTAL))) + text_out("\nYou might need to *identify* the item to know more about it..."); + } + + /* Copying how others seem to do it. -- neil */ + if (o_ptr->tval == TV_RING || o_ptr->tval == TV_AMULET || + !trim_down || ego_item_p(o_ptr) || artifact_p(o_ptr)) + { + /* Where did we found it ? */ + if (o_ptr->found == OBJ_FOUND_MONSTER) + { + char m_name[80]; + + monster_race_desc(m_name, o_ptr->found_aux1, o_ptr->found_aux2); + text_out(format("\nYou found it in the remains of %s %s.", + m_name, object_out_desc_where_found(o_ptr->found_aux4, o_ptr->found_aux3))); + } + else if (o_ptr->found == OBJ_FOUND_FLOOR) + { + text_out(format("\nYou found it lying on the ground %s.", + object_out_desc_where_found(o_ptr->found_aux2, o_ptr->found_aux1))); + } + else if (o_ptr->found == OBJ_FOUND_VAULT) + { + text_out(format("\nYou found it lying in a vault %s.", + object_out_desc_where_found(o_ptr->found_aux2, o_ptr->found_aux1))); + } + else if (o_ptr->found == OBJ_FOUND_SPECIAL) + { + text_out("\nYou found it lying on the floor of a special level."); + } + else if (o_ptr->found == OBJ_FOUND_RUBBLE) + { + text_out("\nYou found it while digging a rubble."); + } + else if (o_ptr->found == OBJ_FOUND_REWARD) + { + text_out("\nIt was given to you as a reward."); + } + else if (o_ptr->found == OBJ_FOUND_STORE) + { + text_out(format("\nYou bought it from the %s.", + st_info[o_ptr->found_aux1].name.c_str())); + } + else if (o_ptr->found == OBJ_FOUND_STOLEN) + { + text_out(format("\nYou stole it from the %s.", + st_info[o_ptr->found_aux1].name.c_str())); + } + else if (o_ptr->found == OBJ_FOUND_SELFMADE) + { + text_out("\nYou made it yourself."); + } + /* useful for debugging + else + { + text_out("\nYou ordered it from a catalog in the Town."); + }*/ + } + + if (fff) + { + /* Flush the line position. */ + text_out("\n"); + text_out_file = NULL; + } + else + { + if (wait_for_it) + { + /* Wait for it */ + inkey(); + + /* Restore the screen */ + Term_load(); + } + character_icky = FALSE; + + } + + /* Reset stuff for text_out */ + text_out_hook = text_out_to_screen; + + /* Gave knowledge */ + return (TRUE); +} + + + +/* + * Convert an inventory index into a one character label + * Note that the label does NOT distinguish inven/equip. + */ +char index_to_label(int i) +{ + /* Indexes for "inven" are easy */ + if (i < INVEN_WIELD) return (I2A(i)); + + /* Indexes for "equip" are offset */ + return (I2A(i - INVEN_WIELD)); +} + + +/* + * Convert a label into the index of an item in the "inven" + * Return "-1" if the label does not indicate a real item + */ +static s16b label_to_inven(int c) +{ + int i; + + /* Convert */ + i = (islower(c) ? A2I(c) : -1); + + /* Verify the index */ + if ((i < 0) || (i > INVEN_PACK)) return ( -1); + + /* Empty slots can never be chosen */ + if (!p_ptr->inventory[i].k_idx) return ( -1); + + /* Return the index */ + return (i); +} + + +/* + * Convert a label into the index of a item in the "equip" + * Return "-1" if the label does not indicate a real item + */ +static s16b label_to_equip(int c) +{ + int i; + + /* Convert */ + i = ((islower(c) || (c > 'z')) ? A2I(c) : -1) + INVEN_WIELD; + + /* Verify the index */ + if ((i < INVEN_WIELD) || (i >= INVEN_TOTAL)) return ( -1); + + /* Empty slots can never be chosen */ + if (!p_ptr->inventory[i].k_idx) return ( -1); + + /* Return the index */ + return (i); +} + +/* + * Returns the next free slot of the given "type", return the first + * if all are used + */ +static int get_slot(int slot) +{ + int i = 0; + + /* If there are at least one body part corretsonding, the find the free one */ + if (p_ptr->body_parts[slot - INVEN_WIELD] == slot) + { + /* Find a free body part */ + while ((i < 6) && (slot + i < INVEN_TOTAL) && (p_ptr->body_parts[slot - INVEN_WIELD + i] == slot)) + { + if (p_ptr->body_parts[slot + i - INVEN_WIELD]) + { + /* Free ? return the slot */ + if (!p_ptr->inventory[slot + i].k_idx) return (slot + i); + } + else break; + + i++; + } + /* Found nothing ? return the first one */ + return slot; + } + /* No body parts ? return -1 */ + else return ( -1); +} + +/* + * Determine which equipment slot (if any) an item likes, ignoring the player's + * current body and stuff if ideal == TRUE + */ +s16b wield_slot_ideal(object_type const *o_ptr, bool_ ideal) +{ + /* Slot for equipment */ + switch (o_ptr->tval) + { + case TV_DIGGING: + case TV_TOOL: + { + return ideal ? INVEN_TOOL : get_slot(INVEN_TOOL); + } + + case TV_HAFTED: + case TV_POLEARM: + case TV_MSTAFF: + case TV_SWORD: + case TV_AXE: + { + return ideal ? INVEN_WIELD : get_slot(INVEN_WIELD); + } + + case TV_BOOMERANG: + case TV_BOW: + case TV_INSTRUMENT: + { + return ideal ? INVEN_BOW : get_slot(INVEN_BOW); + } + + case TV_RING: + { + return ideal ? INVEN_RING : get_slot(INVEN_RING); + } + + case TV_AMULET: + { + return ideal ? INVEN_NECK : get_slot(INVEN_NECK); + } + + case TV_LITE: + { + return ideal ? INVEN_LITE : get_slot(INVEN_LITE); + } + + case TV_DRAG_ARMOR: + case TV_HARD_ARMOR: + case TV_SOFT_ARMOR: + { + return ideal ? INVEN_BODY : get_slot(INVEN_BODY); + } + + case TV_CLOAK: + { + return ideal ? INVEN_OUTER : get_slot(INVEN_OUTER); + } + + case TV_SHIELD: + { + return ideal ? INVEN_ARM : get_slot(INVEN_ARM); + } + + case TV_CROWN: + case TV_HELM: + { + return ideal ? INVEN_HEAD : get_slot(INVEN_HEAD); + } + + case TV_GLOVES: + { + return ideal ? INVEN_HANDS : get_slot(INVEN_HANDS); + } + + case TV_BOOTS: + { + return ideal ? INVEN_FEET : get_slot(INVEN_FEET); + } + + case TV_HYPNOS: + { + return ideal ? INVEN_CARRY : get_slot(INVEN_CARRY); + } + + case TV_SHOT: + { + if (ideal) + { + return INVEN_AMMO; + } + else if (p_ptr->inventory[INVEN_AMMO].k_idx && + object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]) && + p_ptr->inventory[INVEN_AMMO].number + o_ptr->number < MAX_STACK_SIZE) + { + return get_slot(INVEN_AMMO); + } + else if ((p_ptr->inventory[INVEN_BOW].k_idx) && (p_ptr->inventory[INVEN_BOW].tval == TV_BOW)) + { + if (p_ptr->inventory[INVEN_BOW].sval < 10) + return get_slot(INVEN_AMMO); + } + return -1; + } + + case TV_ARROW: + { + if (ideal) + { + return INVEN_AMMO; + } + else if (p_ptr->inventory[INVEN_AMMO].k_idx && + object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]) && + p_ptr->inventory[INVEN_AMMO].number + o_ptr->number < MAX_STACK_SIZE) + { + return get_slot(INVEN_AMMO); + } + else if ((p_ptr->inventory[INVEN_BOW].k_idx) && (p_ptr->inventory[INVEN_BOW].tval == TV_BOW)) + { + if ((p_ptr->inventory[INVEN_BOW].sval >= 10) && (p_ptr->inventory[INVEN_BOW].sval < 20)) + return get_slot(INVEN_AMMO); + } + return -1; + } + + case TV_BOLT: + { + if (ideal) + { + return INVEN_AMMO; + } + else if (p_ptr->inventory[INVEN_AMMO].k_idx && + object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO]) && + p_ptr->inventory[INVEN_AMMO].number + o_ptr->number < MAX_STACK_SIZE) + { + return get_slot(INVEN_AMMO); + } + else if ((p_ptr->inventory[INVEN_BOW].k_idx) && (p_ptr->inventory[INVEN_BOW].tval == TV_BOW)) + { + if (p_ptr->inventory[INVEN_BOW].sval >= 20) + return get_slot(INVEN_AMMO); + } + return -1; + } + + case TV_DAEMON_BOOK: + { + int slot = -1; + + switch (o_ptr->sval) + { + case SV_DEMONBLADE : slot = INVEN_WIELD; break; + case SV_DEMONSHIELD: slot = INVEN_ARM; break; + case SV_DEMONHORN : slot = INVEN_HEAD; break; + } + + if ((slot >= 0) && (!ideal)) + { + slot = get_slot(slot); + } + + return slot; + } + } + + /* No slot available */ + return ( -1); +} + +/* + * Determine which equipment slot (if any) an item likes for the player's + * current body and stuff + */ +s16b wield_slot(object_type const *o_ptr) +{ + return wield_slot_ideal(o_ptr, FALSE); +} + +/* + * Return a string mentioning how a given item is carried + */ +static cptr mention_use(int i) +{ + cptr p; + + /* Examine the location */ + switch (i) + { + case INVEN_WIELD: + case INVEN_WIELD + 1: + case INVEN_WIELD + 2: + p = "Wielding"; + break; + case INVEN_BOW: + p = "Shooting"; + break; + case INVEN_RING: + case INVEN_RING + 1: + case INVEN_RING + 2: + case INVEN_RING + 3: + case INVEN_RING + 4: + case INVEN_RING + 5: + p = "On finger"; + break; + case INVEN_NECK: + case INVEN_NECK + 1: + p = "Around neck"; + break; + case INVEN_LITE: + p = "Light source"; + break; + case INVEN_BODY: + p = "On body"; + break; + case INVEN_OUTER: + p = "About body"; + break; + case INVEN_ARM: + case INVEN_ARM + 1: + case INVEN_ARM + 2: + p = "On arm"; + break; + case INVEN_HEAD: + case INVEN_HEAD + 1: + p = "On head"; + break; + case INVEN_HANDS: + case INVEN_HANDS + 1: + case INVEN_HANDS + 2: + p = "On hands"; + break; + case INVEN_FEET: + case INVEN_FEET + 1: + p = "On feet"; + break; + case INVEN_CARRY: + p = "Symbiote"; + break; + case INVEN_AMMO: + p = "Quiver"; + break; + case INVEN_TOOL: + p = "Using"; + break; + default: + p = "In pack"; + break; + } + + /* Hack -- Heavy weapons */ + if ((INVEN_WIELD <= i) && (i <= INVEN_WIELD + 2)) + { + object_type *o_ptr; + o_ptr = &p_ptr->inventory[i]; + if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10) + { + p = "Just lifting"; + } + } + + /* Hack -- music instruments and heavy bow */ + if (i == INVEN_BOW) + { + object_type *o_ptr; + o_ptr = &p_ptr->inventory[i]; + if (o_ptr->tval == TV_INSTRUMENT) + { + p = "Playing"; + } + else if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10) + { + p = "Just holding"; + } + } + + /* Return the result */ + return (p); +} + + +/* + * Return a string describing how a given item is being worn. + * Currently, only used for items in the equipment, not inventory. + */ +cptr describe_use(int i) +{ + cptr p = nullptr; + + switch (i) + { + case INVEN_WIELD: + case INVEN_WIELD + 1: + case INVEN_WIELD + 2: + p = "attacking monsters with"; + break; + case INVEN_BOW: + p = "shooting missiles with"; + break; + case INVEN_RING: + case INVEN_RING + 1: + case INVEN_RING + 2: + case INVEN_RING + 3: + case INVEN_RING + 4: + case INVEN_RING + 5: + p = "wearing on your finger"; + break; + case INVEN_NECK: + case INVEN_NECK + 1: + p = "wearing around your neck"; + break; + case INVEN_LITE: + p = "using to light the way"; + break; + case INVEN_BODY: + p = "wearing on your body"; + break; + case INVEN_OUTER: + p = "wearing on your back"; + break; + case INVEN_ARM: + case INVEN_ARM + 1: + case INVEN_ARM + 2: + p = "wearing on your arm"; + break; + case INVEN_HEAD: + case INVEN_HEAD + 1: + p = "wearing on your head"; + break; + case INVEN_HANDS: + case INVEN_HANDS + 1: + case INVEN_HANDS + 2: + p = "wearing on your hands"; + break; + case INVEN_FEET: + case INVEN_FEET + 1: + p = "wearing on your feet"; + break; + case INVEN_CARRY: + p = "in symbiosis with"; + break; + case INVEN_AMMO: + p = "carrying in your quiver"; + break; + case INVEN_TOOL: + p = "using as a tool"; + break; + default: + p = "carrying in your pack"; + break; + } + + /* Hack -- Heavy weapons */ + if ((INVEN_WIELD <= i) && (i <= INVEN_WIELD + 2)) + { + object_type *o_ptr; + o_ptr = &p_ptr->inventory[i]; + if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10) + { + p = "just lifting"; + } + } + + /* Hack -- Music instruments and heavy bow */ + if (i == INVEN_BOW) + { + object_type *o_ptr; + o_ptr = &p_ptr->inventory[i]; + if (o_ptr->tval == TV_INSTRUMENT) + { + p = "playing music with"; + } + else if (adj_str_hold[p_ptr->stat_ind[A_STR]] < o_ptr->weight / 10) + { + p = "just holding"; + } + } + + /* Return the result */ + return p; +} + +/* + * Check an item against the item tester info + */ +static bool item_tester_okay(object_type const *o_ptr, object_filter_t const &filter) +{ + /* Hack -- allow listing empty slots */ + if (item_tester_full) + { + return true; + } + + /* Require an item */ + if (!o_ptr->k_idx) + { + return false; + } + + /* Hack -- ignore "gold" */ + if (o_ptr->tval == TV_GOLD) + { + return false; + } + + /* Check against the filter */ + return filter(o_ptr); +} + + + + +static void show_equip_aux(bool_ mirror, object_filter_t const &filter); +static void show_inven_aux(bool_ mirror, object_filter_t const &filter); + +/* + * Choice window "shadow" of the "show_inven()" function + */ +void display_inven() +{ + show_inven_aux(TRUE, object_filter::True()); +} + + + +/* + * Choice window "shadow" of the "show_equip()" function + */ +void display_equip() +{ + show_equip_aux(TRUE, object_filter::True()); +} + + + +/* Get the color of the letter idx */ +byte get_item_letter_color(object_type const *o_ptr) +{ + auto const &a_info = game->edit_data.a_info; + + byte color = TERM_WHITE; + + /* Must have knowlegde */ + if (!object_known_p(o_ptr)) return (TERM_SLATE); + + if (ego_item_p(o_ptr)) color = TERM_L_BLUE; + if (artifact_p(o_ptr)) color = TERM_YELLOW; + if (o_ptr->name1 && ( -1 != a_info[o_ptr->name1].set)) color = TERM_GREEN; + if (o_ptr->name1 && (a_info[o_ptr->name1].flags & TR_ULTIMATE) && (o_ptr->ident & (IDENT_MENTAL))) color = TERM_VIOLET; + + return (color); +} + + +/* + * Display the inventory. + * + * Hack -- do not display "trailing" empty slots + */ +void show_inven_aux(bool_ mirror, const object_filter_t &filter) +{ + int i, j, k, l, z = 0; + int row, col, len, lim; + int wid, hgt; + object_type *o_ptr; + char o_name[80]; + char tmp_val[80]; + int out_index[23]; + byte out_color[23]; + char out_desc[23][80]; + + + /* Retrive current screen size */ + Term_get_size(&wid, &hgt); + + /* Starting row */ + row = mirror ? 0 : 1; + + /* Starting column */ + col = mirror ? 0 : 50; + + /* Default "max-length" */ + len = 79 - col; + + /* Maximum space allowed for descriptions */ + lim = 79 - 3; + + /* Space for weight */ + lim -= 9; + + /* Space for icon */ + lim -= 2; + + /* Find the "final" slot */ + for (i = 0; i < INVEN_PACK; i++) + { + o_ptr = &p_ptr->inventory[i]; + + /* Skip non-objects */ + if (!o_ptr->k_idx) continue; + + /* Track */ + z = i + 1; + } + + /* Display the inventory */ + for (k = 0, i = 0; i < z; i++) + { + o_ptr = &p_ptr->inventory[i]; + + /* Is this item acceptable? */ + if (!item_tester_okay(o_ptr, filter)) + { + /* Skip to next slot */ + continue; + } + + /* Save the object index */ + out_index[k] = i + 1; + + /* Describe the object */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Hack -- enforce max length */ + o_name[lim] = '\0'; + + /* Save the object color, and description */ + out_color[k] = tval_to_attr[o_ptr->tval % 128]; + strcpy(out_desc[k], o_name); + + /* Find the predicted "line length" */ + l = strlen(out_desc[k]) + 5; + + /* Be sure to account for the weight */ + l += 9; + + /* Account for icon */ + l += 2; + + /* Maintain the maximum length */ + if (l > len) len = l; + + /* Advance to next "line" */ + k++; + } + + /* Find the column to start in */ + if (mirror) col = 0; + else col = (len > wid - 4) ? 0 : (wid - len - 1); + + /* Output each entry */ + for (j = 0; j < k; j++) + { + /* Get the index */ + i = out_index[j]; + + /* Get the item */ + o_ptr = &p_ptr->inventory[ABS(i) - 1]; + + /* Clear the line */ + prt("", row + j, col ? col - 2 : col); + + /* Prepare an index --(-- */ + /* Prepare a blank index if not selectable */ + if ( i > 0 ) + sprintf(tmp_val, "%c)", index_to_label(i - 1)); + else + sprintf(tmp_val, " "); + + /* Clear the line with the (possibly indented) index */ + c_put_str(get_item_letter_color(o_ptr), tmp_val, row + j, col); + + /* Display graphics for object */ + { + byte a = object_attr(o_ptr); + char c = object_char(o_ptr); + + if (!o_ptr->k_idx) c = ' '; + + Term_draw(col + 3, row + j, a, c); + } + + /* Display the entry itself */ + c_put_str(out_color[j], out_desc[j], row + j, + col + 5); + + /* Display the weight */ + { + int wgt = o_ptr->weight * o_ptr->number; + sprintf(tmp_val, "%3d.%1d lb", wgt / 10, wgt % 10); + put_str(tmp_val, row + j, wid - 9); + } + } + + /* Shadow windows */ + if (mirror) + { + int hgt; + Term_get_size(nullptr, &hgt); + /* Erase the rest of the window */ + for (j = row + k; j < hgt; j++) + { + /* Erase the line */ + Term_erase(0, j, 255); + } + } + + /* Main window */ + else + { + /* Make a "shadow" below the list (only if needed) */ + if (j && (j < 23)) prt("", row + j, col ? col - 2 : col); + } +} + + +static void show_inven(object_filter_t const &filter) +{ + show_inven_aux(FALSE, filter); +} + +void show_inven_full() +{ + item_tester_full = true; + show_inven(object_filter::True()); + item_tester_full = false; +} + +static void show_equip(object_filter_t const &filter) +{ + show_equip_aux(FALSE, filter); +} + +void show_equip_full() +{ + item_tester_full = true; + show_equip(object_filter::True()); + item_tester_full = false; +} + +/* + * Display the equipment. + */ +void show_equip_aux(bool_ mirror, object_filter_t const &filter) +{ + int i, j, k, l; + int row, col, len, lim, idx; + int wid, hgt; + object_type *o_ptr; + char tmp_val[80]; + char o_name[80]; + int out_index[INVEN_TOOL - INVEN_WIELD]; + int out_rindex[INVEN_TOOL - INVEN_WIELD]; + byte out_color[INVEN_TOOL - INVEN_WIELD]; + char out_desc[INVEN_TOOL - INVEN_WIELD][80]; + + + /* Retrive current screen size */ + Term_get_size(&wid, &hgt); + + /* Starting row */ + row = mirror ? 0 : 1; + + /* Starting column */ + col = mirror ? 0 : 50; + + /* Maximal length */ + len = 79 - col; + + /* Maximum space allowed for descriptions */ + lim = 79 - 3; + + /* Require space for labels */ + lim -= (14 + 2); + + /* Require space for weight */ + lim -= 9; + + /* Require space for icon */ + lim -= 2; + + /* Scan the equipment list */ + idx = 0; + for (k = 0, i = INVEN_WIELD; i < INVEN_TOTAL; i++) + { + /* Is there actualy a body part here ? */ + if (!p_ptr->body_parts[i - INVEN_WIELD]) continue; + + /* Mega Hack -- don't show symbiote slot if we can't use it */ + if ((i == INVEN_CARRY) && (!get_skill(SKILL_SYMBIOTIC))) continue; + + o_ptr = &p_ptr->inventory[i]; + + /* Inform the player that he/she can't use a shield */ + if ((p_ptr->body_parts[i - INVEN_WIELD] == INVEN_ARM) && + !o_ptr->k_idx && + p_ptr->inventory[i - INVEN_ARM + INVEN_WIELD].k_idx) + { + object_type *q_ptr = &p_ptr->inventory[i - INVEN_ARM + INVEN_WIELD]; + char q_name[80]; + + /* Description */ + object_desc(q_name, q_ptr, TRUE, 3); + + /* Get weapon flags */ + auto const flags = object_flags(q_ptr); + + if (flags & TR_MUST2H) + { + sprintf(o_name, "(two handed) %s", q_name); + + /* Truncate the description */ + o_name[lim] = 0; + + /* Save the index */ + out_index[k] = idx; + out_rindex[k] = i; + idx++; + + /* Save the color */ + out_color[k] = TERM_L_RED; + strcpy(out_desc[k], o_name); + continue; + } + } + + if ((p_ptr->body_parts[i - INVEN_WIELD] == INVEN_WIELD) && + !o_ptr->k_idx) + { + sprintf(o_name, "(%s)", get_melee_name()); + + /* Truncate the description */ + o_name[lim] = 0; + + /* Save the index */ + out_index[k] = idx; + out_rindex[k] = i; + idx++; + + /* Save the color */ + out_color[k] = TERM_L_BLUE; + strcpy(out_desc[k], o_name); + } + else + { + idx++; + + /* Description */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Truncate the description */ + o_name[lim] = 0; + + /* Is this item acceptable? */ + if (!item_tester_okay(o_ptr, filter)) + { + /* Skip to next slot */ + continue; + } + + /* Save the index */ + out_index[k] = idx; + out_rindex[k] = i; + + /* Save the color */ + out_color[k] = tval_to_attr[o_ptr->tval % 128]; + strcpy(out_desc[k], o_name); + } + + /* Extract the maximal length (see below) */ + l = strlen(out_desc[k]) + (2 + 3); + + /* Increase length for labels (if needed) */ + l += (14 + 2); + + /* Increase length for weight */ + l += 9; + + /* Icon */ + l += 2; + + /* Maintain the max-length */ + if (l > len) len = l; + + /* Advance the entry */ + k++; + } + + /* Hack -- Find a column to start in */ + if (mirror) col = 0; + else col = (len > wid - 4) ? 0 : (wid - len - 1); + + /* Output each entry */ + for (j = 0; j < k; j++) + { + if (j > 20) break; + + /* Get the index */ + i = out_index[j]; + + /* Get the item */ + o_ptr = &p_ptr->inventory[out_rindex[j]]; + + /* Clear the line */ + prt("", row + j, col ? col - 2 : col); + + /* Prepare an index --(-- */ + if (out_index[j] >= 0 ) + sprintf(tmp_val, "%c)", index_to_label(out_rindex[j])); + else + sprintf(tmp_val, " "); + + /* Clear the line with the (possibly indented) index */ + c_put_str(get_item_letter_color(o_ptr), tmp_val, row + j, col); + + /* Show icon */ + { + byte a = object_attr(o_ptr); + char c = object_char(o_ptr); + + if (!o_ptr->k_idx) c = ' '; + + Term_draw(col + 3, row + j, a, c); + } + + /* Use labels */ + { + /* Mention the use */ + sprintf(tmp_val, "%-14s: ", mention_use(out_rindex[j])); + put_str(tmp_val, row + j, col + 5); + + /* Display the entry itself */ + c_put_str(out_color[j], out_desc[j], row + j, col + 21); + } + + /* Display the weight */ + { + int wgt = o_ptr->weight * o_ptr->number; + sprintf(tmp_val, "%3d.%d lb", wgt / 10, wgt % 10); + put_str(tmp_val, row + j, wid - 9); + } + } + + + /* Shadow windows */ + if (mirror) + { + int hgt; + Term_get_size(nullptr, &hgt); + /* Erase the rest of the window */ + for (j = row + k; j < hgt; j++) + { + /* Erase the line */ + Term_erase(0, j, 255); + } + } + + /* Main window */ + else + { + /* Make a "shadow" below the list (only if needed) */ + if (j && (j < 23)) prt("", row + j, col ? col - 2 : col); + } +} + + + + +/* + * Flip "inven" and "equip" in any sub-windows + */ +void toggle_inven_equip() +{ + int j; + + /* Scan windows */ + for (j = 0; j < ANGBAND_TERM_MAX; j++) + { + /* Unused */ + if (!angband_term[j]) continue; + + /* Flip inven to equip */ + if (window_flag[j] & (PW_INVEN)) + { + /* Flip flags */ + window_flag[j] &= ~(PW_INVEN); + window_flag[j] |= (PW_EQUIP); + + /* Window stuff */ + p_ptr->window |= (PW_EQUIP); + } + + /* Flip inven to equip */ + else if (window_flag[j] & (PW_EQUIP)) + { + /* Flip flags */ + window_flag[j] &= ~(PW_EQUIP); + window_flag[j] |= (PW_INVEN); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN); + } + } +} + + + +/* + * Verify the choice of an item. + * + * The item can be negative to mean "item on floor". + */ +bool_ verify(cptr prompt, int item) +{ + char o_name[80]; + + char out_val[160]; + + object_type *o_ptr; + + /* Get object */ + o_ptr = get_object(item); + + /* Describe */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Prompt */ + sprintf(out_val, "%s %s? ", prompt, o_name); + + /* Query */ + return (get_check(out_val)); +} + + +/* + * Hack -- allow user to "prevent" certain choices + * + * The item can be negative to mean "item on floor". + */ +static bool_ get_item_allow(int item) +{ + /* Get object */ + auto o_ptr = get_object(item); + + /* No inscription */ + if (o_ptr->inscription.empty()) + { + return TRUE; + } + + /* Find a '!' */ + auto s = strchr(o_ptr->inscription.c_str(), '!'); + + /* Process preventions */ + while (s) + { + /* Check the "restriction" */ + if ((s[1] == command_cmd) || (s[1] == '*')) + { + /* Verify the choice */ + if (!verify("Really try", item)) return (FALSE); + } + + /* Find another '!' */ + s = strchr(s + 1, '!'); + } + + /* Allow it */ + return (TRUE); +} + + + +/* + * Auxiliary function for "get_item()" -- test an index + */ +static bool get_item_okay(int i, object_filter_t const &filter) +{ + /* Illegal items */ + if ((i < 0) || (i >= INVEN_TOTAL)) + { + return (FALSE); + } + + /* Verify the item */ + return item_tester_okay(&p_ptr->inventory[i], filter); +} + + + +/* + * Find the "first" inventory object with the given "tag". + * + * A "tag" is a char "n" appearing as "@n" anywhere in the + * inscription of an object. + * + * Also, the tag "@xn" will work as well, where "n" is a tag-char, + * and "x" is the "current" command_cmd code. + */ +static int get_tag(int *cp, char tag) +{ + /* Check every object */ + for (int i = 0; i < INVEN_TOTAL; ++i) + { + object_type *o_ptr = &p_ptr->inventory[i]; + + /* Skip non-objects */ + if (!o_ptr->k_idx) + { + continue; + } + + /* Skip empty inscriptions */ + if (o_ptr->inscription.empty()) + { + continue; + } + + /* Find a '@' */ + auto s = strchr(o_ptr->inscription.c_str(), '@'); + + /* Process all tags */ + while (s) + { + /* Check the normal tags */ + if (s[1] == tag) + { + /* Save the actual inventory ID */ + *cp = i; + + /* Success */ + return (TRUE); + } + + /* Check the special tags */ + if ((s[1] == command_cmd) && (s[2] == tag)) + { + /* Save the actual inventory ID */ + *cp = i; + + /* Success */ + return (TRUE); + } + + /* Find another '@' */ + s = strchr(s + 1, '@'); + } + } + + /* No such tag */ + return (FALSE); +} + +/* + * scan_floor -- + * + * Return a list of o_list[] indexes of items at the given cave + * location. + */ +static std::vector<int> scan_floor(int y, int x, object_filter_t const &filter) +{ + std::vector<int> items; + + /* Sanity */ + if (!in_bounds(y, x)) + { + return items; + } + + /* Scan all objects in the grid */ + for (auto const this_o_idx: cave[y][x].o_idxs) + { + /* Acquire object */ + object_type * o_ptr = &o_list[this_o_idx]; + + /* Item tester */ + if (!item_tester_okay(o_ptr, filter)) + { + continue; + } + + /* Accept this item */ + items.push_back(this_o_idx); + + /* XXX Hack -- Enforce limit */ + if (items.size() == 23) break; + } + + /* Result */ + return items; +} + +/* + * Display a list of the items on the floor at the given location. + */ +static void show_floor(int y, int x, object_filter_t const &filter) +{ + int i, j, k, l; + int col, len, lim; + + object_type *o_ptr; + + char o_name[80]; + + char tmp_val[80]; + + int out_index[23]; + byte out_color[23]; + char out_desc[23][80]; + + /* Default length */ + len = 79 - 50; + + /* Maximum space allowed for descriptions */ + lim = 79 - 3; + + /* Require space for weight */ + lim -= 9; + + /* Scan for objects in the grid, using item_tester_okay() */ + auto const floor_list = scan_floor(y, x, filter); + assert(floor_list.size() <= 23); + int const floor_num = floor_list.size(); // "int" for warning avoidance + + /* Display the inventory */ + for (k = 0, i = 0; i < floor_num; i++) + { + o_ptr = &o_list[floor_list[i]]; + + /* Describe the object */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Hack -- enforce max length */ + o_name[lim] = '\0'; + + /* Save the index */ + out_index[k] = i; + + /* Acquire inventory color */ + out_color[k] = tval_to_attr[o_ptr->tval & 0x7F]; + + /* Save the object description */ + strcpy(out_desc[k], o_name); + + /* Find the predicted "line length" */ + l = strlen(out_desc[k]) + 5; + + /* Account for the weight */ + l += 9; + + /* Maintain the maximum length */ + if (l > len) len = l; + + /* Advance to next "line" */ + k++; + } + + /* Find the column to start in */ + col = (len > 76) ? 0 : (79 - len); + + /* Output each entry */ + for (j = 0; j < k; j++) + { + /* Get the index */ + i = floor_list[out_index[j]]; + + /* Get the item */ + o_ptr = &o_list[i]; + + /* Clear the line */ + prt("", j + 1, col ? col - 2 : col); + + /* Prepare an index --(-- */ + sprintf(tmp_val, "%c)", index_to_label(j)); + + /* Clear the line with the (possibly indented) index */ + put_str(tmp_val, j + 1, col); + + /* Display the entry itself */ + c_put_str(out_color[j], out_desc[j], j + 1, col + 3); + + /* Display the weight if needed */ + { + int wgt = o_ptr->weight * o_ptr->number; + sprintf(tmp_val, "%3d.%1d lb", wgt / 10, wgt % 10); + put_str(tmp_val, j + 1, 71); + } + } + + /* Make a "shadow" below the list (only if needed) */ + if (j && (j < 23)) prt("", j + 1, col ? col - 2 : col); +} + +/* + * This version of get_item() is called by get_item() when + * the easy_floor is on. + */ +static bool_ get_item_floor(int *cp, cptr pmt, cptr str, int mode, object_filter_t const &filter, select_by_name_t const &select_by_name) +{ + char n1 = 0, n2 = 0, which = ' '; + + int j, k, i1, i2, e1, e2; + + bool_ done, item; + + bool_ oops = FALSE; + + bool_ equip = FALSE; + bool_ inven = FALSE; + bool_ floor = FALSE; + bool_ automat = FALSE; + + bool_ allow_equip = FALSE; + bool_ allow_inven = FALSE; + bool_ allow_floor = FALSE; + + bool_ toggle = FALSE; + + char tmp_val[160]; + char out_val[160]; + + int floor_top = 0; + + k = 0; + + /* Get the item index */ + if (repeat_pull(cp)) + { + /* Floor item? */ + if (*cp < 0) + { + object_type *o_ptr; + + /* Special index */ + k = 0 - (*cp); + + /* Acquire object */ + o_ptr = &o_list[k]; + + /* Validate the item */ + if (item_tester_okay(o_ptr, filter)) + { + /* Success */ + return (TRUE); + } + } + + /* Verify the item */ + else if (get_item_okay(*cp, filter)) + { + /* Success */ + return (TRUE); + } + } + + + /* Extract args */ + if (mode & (USE_EQUIP)) equip = TRUE; + if (mode & (USE_INVEN)) inven = TRUE; + if (mode & (USE_FLOOR)) floor = TRUE; + if (mode & (USE_AUTO)) automat = TRUE; + + + /* Paranoia XXX XXX XXX */ + msg_print(NULL); + + + /* Not done */ + done = FALSE; + + /* No item selected */ + item = FALSE; + + + /* Full inventory */ + i1 = 0; + i2 = INVEN_PACK - 1; + + /* Forbid inventory */ + if (!inven) i2 = -1; + + /* Restrict inventory indexes */ + while ((i1 <= i2) && (!get_item_okay(i1, filter))) i1++; + while ((i1 <= i2) && (!get_item_okay(i2, filter))) i2--; + + + /* Full equipment */ + e1 = INVEN_WIELD; + e2 = INVEN_TOTAL - 1; + + /* Forbid equipment */ + if (!equip) e2 = -1; + + /* Restrict equipment indexes */ + while ((e1 <= e2) && (!get_item_okay(e1, filter))) e1++; + while ((e1 <= e2) && (!get_item_okay(e2, filter))) e2--; + + + /* Floor items? */ + auto const floor_list = + floor ? scan_floor(p_ptr->py, p_ptr->px, filter) + : std::vector<int>(); + assert(floor_list.size() <= 23); + int const floor_num = floor_list.size(); // "int" for warning avoidance + + /* Accept inventory */ + if (i1 <= i2) allow_inven = TRUE; + + /* Accept equipment */ + if (e1 <= e2) allow_equip = TRUE; + + /* Accept floor */ + if (!floor_list.empty()) allow_floor = TRUE; + + /* Require at least one legal choice */ + if (!allow_inven && !allow_equip && !allow_floor) + { + /* Oops */ + oops = TRUE; + + /* Done */ + done = TRUE; + } + + /* Analyze choices */ + else + { + /* Hack -- Start on equipment if requested */ + if ((command_wrk == (USE_EQUIP)) && allow_equip) + { + command_wrk = (USE_EQUIP); + } + + /* Use inventory if allowed */ + else if (allow_inven) + { + command_wrk = (USE_INVEN); + } + + /* Use equipment if allowed */ + else if (allow_equip) + { + command_wrk = (USE_EQUIP); + } + + /* Use floor if allowed */ + else if (allow_floor) + { + command_wrk = (USE_FLOOR); + } + } + + /* Save screen */ + screen_save(); + + /* Repeat until done */ + while (!done) + { + /* Show choices */ + { + int ni = 0; + int ne = 0; + + /* Scan windows */ + for (j = 0; j < ANGBAND_TERM_MAX; j++) + { + /* Unused */ + if (!angband_term[j]) continue; + + /* Count windows displaying inven */ + if (window_flag[j] & (PW_INVEN)) ni++; + + /* Count windows displaying equip */ + if (window_flag[j] & (PW_EQUIP)) ne++; + } + + /* Toggle if needed */ + if ((command_wrk == (USE_EQUIP) && ni && !ne) || + (command_wrk == (USE_INVEN) && !ni && ne)) + { + /* Toggle */ + toggle_inven_equip(); + + /* Track toggles */ + toggle = !toggle; + } + + /* Update */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Redraw windows */ + window_stuff(); + } + + /* Inventory screen */ + if (command_wrk == (USE_INVEN)) + { + /* Extract the legal requests */ + n1 = I2A(i1); + n2 = I2A(i2); + + /* Redraw */ + show_inven(filter); + } + + /* Equipment screen */ + else if (command_wrk == (USE_EQUIP)) + { + /* Extract the legal requests */ + n1 = I2A(e1 - INVEN_WIELD); + n2 = I2A(e2 - INVEN_WIELD); + + /* Redraw */ + show_equip(filter); + } + + /* Floor screen */ + else if (command_wrk == (USE_FLOOR)) + { + j = floor_top; + k = MIN(floor_top + 23, floor_num) - 1; + + /* Extract the legal requests */ + n1 = I2A(j - floor_top); + n2 = I2A(k - floor_top); + + /* Redraw */ + show_floor(p_ptr->py, p_ptr->px, filter); + } + + /* Viewing inventory */ + if (command_wrk == (USE_INVEN)) + { + /* Begin the prompt */ + sprintf(out_val, "Inven:"); + + /* Build the prompt */ + sprintf(tmp_val, " %c-%c,", + index_to_label(i1), index_to_label(i2)); + + /* Append */ + strcat(out_val, tmp_val); + + /* Append */ + if (allow_equip) strcat(out_val, " / for Equip,"); + + /* Append */ + if (allow_floor) strcat(out_val, " - for floor,"); + } + + /* Viewing equipment */ + else if (command_wrk == (USE_EQUIP)) + { + /* Begin the prompt */ + sprintf(out_val, "Equip:"); + + /* Build the prompt */ + sprintf(tmp_val, " %c-%c,", + index_to_label(e1), index_to_label(e2)); + + /* Append */ + strcat(out_val, tmp_val); + + /* Append */ + if (allow_inven) strcat(out_val, " / for Inven,"); + + /* Append */ + if (allow_floor) strcat(out_val, " - for floor,"); + } + + /* Viewing floor */ + else if (command_wrk == (USE_FLOOR)) + { + /* Begin the prompt */ + sprintf(out_val, "Floor:"); + + /* Build the prompt */ + sprintf(tmp_val, " %c-%c,", n1, n2); + + /* Append */ + strcat(out_val, tmp_val); + + /* Append */ + if (allow_inven) + { + strcat(out_val, " / for Inven,"); + } + else if (allow_equip) + { + strcat(out_val, " / for Equip,"); + } + } + + /* Do we allow selection by name? */ + if (select_by_name) + { + strcat(out_val, " @ for extra selection,"); + } + + /* Create automatizer rule?? */ + if (automat) + { + if (automatizer_create) + strcat(out_val, " $ new automatizer rule(ON),"); + else + strcat(out_val, " $ new automatizer rule(OFF),"); + } + + /* Finish the prompt */ + strcat(out_val, " ESC"); + + /* Build the prompt */ + sprintf(tmp_val, "(%s) %s", out_val, pmt); + + /* Show the prompt */ + prt(tmp_val, 0, 0); + + /* Get a key */ + which = inkey(); + + /* Parse it */ + switch (which) + { + case ESCAPE: + { + done = TRUE; + break; + } + + case '/': + { + if (command_wrk == (USE_INVEN)) + { + if (!allow_equip) + { + bell(); + break; + } + command_wrk = (USE_EQUIP); + } + else if (command_wrk == (USE_EQUIP)) + { + if (!allow_inven) + { + bell(); + break; + } + command_wrk = (USE_INVEN); + } + else if (command_wrk == (USE_FLOOR)) + { + if (allow_inven) + { + command_wrk = (USE_INVEN); + } + else if (allow_equip) + { + command_wrk = (USE_EQUIP); + } + else + { + bell(); + break; + } + } + + /* Hack -- Fix screen */ + screen_load(); + screen_save(); + + /* Need to redraw */ + break; + } + + case '-': + { + if (!allow_floor) + { + bell(); + break; + } + + /* + * If we are already examining the floor, and there + * is only one item, we will always select it. + * If we aren't examining the floor and there is only + * one item, we will select it if floor_query_flag + * is FALSE. + */ + if (floor_num == 1) + { + if (command_wrk == (USE_FLOOR)) + { + /* Special index */ + k = 0 - floor_list[0]; + + /* Allow player to "refuse" certain actions */ + if (!get_item_allow(k)) + { + done = TRUE; + break; + } + + /* Accept that choice */ + (*cp) = k; + item = TRUE; + done = TRUE; + + break; + } + } + + /* Hack -- Fix screen */ + screen_load(); + screen_save(); + + command_wrk = (USE_FLOOR); + + break; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + /* Look up the tag */ + if (!get_tag(&k, which)) + { + bell(); + break; + } + + /* Hack -- Validate the item */ + if ((k < INVEN_WIELD) ? !inven : !equip) + { + bell(); + break; + } + + /* Validate the item */ + if (!get_item_okay(k, filter)) + { + bell(); + break; + } + + /* Allow player to "refuse" certain actions */ + if (!get_item_allow(k)) + { + done = TRUE; + break; + } + + /* Accept that choice */ + (*cp) = k; + item = TRUE; + done = TRUE; + break; + } + + case '\n': + case '\r': + { + /* Choose "default" inventory item */ + if (command_wrk == (USE_INVEN)) + { + k = ((i1 == i2) ? i1 : -1); + } + + /* Choose "default" equipment item */ + else if (command_wrk == (USE_EQUIP)) + { + k = ((e1 == e2) ? e1 : -1); + } + + /* Choose "default" floor item */ + else if (command_wrk == (USE_FLOOR)) + { + if (floor_num == 1) + { + /* Special index */ + k = 0 - floor_list[0]; + + /* Allow player to "refuse" certain actions */ + if (!get_item_allow(k)) + { + done = TRUE; + break; + } + + /* Accept that choice */ + (*cp) = k; + item = TRUE; + done = TRUE; + } + break; + } + + /* Validate the item */ + if (!get_item_okay(k, filter)) + { + bell(); + break; + } + + /* Allow player to "refuse" certain actions */ + if (!get_item_allow(k)) + { + done = TRUE; + break; + } + + /* Accept that choice */ + (*cp) = k; + item = TRUE; + done = TRUE; + break; + } + + case '@': + { + // Ignore if not "enabled" + if (!select_by_name) + { + break; + } + // Find by name + if (auto i = select_by_name(filter)) + { + (*cp) = *i; + item = TRUE; + done = TRUE; + } + break; + } + + case '$': + { + automatizer_create = !automatizer_create; + break; + } + + default: + { + int ver; + + ver = isupper(which); + which = tolower(which); + + /* Convert letter to inventory index */ + if (command_wrk == (USE_INVEN)) + { + k = label_to_inven(which); + if (k == -1) + { + bell(); + break; + } + } + + /* Convert letter to equipment index */ + else if (command_wrk == (USE_EQUIP)) + { + k = label_to_equip(which); + if (k == -1) + { + bell(); + break; + } + } + + /* Convert letter to floor index */ + else if (command_wrk == (USE_FLOOR)) + { + k = islower(which) ? A2I(which) : -1; + if (k < 0 || k >= floor_num) + { + bell(); + break; + } + + /* Special index */ + k = 0 - floor_list[k]; + } + + /* Validate the item */ + if ((k >= 0) && !get_item_okay(k, filter)) + { + bell(); + break; + } + + /* Verify the item */ + if (ver && !verify("Try", k)) + { + done = TRUE; + break; + } + + /* Allow player to "refuse" certain actions */ + if (!get_item_allow(k)) + { + done = TRUE; + break; + } + + /* Accept that choice */ + (*cp) = k; + item = TRUE; + done = TRUE; + break; + } + } + } + + /* Fix the screen */ + screen_load(); + + /* Track */ + if (item && done) + { + if (*cp >= 0) + { + object_track(&p_ptr->inventory[*cp]); + } + else + { + object_track(&o_list[0 - *cp]); + } + } + + /* Clean up */ + { + /* Toggle again if needed */ + if (toggle) toggle_inven_equip(); + + /* Update */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Window stuff */ + window_stuff(); + } + + + /* Clear the prompt line */ + prt("", 0, 0); + + /* Warning if needed */ + if (oops && str) msg_print(str); + + + if (item) repeat_push(*cp); + + /* Result */ + return (item); +} + + +/* + * Let the user select an item, save its "index" + * + * Return TRUE only if an acceptable item was chosen by the user. + * + * The selected item must satisfy the "item_tester_hook()" function, + * if that hook is set, and the "item_tester_tval", if that value is set. + * + * All "item_tester" restrictions are cleared before this function returns. + * + * The user is allowed to choose acceptable items from the equipment, + * inventory, or floor, respectively, if the proper flag was given, + * and there are any acceptable items in that location. + * + * The equipment or inventory are displayed (even if no acceptable + * items are in that location) if the proper flag was given. + * + * If there are no acceptable items available anywhere, and "str" is + * not NULL, then it will be used as the text of a warning message + * before the function returns. + * + * Note that the user must press "-" to specify the item on the floor, + * and there is no way to "examine" the item on the floor, while the + * use of "capital" letters will "examine" an inventory/equipment item, + * and prompt for its use. + * + * If a legal item is selected from the inventory, we save it in "cp" + * directly (0 to 35), and return TRUE. + * + * If a legal item is selected from the floor, we save it in "cp" as + * a negative (-1 to -511), and return TRUE. + * + * If no item is available, we do nothing to "cp", and we display a + * warning message, using "str" if available, and return FALSE. + * + * If no item is selected, we do nothing to "cp", and return FALSE. + * + * Global "p_ptr->command_new" is used when viewing the inventory or equipment + * to allow the user to enter a command while viewing those screens, and + * also to induce "auto-enter" of stores, and other such stuff. + * + * Global "p_ptr->command_wrk" is used to choose between equip/inven listings. + * If it is TRUE then we are viewing inventory, else equipment. + * + * We always erase the prompt when we are done, leaving a blank line, + * or a warning message, if appropriate, if no items are available. + */ +bool_ get_item(int *cp, cptr pmt, cptr str, int mode, object_filter_t const &filter, select_by_name_t const &select_by_name) +{ + automatizer_create = FALSE; + return get_item_floor(cp, pmt, str, mode, filter, select_by_name); +} + +/* + * Hook to determine if an object is getable + */ +static bool item_tester_hook_getable(object_type const *o_ptr) +{ + if (!inven_carry_okay(o_ptr)) return (FALSE); + + if ((o_ptr->tval == TV_HYPNOS) && (!get_skill(SKILL_SYMBIOTIC))) return FALSE; + + /* Assume yes */ + return (TRUE); +} + +/* + * Wear a single item from o_ptr + */ +int wear_ammo(object_type *o_ptr) +{ + int slot, num = 1; + + object_type forge; + object_type *q_ptr; + + /* Check the slot */ + slot = wield_slot(o_ptr); + + if(slot == -1) + return -1; + + /* Get local object */ + q_ptr = &forge; + + /* Obtain local object */ + object_copy(q_ptr, o_ptr); + + num = o_ptr->number; + + /* Modify quantity */ + q_ptr->number = num; + + /* Access the wield slot */ + o_ptr = &p_ptr->inventory[slot]; + + q_ptr->number += o_ptr->number; + + /* Wear the new stuff */ + object_copy(o_ptr, q_ptr); + + /* Increment the equip counter by hand */ + equip_cnt++; + + /* Cursed! */ + if (cursed_p(o_ptr)) + { + /* Warn the player */ + msg_print("Oops! It feels deathly cold!"); + + /* Note the curse */ + o_ptr->ident |= (IDENT_SENSE); + o_ptr->sense = SENSE_CURSED; + } + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); + + /* Recalculate torch */ + p_ptr->update |= (PU_TORCH); + + /* Recalculate hitpoint */ + p_ptr->update |= (PU_HP); + + /* Recalculate mana */ + p_ptr->update |= (PU_MANA); + + /* Redraw monster hitpoint */ + p_ptr->redraw |= (PR_FRAME); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + return slot; +} + + +/* + * Try to pickup arrows + */ +void pickup_ammo() +{ + /* Copy list of objects since we're manipulating the list */ + auto const object_idxs(cave[p_ptr->py][p_ptr->px].o_idxs); + + /* Scan the pile of objects */ + for (auto const this_o_idx: object_idxs) + { + /* Acquire object */ + object_type *o_ptr = &o_list[this_o_idx]; + + if (object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO])) + { + msg_print("You add the ammo to your quiver."); + s16b slot = wear_ammo(o_ptr); + + if (slot != -1) + { + /* Get the item again */ + o_ptr = &p_ptr->inventory[slot]; + + /* Describe the object */ + char o_name[80]; + object_desc(o_name, o_ptr, TRUE, 3); + + /* Message */ + msg_format("You have %s (%c).", o_name, index_to_label(slot)); + + /* Delete the object */ + delete_object_idx(this_o_idx); + } + } + } +} + + +/** + * Check for encumberance if player were to pick up + * given item. + */ +static bool can_carry_heavy(object_type const *o_ptr) +{ + /* Extract the "weight limit" (in tenth pounds) */ + int i = weight_limit(); + + /* Calculate current encumbarance */ + int j = calc_total_weight(); + + /* Apply encumbarance from weight */ + int old_enc = 0; + if (j > i / 2) old_enc = ((j - (i / 2)) / (i / 10)); + + /* Increase the weight, recalculate encumbarance */ + j += (o_ptr->number * o_ptr->weight); + + /* Apply encumbarance from weight */ + int new_enc = 0; + if (j > i / 2) new_enc = ((j - (i / 2)) / (i / 10)); + + /* If the encumberance is the same, then we pick up without prompt */ + return (new_enc <= old_enc); +} + +/* Do the actuall picking up */ +void object_pickup(int this_o_idx) +{ + int slot = 0; + char o_name[80] = ""; + object_type *q_ptr, *o_ptr; + + /* Access the item */ + o_ptr = &o_list[this_o_idx]; + + if (p_ptr->auto_id) + { + object_aware(o_ptr); + object_known(o_ptr); + } + + /* Describe the object */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Note that the pack is too full */ + if (!inven_carry_okay(o_ptr) && !object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO])) + { + msg_format("You have no room for %s.", o_name); + } + + /* Pick up object */ + else + { + /* Hooks */ + { + hook_get_in in = { o_ptr, this_o_idx }; + if (process_hooks_new(HOOK_GET, &in, NULL)) + { + return; + } + } + + q_ptr = &p_ptr->inventory[INVEN_AMMO]; + + /* Carry the item */ + if (object_similar(o_ptr, q_ptr)) + { + msg_print("You add the ammo to your quiver."); + slot = wear_ammo(o_ptr); + } + else + { + slot = inven_carry(o_ptr, FALSE); + } + + /* Sanity check */ + if (slot != -1) + { + /* Get the item again */ + o_ptr = &p_ptr->inventory[slot]; + + object_track(o_ptr); + + /* Describe the object */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Message */ + msg_format("You have %s (%c).", o_name, index_to_label(slot)); + + /* Delete the object */ + delete_object_idx(this_o_idx); + + /* Sense object. */ + sense_inventory(); + } + } +} + + +static void absorb_gold(cave_type const *c_ptr) +{ + /* Copy list of objects since we're going to manipulate the list itself */ + auto const object_idxs(c_ptr->o_idxs); + + /* Go through everything */ + for (auto const this_o_idx: object_idxs) + { + /* Acquire object */ + object_type *o_ptr = &o_list[this_o_idx]; + + /* Hack -- disturb */ + disturb(); + + /* Pick up gold */ + if (o_ptr->tval == TV_GOLD) + { + char goldname[80]; + object_desc(goldname, o_ptr, TRUE, 3); + /* Message */ + msg_format("You have found %ld gold pieces worth of %s.", + (long)o_ptr->pval, goldname); + + /* Collect the gold */ + p_ptr->au += o_ptr->pval; + + /* Redraw gold */ + p_ptr->redraw |= (PR_FRAME); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + + /* Delete the gold */ + delete_object_idx(this_o_idx); + } + } +} + +static void sense_floor(cave_type const *c_ptr) +{ + /* Build a list of the floor objects. */ + std::vector<int> floor_object_idxs; + { + /* Reserve the correct number of slots */ + floor_object_idxs.reserve(c_ptr->o_idxs.size()); + /* Fill in the indexes */ + for (auto const this_o_idx: c_ptr->o_idxs) + { + // Note the "-"! We need it for get_object() + // lookups to function correctly. + floor_object_idxs.push_back(0 - this_o_idx); + } + } + + /* Mega Hack -- If we have auto-Id, do an ID sweep *before* squleching, + * so that we don't have to walk over things twice to get them + * squelched. --dsb */ + if (p_ptr->auto_id) + { + for (auto const o_idx: floor_object_idxs) + { + object_type *o_ptr = get_object(o_idx); + object_aware(o_ptr); + object_known(o_ptr); + } + } + + /* Sense floor tile */ + sense_objects(floor_object_idxs); +} + +void py_pickup_floor(int pickup) +{ + /* Get the tile */ + auto c_ptr = &cave[p_ptr->py][p_ptr->px]; + + /* Try to grab ammo */ + pickup_ammo(); + + /* Auto-ID and pseudo-ID */ + sense_floor(c_ptr); + + /* Squeltch the floor */ + squeltch_grid(); + + /* Absorb gold on the tile */ + absorb_gold(&cave[p_ptr->py][p_ptr->px]); + + /* We handle 0, 1, or "many" items cases separately */ + if (c_ptr->o_idxs.empty()) + { + /* Nothing to do */ + } + else if (c_ptr->o_idxs.size() == 1) + { + /* Acquire object */ + auto floor_o_idx = c_ptr->o_idxs.front(); + auto o_ptr = &o_list[floor_o_idx]; + + /* Describe or pick up? */ + if (!pickup) + { + /* Describe */ + char o_name[80] = ""; + object_desc(o_name, o_ptr, TRUE, 3); + + /* Message */ + msg_format("You see %s.", o_name); + } + else + { + /* Are we actually going to pick up? */ + bool_ do_pickup = TRUE; + + /* Hack -- query every item */ + if (options->carry_query_flag || !can_carry_heavy(&o_list[floor_o_idx])) + { + char o_name[80] = ""; + object_desc(o_name, o_ptr, TRUE, 3); + + if (!inven_carry_okay(o_ptr) && !object_similar(o_ptr, &p_ptr->inventory[INVEN_AMMO])) + { + msg_format("You have no room for %s.", o_name); + return; /* Done */ + } + else + { + char out_val[160]; + sprintf(out_val, "Pick up %s? ", o_name); + do_pickup = get_check(out_val); + } + } + + /* Just pick it up; unless it's a symbiote and we don't have Symbiosis */ + if (do_pickup && ((o_list[floor_o_idx].tval != TV_HYPNOS) || (get_skill(SKILL_SYMBIOTIC)))) + { + object_pickup(floor_o_idx); + } + } + } + else + { + /* Describe or pick up? */ + if (!pickup) + { + /* Message */ + msg_format("You see a pile of %d items.", c_ptr->o_idxs.size()); + } + else + { + /* Prompt for the item to pick up */ + cptr q = "Get which item? "; + cptr s = "You have no room in your pack for any of the items here."; + int item; + if (get_item(&item, q, s, (USE_FLOOR), item_tester_hook_getable)) + { + s16b this_o_idx = 0 - item; + + bool_ do_pickup = TRUE; + if (!can_carry_heavy(&o_list[this_o_idx])) + { + /* Describe the object */ + char o_name[80] = ""; + object_desc(o_name, &o_list[this_o_idx], TRUE, 3); + + /* Prompt */ + char out_val[160]; + sprintf(out_val, "Pick up %s? ", o_name); + do_pickup = get_check(out_val); + } + + /* Pick up the item */ + if (do_pickup) + { + object_pickup(this_o_idx); + } + } + } + } +} + +/* Add a flags group */ +static void gain_flag_group(object_type *o_ptr) +{ + int grp = 0; + int tries = 1000; + + while (tries--) + { + grp = rand_int(flags_groups().size()); + + /* If we already got this group continue */ + if (o_ptr->pval3 & BIT(grp)) continue; + + /* Not enough points ? */ + if (flags_groups()[grp].price > o_ptr->pval2) continue; + + /* Ok, enough points and not already got it */ + break; + } + + /* Ack, nothing found */ + if (tries <= 1) return; + + o_ptr->pval2 -= flags_groups()[grp].price; + o_ptr->pval3 |= BIT(grp); + + /* Message */ + { + char o_name[80]; + + object_desc(o_name, o_ptr, FALSE, 0); + msg_format("%s gains access to the %s realm.", o_name, flags_groups()[grp].name); + } +} + +static object_flag_set get_flag(object_type *o_ptr, int grp) +{ + int tries = 1000; + + /* get the corresponding flag set of the group */ + auto const flag_set = flags_groups()[grp].flags; + auto const flag_test = object_flags(o_ptr); + + /* If no flags, no need to look */ + if (flag_set.empty()) + { + return object_flag_set(); + } + + while (tries--) + { + // Choose a random flag + auto const f = object_flag_set::make_bit(rand_int(object_flag_set::nbits)); + + // Ignore if not part of the group + if (!(f & flag_set)) + { + continue; + } + + // Ignore if already present + if (f & flag_test) + { + continue; + } + + // Got a match! + return f; + } + + // Exhausted the number of tries + return object_flag_set(); +} + +/* Add a flags from a flag group */ +static void gain_flag_group_flag(object_type *o_ptr) +{ + // Try a "few" times to see if we can't find a flag. + for (int tries = 20000; tries > 0; tries--) + { + // Choose a random flag group + auto grp = rand_int(flags_groups().size()); + + // If that group isn't available to the object, then choose a new one. + if (!(BIT(grp) & o_ptr->pval3)) + { + continue; + } + + // Get an as-yet unused flag from the group, if possible. + auto const f = get_flag(o_ptr, grp); + + // If we couldn't find a flag, then we try again. + if (f.empty()) + { + continue; + } + + // Got a flag; mix it into the object. + o_ptr->art_flags |= f; + + // Describe what happened + char o_name[80]; + object_desc(o_name, o_ptr, FALSE, 0); + msg_format("%s gains a new power from the %s realm.", o_name, flags_groups()[grp].name); + + // We're done. + return; + } +} + +/* + * When an object gain a level, he can gain some attributes + */ +void object_gain_level(object_type *o_ptr) +{ + /* First it can gain some tohit and todam */ + if ((o_ptr->tval == TV_AXE) || (o_ptr->tval == TV_SWORD) || (o_ptr->tval == TV_POLEARM) || + (o_ptr->tval == TV_HAFTED) || (o_ptr->tval == TV_MSTAFF)) + { + int k = rand_int(100); + + /* gain +2,+1 */ + if (k < 33) + { + o_ptr->to_h += randint(2); + o_ptr->to_d += 1; + } + /* +1 and 1 point */ + else if (k < 66) + { + o_ptr->to_h += 1; + o_ptr->pval2++; + + if (magik(NEW_GROUP_CHANCE)) gain_flag_group(o_ptr); + } + else + { + // Gain a group if none are available. + if (!o_ptr->pval3) + { + gain_flag_group(o_ptr); + } + + // Gain a flag + gain_flag_group_flag(o_ptr); + + // Increase/grant PVAL + if (!o_ptr->pval) + { + o_ptr->pval = 1; + } + else + { + while (magik(20 - (o_ptr->pval * 2))) + { + o_ptr->pval++; + } + + if (o_ptr->pval > 5) + { + o_ptr->pval = 5; + } + } + } + } +} + + +/* + * Item sets fcts + */ +bool_ wield_set(s16b a_idx, s16b set_idx, bool_ silent) +{ + auto &set_info = game->edit_data.set_info; + auto const &a_info = game->edit_data.a_info; + + if ( -1 == a_info[a_idx].set) return (FALSE); + + auto s_ptr = &set_info[set_idx]; + + int i; + for (i = 0; i < s_ptr->num; i++) + { + if (a_idx == s_ptr->arts[i].a_idx) + { + break; + } + } + + if (!s_ptr->arts[i].present) + { + s_ptr->num_use++; + s_ptr->arts[i].present = TRUE; + if (s_ptr->num_use > s_ptr->num) + { + msg_print("ERROR!! s_ptr->num_use > s_ptr->use"); + } + else if ((s_ptr->num_use == s_ptr->num) && (!silent)) + { + cmsg_format(TERM_GREEN, "%s item set completed.", s_ptr->name.c_str()); + } + return (TRUE); + } + + return (FALSE); +} + +bool_ takeoff_set(s16b a_idx, s16b set_idx) +{ + auto &set_info = game->edit_data.set_info; + auto const &a_info = game->edit_data.a_info; + + if ( -1 == a_info[a_idx].set) return (FALSE); + + auto s_ptr = &set_info[set_idx]; + + int i; + for (i = 0; i < s_ptr->num; i++) + { + if (a_idx == s_ptr->arts[i].a_idx) + { + break; + } + } + + if (s_ptr->arts[i].present) + { + s_ptr->arts[i].present = FALSE; + + assert(s_ptr->num_use > 0); + s_ptr->num_use--; + + if (s_ptr->num_use == s_ptr->num - 1) + { + cmsg_format(TERM_GREEN, "%s item set not complete anymore.", s_ptr->name.c_str()); + } + + return (TRUE); + } + + return (FALSE); +} + +void apply_set(s16b a_idx, s16b set_idx) +{ + auto const &set_info = game->edit_data.set_info; + auto const &a_info = game->edit_data.a_info; + + if ( -1 == a_info[a_idx].set) + { + return; + } + + auto s_ptr = &set_info[set_idx]; + + int i; + for (i = 0; i < s_ptr->num; i++) + { + if (a_idx == s_ptr->arts[i].a_idx) + { + break; + } + } + + if (s_ptr->arts[i].present) + { + for (int j = 0; j < s_ptr->num_use; j++) + { + apply_flags(s_ptr->arts[i].flags[j], + s_ptr->arts[i].pval[j], + 0, 0, 0, 0); + } + } +} + +static void apply_flags_set(s16b a_idx, s16b set_idx, object_flag_set *f) +{ + auto const &set_info = game->edit_data.set_info; + auto const &a_info = game->edit_data.a_info; + + if ( -1 == a_info[a_idx].set) + { + return; + } + + auto s_ptr = &set_info[set_idx]; + + int i; + for (i = 0; i < s_ptr->num; i++) + { + if (a_idx == s_ptr->arts[i].a_idx) break; + } + + if (s_ptr->arts[i].present) + { + for (int j = 0; j < s_ptr->num_use; j++) + { + (*f) |= s_ptr->arts[i].flags[j]; + } + } +} + +/* + * Return the "attr" for a given item. + * Use "flavor" if available. + * Default to user definitions. + */ + +byte object_attr(object_type const *o_ptr) +{ + auto const &k_info = game->edit_data.k_info; + auto const &random_artifacts = game->random_artifacts; + + if (o_ptr->tval == TV_RANDART) + { + return random_artifacts[o_ptr->sval].attr; + } + else if (k_info[o_ptr->k_idx].flavor) + { + return misc_to_attr[k_info[o_ptr->k_idx].flavor]; + } + else + { + return k_info[o_ptr->k_idx].x_attr; + } +} + +byte object_attr_default(object_type *o_ptr) +{ + auto const &k_info = game->edit_data.k_info; + auto const &random_artifacts = game->random_artifacts; + + if (o_ptr->tval == TV_RANDART) + { + return random_artifacts[o_ptr->sval].attr; + } + else if (k_info[o_ptr->k_idx].flavor) + { + return misc_to_attr[k_info[o_ptr->k_idx].flavor]; + } + else + { + return k_info[o_ptr->k_idx].d_attr; + } +} + +/* + * Return the "char" for a given item. + * Use "flavor" if available. + * Default to user definitions. + */ + +char object_char(object_type const *o_ptr) +{ + auto const &k_info = game->edit_data.k_info; + + if (k_info[o_ptr->k_idx].flavor) + { + return misc_to_char[k_info[o_ptr->k_idx].flavor]; + } + else + { + return k_info[o_ptr->k_idx].x_char; + } +} + +char object_char_default(object_type const *o_ptr) +{ + auto const &k_info = game->edit_data.k_info; + + if (k_info[o_ptr->k_idx].flavor) + { + return misc_to_char[k_info[o_ptr->k_idx].flavor]; + } + else + { + return k_info[o_ptr->k_idx].d_char; + } +} + +/** + * Is the given object an artifact? + */ +bool artifact_p(object_type const *o_ptr) +{ + auto const &k_info = game->edit_data.k_info; + + return + (o_ptr->tval == TV_RANDART) || + (o_ptr->name1 ? true : false) || + (!o_ptr->artifact_name.empty()) || + ((k_info[o_ptr->k_idx].flags & TR_NORM_ART) ? true : false); +} + +/** + * Is the given object an ego item? + */ +bool ego_item_p(object_type const *o_ptr) +{ + return o_ptr->name2 || (o_ptr->name2b ? TRUE : FALSE); +} + +/* + * Is the given object an ego item of the given type? + */ +bool is_ego_p(object_type const *o_ptr, s16b ego) +{ + return (o_ptr->name2 == ego) || (o_ptr->name2b == ego); +} + +/** + * Is the given object identified as cursed? + */ +bool cursed_p(object_type const *o_ptr) +{ + return o_ptr->ident & (IDENT_CURSED); +} |