From cbef37bd5bfb938a2303ee3887520c08be85d8e8 Mon Sep 17 00:00:00 2001 From: Bardur Arantsson Date: Tue, 26 Mar 2013 17:10:10 +0100 Subject: Switch almost everything over to C++ --- src/CMakeLists.txt | 44 +- src/birth.c | 3942 ------------- src/birth.cc | 3949 +++++++++++++ src/bldg.c | 2234 ------- src/bldg.cc | 2234 +++++++ src/cave.c | 5022 ---------------- src/cave.cc | 4952 ++++++++++++++++ src/cmd1.c | 5147 ----------------- src/cmd1.cc | 5147 +++++++++++++++++ src/cmd2.c | 5182 ----------------- src/cmd2.cc | 5182 +++++++++++++++++ src/cmd3.c | 2333 -------- src/cmd3.cc | 2267 ++++++++ src/cmd4.c | 4632 --------------- src/cmd4.cc | 4607 +++++++++++++++ src/cmd5.c | 2471 -------- src/cmd5.cc | 2471 ++++++++ src/cmd6.c | 7792 ------------------------- src/cmd6.cc | 7792 +++++++++++++++++++++++++ src/cmd7.c | 7632 ------------------------ src/cmd7.cc | 7632 ++++++++++++++++++++++++ src/cmovie.c | 496 -- src/cmovie.cc | 500 ++ src/corrupt.c | 973 ---- src/corrupt.cc | 973 ++++ src/device_allocation.c | 26 - src/device_allocation.cc | 26 + src/dice.c | 94 - src/dice.cc | 94 + src/dungeon.c | 5849 ------------------- src/dungeon.cc | 5849 +++++++++++++++++++ src/externs.h | 7 +- src/files.c | 5842 ------------------- src/files.cc | 5813 +++++++++++++++++++ src/gen_evol.c | 156 - src/gen_evol.cc | 156 + src/gen_maze.c | 294 - src/gen_maze.cc | 292 + src/generate.c | 8864 ---------------------------- src/generate.cc | 8849 ++++++++++++++++++++++++++++ src/gods.c | 200 - src/gods.cc | 200 + src/help.c | 750 --- src/help.cc | 750 +++ src/hiscore.c | 85 - src/hiscore.cc | 85 + src/hooks.c | 275 - src/hooks.cc | 276 + src/include/tome/make_array.hpp | 13 + src/init1.c | 11816 -------------------------------------- src/init1.cc | 11816 ++++++++++++++++++++++++++++++++++++++ src/init2.c | 2663 --------- src/init2.cc | 2476 ++++++++ src/joke.c | 34 - src/joke.cc | 34 + src/levels.c | 234 - src/levels.cc | 234 + src/loadsave.c | 3177 ---------- src/loadsave.cc | 3190 ++++++++++ src/lua_bind.c | 356 -- src/lua_bind.cc | 356 ++ src/melee1.c | 3065 ---------- src/melee1.cc | 3065 ++++++++++ src/melee2.c | 7561 ------------------------ src/melee2.cc | 7561 ++++++++++++++++++++++++ src/messages.c | 364 -- src/messages.cc | 365 ++ src/mimic.c | 694 --- src/mimic.cc | 694 +++ src/modules.c | 1215 ---- src/modules.cc | 1215 ++++ src/monster1.c | 1901 ------ src/monster1.cc | 1901 ++++++ src/monster2.c | 3979 ------------- src/monster2.cc | 3979 +++++++++++++ src/monster3.c | 706 --- src/monster3.cc | 706 +++ src/notes.c | 188 - src/notes.cc | 188 + src/object1.c | 6696 --------------------- src/object1.cc | 6696 +++++++++++++++++++++ src/object2.c | 6697 --------------------- src/object2.cc | 6695 +++++++++++++++++++++ src/powers.c | 1406 ----- src/powers.cc | 1406 +++++ src/q_betwen.c | 196 - src/q_betwen.cc | 196 + src/q_bounty.c | 112 - src/q_bounty.cc | 112 + src/q_dragons.c | 152 - src/q_dragons.cc | 152 + src/q_eol.c | 202 - src/q_eol.cc | 202 + src/q_evil.c | 119 - src/q_evil.cc | 119 + src/q_fireprof.c | 588 -- src/q_fireprof.cc | 588 ++ src/q_god.c | 1196 ---- src/q_god.cc | 1196 ++++ src/q_haunted.c | 149 - src/q_haunted.cc | 149 + src/q_hobbit.c | 207 - src/q_hobbit.cc | 207 + src/q_invas.c | 204 - src/q_invas.cc | 204 + src/q_library.c | 510 -- src/q_library.cc | 510 ++ src/q_main.c | 193 - src/q_main.cc | 193 + src/q_narsil.c | 113 - src/q_narsil.cc | 113 + src/q_nazgul.c | 123 - src/q_nazgul.cc | 123 + src/q_nirna.c | 115 - src/q_nirna.cc | 115 + src/q_one.c | 365 -- src/q_one.cc | 365 ++ src/q_poison.c | 247 - src/q_poison.cc | 247 + src/q_rand.c | 473 -- src/q_rand.cc | 469 ++ src/q_shroom.c | 304 - src/q_shroom.cc | 304 + src/q_spider.c | 113 - src/q_spider.cc | 113 + src/q_thief.c | 176 - src/q_thief.cc | 176 + src/q_thrain.c | 241 - src/q_thrain.cc | 241 + src/q_troll.c | 183 - src/q_troll.cc | 183 + src/q_ultrae.c | 8 - src/q_ultrae.cc | 8 + src/q_ultrag.c | 275 - src/q_ultrag.cc | 275 + src/q_wight.c | 162 - src/q_wight.cc | 162 + src/q_wolves.c | 132 - src/q_wolves.cc | 132 + src/quark.c | 88 - src/quark.cc | 92 + src/randart.c | 477 -- src/randart.cc | 478 ++ src/range.c | 9 - src/range.cc | 9 + src/script.c | 29 - src/script.cc | 29 + src/skills.c | 1842 ------ src/skills.cc | 1814 ++++++ src/spell_type.c | 469 -- src/spell_type.cc | 472 ++ src/spell_type.h | 4 +- src/spells1.c | 9347 ------------------------------ src/spells1.cc | 9347 ++++++++++++++++++++++++++++++ src/spells2.c | 8450 --------------------------- src/spells2.cc | 8449 +++++++++++++++++++++++++++ src/spells3.c | 4926 ---------------- src/spells3.cc | 4924 ++++++++++++++++ src/spells4.c | 631 -- src/spells4.cc | 631 ++ src/spells5.c | 2426 -------- src/spells5.cc | 2426 ++++++++ src/spells6.c | 392 -- src/spells6.cc | 401 ++ src/status.c | 772 --- src/status.cc | 772 +++ src/store.c | 3935 ------------- src/store.cc | 3935 +++++++++++++ src/string_list.c | 52 - src/string_list.cc | 52 + src/tables.c | 4761 --------------- src/tables.cc | 4761 +++++++++++++++ src/traps.c | 3180 ---------- src/traps.cc | 3180 ++++++++++ src/types.h | 16 +- src/util.c | 4029 ------------- src/util.cc | 4023 +++++++++++++ src/util.hpp | 8 + src/variable.c | 1443 ----- src/variable.cc | 1441 +++++ src/wild.c | 1280 ----- src/wild.cc | 1272 ++++ src/wizard1.c | 2750 --------- src/wizard1.cc | 2748 +++++++++ src/wizard2.c | 1894 ------ src/wizard2.cc | 1894 ++++++ src/xtra1.c | 4954 ---------------- src/xtra1.cc | 4954 ++++++++++++++++ src/xtra2.c | 5825 ------------------- src/xtra2.cc | 5825 +++++++++++++++++++ src/z-virt.h | 4 - 191 files changed, 193504 insertions(+), 193910 deletions(-) delete mode 100644 src/birth.c create mode 100644 src/birth.cc delete mode 100644 src/bldg.c create mode 100644 src/bldg.cc delete mode 100644 src/cave.c create mode 100644 src/cave.cc delete mode 100644 src/cmd1.c create mode 100644 src/cmd1.cc delete mode 100644 src/cmd2.c create mode 100644 src/cmd2.cc delete mode 100644 src/cmd3.c create mode 100644 src/cmd3.cc delete mode 100644 src/cmd4.c create mode 100644 src/cmd4.cc delete mode 100644 src/cmd5.c create mode 100644 src/cmd5.cc delete mode 100644 src/cmd6.c create mode 100644 src/cmd6.cc delete mode 100644 src/cmd7.c create mode 100644 src/cmd7.cc delete mode 100644 src/cmovie.c create mode 100644 src/cmovie.cc delete mode 100644 src/corrupt.c create mode 100644 src/corrupt.cc delete mode 100644 src/device_allocation.c create mode 100644 src/device_allocation.cc delete mode 100644 src/dice.c create mode 100644 src/dice.cc delete mode 100644 src/dungeon.c create mode 100644 src/dungeon.cc delete mode 100644 src/files.c create mode 100644 src/files.cc delete mode 100644 src/gen_evol.c create mode 100644 src/gen_evol.cc delete mode 100644 src/gen_maze.c create mode 100644 src/gen_maze.cc delete mode 100644 src/generate.c create mode 100644 src/generate.cc delete mode 100644 src/gods.c create mode 100644 src/gods.cc delete mode 100644 src/help.c create mode 100644 src/help.cc delete mode 100644 src/hiscore.c create mode 100644 src/hiscore.cc delete mode 100644 src/hooks.c create mode 100644 src/hooks.cc create mode 100644 src/include/tome/make_array.hpp delete mode 100644 src/init1.c create mode 100644 src/init1.cc delete mode 100644 src/init2.c create mode 100644 src/init2.cc delete mode 100644 src/joke.c create mode 100644 src/joke.cc delete mode 100644 src/levels.c create mode 100644 src/levels.cc delete mode 100644 src/loadsave.c create mode 100644 src/loadsave.cc delete mode 100644 src/lua_bind.c create mode 100644 src/lua_bind.cc delete mode 100644 src/melee1.c create mode 100644 src/melee1.cc delete mode 100644 src/melee2.c create mode 100644 src/melee2.cc delete mode 100644 src/messages.c create mode 100644 src/messages.cc delete mode 100644 src/mimic.c create mode 100644 src/mimic.cc delete mode 100644 src/modules.c create mode 100644 src/modules.cc delete mode 100644 src/monster1.c create mode 100644 src/monster1.cc delete mode 100644 src/monster2.c create mode 100644 src/monster2.cc delete mode 100644 src/monster3.c create mode 100644 src/monster3.cc delete mode 100644 src/notes.c create mode 100644 src/notes.cc delete mode 100644 src/object1.c create mode 100644 src/object1.cc delete mode 100644 src/object2.c create mode 100644 src/object2.cc delete mode 100644 src/powers.c create mode 100644 src/powers.cc delete mode 100644 src/q_betwen.c create mode 100644 src/q_betwen.cc delete mode 100644 src/q_bounty.c create mode 100644 src/q_bounty.cc delete mode 100644 src/q_dragons.c create mode 100644 src/q_dragons.cc delete mode 100644 src/q_eol.c create mode 100644 src/q_eol.cc delete mode 100644 src/q_evil.c create mode 100644 src/q_evil.cc delete mode 100644 src/q_fireprof.c create mode 100644 src/q_fireprof.cc delete mode 100644 src/q_god.c create mode 100644 src/q_god.cc delete mode 100644 src/q_haunted.c create mode 100644 src/q_haunted.cc delete mode 100644 src/q_hobbit.c create mode 100644 src/q_hobbit.cc delete mode 100644 src/q_invas.c create mode 100644 src/q_invas.cc delete mode 100644 src/q_library.c create mode 100644 src/q_library.cc delete mode 100644 src/q_main.c create mode 100644 src/q_main.cc delete mode 100644 src/q_narsil.c create mode 100644 src/q_narsil.cc delete mode 100644 src/q_nazgul.c create mode 100644 src/q_nazgul.cc delete mode 100644 src/q_nirna.c create mode 100644 src/q_nirna.cc delete mode 100644 src/q_one.c create mode 100644 src/q_one.cc delete mode 100644 src/q_poison.c create mode 100644 src/q_poison.cc delete mode 100644 src/q_rand.c create mode 100644 src/q_rand.cc delete mode 100644 src/q_shroom.c create mode 100644 src/q_shroom.cc delete mode 100644 src/q_spider.c create mode 100644 src/q_spider.cc delete mode 100644 src/q_thief.c create mode 100644 src/q_thief.cc delete mode 100644 src/q_thrain.c create mode 100644 src/q_thrain.cc delete mode 100644 src/q_troll.c create mode 100644 src/q_troll.cc delete mode 100644 src/q_ultrae.c create mode 100644 src/q_ultrae.cc delete mode 100644 src/q_ultrag.c create mode 100644 src/q_ultrag.cc delete mode 100644 src/q_wight.c create mode 100644 src/q_wight.cc delete mode 100644 src/q_wolves.c create mode 100644 src/q_wolves.cc delete mode 100644 src/quark.c create mode 100644 src/quark.cc delete mode 100644 src/randart.c create mode 100644 src/randart.cc delete mode 100644 src/range.c create mode 100644 src/range.cc delete mode 100644 src/script.c create mode 100644 src/script.cc delete mode 100644 src/skills.c create mode 100644 src/skills.cc delete mode 100644 src/spell_type.c create mode 100644 src/spell_type.cc delete mode 100644 src/spells1.c create mode 100644 src/spells1.cc delete mode 100644 src/spells2.c create mode 100644 src/spells2.cc delete mode 100644 src/spells3.c create mode 100644 src/spells3.cc delete mode 100644 src/spells4.c create mode 100644 src/spells4.cc delete mode 100644 src/spells5.c create mode 100644 src/spells5.cc delete mode 100644 src/spells6.c create mode 100644 src/spells6.cc delete mode 100644 src/status.c create mode 100644 src/status.cc delete mode 100644 src/store.c create mode 100644 src/store.cc delete mode 100644 src/string_list.c create mode 100644 src/string_list.cc delete mode 100644 src/tables.c create mode 100644 src/tables.cc delete mode 100644 src/traps.c create mode 100644 src/traps.cc delete mode 100644 src/util.c create mode 100644 src/util.cc create mode 100644 src/util.hpp delete mode 100644 src/variable.c create mode 100644 src/variable.cc delete mode 100644 src/wild.c create mode 100644 src/wild.cc delete mode 100644 src/wizard1.c create mode 100644 src/wizard1.cc delete mode 100644 src/wizard2.c create mode 100644 src/wizard2.cc delete mode 100644 src/xtra1.c create mode 100644 src/xtra1.cc delete mode 100644 src/xtra2.c create mode 100644 src/xtra2.cc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ba0fd9d2..221dfa2b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,29 +8,29 @@ ADD_SUBDIRECTORY (squelch) SET(SRCS main-gcu.c main-x11.c main-xaw.c main-sdl.c main-gtk2.c z-rand.c z-util.c z-form.c z-virt.c z-term.c - variable.c tables.c hooks.c util.c cave.c dungeon.c - melee1.c melee2.c messages.c modules.c - q_god.c q_library.c q_fireprof.c q_bounty.c q_thrain.c - q_narsil.c q_evil.c q_betwen.c q_haunted.c q_invas.c - q_nirna.c q_eol.c q_god.c q_dragons.c q_poison.c - q_spider.c q_wolves.c q_shroom.c q_nazgul.c q_wight.c - q_troll.c q_hobbit.c q_thief.c q_ultrae.c q_ultrag.c - q_one.c q_main.c q_rand.c - object1.c object2.c randart.c squeltch.cc traps.c - monster1.c monster2.c monster3.c - xtra1.c xtra2.c skills.c powers.c gods.c - spells1.c spells2.c spells3.c spells4.c spells5.c spells6.c - spell_type.c device_allocation.c - corrupt.c joke.c mimic.c - status.c files.c notes.c loadsave.c string_list.c - cmd1.c cmd2.c cmd3.c cmd4.c cmd5.c cmd6.c cmd7.c - help.c hiscore.c range.c dice.c - generate.c gen_maze.c gen_evol.c wild.c levels.c store.c bldg.c - cmovie.c - wizard2.c init2.c birth.c wizard1.c init1.c main.c - quark.c + variable.cc tables.cc hooks.cc util.cc cave.cc dungeon.cc + melee1.cc melee2.cc messages.cc modules.cc + q_god.cc q_library.cc q_fireprof.cc q_bounty.cc q_thrain.cc + q_narsil.cc q_evil.cc q_betwen.cc q_haunted.cc q_invas.cc + q_nirna.cc q_eol.cc q_god.cc q_dragons.cc q_poison.cc + q_spider.cc q_wolves.cc q_shroom.cc q_nazgul.cc q_wight.cc + q_troll.cc q_hobbit.cc q_thief.cc q_ultrae.cc q_ultrag.cc + q_one.cc q_main.cc q_rand.cc + object1.cc object2.cc randart.cc squeltch.cc traps.cc + monster1.cc monster2.cc monster3.cc + xtra1.cc xtra2.cc skills.cc powers.cc gods.cc + spells1.cc spells2.cc spells3.cc spells4.cc spells5.cc spells6.cc + spell_type.cc device_allocation.cc + corrupt.cc joke.cc mimic.cc + status.cc files.cc notes.cc loadsave.cc string_list.cc + cmd1.cc cmd2.cc cmd3.cc cmd4.cc cmd5.cc cmd6.cc cmd7.cc + help.cc hiscore.cc range.cc dice.cc + generate.cc gen_maze.cc gen_evol.cc wild.cc levels.cc store.cc bldg.cc + cmovie.cc + wizard1.cc wizard2.cc init1.cc init2.cc birth.cc main.c + quark.cc # Lua bits: - lua_bind.c script.c + lua_bind.cc script.cc ) # Need a few additional source files for Windows. diff --git a/src/birth.c b/src/birth.c deleted file mode 100644 index 60a6b3ee..00000000 --- a/src/birth.c +++ /dev/null @@ -1,3942 +0,0 @@ -/* File: birth.c */ - -/* Purpose: create a player character */ - -/* - * 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 "angband.h" -#include "messages.h" -#include "hooks.h" -#include "q_rand.h" - -/* - * How often the autoroller will update the display and pause - * to check for user interuptions. - * Bigger values will make the autoroller faster, but slower - * system may have problems because the user can't stop the - * autoroller for this number of rolls. - */ -#define AUTOROLLER_STEP 25L - -/* - * Maximum number of tries for selection of a proper quest monster - */ -#define MAX_TRIES 100 - -/* Max quests */ -static byte max_quests = 0; - -/* - * Current stats - */ -static s16b stat_use[6]; - -/* - * Autoroll limit - */ -static s16b stat_limit[6]; - -/* - * Autoroll matches - */ -static s32b stat_match[6]; - -/* - * Autoroll round - */ -static s32b auto_round; - -/* - * Last round - */ -static s32b last_round; - -/* Human */ -static const char *human_syllable1[] = -{ - "Ab", "Ac", "Ad", "Af", "Agr", "Ast", "As", "Al", "Adw", "Adr", "Ar", - "B", "Br", "C", "Cr", "Ch", "Cad", "D", "Dr", "Dw", "Ed", "Eth", "Et", - "Er", "El", "Eow", "F", "Fr", "G", "Gr", "Gw", "Gal", "Gl", "H", "Ha", - "Ib", "Jer", "K", "Ka", "Ked", "L", "Loth", "Lar", "Leg", "M", "Mir", - "N", "Nyd", "Ol", "Oc", "On", "P", "Pr", "R", "Rh", "S", "Sev", "T", - "Tr", "Th", "V", "Y", "Z", "W", "Wic", -}; - -static const char *human_syllable2[] = -{ - "a", "ae", "au", "ao", "are", "ale", "ali", "ay", "ardo", "e", "ei", - "ea", "eri", "era", "ela", "eli", "enda", "erra", "i", "ia", "ie", - "ire", "ira", "ila", "ili", "ira", "igo", "o", "oa", "oi", "oe", - "ore", "u", "y", -}; - -static const char *human_syllable3[] = -{ - "a", "and", "b", "bwyn", "baen", "bard", "c", "ctred", "cred", "ch", - "can", "d", "dan", "don", "der", "dric", "dfrid", "dus", "f", "g", - "gord", "gan", "l", "li", "lgrin", "lin", "lith", "lath", "loth", - "ld", "ldric", "ldan", "m", "mas", "mos", "mar", "mond", "n", - "nydd", "nidd", "nnon", "nwan", "nyth", "nad", "nn", "nnor", "nd", - "p", "r", "ron", "rd", "s", "sh", "seth", "sean", "t", "th", "tha", - "tlan", "trem", "tram", "v", "vudd", "w", "wan", "win", "wyn", "wyr", - "wyr", "wyth", -}; - -/* - * Random Name Generator - * based on a Javascript by Michael Hensley - * "http://geocities.com/timessquare/castle/6274/" - */ -static void create_random_name(int race, char *name) -{ - const char *syl1, *syl2, *syl3; - - int idx; - - - /* Paranoia */ - if (!name) return; - - /* Select the monster type */ - switch (race) - { - /* Create the monster name */ - - /* Use human ones */ - default: - { - idx = rand_int(sizeof(human_syllable1) / sizeof(char *)); - syl1 = human_syllable1[idx]; - idx = rand_int(sizeof(human_syllable2) / sizeof(char *)); - syl2 = human_syllable2[idx]; - idx = rand_int(sizeof(human_syllable3) / sizeof(char *)); - syl3 = human_syllable3[idx]; - - break; - } - } - - /* Concatenate selected syllables */ - strnfmt(name, 32, "%s%s%s", syl1, syl2, syl3); -} - - -void print_desc_aux(cptr txt, int y, int xx) -{ - int i = -1, x = xx; - - - while (txt[++i] != 0) - { - if (txt[i] == '\n') - { - x = xx; - y++; - } - else - { - Term_putch(x++, y, TERM_YELLOW, txt[i]); - } - } -} - -void print_desc(cptr txt) -{ - print_desc_aux(txt, 12, 1); -} - -/* - * Save the current data for later - */ -static void save_prev_data(void) -{ - int i; - - - /*** Save the current data ***/ - - /* Save the data */ - previous_char.sex = p_ptr->psex; - previous_char.race = p_ptr->prace; - previous_char.rmod = p_ptr->pracem; - previous_char.pclass = p_ptr->pclass; - previous_char.spec = p_ptr->pspec; - - previous_char.quests = max_quests; - - previous_char.god = p_ptr->pgod; - previous_char.grace = p_ptr->grace; - - previous_char.age = p_ptr->age; - previous_char.wt = p_ptr->wt; - previous_char.ht = p_ptr->ht; - previous_char.sc = p_ptr->sc; - previous_char.au = p_ptr->au; - - /* Save the stats */ - for (i = 0; i < 6; i++) - { - previous_char.stat[i] = p_ptr->stat_max[i]; - } - previous_char.luck = p_ptr->luck_base; - - /* Save the weapon specialty */ - previous_char.weapon = 0; - - /* Save the history */ - for (i = 0; i < 4; i++) - { - strcpy(previous_char.history[i], history[i]); - } -} - - -/* - * Load the previous data - */ -static void load_prev_data(bool_ save) -{ - int i; - - birther temp; - - - /*** Save the current data ***/ - - /* Save the data */ - temp.age = p_ptr->age; - temp.wt = p_ptr->wt; - temp.ht = p_ptr->ht; - temp.sc = p_ptr->sc; - temp.au = p_ptr->au; - - /* Save the stats */ - for (i = 0; i < 6; i++) - { - temp.stat[i] = p_ptr->stat_max[i]; - } - temp.luck = p_ptr->luck_base; - - /* Save the weapon specialty */ - temp.weapon = 0; - - /* Save the history */ - for (i = 0; i < 4; i++) - { - strcpy(temp.history[i], history[i]); - } - - - /*** Load the previous data ***/ - - /* Load the data */ - p_ptr->age = previous_char.age; - p_ptr->wt = previous_char.wt; - p_ptr->ht = previous_char.ht; - p_ptr->sc = previous_char.sc; - p_ptr->au = previous_char.au; - - /* Load the stats */ - for (i = 0; i < 6; i++) - { - p_ptr->stat_max[i] = previous_char.stat[i]; - p_ptr->stat_cur[i] = previous_char.stat[i]; - } - p_ptr->luck_base = previous_char.luck; - p_ptr->luck_max = previous_char.luck; - - /* Load the history */ - for (i = 0; i < 4; i++) - { - strcpy(history[i], previous_char.history[i]); - } - - - /*** Save the current data ***/ - if (!save) return; - - /* Save the data */ - previous_char.age = temp.age; - previous_char.wt = temp.wt; - previous_char.ht = temp.ht; - previous_char.sc = temp.sc; - previous_char.au = temp.au; - - /* Save the stats */ - for (i = 0; i < 6; i++) - { - previous_char.stat[i] = temp.stat[i]; - } - previous_char.luck = temp.luck; - - /* Save the chaos patron */ - previous_char.chaos_patron = temp.chaos_patron; - - /* Save the weapon specialty */ - previous_char.weapon = temp.weapon; - - /* Save the history */ - for (i = 0; i < 4; i++) - { - strcpy(previous_char.history[i], temp.history[i]); - } -} - - - - -/* - * Returns adjusted stat -JK- Algorithm by -JWT- - * - * auto_roll is boolean and states maximum changes should be used rather - * than random ones to allow specification of higher values to wait for - * - * The "p_ptr->maximize" code is important -BEN- - */ -static int adjust_stat(int value, int amount, int auto_roll) -{ - int i; - - - /* Negative amounts */ - if (amount < 0) - { - /* Apply penalty */ - for (i = 0; i < (0 - amount); i++) - { - if (value >= 18 + 10) - { - value -= 10; - } - else if (value > 18) - { - value = 18; - } - else if (value > 3) - { - value--; - } - } - } - - /* Positive amounts */ - else if (amount > 0) - { - /* Apply reward */ - for (i = 0; i < amount; i++) - { - if (value < 18) - { - value++; - } - else if (p_ptr->maximize) - { - value += 10; - } - else if (value < 18 + 70) - { - value += ((auto_roll ? 15 : randint(15)) + 5); - } - else if (value < 18 + 90) - { - value += ((auto_roll ? 6 : randint(6)) + 2); - } - else if (value < 18 + 100) - { - value++; - } - } - } - - /* Return the result */ - return (value); -} - - - - -/* - * Roll for a characters stats - * - * For efficiency, we include a chunk of "calc_bonuses()". - */ -static void get_stats(void) -{ - int i, j; - - int bonus; - - int dice[18]; - - - /* Roll and verify some stats */ - while (TRUE) - { - /* Roll some dice */ - for (j = i = 0; i < 18; i++) - { - /* Roll the dice */ - dice[i] = randint(3 + i % 3); - - /* Collect the maximum */ - j += dice[i]; - } - - /* - * Verify totals - * - * 57 was 54... I hate 'magic numbers' :< TY - * - * (d3 + d4 + d5) ~= 7.5 (+- 4.5) - * with 5 makes avg. stat value of 12.5 (min 8, max 17) - * - * (d3 + d4 + d5) x 6 ~= 45 (+- 18) - * - * So the original value (still used by Vanilla as of 2.9.3) - * allows (avg - 2)..(avg + 8), while this Z version - * (avg - 2)..(avg + 11). I don't understand what TY meant - * by "magic numbers", but I like big stats :) -- pelpel - * - */ - if ((j > 42) && (j < 57)) break; - } - - /* Acquire the stats */ - for (i = 0; i < 6; i++) - { - /* Extract 5 + 1d3 + 1d4 + 1d5 */ - j = 5 + dice[3 * i] + dice[3 * i + 1] + dice[3 * i + 2]; - - /* Save that value */ - p_ptr->stat_max[i] = j; - - /* Obtain a "bonus" for "race" and "class" */ - bonus = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i]; - - /* Variable stat maxes */ - if (p_ptr->maximize) - { - /* Start fully healed */ - p_ptr->stat_cur[i] = p_ptr->stat_max[i]; - - /* Efficiency -- Apply the racial/class bonuses */ - stat_use[i] = modify_stat_value(p_ptr->stat_max[i], bonus); - } - - /* Fixed stat maxes */ - else - { - /* Apply the bonus to the stat (somewhat randomly) */ - stat_use[i] = adjust_stat(p_ptr->stat_max[i], bonus, FALSE); - - /* Save the resulting stat maximum */ - p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stat_use[i]; - } - - /* No temporary drain (yet...) */ - p_ptr->stat_cnt[i] = 0; - p_ptr->stat_los[i] = 0; - } - - /* Get luck */ - p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5); - p_ptr->luck_max = p_ptr->luck_base; -} - - -/* - * Roll for some info that the auto-roller ignores - */ -static void get_extra(void) -{ - int i, j, min_value, max_value; - - - /* Level one */ - p_ptr->max_plv = p_ptr->lev = 1; - - /* Experience factor */ - p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp; - - /* Initialize arena and rewards information -KMW- */ - p_ptr->arena_number = 0; - p_ptr->inside_arena = 0; - p_ptr->inside_quest = 0; - p_ptr->exit_bldg = TRUE; /* only used for arena now -KMW- */ - - /* Hitdice */ - p_ptr->hitdie = rp_ptr->r_mhp + rmp_ptr->r_mhp + cp_ptr->c_mhp; - - /* Initial hitpoints */ - p_ptr->mhp = p_ptr->hitdie; - - /* Minimum hitpoints at highest level */ - min_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 3) / 8; - min_value += PY_MAX_LEVEL; - - /* Maximum hitpoints at highest level */ - max_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 5) / 8; - max_value += PY_MAX_LEVEL; - - /* Pre-calculate level 1 hitdice */ - player_hp[0] = p_ptr->hitdie; - - /* Roll out the hitpoints */ - while (TRUE) - { - /* Roll the hitpoint values */ - for (i = 1; i < PY_MAX_LEVEL; i++) - { - j = randint(p_ptr->hitdie); - player_hp[i] = player_hp[i - 1] + j; - } - - /* XXX Could also require acceptable "mid-level" hitpoints */ - - /* Require "valid" hitpoints at highest level */ - if (player_hp[PY_MAX_LEVEL - 1] < min_value) continue; - if (player_hp[PY_MAX_LEVEL - 1] > max_value) continue; - - /* Acceptable */ - break; - } - - p_ptr->tactic = 4; - p_ptr->movement = 4; -} - - -/* - * Get the racial history, and social class, using the "history charts". - */ -static void get_history(void) -{ - int i, n, chart, roll, social_class; - - char *s, *t; - - char buf[240]; - - - /* Clear the previous history strings */ - for (i = 0; i < 4; i++) history[i][0] = '\0'; - - /* Clear the history text */ - buf[0] = '\0'; - - /* Initial social class */ - social_class = randint(4); - - /* Starting place */ - chart = rp_ptr->chart; - - /* Process the history */ - while (chart) - { - /* Start over */ - i = 0; - - /* Roll for nobility */ - roll = randint(100); - - - /* Access the proper entry in the table */ - while ((chart != bg[i].chart) || (roll > bg[i].roll)) i++; - - /* Acquire the textual history */ - (void)strcat(buf, bg[i].info + rp_text); - - /* Add in the social class */ - social_class += (int)(bg[i].bonus) - 50; - - /* Enter the next chart */ - chart = bg[i].next; - } - - - - /* Verify social class */ - if (social_class > 100) social_class = 100; - else if (social_class < 1) social_class = 1; - - /* Save the social class */ - p_ptr->sc = social_class; - - - /* Skip leading spaces */ - for (s = buf; *s == ' '; s++) /* loop */; - - /* Get apparent length */ - n = strlen(s); - - /* Kill trailing spaces */ - while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0'; - - - /* Start at first line */ - i = 0; - - /* Collect the history */ - while (TRUE) - { - /* Extract remaining length */ - n = strlen(s); - - /* All done */ - if (n < 60) - { - /* Save one line of history */ - strcpy(history[i++], s); - - /* All done */ - break; - } - - /* Find a reasonable break-point */ - for (n = 60; ((n > 0) && (s[n - 1] != ' ')); n--) /* loop */; - - /* Save next location */ - t = s + n; - - /* Wipe trailing spaces */ - while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0'; - - /* Save one line of history */ - strcpy(history[i++], s); - - /* Start next line */ - for (s = t; *s == ' '; s++) /* loop */; - } -} - - -/* - * Fill the random_artifacts array with relevant info. - */ -errr init_randart(void) -{ - int i; - - long cost; - - random_artifact* ra_ptr; - - char buf[80]; - - - for (i = 0; i < MAX_RANDARTS; i++) - { - ra_ptr = &random_artifacts[i]; - - strcpy(ra_ptr->name_short, - get_line("rart_s.txt", ANGBAND_DIR_FILE, buf, i)); - strcpy(ra_ptr->name_full, - get_line("rart_f.txt", ANGBAND_DIR_FILE, buf, i)); - - ra_ptr->attr = randint(15); - ra_ptr->activation = rand_int(MAX_T_ACT); - ra_ptr->generated = FALSE; - - cost = randnor(0, 250); - - if (cost < 0) cost = 0; - - ra_ptr->cost = cost; - } - - return 0; -} - - -/* - * A helper function for get_ahw(), also called by polymorph code - */ -void get_height_weight(void) -{ - int h_mean, h_stddev; - - int w_mean, w_stddev; - - - /* Extract mean and standard deviation -- Male */ - if (p_ptr->psex == SEX_MALE) - { - h_mean = rp_ptr->m_b_ht + rmp_ptr->m_b_ht; - h_stddev = rp_ptr->m_m_ht + rmp_ptr->m_m_ht; - - w_mean = rp_ptr->m_b_wt + rmp_ptr->m_b_wt; - w_stddev = rp_ptr->m_m_wt + rmp_ptr->m_m_wt; - } - - /* Female */ - else if (p_ptr->psex == SEX_FEMALE) - { - h_mean = rp_ptr->f_b_ht + rmp_ptr->f_b_ht; - h_stddev = rp_ptr->f_m_ht + rmp_ptr->f_m_ht; - - w_mean = rp_ptr->f_b_wt + rmp_ptr->f_b_wt; - w_stddev = rp_ptr->f_m_wt + rmp_ptr->f_m_wt; - } - - /* Neuter XXX */ - else - { - h_mean = (rp_ptr->m_b_ht + rmp_ptr->m_b_ht + - rp_ptr->f_b_ht + rmp_ptr->f_b_ht) / 2, - h_stddev = (rp_ptr->m_m_ht + rmp_ptr->m_m_ht + - rp_ptr->f_m_ht + rmp_ptr->f_m_ht) / 2; - - w_mean = (rp_ptr->m_b_wt + rmp_ptr->m_b_wt + - rp_ptr->f_b_wt + rmp_ptr->f_b_wt) / 2, - w_stddev = (rp_ptr->m_m_wt + rmp_ptr->m_m_wt + - rp_ptr->f_m_wt + rmp_ptr->f_m_wt) / 2; - } - - /* Calculate height/weight */ - p_ptr->ht = randnor(h_mean, h_stddev); - p_ptr->wt = randnor(w_mean, w_stddev); - - /* Weight/height shouldn't be negative */ - if (p_ptr->ht < 1) p_ptr->ht = 1; - if (p_ptr->wt < 1) p_ptr->wt = 1; -} - - -/* - * Computes character's age, height, and weight - */ -static void get_ahw(void) -{ - /* Calculate the age */ - p_ptr->age = rp_ptr->b_age + rmp_ptr->b_age + - randint(rp_ptr->m_age + rmp_ptr->m_age); - - /* Calculate the height/weight */ - get_height_weight(); -} - - - - -/* - * Get the player's starting money - */ -static void get_money(void) -{ - int i, gold; - - - /* Social Class determines starting gold */ - gold = (p_ptr->sc * 6) + randint(100) + 300; - - /* Process the stats */ - for (i = 0; i < 6; i++) - { - /* Mega-Hack -- reduce gold for high stats */ - if (stat_use[i] >= 18 + 50) gold -= 300; - else if (stat_use[i] >= 18 + 20) gold -= 200; - else if (stat_use[i] > 18) gold -= 150; - else gold -= (stat_use[i] - 8) * 10; - } - - /* Minimum 100 gold */ - if (gold < 100) gold = 100; - - /* Save the gold */ - p_ptr->au = gold; -} - - - -/* - * Display stat values, subset of "put_stats()" - * - * See 'display_player()' for basic method. - */ -static void birth_put_stats(void) -{ - int i, p; - - byte attr; - - char buf[80]; - - - /* Put the stats (and percents) */ - for (i = 0; i < 6; i++) - { - /* Put the stat */ - cnv_stat(p_ptr->stat_use[i], buf); - c_put_str(TERM_L_GREEN, buf, 2 + i, 66); - - /* Put the percent */ - if (stat_match[i]) - { - p = 1000L * stat_match[i] / auto_round; - attr = (p < 100) ? TERM_YELLOW : TERM_L_GREEN; - strnfmt(buf, 80, "%3d.%d%%", p / 10, p % 10); - c_put_str(attr, buf, 2 + i, 73); - } - - /* Never happened */ - else - { - c_put_str(TERM_RED, "(NONE)", 2 + i, 73); - } - } -} - - -/* - * Clear all the global "character" data - */ -static void player_wipe(void) -{ - int i, j; - - - /* Wipe special levels */ - wipe_saved(); - - /* Hack -- zero the struct */ - p_ptr = WIPE(p_ptr, player_type); - - /* Not dead yet */ - p_ptr->lives = 0; - - /* Wipe the history */ - for (i = 0; i < 4; i++) - { - for (j = 0; j < 60; j++) - { - if (j < 59) history[i][j] = ' '; - else history[i][j] = '\0'; - } - } - - /* Wipe the towns */ - for (i = 0; i < max_d_idx; i++) - { - for (j = 0; j < MAX_DUNGEON_DEPTH; j++) - { - special_lvl[j][i] = 0; - } - } - - /* Wipe the towns */ - for (i = max_real_towns + 1; i < max_towns; i++) - { - town_info[i].flags = 0; - } - - /* Wipe the quests */ - for (i = 0; i < MAX_Q_IDX; i++) - { - quest[i].status = QUEST_STATUS_UNTAKEN; - for (j = 0; j < sizeof(quest[i].data)/sizeof(quest[i].data[0]); j++) - { - quest[i].data[j] = 0; - } - } - - /* Wipe the rune spells */ - rune_num = 0; - for (i = 0; i < MAX_RUNES; i++) - { - strcpy(rune_spells[i].name, ""); - rune_spells[i].type = 0; - rune_spells[i].rune2 = 0; - rune_spells[i].mana = 0; - } - - /* No items */ - inven_cnt = 0; - equip_cnt = 0; - - /* Clear the inventory */ - for (i = 0; i < INVEN_TOTAL; i++) - { - object_wipe(&p_ptr->inventory[i]); - } - - /* Generate random artifacts */ - init_randart(); - - /* Start with no artifacts made yet */ - for (i = 0; i < max_a_idx; i++) - { - artifact_type *a_ptr = &a_info[i]; - a_ptr->cur_num = 0; - } - - /* Reset the "objects" */ - for (i = 1; i < max_k_idx; i++) - { - object_kind *k_ptr = &k_info[i]; - - /* Reset "tried" */ - k_ptr->tried = FALSE; - - /* Reset "aware" */ - k_ptr->aware = FALSE; - - /* Reset "know" */ - k_ptr->know = FALSE; - - /* Reset "artifact" */ - k_ptr->artifact = 0; - } - - - /* Reset the "monsters" */ - for (i = 1; i < max_r_idx; i++) - { - monster_race *r_ptr = &r_info[i]; - - /* Hack -- Reset the counter */ - r_ptr->cur_num = 0; - - /* Hack -- Reset the max counter */ - r_ptr->max_num = 100; - - /* Hack -- Reset the max counter */ - if (r_ptr->flags1 & RF1_UNIQUE) r_ptr->max_num = 1; - if (r_ptr->flags3 & RF3_UNIQUE_4) r_ptr->max_num = 4; - - /* Clear player kills */ - r_ptr->r_pkills = 0; - - /* Clear saved flag */ - r_ptr->on_saved = FALSE; - } - - - /* Hack -- Well fed player */ - p_ptr->food = PY_FOOD_FULL - 1; - - /* Wipe the alchemists' recipes */ - for ( i = 0 ; i < 32 ; i++) - alchemist_known_egos[i] = 0; - for ( i = 0 ; i < 6 ; i++) - alchemist_known_artifacts[i] = 0; - alchemist_gained = 0; - - /* Clear "cheat" options */ - cheat_peek = FALSE; - cheat_hear = FALSE; - cheat_room = FALSE; - cheat_xtra = FALSE; - cheat_know = FALSE; - cheat_live = FALSE; - - /* Assume no winning game */ - total_winner = 0; - has_won = FALSE; - - /* Assume no cheating */ - noscore = 0; - wizard = 0; - - /* Assume no innate spells */ - spell_num = 0; - - /* Clear the fate */ - for (i = 0; i < MAX_FATES; i++) - { - fates[i].fate = 0; - } - p_ptr->no_mortal = FALSE; - - /* Player don't have the black breath from the beginning !*/ - p_ptr->black_breath = FALSE; - - /* Default pet command settings */ - p_ptr->pet_follow_distance = 6; - p_ptr->pet_open_doors = FALSE; - p_ptr->pet_pickup_items = FALSE; - - /* Body changing initialisation */ - p_ptr->body_monster = 0; - p_ptr->disembodied = FALSE; - - /* Wipe the bounties */ - total_bounties = 0; - - /* Wipe spells */ - p_ptr->xtra_spells = 0; - - /* Wipe xtra hp */ - p_ptr->hp_mod = 0; - - /* Wipe the monsters */ - wipe_m_list(); - - /* Wipe the doppleganger */ - doppleganger = 0; - - /* Wipe the recall depths */ - for (i = 0; i < max_d_idx; i++) - { - max_dlv[i] = 0; - } - - /* Wipe the known inscription list */ - for (i = 0; i < MAX_INSCRIPTIONS; i++) - { - inscription_info[i].know = FALSE; - } - - /* Wipe the known traps list */ - for (i = 0; i < max_t_idx; i++) - { - t_info[i].known = 0; - t_info[i].ident = FALSE; - } - - /* Reset wild_mode to FALSE */ - p_ptr->wild_mode = FALSE; - p_ptr->old_wild_mode = FALSE; - - /* Initialize allow_one_death */ - p_ptr->allow_one_death = 0; - - p_ptr->loan = p_ptr->loan_time = 0; - - /* Wipe the power list */ - for (i = 0; i < POWER_MAX; i++) - { - p_ptr->powers_mod[i] = 0; - } - - /* No companions killed */ - p_ptr->companion_killed = 0; - - /* Inertia control */ - p_ptr->inertia_controlled_spell = -1; - - /* Automatic stat-gain */ - p_ptr->last_rewarded_level = 1; -} - - -/* Create an object */ -void outfit_obj(int tv, int sv, int pval, int dd, int ds) -{ - object_type forge; - object_type *q_ptr; - - /* Get local object */ - q_ptr = &forge; - q_ptr->pval = 0; - q_ptr->pval2 = 0; - - /* Hack -- Give the player an object */ - object_prep(q_ptr, lookup_kind(tv, sv)); - - if (pval) - q_ptr->pval = pval; - - /* These objects are "storebought" */ - q_ptr->ident |= IDENT_MENTAL; - q_ptr->number = damroll(dd, ds); - - object_aware(q_ptr); - object_known(q_ptr); - (void)inven_carry(q_ptr, FALSE); -} - - -/* - * Give the player an object. - */ -static void player_outfit_object(int qty, int tval, int sval) -{ - object_type forge; - object_type *q_ptr = &forge; - object_prep(q_ptr, lookup_kind(tval, sval)); - q_ptr->number = qty; - object_aware(q_ptr); - object_known(q_ptr); - (void)inven_carry(q_ptr, FALSE); -} - - -/* - * Give player a spell book. - */ -static void player_outfit_spellbook(cptr spell_name) -{ - object_type forge; - object_type *q_ptr = &forge; - object_prep(q_ptr, lookup_kind(TV_BOOK, 255)); - q_ptr->pval = find_spell(spell_name); - q_ptr->ident |= IDENT_MENTAL | IDENT_KNOWN; - inven_carry(q_ptr, FALSE); -} - - -/* - * Init players with some belongings - * - * Having an item makes the player "aware" of its purpose. - */ -static void player_outfit(void) -{ - int i; - cptr class_name = spp_ptr->title + c_name; - cptr subrace_name = rmp_ptr->title + rmp_name; - - /* - * Get an adventurer guide describing a bit of the - * wilderness. - */ - { - /* Hack -- Give the player an adventurer guide */ - player_outfit_object(1, TV_PARCHMENT, 20); - } - - /* - * Provide spell books - */ - if (game_module_idx == MODULE_TOME) - { - if (streq(class_name, "Ranger")) - { - player_outfit_spellbook("Phase Door"); - } - } - if (streq(class_name, "Geomancer")) - { - player_outfit_spellbook("Geyser"); - } - if (streq(class_name, "Priest(Eru)")) - { - player_outfit_spellbook("See the Music"); - } - if (streq(class_name, "Priest(Manwe)")) - { - player_outfit_spellbook("Manwe's Blessing"); - } - if (streq(class_name, "Druid")) - { - player_outfit_spellbook("Charm Animal"); - } - if (streq(class_name, "Dark-Priest")) - { - player_outfit_spellbook("Curse"); - } - if (streq(class_name, "Paladin")) - { - player_outfit_spellbook("Divine Aim"); - } - if (game_module_idx == MODULE_THEME) - { - /* Priests */ - if (streq(class_name, "Stonewright")) - { - player_outfit_spellbook("Firebrand"); - } - if (streq(class_name, "Priest(Varda)")) - { - player_outfit_spellbook("Light of Valinor"); - } - if (streq(class_name, "Priest(Ulmo)")) - { - player_outfit_spellbook("Song of Belegaer"); - } - if (streq(class_name, "Priest(Mandos)")) - { - player_outfit_spellbook("Tears of Luthien"); - } - - /* Dragons */ - if (streq(subrace_name, "Red")) - { - player_outfit_spellbook("Globe of Light"); - } - if (streq(subrace_name, "Black")) - { - player_outfit_spellbook("Geyser"); - } - if (streq(subrace_name, "Green")) - { - player_outfit_spellbook("Noxious Cloud"); - } - if (streq(subrace_name, "Blue")) - { - player_outfit_spellbook("Stone Skin"); - } - if (streq(subrace_name, "White")) - { - player_outfit_spellbook("Sense Monsters"); - } - if (streq(subrace_name, "Ethereal")) - { - player_outfit_spellbook("Recharge"); - } - - /* Demons */ - if (streq(subrace_name, "(Aewrog)")) - { - player_outfit_spellbook("Charm"); - } - if (streq(subrace_name, "(Narrog)")) - { - player_outfit_spellbook("Phase Door"); - } - - /* Peace-mages */ - if (streq(class_name, "Peace-mage")) - { - player_outfit_spellbook("Phase Door"); - } - - /* Wainriders */ - if (streq(class_name, "Wainrider")) - { - player_outfit_spellbook("Curse"); - } - } - - if (streq(class_name, "Mimic")) - { - object_type forge; - object_type *q_ptr = &forge; - - object_prep(q_ptr, lookup_kind(TV_CLOAK, SV_MIMIC_CLOAK)); - q_ptr->pval2 = resolve_mimic_name("Mouse"); - q_ptr->ident |= IDENT_MENTAL | IDENT_KNOWN; - inven_carry(q_ptr, FALSE); - } - - if (game_module_idx == MODULE_THEME) - { - /* Give everyone a scroll of WoR. */ - player_outfit_object(1, TV_SCROLL, SV_SCROLL_WORD_OF_RECALL); - - /* Identify everything in pack. */ - identify_pack_fully(); - } - - if (streq(rmp_ptr->title + rmp_name, "Vampire")) - { - player_gain_corruption(CORRUPT_VAMPIRE_TEETH); - player_gain_corruption(CORRUPT_VAMPIRE_STRENGTH); - player_gain_corruption(CORRUPT_VAMPIRE_VAMPIRE); - } - - process_hooks(HOOK_BIRTH_OBJECTS, "()"); - meta_inertia_control_hook_birth_objects(); - - { - /* Hack -- Give the player some food */ - int qty = (byte)rand_range(3, 7); - player_outfit_object(qty, TV_FOOD, SV_FOOD_RATION); - } - - { - object_type forge; - object_type *q_ptr = &forge; - /* Hack -- Give the player some torches */ - object_prep(q_ptr, lookup_kind(TV_LITE, SV_LITE_TORCH)); - q_ptr->number = (byte)rand_range(3, 7); - q_ptr->timeout = rand_range(3, 7) * 500; - object_aware(q_ptr); - object_known(q_ptr); - (void)inven_carry(q_ptr, FALSE); - } - - /* Rogues have a better knowledge of traps */ - if (has_ability(AB_TRAPPING)) - { - t_info[TRAP_OF_DAGGER_I].known = randint(50) + 50; - t_info[TRAP_OF_POISON_NEEDLE].known = randint(50) + 50; - t_info[TRAP_OF_FIRE_BOLT].known = randint(50) + 50; - t_info[TRAP_OF_DAGGER_I].ident = TRUE; - t_info[TRAP_OF_POISON_NEEDLE].ident = TRUE; - t_info[TRAP_OF_FIRE_BOLT].ident = TRUE; - - /* Hack -- Give the player a some ammo for the traps */ - object_type forge; - object_type *q_ptr = &forge; - object_prep(q_ptr, lookup_kind(TV_SHOT, SV_AMMO_NORMAL)); - q_ptr->number = (byte)rand_range(5, 15); - object_aware(q_ptr); - object_known(q_ptr); - - /* These objects are "storebought" */ - q_ptr->ident |= IDENT_MENTAL; - - (void)inven_carry(q_ptr, FALSE); - } - - /* Hack -- Give the player some useful objects */ - for (i = 0; i < rp_ptr->obj_num; i++) - outfit_obj(rp_ptr->obj_tval[i], rp_ptr->obj_sval[i], rp_ptr->obj_pval[i], rp_ptr->obj_dd[i], rp_ptr->obj_ds[i]); - for (i = 0; i < rmp_ptr->obj_num; i++) - outfit_obj(rmp_ptr->obj_tval[i], rmp_ptr->obj_sval[i], rmp_ptr->obj_pval[i], rmp_ptr->obj_dd[i], rmp_ptr->obj_ds[i]); - for (i = 0; i < cp_ptr->obj_num; i++) - outfit_obj(cp_ptr->obj_tval[i], cp_ptr->obj_sval[i], cp_ptr->obj_pval[i], cp_ptr->obj_dd[i], cp_ptr->obj_ds[i]); - for (i = 0; i < cp_ptr->spec[p_ptr->pspec].obj_num; i++) - outfit_obj(cp_ptr->spec[p_ptr->pspec].obj_tval[i], cp_ptr->spec[p_ptr->pspec].obj_sval[i], cp_ptr->spec[p_ptr->pspec].obj_pval[i], cp_ptr->spec[p_ptr->pspec].obj_dd[i], cp_ptr->spec[p_ptr->pspec].obj_ds[i]); -} - - -/* Possible number(and layout) or random quests */ -#define MAX_RANDOM_QUESTS_TYPES ((8 * 3) + (8 * 1)) -int random_quests_types[MAX_RANDOM_QUESTS_TYPES] = -{ - 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */ - 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */ - 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */ - 20, 13, 15, 16, 9, 17, 18, 8, /* Hero Sword Quest */ -}; - -/* Enforce OoD monsters until this level */ -#define RQ_LEVEL_CAP 49 - -static void gen_random_quests(int n) -{ - int step, lvl, i, k; - int old_type = dungeon_type; - - /* Factor dlev value by 1000 to keep precision */ - step = (98 * 1000) / n; - - lvl = step / 2; - - quest[QUEST_RANDOM].status = QUEST_STATUS_TAKEN; - - for (i = 0; i < n; i++) - { - monster_race *r_ptr = &r_info[2]; - - int rl = (lvl / 1000) + 1; - - int min_level; - - int tries = 5000; - - random_quest *q_ptr = &random_quests[rl]; - - int j; - - /* Find the appropriate dungeon */ - for (j = 0; j < max_d_idx; j++) - { - dungeon_info_type *d_ptr = &d_info[j]; - - if (!(d_ptr->flags1 & DF1_PRINCIPAL)) continue; - - if ((d_ptr->mindepth <= rl) && (rl <= d_ptr->maxdepth)) - { - dungeon_type = j; - break; - } - } - - q_ptr->type = random_quests_types[rand_int(MAX_RANDOM_QUESTS_TYPES)]; - - /* XXX XXX XXX Try until valid choice is found */ - while (tries) - { - bool_ ok; - - tries--; - - /* Random monster 5 - 10 levels out of depth */ - q_ptr->r_idx = get_mon_num(rl + 4 + randint(6)); - - if (!q_ptr->r_idx) continue; - - r_ptr = &r_info[q_ptr->r_idx]; - - /* Accept only monsters that can be generated */ - if (r_ptr->flags9 & RF9_SPECIAL_GENE) continue; - if (r_ptr->flags9 & RF9_NEVER_GENE) continue; - - /* Accept only monsters that are not breeders */ - if (r_ptr->flags4 & RF4_MULTIPLY) continue; - - /* Forbid joke monsters */ - if (r_ptr->flags8 & RF8_JOKEANGBAND) continue; - - /* Accept only monsters that are not friends */ - if (r_ptr->flags7 & RF7_PET) continue; - - /* Refuse nazguls */ - if (r_ptr->flags7 & RF7_NAZGUL) continue; - - /* Accept only monsters that are not good */ - if (r_ptr->flags3 & RF3_GOOD) continue; - - /* If module says a monster race is friendly, then skip */ - if (modules[game_module_idx].race_status != NULL) - { - s16b *status = (*modules[game_module_idx].race_status)(q_ptr->r_idx); - if ((status != NULL) && (*status >= 0)) - { - continue; - } - } - - /* Assume no explosion attacks */ - ok = TRUE; - - /* Reject monsters with exploding attacks */ - for (k = 0; k < 4; k++) - { - if (r_ptr->blow[k].method == RBM_EXPLODE) ok = FALSE; - } - if (!ok) continue; - - /* No mutliple uniques */ - if ((r_ptr->flags1 & RF1_UNIQUE) && - ((q_ptr->type != 1) || (r_ptr->max_num == -1))) continue; - - /* No single non uniques */ - if ((!(r_ptr->flags1 & RF1_UNIQUE)) && (q_ptr->type == 1)) continue; - - /* Level restriction */ - min_level = (rl > RQ_LEVEL_CAP) ? RQ_LEVEL_CAP : rl; - - /* Accept monsters matching the level restriction */ - if (r_ptr->level > min_level) break; - } - - /* Arg could not find anything ??? */ - if (!tries) - { - if (wizard) - { - message_add(format("Could not find quest monster on lvl %d", rl), TERM_RED); - } - q_ptr->type = 0; - } - else - { - if (r_ptr->flags1 & RF1_UNIQUE) - { - r_ptr->max_num = -1; - } - - q_ptr->done = FALSE; - - if (wizard) - { - message_add(format("Quest for %d on lvl %d", - q_ptr->r_idx, rl), TERM_RED); - } - } - - lvl += step; - } - - dungeon_type = old_type; -} - -int dump_classes(s16b *classes, int sel, u32b *restrictions) -{ - int n = 0; - - char buf[80]; - char *desc; - - cptr str; - - C_MAKE(desc, c_head->text_size, char); - - /* Clean up */ - clear_from(12); - - while (classes[n] != -1) - { - cptr mod = ""; - char p2 = ')', p1 = ' '; - - /* Analyze */ - p_ptr->pclass = classes[n]; - cp_ptr = &class_info[p_ptr->pclass]; - str = cp_ptr->title + c_name; - - if (sel == n) - { - p1 = '['; - p2 = ']'; - } - - /* Display */ - strnfmt(buf, 80, "%c%c%c %s%s", p1, - (n <= 25) ? I2A(n) : I2D(n - 26), p2, str, mod); - - /* Print some more info */ - if (sel == n) - { - strnfmt(desc, c_head->text_size, "%s%s", cp_ptr->desc + c_text, - cp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : ""); - print_desc(desc); - - if (!(restrictions[classes[n] / 32] & BIT(classes[n])) || - cp_ptr->flags1 & PR1_EXPERIMENTAL) - c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); - else - c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); - } - else - { - if (!(restrictions[classes[n] / 32] & BIT(classes[n])) || - cp_ptr->flags1 & PR1_EXPERIMENTAL) - c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); - else - put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4)); - } - n++; - } - - C_FREE(desc, c_head->text_size, char); - - return (n); -} - -int dump_specs(int sel) -{ - int n = 0; - - char buf[80]; - char *desc; - - cptr str; - - C_MAKE(desc, c_head->text_size, char); - - /* Clean up */ - clear_from(12); - - for (n = 0; n < MAX_SPEC; n++) - { - char p2 = ')', p1 = ' '; - - /* Found the last one ? */ - if (!class_info[p_ptr->pclass].spec[n].title) break; - - /* Analyze */ - p_ptr->pspec = n; - spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; - str = spp_ptr->title + c_name; - - if (sel == n) - { - p1 = '['; - p2 = ']'; - } - - /* Display */ - strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str); - - /* Print some more info */ - if (sel == n) - { - strnfmt(desc, c_head->text_size, "%s%s", spp_ptr->desc + c_text, - spp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : ""); - print_desc(desc); - - if (spp_ptr->flags1 & PR1_EXPERIMENTAL) - c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); - else - c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); - } - else - { - if (spp_ptr->flags1 & PR1_EXPERIMENTAL) - c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); - else - put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4)); - } - } - - C_FREE(desc, c_head->text_size, char); - - return (n); -} - -int dump_races(int sel) -{ - int n = 0; - - char buf[80]; - char *desc; - - cptr str; - - C_MAKE(desc, rp_head->text_size, char); - - /* Clean up */ - clear_from(12); - - for (n = 0; n < max_rp_idx; n++) - { - char p2 = ')', p1 = ' '; - - /* Analyze */ - p_ptr->prace = n; - rp_ptr = &race_info[p_ptr->prace]; - str = rp_ptr->title + rp_name; - - if (sel == n) - { - p1 = '['; - p2 = ']'; - } - - /* Display */ - strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str); - - /* Print some more info */ - if (sel == n) - { - strnfmt(desc, rp_head->text_size, "%s%s", rp_ptr->desc + rp_text, - rp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : ""); - print_desc(desc); - - if (rp_ptr->flags1 & PR1_EXPERIMENTAL) - c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); - else - c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); - } - else - { - if (rp_ptr->flags1 & PR1_EXPERIMENTAL) - c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); - else - put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5)); - } - } - - C_FREE(desc, rp_head->text_size, char); - - return (n); -} - - -int dump_rmods(int sel, int *racem, int max) -{ - int n = 0; - - char buf[80]; - char *desc; - - cptr str; - - C_MAKE(desc, rmp_head->text_size, char); - - /* Clean up */ - clear_from(12); - - /* Dump races */ - for (n = 0; n < max; n++) - { - char p2 = ')', p1 = ' '; - - /* Analyze */ - p_ptr->pracem = racem[n]; - rmp_ptr = &race_mod_info[p_ptr->pracem]; - str = rmp_ptr->title + rmp_name; - - if (sel == n) - { - p1 = '['; - p2 = ']'; - } - - /* Display */ - if (racem[n]) - strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str); - else - strnfmt(buf, 80, "%c%c%c Classical", p1, I2A(n), p2); - - /* Print some more info */ - if (sel == n) - { - strnfmt(desc, rmp_head->text_size, "%s%s", rmp_ptr->desc + rmp_text, - rmp_ptr->flags1 & PR1_EXPERIMENTAL ? "\nEXPERIMENTAL" : ""); - print_desc(desc); - - if (rmp_ptr->flags1 & PR1_EXPERIMENTAL) - c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); - else - c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); - } - else - { - if (rmp_ptr->flags1 & PR1_EXPERIMENTAL) - c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); - else - put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5)); - } - } - - C_FREE(desc, rmp_head->text_size, char); - - return (n); -} - -int dump_gods(int sel, int *choice, int max) -{ - int i, j; - char buf[80]; - cptr str; - - /* Clean up */ - clear_from(12); - - Term_putstr(5, 17, -1, TERM_WHITE, - "You can choose to worship a god, some class must start with a god."); - - for (i = 0; i < max; i++) - { - char p2 = ')', p1 = ' '; - int n = choice[i]; - deity_type *g_ptr = &deity_info[0]; - - if (!n) str = "No God"; - else - { - g_ptr = &deity_info[n]; - str = g_ptr->name; - } - - if (sel == i) - { - p1 = '['; - p2 = ']'; - } - - /* Display */ - strnfmt(buf, 80, "%c%c%c %s", p1, I2A(i), p2, str); - - /* Print some more info */ - if (sel == i) - { - if (n) - { - /* Display the first four lines of the god description */ - for (j = 0; j < 4; j++) - if (strcmp(g_ptr->desc[j], "")) - print_desc_aux(g_ptr->desc[j], 12 + j, 1); - } - else print_desc("You can begin as an atheist and still convert to a god later."); - - c_put_str(TERM_L_BLUE, buf, 20 + (i / 4), 1 + 20 * (i % 4)); - } - else - { - put_str(buf, 20 + (i / 4), 1 + 20 * (i % 4)); - } - } - - return (max); -} - - -/* Ask questions */ -static bool_ do_quick_start = FALSE; - -static bool_ player_birth_aux_ask() -{ - int i, k, n, v, sel; - - int racem[100], max_racem = 0; - - u32b restrictions[2]; - - cptr str; - - char c; - - char p2 = ')'; - - char buf[200]; - char inp[200]; - - s16b *class_types; - - /*** Intro ***/ - - /* Clear screen */ - Term_clear(); - - /* Title everything */ - put_str("Name :", 2, 1); - put_str("Sex :", 3, 1); - put_str("Race :", 4, 1); - put_str("Class :", 5, 1); - - /* Dump the default name */ - c_put_str(TERM_L_BLUE, player_name, 2, 9); - - - /*** Instructions ***/ - - /* Display some helpful information */ - Term_putstr(5, 8, -1, TERM_WHITE, - "Please answer the following questions. Most of the questions"); - Term_putstr(5, 9, -1, TERM_WHITE, - "display a set of standard answers, and many will also accept"); - Term_putstr(5, 10, -1, TERM_WHITE, - "some special responses, including 'Q' to quit, 'S' to restart,"); - Term_putstr(5, 11, -1, TERM_WHITE, - "and '?' for help. Note that 'Q' and 'S' must be capitalized."); - - - /*** Quick Start ***/ - - if (previous_char.quick_ok) - { - /* Extra info */ - Term_putstr(1, 15, -1, TERM_WHITE, - "Do you want to use the quick start function(same character as your last one)."); - - /* Choose */ - while (1) - { - put_str("Use quick start (y/n)?", 20, 2); - c = inkey(); - if (c == 'Q') quit(NULL); - else if (c == 'S') return (FALSE); - else if ((c == 'y') || (c == 'Y')) - { - do_quick_start = TRUE; - break; - } - else - { - do_quick_start = FALSE; - break; - } - } - } - - /* Clean up */ - clear_from(15); - - /*** Player sex ***/ - - if (do_quick_start) - { - k = previous_char.sex; - } - else - { - /* Extra info */ - Term_putstr(5, 15, -1, TERM_WHITE, - "Your 'sex' does not have any significant gameplay effects."); - - /* Prompt for "Sex" */ - for (n = 0; n < MAX_SEXES; n++) - { - /* Analyze */ - p_ptr->psex = n; - sp_ptr = &sex_info[p_ptr->psex]; - str = sp_ptr->title; - - /* Display */ - strnfmt(buf, 200, "%c%c %s", I2A(n), p2, str); - put_str(buf, 21 + (n / 5), 2 + 15 * (n % 5)); - } - - /* Choose */ - while (1) - { - strnfmt(buf, 200, "Choose a sex (%c-%c), * for random, = for options: ", I2A(0), I2A(n - 1)); - put_str(buf, 20, 2); - c = inkey(); - if (c == 'Q') quit(NULL); - if (c == 'S') return (FALSE); - if (c == '*') - { - k = rand_int(MAX_SEXES); - break; - } - k = (islower(c) ? A2I(c) : -1); - if ((k >= 0) && (k < n)) break; - if (c == '?') do_cmd_help(); - else if (c == '=') - { - screen_save(); - do_cmd_options_aux(6, "Startup Options", FALSE); - screen_load(); - } - else bell(); - } - } - - /* Set sex */ - p_ptr->psex = k; - sp_ptr = &sex_info[p_ptr->psex]; - str = sp_ptr->title; - - /* Display */ - c_put_str(TERM_L_BLUE, str, 3, 9); - - /* Clean up */ - clear_from(15); - - - /*** Player race ***/ - - if (do_quick_start) - { - k = previous_char.race; - } - else - { - /* Only one choice = instant choice */ - if (max_rp_idx == 1) - k = 0; - else - { - /* Extra info */ - Term_putstr(5, 16, -1, TERM_WHITE, - "Your 'race' determines various intrinsic factors and bonuses."); - - /* Dump races */ - sel = 0; - n = dump_races(sel); - - /* Choose */ - while (1) - { - strnfmt(buf, 200, "Choose a race (%c-%c), * for a random choice, = for options, 8/2/4/6 for movement: ", - I2A(0), I2A(max_rp_idx - 1)); - put_str(buf, 17, 2); - - c = inkey(); - if (c == 'Q') quit(NULL); - if (c == 'S') return (FALSE); - if (c == '*') - { - k = rand_int(max_rp_idx); - break; - } - k = (islower(c) ? A2I(c) : -1); - if ((k >= 0) && (k < n)) break; - if (c == '?') - { - help_race(race_info[sel].title + rp_name); - } - else if (c == '=') - { - screen_save(); - do_cmd_options_aux(6, "Startup Options", FALSE); - screen_load(); - } - else if (c == '2') - { - sel += 5; - if (sel >= n) sel %= 5; - dump_races(sel); - } - else if (c == '8') - { - sel -= 5; - if (sel < 0) sel = n - 1 -( ( -sel) % 5); - /* C's modulus operator does not have defined - results for negative first values. Damn. */ - dump_races(sel); - } - else if (c == '6') - { - sel++; - if (sel >= n) sel = 0; - dump_races(sel); - } - else if (c == '4') - { - sel--; - if (sel < 0) sel = n - 1; - dump_races(sel); - } - else if (c == '\r') - { - k = sel; - break; - } - else bell(); - } - } - } - /* Set race */ - p_ptr->prace = k; - rp_ptr = &race_info[p_ptr->prace]; - str = rp_ptr->title + rp_name; - - /* Display */ - c_put_str(TERM_L_BLUE, str, 4, 9); - - /* Get a random name */ - if (!do_quick_start) create_random_name(p_ptr->prace, player_name); - - /* Display */ - c_put_str(TERM_L_BLUE, player_name, 2, 9); - - /* Clean up */ - clear_from(12); - - - /*** Player race mod ***/ - if (do_quick_start) - { - k = previous_char.rmod; - p_ptr->pracem = k; - rmp_ptr = &race_mod_info[p_ptr->pracem]; - } - else - { - /* Only one choice = instant choice */ - if (max_rmp_idx == 1) - k = 0; - else - { - for (n = 0; n < 100; n++) racem[n] = 0; - - max_racem = 0; - for (n = 0; n < max_rmp_idx; n++) - { - /* Analyze */ - p_ptr->pracem = n; - rmp_ptr = &race_mod_info[p_ptr->pracem]; - - /* Must be an ok choice */ - if (!(BIT(p_ptr->prace) & rmp_ptr->choice[p_ptr->prace / 32])) continue; - - /* Ok thats a possibility */ - racem[max_racem++] = n; - } - - /* Ah ! nothing found, lets use the default */ - if (!max_racem) p_ptr->pracem = 0; - /* Only one ? use it */ - else if (max_racem == 1) p_ptr->pracem = racem[0]; - /* We got to ask the player */ - else - { - /* Extra info */ - Term_putstr(5, 15, -1, TERM_WHITE, - "Your 'race modifier' determines various intrinsic factors and bonuses."); - - /* Dump races */ - sel = 0; - n = dump_rmods(sel, racem, max_racem); - - /* Choose */ - while (1) - { - strnfmt(buf, 200, "Choose a race modifier (%c-%c), * for a random choice, = for options: ", - I2A(0), I2A(max_racem - 1)); - put_str(buf, 17, 2); - c = inkey(); - if (c == 'Q') quit(NULL); - if (c == 'S') return (FALSE); - if (c == '*') - { - do - { - k = rand_int(max_racem); - } - while (!(BIT(racem[k]) & rmp_ptr->choice[racem[k] / 32])); - break; - } - else if (c == '?') - { - help_subrace(race_mod_info[racem[sel]].title + rmp_name); - } - - k = (islower(c) ? A2I(c) : -1); - if ((k >= 0) && (k < max_racem) && - (BIT(p_ptr->prace) & race_mod_info[racem[k]].choice[p_ptr->prace / 32])) break; - - else if (c == '=') - { - screen_save(); - do_cmd_options_aux(6, "Startup Options", FALSE); - screen_load(); - } - else if (c == '2') - { - sel += 5; - if (sel >= n) sel = sel - n + 1; - dump_rmods(sel, racem, max_racem); - } - else if (c == '8') - { - sel -= 5; - if (sel < 0) sel = n - 1 + sel; - dump_rmods(sel, racem, max_racem); - } - else if (c == '6') - { - sel++; - if (sel >= n) sel = 0; - dump_rmods(sel, racem, max_racem); - } - else if (c == '4') - { - sel--; - if (sel < 0) sel = n - 1; - dump_rmods(sel, racem, max_racem); - } - else if (c == '\r') - { - k = sel; - break; - } - else bell(); - } - - /* Set race */ - p_ptr->pracem = racem[k]; - } - rmp_ptr = &race_mod_info[p_ptr->pracem]; - - /* Display */ - c_put_str(TERM_L_BLUE, get_player_race_name(p_ptr->prace, p_ptr->pracem), 4, 9); - } - } - - /* Clean up */ - clear_from(12); - - - /*** Player class ***/ - if (do_quick_start) - { - k = previous_char.pclass; - p_ptr->pclass = k; - cp_ptr = &class_info[p_ptr->pclass]; - k = previous_char.spec; - p_ptr->pspec = k; - spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; - } - else - { - int z; - - for (z = 0; z < 2; z++) - restrictions[z] = (rp_ptr->choice[z] | rmp_ptr->pclass[z]) & (~rmp_ptr->mclass[z]); - - if (max_mc_idx > 1) - { - /* Extra info */ - Term_putstr(5, 13, -1, TERM_WHITE, - "Your 'class' determines various intrinsic abilities and bonuses."); - - /* Get a class type */ - for (i = 0; i < max_mc_idx; i++) - c_put_str(meta_class_info[i].color, format("%c) %s", I2A(i), meta_class_info[i].name), 16 + i, 2); - while (1) - { - strnfmt(buf, 200, "Choose a class type (a-%c), * for random, = for options: ", I2A(max_mc_idx - 1)); - put_str(buf, 15, 2); - c = inkey(); - if (c == 'Q') quit(NULL); - if (c == 'S') return (FALSE); - if (c == '*') - { - k = rand_int(max_mc_idx); - break; - } - k = (islower(c) ? A2I(c) : (D2I(c) + 26)); - if ((k >= 0) && (k < max_mc_idx)) break; - if (c == '?') do_cmd_help(); - else if (c == '=') - { - screen_save(); - do_cmd_options_aux(6, "Startup Options", FALSE); - screen_load(); - } - else bell(); - } - } - else - { - k = 0; - } - class_types = meta_class_info[k].classes; - clear_from(15); - - /* Count classes */ - n = 0; - while (class_types[n] != -1) n++; - - /* Only one choice = instant choice */ - if (n == 1) - k = 0; - else - { - /* Dump classes */ - sel = 0; - n = dump_classes(class_types, sel, restrictions); - - /* Get a class */ - while (1) - { - strnfmt(buf, 200, "Choose a class (%c-%c), * for random, = for options, 8/2/4 for up/down/back: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1)); - put_str(buf, 15, 2); - c = inkey(); - if (c == 'Q') quit(NULL); - if (c == 'S') return (FALSE); - if (c == '*') - { - k = randint(n) - 1; - break; - } - k = (islower(c) ? A2I(c) : (D2I(c) + 26)); - if ((k >= 0) && (k < n)) break; - if (c == '?') - { - help_class(class_info[class_types[sel]].title + c_name); - } - else if (c == '=') - { - screen_save(); - do_cmd_options_aux(6, "Startup Options", FALSE); - screen_load(); - } - else if (c == '2') - { - sel += 4; - if (sel >= n) sel %= 4; - dump_classes(class_types, sel, restrictions); - } - else if (c == '8') - { - sel -= 4; - if (sel < 0) sel = n - 1 -( ( -sel) % 4); - /* C's modulus operator does not have defined - results for negative first values. Damn. */ - dump_classes(class_types, sel, restrictions); - } - else if (c == '6') - { - sel++; - if (sel >= n) sel = 0; - dump_classes(class_types, sel, restrictions); - } - else if (c == '4') - { - sel--; - if (sel < 0) sel = n - 1; - dump_classes(class_types, sel, restrictions); - } - else if (c == '\r') - { - k = sel; - break; - } - else bell(); - } - } - - /* Set class */ - p_ptr->pclass = class_types[k]; - - /* Choose class spec */ - clear_from(15); - - /* Count choices */ - for (n = 0; n < MAX_SPEC; n++) - { - /* Found the last one ? */ - if (!class_info[p_ptr->pclass].spec[n].title) break; - } - - /* Only one choice = auto choice */ - if (n == 1) - k = 0; - else - { - /* Dump classes spec */ - sel = 0; - n = dump_specs(sel); - - /* Get a class */ - while (1) - { - strnfmt(buf, 200, "Choose a class specialisation (%c-%c), * for random, = for options, 8/2/4/6 for up/down/left/right: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1)); - put_str(buf, 15, 2); - c = inkey(); - if (c == 'Q') quit(NULL); - if (c == 'S') return (FALSE); - if (c == '*') - { - k = randint(n) - 1; - break; - } - k = (islower(c) ? A2I(c) : (D2I(c) + 26)); - if ((k >= 0) && (k < n)) break; - if (c == '?') - { - help_class(class_info[p_ptr->pclass].spec[sel].title + c_name); - } - else if (c == '=') - { - screen_save(); - do_cmd_options_aux(6, "Startup Options", FALSE); - screen_load(); - } - else if (c == '2') - { - sel += 4; - if (sel >= n) sel = sel - n + 1; - dump_specs(sel); - } - else if (c == '8') - { - sel -= 4; - if (sel < 0) sel = n - 1 + sel; - dump_specs(sel); - } - else if (c == '6') - { - sel++; - if (sel >= n) sel = 0; - dump_specs(sel); - } - else if (c == '4') - { - sel--; - if (sel < 0) sel = n - 1; - dump_specs(sel); - } - else if (c == '\r') - { - k = sel; - break; - } - else bell(); - } - } - - /* Set class spec */ - p_ptr->pspec = k; - } - cp_ptr = &class_info[p_ptr->pclass]; - spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; - str = spp_ptr->title + c_name; - - /* Display */ - c_put_str(TERM_L_BLUE, str, 5, 9); - - /* Clean up */ - clear_from(15); - - /*** Player god ***/ - if (do_quick_start) - { - k = previous_char.god; - p_ptr->pgod = k; - set_grace(previous_char.grace); - } - else if (PRACE_FLAG(PR1_NO_GOD)) - { - p_ptr->pgod = GOD_NONE; - } - else - { - int choice[MAX_GODS]; - int max = 0; - - /* Get the list of possible gods */ - for (n = 0; n < MAX_GODS; n++) - { - if (god_enabled(&deity_info[n]) && - ((cp_ptr->gods | spp_ptr->gods) & BIT(n))) - { - choice[max++] = n; - } - } - - if (!max) - { - p_ptr->pgod = GOD_NONE; - } - else if (max == 1) - { - p_ptr->pgod = choice[0]; - } - else if (max > 1) - { - sel = 0; - n = dump_gods(sel, choice, max); - - /* Choose */ - while (1) - { - strnfmt(buf, 200, "Choose a god (%c-%c), * for a random choice, " - "= for options, 8/2/4/6 for movement: ", - I2A(0), I2A(max - 1)); - put_str(buf, 19, 2); - - c = inkey(); - if (c == 'Q') quit(NULL); - if (c == 'S') - { - return (FALSE); - } - if (c == '*') - { - k = choice[randint(max) - 1]; - break; - } - k = (islower(c) ? A2I(c) : -1); - if ((k >= 0) && (k < max)) - { - k = choice[k]; - break; - } - if (c == '?') - { - help_god(deity_info[choice[sel]].name); - } - else if (c == '=') - { - screen_save(); - do_cmd_options_aux(6, "Startup Options", FALSE); - screen_load(); - } - else if (c == '2') - { - sel += 4; - if (sel >= n) sel %= 4; - dump_gods(sel, choice, max); - } - else if (c == '8') - { - sel -= 4; - /* C's modulus operator does not have defined - results for negative first values. Damn. */ - if (sel < 0) sel = n - 1 -( ( -sel) % 4); - dump_gods(sel, choice, max); - } - else if (c == '6') - { - sel++; - if (sel >= n) sel = 0; - dump_gods(sel, choice, max); - } - else if (c == '4') - { - sel--; - if (sel < 0) sel = n - 1; - dump_gods(sel, choice, max); - } - else if (c == '\r') - { - k = choice[sel]; - break; - } - else bell(); - } - - /* Set god */ - p_ptr->pgod = k; - p_ptr->grace = 0; - } - - /* A god that like us ? more grace ! */ - if (PRACE_FLAGS(PR1_GOD_FRIEND)) - { - set_grace(200); - } - else - { - set_grace(100); - } - } - - /* Clean up */ - clear_from(12); - - if (!do_quick_start) - { - /* Clear */ - clear_from(15); - - /* */ - if (get_check("Do you want to modify the options")) - { - screen_save(); - do_cmd_options_aux(6, "Startup Options", FALSE); - screen_load(); - } - } - - /* Set birth options: maximize, preserve, sepcial levels and astral */ - p_ptr->maximize = maximize; - p_ptr->preserve = preserve; - p_ptr->special = special_lvls; - p_ptr->astral = (PRACE_FLAG2(PR2_ASTRAL)) ? TRUE : FALSE; - - /* - * A note by pelpel. (remove this please) - * Be it the new Vanilla way (adult vs. birth options) or - * the old one (player_type members), it would be less confusing - * to handle birth-only options in a uniform fashion,the above and - * the following: - * ironman_rooms, - * joke_monsters, - * always_small_level, and - * fate_option - */ - - - /* Set the recall dungeon accordingly */ - dungeon_type = DUNGEON_BASE; - p_ptr->recall_dungeon = dungeon_type; - max_dlv[dungeon_type] = d_info[dungeon_type].mindepth; - - if (p_ptr->astral) - { - /* Somewhere in the misty mountains */ - dungeon_type = DUNGEON_ASTRAL; - p_ptr->wilderness_x = DUNGEON_ASTRAL_WILD_X; - p_ptr->wilderness_y = DUNGEON_ASTRAL_WILD_Y; - } - - /* Clean up */ - clear_from(10); - - /*** User enters number of quests ***/ - /* Heino Vander Sanden and Jimmy De Laet */ - - if (!ironman_rooms) - { - if (do_quick_start) - { - v = previous_char.quests; - } - else - { - /* Extra info */ - Term_putstr(5, 15, -1, TERM_WHITE, - "Select the number of optional random quests you'd like to receive."); - Term_putstr(5, 16, -1, TERM_WHITE, - "If you do not want any optional quests, enter 0."); - - /* Ask the number of additional quests */ - while (TRUE) - { - put_str(format("Number of quests? (0-%u) ", - MAX_RANDOM_QUEST - 1), 20, 2); - - /* Get a the number of additional quest */ - while (TRUE) - { - /* Move the cursor */ - put_str("", 20, 27); - - /* Default */ - strcpy(inp, "20"); - - /* Get a response (or escape) */ - if (!askfor_aux(inp, 2)) inp[0] = '\0'; - if (inp[0] == '*') v = rand_int(MAX_RANDOM_QUEST); - else v = atoi(inp); - - /* Break on valid input */ - if ((v < MAX_RANDOM_QUEST) && ( v >= 0 )) break; - } - break; - } - - /* Clear */ - clear_from(15); - } - } - else - { - /* NO quests for ironman rooms or persistent levels, since they - don't work */ - v = 0; - } - - /* Set the quest monster hook */ - get_mon_num_hook = monster_quest; - - /* Prepare allocation table */ - get_mon_num_prep(); - - /* Generate quests */ - for (i = 0; i < MAX_RANDOM_QUEST; i++) random_quests[i].type = 0; - if (v) gen_random_quests(v); - max_quests = v; - - p_ptr->inside_quest = 0; - - /* Init the plots */ - { - plots[PLOT_MAIN] = QUEST_NECRO; - quest[plots[PLOT_MAIN]].status = QUEST_STATUS_TAKEN; - - plots[PLOT_BREE] = QUEST_THIEVES; - quest[plots[PLOT_BREE]].status = QUEST_STATUS_UNTAKEN; - - plots[PLOT_LORIEN] = QUEST_WOLVES; - quest[plots[PLOT_LORIEN]].status = QUEST_STATUS_UNTAKEN; - - plots[PLOT_GONDOLIN] = QUEST_DRAGONS; - quest[plots[PLOT_GONDOLIN]].status = QUEST_STATUS_UNTAKEN; - - plots[PLOT_MINAS] = QUEST_HAUNTED; - quest[plots[PLOT_MINAS]].status = QUEST_STATUS_UNTAKEN; - - plots[PLOT_KHAZAD] = QUEST_EVIL; - quest[plots[PLOT_KHAZAD]].status = QUEST_STATUS_UNTAKEN; - - plots[PLOT_OTHER] = QUEST_NULL; - } - - quest_random_init_hook(QUEST_RANDOM); - - /* Ok */ - return (TRUE); -} - - - - -/* - * Initial stat costs (initial stats always range from 10 to 18 inclusive). - */ -static const int birth_stat_costs[(18-10) + 1] = -{ - 0, 1, 2, 4, 7, 11, 16, 22, 30 -}; - - -/* - * Helper function for 'player_birth()'. - * - * This function handles "point-based" character creation. - * - * The player selects, for each stat, a value from 10 to 18 (inclusive), - * each costing a certain amount of points (as above), from a pool of 48 - * available points, to which race/class modifiers are then applied. - * - * Each unused point is converted into 100 gold pieces, with a maximum of - * 600 gp at birth. - * - * Taken from V 2.9.0 - */ -static bool_ player_birth_aux_point(void) -{ - int i; - - int row = 3; - - int col = 42; - - int stat = 0; - - int stats[6]; - - int cost; - - char ch; - - char buf[80]; - - int mode = 0; - - - /* Initialize stats */ - for (i = 0; i < 6; i++) - { - /* Initial stats */ - stats[i] = 10; - } - - - /* Roll for base hitpoints */ - get_extra(); - - /* Roll for age/height/weight */ - get_ahw(); - - /* Roll for social class */ - get_history(); - - /*** Generate ***/ - process_hooks(HOOK_BIRTH, "()"); - - /* Get luck */ - p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5); - p_ptr->luck_max = p_ptr->luck_base; - - /* Interact */ - while (1) - { - /* Reset cost */ - cost = 0; - - /* Process stats */ - for (i = 0; i < 6; i++) - { - /* Variable stat maxes */ - if (p_ptr->maximize) - { - /* Reset stats */ - p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stats[i]; - - } - - /* Fixed stat maxes */ - else - { - /* Obtain a "bonus" for "race" and "class" */ - int bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i]; - - /* Apply the racial/class bonuses */ - p_ptr->stat_cur[i] = p_ptr->stat_max[i] = - modify_stat_value(stats[i], bonus); - } - - /* Total cost */ - cost += birth_stat_costs[stats[i] - 10]; - } - - /* Restrict cost */ - if (cost > 48) - { - /* Warning */ - bell(); - - /* Reduce stat */ - stats[stat]--; - - /* Recompute costs */ - continue; - } - - /* Gold is inversely proportional to cost */ - p_ptr->au = (100 * (48 - cost)) + 100; - - /* Maximum of 600 gold */ - if (p_ptr->au > 600) p_ptr->au = 600; - - /* Calculate the bonuses and hitpoints */ - p_ptr->update |= (PU_BONUS | PU_HP); - - /* Update stuff */ - update_stuff(); - - /* Fully healed */ - p_ptr->chp = p_ptr->mhp; - - /* Fully rested */ - p_ptr->csp = p_ptr->msp; - - /* Display the player */ - display_player(mode); - - /* Display the costs header */ - put_str("Cost", row - 2, col + 32); - - /* Display the costs */ - for (i = 0; i < 6; i++) - { - /* Display cost */ - strnfmt(buf, 80, "%4d", birth_stat_costs[stats[i] - 10]); - put_str(buf, row + (i - 1), col + 32); - } - - - /* Prompt XXX XXX XXX */ - strnfmt(buf, 80, "Total Cost %2d/48. Use 2/8 to move, 4/6 to modify, ESC to accept.", cost); - prt(buf, 0, 0); - - /* Place cursor just after cost of current stat */ - Term_gotoxy(col + 36, row + stat - 1); - - /* Get key */ - ch = inkey(); - - /* Quit */ - if (ch == 'Q') quit(NULL); - - /* Start over */ - if (ch == 'S') return (FALSE); - - /* Done */ - if (ch == ESCAPE) break; - - /* Prev stat */ - if (ch == '8') - { - stat = (stat + 6 - 1) % 6; - } - - /* Next stat */ - if (ch == '2') - { - stat = (stat + 1) % 6; - } - - /* Decrease stat */ - if ((ch == '4') && (stats[stat] > 10)) - { - stats[stat]--; - } - - /* Increase stat */ - if ((ch == '6') && (stats[stat] < 18)) - { - stats[stat]++; - } - } - - - /* Done */ - return (TRUE); -} - -/* - * Use the autoroller or not to generate a char - */ -static bool_ player_birth_aux_auto() -{ - int i, j, m, v; - - int mode = 0; - - bool_ flag = FALSE; - - bool_ prev = FALSE; - - char c; - - char b1 = '['; - - char b2 = ']'; - - char buf[80]; - - char inp[80]; - - - /* Initialize */ - if (autoroll) - { - int mval[6]; - - - /* Clear fields */ - auto_round = 0L; - last_round = 0L; - - /* Clean up */ - clear_from(10); - - /* Prompt for the minimum stats */ - put_str("Enter minimum attribute for: ", 15, 2); - - /* Output the maximum stats */ - for (i = 0; i < 6; i++) - { - char stat_buf[15]; - - /* Reset the "success" counter */ - stat_match[i] = 0; - - /* Race/Class bonus */ - j = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i]; - - /* Obtain the "maximal" stat */ - m = adjust_stat(17, j, TRUE); - - - /* Save the maximum */ - mval[i] = m; - - /* Extract a textual format */ - cnv_stat(m, stat_buf); - - strnfmt(inp, 80, "(Max of %s):", stat_buf); - - /* Prepare a prompt */ - strnfmt(buf, 80, "%-5s: %-20s", stat_names[i], inp); - - /* Dump the prompt */ - put_str(buf, 16 + i, 5); - } - - /* Input the minimum stats */ - for (i = 0; i < 6; i++) - { - /* Get a minimum stat */ - while (TRUE) - { - char *s; - - /* Move the cursor */ - put_str("", 16 + i, 30); - - /* Default */ - strcpy(inp, ""); - - /* Get a response (or escape) */ - if (!askfor_aux(inp, 8)) inp[0] = '\0'; - - /* Weirdos stat display .. erm .. I mean, original stat display */ - if (!linear_stats) - { - /* Hack -- add a fake slash */ - strcat(inp, "/"); - - /* Hack -- look for the "slash" */ - s = strchr(inp, '/'); - - /* Hack -- Nuke the slash */ - *s++ = '\0'; - - /* Hack -- Extract an input */ - v = atoi(inp) + atoi(s); - } - else - { - int z = atoi(inp); - - if (z <= 18) - v = z; - else - { - int extra = z - 18; - v = 18 + (extra * 10); - } - } - - /* Break on valid input */ - if (v <= mval[i]) break; - } - - /* Save the minimum stat */ - stat_limit[i] = (v > 0) ? v : 0; - } - } - - /* Roll */ - while (TRUE) - { - /* Feedback */ - if (autoroll) - { - Term_clear(); - - put_str("Name :", 2, 1); - put_str("Sex :", 3, 1); - put_str("Race :", 4, 1); - put_str("Class:", 5, 1); - - c_put_str(TERM_L_BLUE, player_name, 2, 9); - c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 9); - strnfmt(buf, 80, "%s", get_player_race_name(p_ptr->prace, p_ptr->pracem)); - c_put_str(TERM_L_BLUE, buf, 4, 9); - c_put_str(TERM_L_BLUE, spp_ptr->title + c_name, 5, 9); - - /* Label stats */ - put_str("STR:", 2 + A_STR, 61); - put_str("INT:", 2 + A_INT, 61); - put_str("WIS:", 2 + A_WIS, 61); - put_str("DEX:", 2 + A_DEX, 61); - put_str("CON:", 2 + A_CON, 61); - put_str("CHR:", 2 + A_CHR, 61); - - /* Note when we started */ - last_round = auto_round; - - /* Indicate the state */ - put_str("(Hit ESC to abort)", 11, 61); - - /* Label count */ - put_str("Round:", 9, 61); - } - - /* Otherwise just get a character */ - else - { - /* Get a new character */ - get_stats(); - } - - /* Auto-roll */ - while (autoroll) - { - bool_ accept = TRUE; - - /* Get a new character */ - get_stats(); - - /* Advance the round */ - auto_round++; - - /* Hack -- Prevent overflow */ - if (auto_round >= 1000000L) break; - - /* Check and count acceptable stats */ - for (i = 0; i < 6; i++) - { - /* This stat is okay */ - if (stat_use[i] >= stat_limit[i]) - { - stat_match[i]++; - } - - /* This stat is not okay */ - else - { - accept = FALSE; - } - } - - /* Break if "happy" */ - if (accept) break; - - /* Take note every 25 rolls */ - flag = (!(auto_round % AUTOROLLER_STEP)); - - /* Update display occasionally */ - if (flag || (auto_round < last_round + 100)) - { - /* Dump data */ - birth_put_stats(); - - /* Dump round */ - put_str(format("%6ld", auto_round), 9, 73); - - /* Make sure they see everything */ - Term_fresh(); - - /* Do not wait for a key */ - inkey_scan = TRUE; - - /* Check for a keypress */ - if (inkey()) break; - } - } - - /* Flush input */ - flush(); - - - /*** Display ***/ - - /* Mode */ - mode = 0; - - /* Roll for base hitpoints */ - get_extra(); - - /* Roll for age/height/weight */ - get_ahw(); - - /* Roll for social class */ - get_history(); - - /* Roll for gold */ - get_money(); - - /*** Generate ***/ - process_hooks(HOOK_BIRTH, "()"); - - /* Input loop */ - while (TRUE) - { - /* Calculate the bonuses and hitpoints */ - p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY); - - /* Update stuff */ - update_stuff(); - - /* Fully healed */ - p_ptr->chp = p_ptr->mhp; - - /* Fully rested */ - p_ptr->csp = p_ptr->msp; - - /* Display the player */ - display_player(mode); - - /* Prepare a prompt (must squeeze everything in) */ - Term_gotoxy(2, 23); - Term_addch(TERM_WHITE, b1); - Term_addstr( -1, TERM_WHITE, "'r' to reroll"); - if (prev) Term_addstr( -1, TERM_WHITE, ", 'p' for prev"); - if (mode) Term_addstr( -1, TERM_WHITE, ", 'h' for Misc."); - else Term_addstr( -1, TERM_WHITE, ", 'h' for History"); - Term_addstr( -1, TERM_WHITE, ", or ESC to accept"); - Term_addch(TERM_WHITE, b2); - - /* Prompt and get a command */ - c = inkey(); - - /* Quit */ - if (c == 'Q') quit(NULL); - - /* Start over */ - if (c == 'S') return (FALSE); - - /* Escape accepts the roll */ - if (c == ESCAPE) break; - - /* Reroll this character */ - if ((c == ' ') || (c == 'r')) break; - - /* Previous character */ - if (prev && (c == 'p')) - { - load_prev_data(TRUE); - continue; - } - - /* Toggle the display */ - if ((c == 'H') || (c == 'h')) - { - mode = ((mode != 0) ? 0 : 1); - continue; - } - - /* Help */ - if (c == '?') - { - do_cmd_help(); - continue; - } - - /* Warning */ - bell(); - } - - /* Are we done? */ - if (c == ESCAPE) break; - - /* Save this for the "previous" character */ - save_prev_data(); - - /* Note that a previous roll exists */ - prev = TRUE; - } - - /* Clear prompt */ - clear_from(23); - - return (TRUE); -} - - -/* - * Helper function for 'player_birth()' - * - * The delay may be reduced, but is recommended to keep players - * from continuously rolling up characters, which can be VERY - * expensive CPU wise. And it cuts down on player stupidity. - */ -static bool_ player_birth_aux() -{ - char c; - - int i, j; - - int y = 0, x = 0; - - char old_history[4][60]; - - /* Ask */ - if (!player_birth_aux_ask()) return (FALSE); - - for (i = 1; i < max_s_idx; i++) - s_info[i].dev = FALSE; - for (i = 1; i < max_s_idx; i++) - { - s32b value = 0, mod = 0; - - compute_skills(&value, &mod, i); - - init_skill(value, mod, i); - - /* Develop only revelant branches */ - if (s_info[i].value || s_info[i].mod) - { - int z = s_info[i].father; - - while (z != -1) - { - s_info[z].dev = TRUE; - z = s_info[z].father; - if (z == 0) - break; - } - } - } - - if (do_quick_start) - { - load_prev_data(FALSE); - - /* Roll for base hitpoints */ - get_extra(); - - /* Calculate the bonuses and hitpoints */ - p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY); - - /* Update stuff */ - update_stuff(); - - /* Fully healed */ - p_ptr->chp = p_ptr->mhp; - - /* Fully rested */ - p_ptr->csp = p_ptr->msp; - } - else - { - /* Point based */ - if (point_based) - { - if (!player_birth_aux_point()) return FALSE; - } - /* Auto-roll */ - else - { - if (!player_birth_aux_auto()) return FALSE; - } - - /* Edit character background */ - for (i = 0; i < 4; i++) - { - strnfmt(old_history[i], 60, "%s", history[i]); - } - /* Turn 0 to space */ - for (i = 0; i < 4; i++) - { - for (j = 0; history[i][j]; j++) /* loop */; - - for (; j < 59; j++) history[i][j] = ' '; - } - display_player(1); - c_put_str(TERM_L_GREEN, "(Character Background - Edit Mode)", 15, 20); - while (TRUE) - { - for (i = 0; i < 4; i++) - { - put_str(history[i], i + 16, 10); - } - c_put_str(TERM_L_BLUE, format("%c", history[y][x]), y + 16, x + 10); - - /* Place cursor just after cost of current stat */ - Term_gotoxy(x + 10, y + 16); - - c = inkey(); - - if (c == '8') - { - y--; - if (y < 0) y = 3; - } - else if (c == '2') - { - y++; - if (y > 3) y = 0; - } - else if (c == '6') - { - x++; - if (x > 59) x = 0; - } - else if (c == '4') - { - x--; - if (x < 0) x = 59; - } - else if (c == '\r') - { - break; - } - else if (c == ESCAPE) - { - for (i = 0; i < 4; i++) - { - strnfmt(history[i], 60, "%s", old_history[i]); - put_str(history[i], i + 16, 10); - } - break; - } - else - { - history[y][x++] = c; - if (x > 58) - { - x = 0; - y++; - if (y > 3) y = 0; - } - } - } - - - /*** Finish up ***/ - - /* Get a name, recolor it, prepare savefile */ - - get_name(); - - - /* Prompt for it */ - prt("['Q' to suicide, 'S' to start over, or ESC to continue]", 23, 10); - - /* Get a key */ - c = inkey(); - - /* Quit */ - if (c == 'Q') quit(NULL); - - /* Start over */ - if (c == 'S') return (FALSE); - } - - /* Save this for the next character */ - previous_char.quick_ok = TRUE; - save_prev_data(); - - /* Accept */ - return (TRUE); -} - - -/* - * Helper function for validate_bg(). - */ -static void validate_bg_aux(int chart, bool_ chart_checked[], char *buf) -{ - char *s; - - int i; - - - /* Assume the chart does not exist */ - bool_ chart_exists = FALSE; - - /* Assume the chart is not complete */ - bool_ chart_complete = FALSE; - - int bg_max = max_bg_idx; - - /* No chart */ - if (!chart) return; - - /* Already saw this chart */ - if (chart_checked[chart]) return; - - /* Build a debug message */ - s = buf + strlen(buf); - - /* XXX XXX XXX */ - (void) strnfmt(s, -1, "%d --> ", chart); - - /* Check each chart */ - for (i = 0; i < bg_max; i++) - { - /* Require same chart */ - if (bg[i].chart != chart) continue; - - /* The chart exists */ - chart_exists = TRUE; - - /* Validate the "next" chart recursively */ - validate_bg_aux(bg[i].next, chart_checked, buf); - - /* Require a terminator */ - if (bg[i].roll != 100) continue; - - /* The chart is complete */ - chart_complete = TRUE; - } - - /* Failed: The chart does not exist */ - if (!chart_exists) - { - quit_fmt("birth.c: bg[] chart %d does not exist\n%s", chart, buf); - } - - /* Failed: The chart is not complete */ - if (!chart_complete) - { - quit_fmt("birth.c: bg[] chart %d is not complete", chart); - } - - /* Remember we saw this chart */ - chart_checked[chart] = TRUE; - - /* Build a debug message */ - *s = 0; -} - - -/* - * Verify that the bg[] table is valid. - */ -static void validate_bg(void) -{ - int i, race; - - bool_ chart_checked[512]; - - char buf[1024]; - - - for (i = 0; i < 512; i++) chart_checked[i] = FALSE; - - /* Check each race */ - for (race = 0; race < max_rp_idx; race++) - { - /* Get the first chart for this race */ - int chart = race_info[race].chart; - - (void) strcpy(buf, ""); - - /* Validate the chart recursively */ - validate_bg_aux(chart, chart_checked, buf); - } -} - -/* - * Initialize a random town - */ -void init_town(int t_idx, int level) -{ - town_type *t_ptr = &town_info[t_idx]; - - /* Mark it as existent */ - t_ptr->flags |= (TOWN_REAL); - - /* Mark it as not found */ - t_ptr->flags &= ~(TOWN_KNOWN); - - /* Generation seed for the town */ - t_ptr->seed = randint(0x10000000); - - /* Total hack and not even used */ - t_ptr->numstores = 8; -} - -/* - * Create a new character. - * - * Note that we may be called with "junk" leftover in the various - * fields, so we must be sure to clear them first. - */ -void player_birth(void) -{ - int i, j, rtown = TOWN_RANDOM; - - /* Validate the bg[] table */ - validate_bg(); - - /* Create a new character */ - while (1) - { - /* Wipe the player */ - player_wipe(); - - /* Roll up a new character */ - if (player_birth_aux()) break; - } - - /* Finish skills */ - p_ptr->skill_points = 0; - p_ptr->skill_last_level = 1; - - recalc_skills(FALSE); - - /* grab level 1 abilities */ - for (i = 0; i < max_ab_idx; i++) - ab_info[i].acquired = FALSE; - apply_level_abilities(1); - - /* Complete the god */ - i = p_ptr->pgod; - p_ptr->pgod = 0; - follow_god(i, TRUE); - - /* Select the default melee type */ - select_default_melee(); - - /* Make a note file if that option is set */ - add_note_type(NOTE_BIRTH); - - /* Note player birth in the message recall */ - message_add(" ", TERM_L_BLUE); - message_add(" ", TERM_L_BLUE); - message_add("====================", TERM_L_BLUE); - message_add(" ", TERM_L_BLUE); - message_add(" ", TERM_L_BLUE); - - /* Hack -- outfit the player */ - player_outfit(); - - /* Initialize random towns in the dungeons */ - for (i = 0; i < max_d_idx; i++) - { - dungeon_info_type *d_ptr = &d_info[i]; - int num = 0, z; - - d_ptr->t_num = 0; - for (z = 0; z < TOWN_DUNGEON; z++) - { - d_ptr->t_idx[z] = 0; - d_ptr->t_level[z] = 0; - } - if (!(d_ptr->flags1 & DF1_RANDOM_TOWNS)) continue; - - /* Can we add a town ? */ - while (magik(TOWN_CHANCE - (num * 10))) - { - int lev; - - d_ptr->t_idx[num] = rtown; - rtown++; - - while (TRUE) - { - int j; - bool_ ok = TRUE; - - lev = rand_range(d_ptr->mindepth, d_ptr->maxdepth - 1); - - /* Be sure it wasnt already used */ - for (j = 0; j < num; j++) - { - if (d_ptr->t_level[j] == lev) ok = FALSE; - } - - /* Ok found one */ - if (ok) break; - } - d_ptr->t_level[num] = lev; - - if (wizard) - { - message_add(format("Random dungeon town: d_idx:%d, lev:%d", i, lev), TERM_WHITE); - } - - /* Create the town */ - init_town(d_ptr->t_idx[num], d_ptr->t_level[num]); - - num++; - - /* No free slots left */ - if (num >= TOWN_DUNGEON) break; - } - - d_ptr->t_num = num; - } - - /* Init the towns */ - for (i = 1; i < max_towns; i++) - { - /* Not destroyed ! yet .. ;) */ - town_info[i].destroyed = FALSE; - - /* Ignore non-existent towns */ - if (!(town_info[i].flags & (TOWN_REAL))) continue; - - create_stores_stock(i); - - /* Init the stores */ - for (j = 0; j < max_st_idx; j++) - { - /* Initialize */ - store_init(i, j); - } - } - - /* Init wilderness seeds */ - for (i = 0; i < max_wild_x; i++) - { - for (j = 0; j < max_wild_y; j++) - { - wild_map[j][i].seed = rand_int(0x10000000); - wild_map[j][i].entrance = 0; - wild_map[j][i].known = FALSE; - } - } - - /* Select bounty monsters. */ - select_bounties(); -} - - - - -char savefile_module[46][80]; -char savefile_names[46][30]; -char savefile_desc[46][80]; -bool_ savefile_alive[46]; -int savefile_idx[46]; - -/* - * Grab all the names from an index - */ -int load_savefile_names() -{ - FILE *fff; - char buf[1024]; - char tmp[50]; - char player_base_save[32]; - int max = 0, fd; - - - /* Build the filename */ - strcpy(tmp, "global.svg"); - path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp); - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Read the file */ - fff = my_fopen(buf, "r"); - - /* Failure */ - if (!fff) return (0); - - - /* Save the current 'player_base' */ - strncpy(player_base_save, player_base, 32); - - - /* - * Parse, use '@' intead of ':' as a separator because it cannot exists - * in savefiles - */ - while (0 == my_fgets(fff, buf, 1024)) - { - int i = 0, start, count; - - /* Check for pre-ToME 2.1.2 file */ - count = 0; - i = 0; - while (buf[i] && buf[i] != '\n') - { - if (buf[i] == '@') - ++count; - ++i; - } - - /* Check module if a current svg file */ - start = 0; - i = 0; - if (count > 1) - { - while (buf[i] != '@') - { - savefile_module[max][i - start] = buf[i]; - i++; - } - savefile_module[max][i] = '\0'; - i++; - } - /* Default to ToME for old files */ - else - { - savefile_module[max][0] = 'T'; - savefile_module[max][1] = 'o'; - savefile_module[max][2] = 'M'; - savefile_module[max][3] = 'E'; - savefile_module[max][4] = '\0'; - } - - if (buf[i] == '0') savefile_alive[max] = FALSE; - else if (buf[i] == '1') savefile_alive[max] = TRUE; - - i++; - start = i; - while (buf[i] != '@') - { - savefile_names[max][i - start] = buf[i]; - i++; - } - savefile_names[max][i - start] = '\0'; - i++; - strcpy(savefile_desc[max], buf + i); - - /* Build platform-dependent savefile name */ - strncpy(player_base, savefile_names[max], 32); - process_player_name(TRUE); - - /* File type is 'SAVE' */ - FILE_TYPE(FILE_TYPE_SAVE); - - /* Try to open the savefile */ - fd = fd_open(savefile, O_RDONLY); - - /* Still existing ? */ - if (fd >= 0) - { - fd_close(fd); - max++; - } - } - - my_fclose(fff); - - /* Restore the values of 'player_base' and 'savefile' */ - strncpy(player_base, player_base_save, 32); - process_player_name(TRUE); - - return (max); -} - - -/* - * Save all the names from an index - */ -void save_savefile_names() -{ - FILE *fff; - char buf[1024]; - char tmp[50]; - int max = load_savefile_names(), i; - - - /* Build the filename */ - strcpy(tmp, "global.svg"); - path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp); - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Read the file */ - fff = my_fopen(buf, "w"); - - /* Failure */ - if (!fff) return; - - /* - * Save, use '@' intead of ':' as a separator because it cannot exists - * in savefiles - */ - fprintf(fff, "%s@%c%s@%s, the %s %s is %s\n", game_module, - (death) ? '0' : '1', player_base, player_name, - get_player_race_name(p_ptr->prace, p_ptr->pracem), - spp_ptr->title + c_name, - (!death) ? "alive" : "dead"); - - for (i = 0; i < max; i++) - { - if (!strcmp(savefile_names[i], player_base)) continue; - fprintf(fff, "%s@%c%s@%s\n", savefile_module[i], - (savefile_alive[i]) ? '1' : '0', savefile_names[i], savefile_desc[i]); - } - - my_fclose(fff); -} - - -static void dump_savefiles(int sel, int max) -{ - int i; - - char buf[40], pre = ' ', post = ')'; - - char ind; - - - for (i = 0; i < max; i++) - { - ind = I2A(i % 26); - if (i >= 26) ind = toupper(ind); - - if (sel == i) - { - pre = '['; - post = ']'; - } - else - { - pre = ' '; - post = ')'; - } - - if (i == 0) strnfmt(buf, 40, "%c%c%c New Character", pre, ind, post); - else if (i == 1) strnfmt(buf, 40, "%c%c%c Load Savefile", pre, ind, post); - else strnfmt(buf, 40, "%c%c%c %s", pre, ind, post, savefile_names[savefile_idx[i - 2]]); - - if (sel == i) - { - if (i >= 2) - { - if (savefile_alive[i - 2]) c_put_str(TERM_L_GREEN, savefile_desc[savefile_idx[i - 2]], 5, 0); - else c_put_str(TERM_L_RED, savefile_desc[savefile_idx[i - 2]], 5, 0); - } - else if (i == 1) c_put_str(TERM_YELLOW, "Load an existing savefile that is not in the list", 5, 0); - else c_put_str(TERM_YELLOW, "Create a new character", 5, 0); - c_put_str(TERM_L_BLUE, buf, 6 + (i / 4), 20 * (i % 4)); - } - else - put_str(buf, 6 + (i / 4), 20 * (i % 4)); - } -} - - -/* Asks for new game or load game */ -bool_ no_begin_screen = FALSE; - -bool_ begin_screen() -{ - int m, k, sel, max; - -savefile_try_again: - sel = 0; - - /* Grab the savefiles */ - max = load_savefile_names(); - - /* Get only the usable savefiles */ - for (k = 0, m = 0; k < max; k++) - { - s32b can_use; - - can_use = module_savefile_loadable(savefile_module[k]); - if (can_use) - { - savefile_idx[m++] = k; - } - } - max = m + 2; - if (max > 2) sel = 2; - - while (TRUE) - { - /* Clear screen */ - Term_clear(); - - /* Let the user choose */ - c_put_str(TERM_YELLOW, format("Welcome to %s! To play you will need a character.", game_module), 1, 10); - put_str("Press 8/2/4/6 to move, Return to select, Backspace to delete a savefile.", 3, 3); - put_str("and Esc to quit.", 4, 32); - - dump_savefiles(sel, max); - - k = inkey(); - - if (k == ESCAPE) - { - quit(NULL); - } - if (k == '6') - { - sel++; - if (sel >= max) sel = 0; - continue; - } - else if (k == '4') - { - sel--; - if (sel < 0) sel = max - 1; - continue; - } - else if (k == '2') - { - sel += 4; - if (sel >= max) sel = sel % max; - continue; - } - else if (k == '8') - { - sel -= 4; - if (sel < 0) sel = (sel + max - 1) % max; - continue; - } - else if (k == '\r') - { - if (sel < 26) k = I2A(sel); - else k = toupper(I2A(sel)); - } - else if (((k == 0x7F) || (k == '\010')) && (sel >= 2)) - { - char player_base_save[32]; - - if (!get_check(format("Really delete '%s'?", savefile_names[savefile_idx[sel - 2]]))) continue; - - /* Save current 'player_base' */ - strncpy(player_base_save, player_base, 32); - - /* Build platform-dependent save file name */ - strncpy(player_base, savefile_names[savefile_idx[sel - 2]], 32); - process_player_name(TRUE); - - /* Remove the savefile */ - fd_kill(savefile); - - /* Restore 'player_base' and 'savefile' */ - strncpy(player_base, player_base_save, 32); - process_player_name(TRUE); - - /* Reload, gods I hate using goto .. */ - goto savefile_try_again; - - continue; - } - - if (k == 'a') - { - /* Display prompt */ - prt("Enter the name of the savefile that will hold this character: ", 23, 0); - - /* Ask the user for a string */ - if (!askfor_aux(player_base, 15)) continue; - - /* Process the player name */ - process_player_name(TRUE); - - return (TRUE); - } - if (k == 'b') - { - /* Display prompt */ - prt("Enter the name of a savefile: ", 23, 0); - - /* Ask the user for a string */ - if (!askfor_aux(player_base, 15)) continue; - - /* Process the player name */ - process_player_name(TRUE); - - return (FALSE); - } - else - { - int x; - - if (islower(k)) x = A2I(k); - else x = A2I(tolower(k)) + 26; - - if ((x < 2) || (x >= max)) continue; - - strnfmt(player_base, 32, "%s", savefile_names[savefile_idx[x - 2]]); - - /* Process the player name */ - process_player_name(TRUE); - - return (FALSE); - } - } - - /* Shouldnt happen */ - return (FALSE); -} diff --git a/src/birth.cc b/src/birth.cc new file mode 100644 index 00000000..8b6ef896 --- /dev/null +++ b/src/birth.cc @@ -0,0 +1,3949 @@ +/* File: birth.c */ + +/* Purpose: create a player character */ + +/* + * 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 "angband.h" + +#include "messages.h" +#include "hooks.h" +#include "q_rand.h" + +#include + +/* + * How often the autoroller will update the display and pause + * to check for user interuptions. + * Bigger values will make the autoroller faster, but slower + * system may have problems because the user can't stop the + * autoroller for this number of rolls. + */ +#define AUTOROLLER_STEP 25L + +/* + * Maximum number of tries for selection of a proper quest monster + */ +#define MAX_TRIES 100 + +/* Max quests */ +static byte max_quests = 0; + +/* + * Current stats + */ +static s16b stat_use[6]; + +/* + * Autoroll limit + */ +static s16b stat_limit[6]; + +/* + * Autoroll matches + */ +static s32b stat_match[6]; + +/* + * Autoroll round + */ +static s32b auto_round; + +/* + * Last round + */ +static s32b last_round; + +/* Human */ +static const char *human_syllable1[] = +{ + "Ab", "Ac", "Ad", "Af", "Agr", "Ast", "As", "Al", "Adw", "Adr", "Ar", + "B", "Br", "C", "Cr", "Ch", "Cad", "D", "Dr", "Dw", "Ed", "Eth", "Et", + "Er", "El", "Eow", "F", "Fr", "G", "Gr", "Gw", "Gal", "Gl", "H", "Ha", + "Ib", "Jer", "K", "Ka", "Ked", "L", "Loth", "Lar", "Leg", "M", "Mir", + "N", "Nyd", "Ol", "Oc", "On", "P", "Pr", "R", "Rh", "S", "Sev", "T", + "Tr", "Th", "V", "Y", "Z", "W", "Wic", +}; + +static const char *human_syllable2[] = +{ + "a", "ae", "au", "ao", "are", "ale", "ali", "ay", "ardo", "e", "ei", + "ea", "eri", "era", "ela", "eli", "enda", "erra", "i", "ia", "ie", + "ire", "ira", "ila", "ili", "ira", "igo", "o", "oa", "oi", "oe", + "ore", "u", "y", +}; + +static const char *human_syllable3[] = +{ + "a", "and", "b", "bwyn", "baen", "bard", "c", "ctred", "cred", "ch", + "can", "d", "dan", "don", "der", "dric", "dfrid", "dus", "f", "g", + "gord", "gan", "l", "li", "lgrin", "lin", "lith", "lath", "loth", + "ld", "ldric", "ldan", "m", "mas", "mos", "mar", "mond", "n", + "nydd", "nidd", "nnon", "nwan", "nyth", "nad", "nn", "nnor", "nd", + "p", "r", "ron", "rd", "s", "sh", "seth", "sean", "t", "th", "tha", + "tlan", "trem", "tram", "v", "vudd", "w", "wan", "win", "wyn", "wyr", + "wyr", "wyth", +}; + +/* + * Random Name Generator + * based on a Javascript by Michael Hensley + * "http://geocities.com/timessquare/castle/6274/" + */ +static void create_random_name(int race, char *name) +{ + const char *syl1, *syl2, *syl3; + + int idx; + + + /* Paranoia */ + if (!name) return; + + /* Select the monster type */ + switch (race) + { + /* Create the monster name */ + + /* Use human ones */ + default: + { + idx = rand_int(sizeof(human_syllable1) / sizeof(char *)); + syl1 = human_syllable1[idx]; + idx = rand_int(sizeof(human_syllable2) / sizeof(char *)); + syl2 = human_syllable2[idx]; + idx = rand_int(sizeof(human_syllable3) / sizeof(char *)); + syl3 = human_syllable3[idx]; + + break; + } + } + + /* Concatenate selected syllables */ + strnfmt(name, 32, "%s%s%s", syl1, syl2, syl3); +} + + +void print_desc_aux(cptr txt, int y, int xx) +{ + int i = -1, x = xx; + + + while (txt[++i] != 0) + { + if (txt[i] == '\n') + { + x = xx; + y++; + } + else + { + Term_putch(x++, y, TERM_YELLOW, txt[i]); + } + } +} + +void print_desc(cptr txt) +{ + print_desc_aux(txt, 12, 1); +} + +/* + * Save the current data for later + */ +static void save_prev_data(void) +{ + int i; + + + /*** Save the current data ***/ + + /* Save the data */ + previous_char.sex = p_ptr->psex; + previous_char.race = p_ptr->prace; + previous_char.rmod = p_ptr->pracem; + previous_char.pclass = p_ptr->pclass; + previous_char.spec = p_ptr->pspec; + + previous_char.quests = max_quests; + + previous_char.god = p_ptr->pgod; + previous_char.grace = p_ptr->grace; + + previous_char.age = p_ptr->age; + previous_char.wt = p_ptr->wt; + previous_char.ht = p_ptr->ht; + previous_char.sc = p_ptr->sc; + previous_char.au = p_ptr->au; + + /* Save the stats */ + for (i = 0; i < 6; i++) + { + previous_char.stat[i] = p_ptr->stat_max[i]; + } + previous_char.luck = p_ptr->luck_base; + + /* Save the weapon specialty */ + previous_char.weapon = 0; + + /* Save the history */ + for (i = 0; i < 4; i++) + { + strcpy(previous_char.history[i], history[i]); + } +} + + +/* + * Load the previous data + */ +static void load_prev_data(bool_ save) +{ + int i; + + birther temp; + + + /*** Save the current data ***/ + + /* Save the data */ + temp.age = p_ptr->age; + temp.wt = p_ptr->wt; + temp.ht = p_ptr->ht; + temp.sc = p_ptr->sc; + temp.au = p_ptr->au; + + /* Save the stats */ + for (i = 0; i < 6; i++) + { + temp.stat[i] = p_ptr->stat_max[i]; + } + temp.luck = p_ptr->luck_base; + + /* Save the weapon specialty */ + temp.weapon = 0; + + /* Save the history */ + for (i = 0; i < 4; i++) + { + strcpy(temp.history[i], history[i]); + } + + + /*** Load the previous data ***/ + + /* Load the data */ + p_ptr->age = previous_char.age; + p_ptr->wt = previous_char.wt; + p_ptr->ht = previous_char.ht; + p_ptr->sc = previous_char.sc; + p_ptr->au = previous_char.au; + + /* Load the stats */ + for (i = 0; i < 6; i++) + { + p_ptr->stat_max[i] = previous_char.stat[i]; + p_ptr->stat_cur[i] = previous_char.stat[i]; + } + p_ptr->luck_base = previous_char.luck; + p_ptr->luck_max = previous_char.luck; + + /* Load the history */ + for (i = 0; i < 4; i++) + { + strcpy(history[i], previous_char.history[i]); + } + + + /*** Save the current data ***/ + if (!save) return; + + /* Save the data */ + previous_char.age = temp.age; + previous_char.wt = temp.wt; + previous_char.ht = temp.ht; + previous_char.sc = temp.sc; + previous_char.au = temp.au; + + /* Save the stats */ + for (i = 0; i < 6; i++) + { + previous_char.stat[i] = temp.stat[i]; + } + previous_char.luck = temp.luck; + + /* Save the chaos patron */ + previous_char.chaos_patron = temp.chaos_patron; + + /* Save the weapon specialty */ + previous_char.weapon = temp.weapon; + + /* Save the history */ + for (i = 0; i < 4; i++) + { + strcpy(previous_char.history[i], temp.history[i]); + } +} + + + + +/* + * Returns adjusted stat -JK- Algorithm by -JWT- + * + * auto_roll is boolean and states maximum changes should be used rather + * than random ones to allow specification of higher values to wait for + * + * The "p_ptr->maximize" code is important -BEN- + */ +static int adjust_stat(int value, int amount, int auto_roll) +{ + int i; + + + /* Negative amounts */ + if (amount < 0) + { + /* Apply penalty */ + for (i = 0; i < (0 - amount); i++) + { + if (value >= 18 + 10) + { + value -= 10; + } + else if (value > 18) + { + value = 18; + } + else if (value > 3) + { + value--; + } + } + } + + /* Positive amounts */ + else if (amount > 0) + { + /* Apply reward */ + for (i = 0; i < amount; i++) + { + if (value < 18) + { + value++; + } + else if (p_ptr->maximize) + { + value += 10; + } + else if (value < 18 + 70) + { + value += ((auto_roll ? 15 : randint(15)) + 5); + } + else if (value < 18 + 90) + { + value += ((auto_roll ? 6 : randint(6)) + 2); + } + else if (value < 18 + 100) + { + value++; + } + } + } + + /* Return the result */ + return (value); +} + + + + +/* + * Roll for a characters stats + * + * For efficiency, we include a chunk of "calc_bonuses()". + */ +static void get_stats(void) +{ + int i, j; + + int bonus; + + int dice[18]; + + + /* Roll and verify some stats */ + while (TRUE) + { + /* Roll some dice */ + for (j = i = 0; i < 18; i++) + { + /* Roll the dice */ + dice[i] = randint(3 + i % 3); + + /* Collect the maximum */ + j += dice[i]; + } + + /* + * Verify totals + * + * 57 was 54... I hate 'magic numbers' :< TY + * + * (d3 + d4 + d5) ~= 7.5 (+- 4.5) + * with 5 makes avg. stat value of 12.5 (min 8, max 17) + * + * (d3 + d4 + d5) x 6 ~= 45 (+- 18) + * + * So the original value (still used by Vanilla as of 2.9.3) + * allows (avg - 2)..(avg + 8), while this Z version + * (avg - 2)..(avg + 11). I don't understand what TY meant + * by "magic numbers", but I like big stats :) -- pelpel + * + */ + if ((j > 42) && (j < 57)) break; + } + + /* Acquire the stats */ + for (i = 0; i < 6; i++) + { + /* Extract 5 + 1d3 + 1d4 + 1d5 */ + j = 5 + dice[3 * i] + dice[3 * i + 1] + dice[3 * i + 2]; + + /* Save that value */ + p_ptr->stat_max[i] = j; + + /* Obtain a "bonus" for "race" and "class" */ + bonus = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i]; + + /* Variable stat maxes */ + if (p_ptr->maximize) + { + /* Start fully healed */ + p_ptr->stat_cur[i] = p_ptr->stat_max[i]; + + /* Efficiency -- Apply the racial/class bonuses */ + stat_use[i] = modify_stat_value(p_ptr->stat_max[i], bonus); + } + + /* Fixed stat maxes */ + else + { + /* Apply the bonus to the stat (somewhat randomly) */ + stat_use[i] = adjust_stat(p_ptr->stat_max[i], bonus, FALSE); + + /* Save the resulting stat maximum */ + p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stat_use[i]; + } + + /* No temporary drain (yet...) */ + p_ptr->stat_cnt[i] = 0; + p_ptr->stat_los[i] = 0; + } + + /* Get luck */ + p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5); + p_ptr->luck_max = p_ptr->luck_base; +} + + +/* + * Roll for some info that the auto-roller ignores + */ +static void get_extra(void) +{ + int i, j, min_value, max_value; + + + /* Level one */ + p_ptr->max_plv = p_ptr->lev = 1; + + /* Experience factor */ + p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp; + + /* Initialize arena and rewards information -KMW- */ + p_ptr->arena_number = 0; + p_ptr->inside_arena = 0; + p_ptr->inside_quest = 0; + p_ptr->exit_bldg = TRUE; /* only used for arena now -KMW- */ + + /* Hitdice */ + p_ptr->hitdie = rp_ptr->r_mhp + rmp_ptr->r_mhp + cp_ptr->c_mhp; + + /* Initial hitpoints */ + p_ptr->mhp = p_ptr->hitdie; + + /* Minimum hitpoints at highest level */ + min_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 3) / 8; + min_value += PY_MAX_LEVEL; + + /* Maximum hitpoints at highest level */ + max_value = (PY_MAX_LEVEL * (p_ptr->hitdie - 1) * 5) / 8; + max_value += PY_MAX_LEVEL; + + /* Pre-calculate level 1 hitdice */ + player_hp[0] = p_ptr->hitdie; + + /* Roll out the hitpoints */ + while (TRUE) + { + /* Roll the hitpoint values */ + for (i = 1; i < PY_MAX_LEVEL; i++) + { + j = randint(p_ptr->hitdie); + player_hp[i] = player_hp[i - 1] + j; + } + + /* XXX Could also require acceptable "mid-level" hitpoints */ + + /* Require "valid" hitpoints at highest level */ + if (player_hp[PY_MAX_LEVEL - 1] < min_value) continue; + if (player_hp[PY_MAX_LEVEL - 1] > max_value) continue; + + /* Acceptable */ + break; + } + + p_ptr->tactic = 4; + p_ptr->movement = 4; +} + + +/* + * Get the racial history, and social class, using the "history charts". + */ +static void get_history(void) +{ + int i, n, chart, roll, social_class; + + char *s, *t; + + char buf[240]; + + + /* Clear the previous history strings */ + for (i = 0; i < 4; i++) history[i][0] = '\0'; + + /* Clear the history text */ + buf[0] = '\0'; + + /* Initial social class */ + social_class = randint(4); + + /* Starting place */ + chart = rp_ptr->chart; + + /* Process the history */ + while (chart) + { + /* Start over */ + i = 0; + + /* Roll for nobility */ + roll = randint(100); + + + /* Access the proper entry in the table */ + while ((chart != bg[i].chart) || (roll > bg[i].roll)) i++; + + /* Acquire the textual history */ + (void)strcat(buf, bg[i].info + rp_text); + + /* Add in the social class */ + social_class += (int)(bg[i].bonus) - 50; + + /* Enter the next chart */ + chart = bg[i].next; + } + + + + /* Verify social class */ + if (social_class > 100) social_class = 100; + else if (social_class < 1) social_class = 1; + + /* Save the social class */ + p_ptr->sc = social_class; + + + /* Skip leading spaces */ + for (s = buf; *s == ' '; s++) /* loop */; + + /* Get apparent length */ + n = strlen(s); + + /* Kill trailing spaces */ + while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0'; + + + /* Start at first line */ + i = 0; + + /* Collect the history */ + while (TRUE) + { + /* Extract remaining length */ + n = strlen(s); + + /* All done */ + if (n < 60) + { + /* Save one line of history */ + strcpy(history[i++], s); + + /* All done */ + break; + } + + /* Find a reasonable break-point */ + for (n = 60; ((n > 0) && (s[n - 1] != ' ')); n--) /* loop */; + + /* Save next location */ + t = s + n; + + /* Wipe trailing spaces */ + while ((n > 0) && (s[n - 1] == ' ')) s[--n] = '\0'; + + /* Save one line of history */ + strcpy(history[i++], s); + + /* Start next line */ + for (s = t; *s == ' '; s++) /* loop */; + } +} + + +/* + * Fill the random_artifacts array with relevant info. + */ +errr init_randart(void) +{ + int i; + + long cost; + + random_artifact* ra_ptr; + + char buf[80]; + + + for (i = 0; i < MAX_RANDARTS; i++) + { + ra_ptr = &random_artifacts[i]; + + strcpy(ra_ptr->name_short, + get_line("rart_s.txt", ANGBAND_DIR_FILE, buf, i)); + strcpy(ra_ptr->name_full, + get_line("rart_f.txt", ANGBAND_DIR_FILE, buf, i)); + + ra_ptr->attr = randint(15); + ra_ptr->activation = rand_int(MAX_T_ACT); + ra_ptr->generated = FALSE; + + cost = randnor(0, 250); + + if (cost < 0) cost = 0; + + ra_ptr->cost = cost; + } + + return 0; +} + + +/* + * A helper function for get_ahw(), also called by polymorph code + */ +void get_height_weight(void) +{ + int h_mean, h_stddev; + + int w_mean, w_stddev; + + + /* Extract mean and standard deviation -- Male */ + if (p_ptr->psex == SEX_MALE) + { + h_mean = rp_ptr->m_b_ht + rmp_ptr->m_b_ht; + h_stddev = rp_ptr->m_m_ht + rmp_ptr->m_m_ht; + + w_mean = rp_ptr->m_b_wt + rmp_ptr->m_b_wt; + w_stddev = rp_ptr->m_m_wt + rmp_ptr->m_m_wt; + } + + /* Female */ + else if (p_ptr->psex == SEX_FEMALE) + { + h_mean = rp_ptr->f_b_ht + rmp_ptr->f_b_ht; + h_stddev = rp_ptr->f_m_ht + rmp_ptr->f_m_ht; + + w_mean = rp_ptr->f_b_wt + rmp_ptr->f_b_wt; + w_stddev = rp_ptr->f_m_wt + rmp_ptr->f_m_wt; + } + + /* Neuter XXX */ + else + { + h_mean = (rp_ptr->m_b_ht + rmp_ptr->m_b_ht + + rp_ptr->f_b_ht + rmp_ptr->f_b_ht) / 2, + h_stddev = (rp_ptr->m_m_ht + rmp_ptr->m_m_ht + + rp_ptr->f_m_ht + rmp_ptr->f_m_ht) / 2; + + w_mean = (rp_ptr->m_b_wt + rmp_ptr->m_b_wt + + rp_ptr->f_b_wt + rmp_ptr->f_b_wt) / 2, + w_stddev = (rp_ptr->m_m_wt + rmp_ptr->m_m_wt + + rp_ptr->f_m_wt + rmp_ptr->f_m_wt) / 2; + } + + /* Calculate height/weight */ + p_ptr->ht = randnor(h_mean, h_stddev); + p_ptr->wt = randnor(w_mean, w_stddev); + + /* Weight/height shouldn't be negative */ + if (p_ptr->ht < 1) p_ptr->ht = 1; + if (p_ptr->wt < 1) p_ptr->wt = 1; +} + + +/* + * Computes character's age, height, and weight + */ +static void get_ahw(void) +{ + /* Calculate the age */ + p_ptr->age = rp_ptr->b_age + rmp_ptr->b_age + + randint(rp_ptr->m_age + rmp_ptr->m_age); + + /* Calculate the height/weight */ + get_height_weight(); +} + + + + +/* + * Get the player's starting money + */ +static void get_money(void) +{ + int i, gold; + + + /* Social Class determines starting gold */ + gold = (p_ptr->sc * 6) + randint(100) + 300; + + /* Process the stats */ + for (i = 0; i < 6; i++) + { + /* Mega-Hack -- reduce gold for high stats */ + if (stat_use[i] >= 18 + 50) gold -= 300; + else if (stat_use[i] >= 18 + 20) gold -= 200; + else if (stat_use[i] > 18) gold -= 150; + else gold -= (stat_use[i] - 8) * 10; + } + + /* Minimum 100 gold */ + if (gold < 100) gold = 100; + + /* Save the gold */ + p_ptr->au = gold; +} + + + +/* + * Display stat values, subset of "put_stats()" + * + * See 'display_player()' for basic method. + */ +static void birth_put_stats(void) +{ + int i, p; + + byte attr; + + char buf[80]; + + + /* Put the stats (and percents) */ + for (i = 0; i < 6; i++) + { + /* Put the stat */ + cnv_stat(p_ptr->stat_use[i], buf); + c_put_str(TERM_L_GREEN, buf, 2 + i, 66); + + /* Put the percent */ + if (stat_match[i]) + { + p = 1000L * stat_match[i] / auto_round; + attr = (p < 100) ? TERM_YELLOW : TERM_L_GREEN; + strnfmt(buf, 80, "%3d.%d%%", p / 10, p % 10); + c_put_str(attr, buf, 2 + i, 73); + } + + /* Never happened */ + else + { + c_put_str(TERM_RED, "(NONE)", 2 + i, 73); + } + } +} + + +/* + * Clear all the global "character" data + */ +static void player_wipe(void) +{ + int i, j; + + + /* Wipe special levels */ + wipe_saved(); + + /* Hack -- zero the struct */ + WIPE(p_ptr, player_type); + + /* Not dead yet */ + p_ptr->lives = 0; + + /* Wipe the history */ + for (i = 0; i < 4; i++) + { + for (j = 0; j < 60; j++) + { + if (j < 59) history[i][j] = ' '; + else history[i][j] = '\0'; + } + } + + /* Wipe the towns */ + for (i = 0; i < max_d_idx; i++) + { + for (j = 0; j < MAX_DUNGEON_DEPTH; j++) + { + special_lvl[j][i] = 0; + } + } + + /* Wipe the towns */ + for (i = max_real_towns + 1; i < max_towns; i++) + { + town_info[i].flags = 0; + } + + /* Wipe the quests */ + for (i = 0; i < MAX_Q_IDX; i++) + { + quest[i].status = QUEST_STATUS_UNTAKEN; + for (j = 0; j < sizeof(quest[i].data)/sizeof(quest[i].data[0]); j++) + { + quest[i].data[j] = 0; + } + } + + /* Wipe the rune spells */ + rune_num = 0; + for (i = 0; i < MAX_RUNES; i++) + { + strcpy(rune_spells[i].name, ""); + rune_spells[i].type = 0; + rune_spells[i].rune2 = 0; + rune_spells[i].mana = 0; + } + + /* No items */ + inven_cnt = 0; + equip_cnt = 0; + + /* Clear the inventory */ + for (i = 0; i < INVEN_TOTAL; i++) + { + object_wipe(&p_ptr->inventory[i]); + } + + /* Generate random artifacts */ + init_randart(); + + /* Start with no artifacts made yet */ + for (i = 0; i < max_a_idx; i++) + { + artifact_type *a_ptr = &a_info[i]; + a_ptr->cur_num = 0; + } + + /* Reset the "objects" */ + for (i = 1; i < max_k_idx; i++) + { + object_kind *k_ptr = &k_info[i]; + + /* Reset "tried" */ + k_ptr->tried = FALSE; + + /* Reset "aware" */ + k_ptr->aware = FALSE; + + /* Reset "know" */ + k_ptr->know = FALSE; + + /* Reset "artifact" */ + k_ptr->artifact = 0; + } + + + /* Reset the "monsters" */ + for (i = 1; i < max_r_idx; i++) + { + monster_race *r_ptr = &r_info[i]; + + /* Hack -- Reset the counter */ + r_ptr->cur_num = 0; + + /* Hack -- Reset the max counter */ + r_ptr->max_num = 100; + + /* Hack -- Reset the max counter */ + if (r_ptr->flags1 & RF1_UNIQUE) r_ptr->max_num = 1; + if (r_ptr->flags3 & RF3_UNIQUE_4) r_ptr->max_num = 4; + + /* Clear player kills */ + r_ptr->r_pkills = 0; + + /* Clear saved flag */ + r_ptr->on_saved = FALSE; + } + + + /* Hack -- Well fed player */ + p_ptr->food = PY_FOOD_FULL - 1; + + /* Wipe the alchemists' recipes */ + for ( i = 0 ; i < 32 ; i++) + alchemist_known_egos[i] = 0; + for ( i = 0 ; i < 6 ; i++) + alchemist_known_artifacts[i] = 0; + alchemist_gained = 0; + + /* Clear "cheat" options */ + cheat_peek = FALSE; + cheat_hear = FALSE; + cheat_room = FALSE; + cheat_xtra = FALSE; + cheat_know = FALSE; + cheat_live = FALSE; + + /* Assume no winning game */ + total_winner = 0; + has_won = FALSE; + + /* Assume no cheating */ + noscore = 0; + wizard = 0; + + /* Assume no innate spells */ + spell_num = 0; + + /* Clear the fate */ + for (i = 0; i < MAX_FATES; i++) + { + fates[i].fate = 0; + } + p_ptr->no_mortal = FALSE; + + /* Player don't have the black breath from the beginning !*/ + p_ptr->black_breath = FALSE; + + /* Default pet command settings */ + p_ptr->pet_follow_distance = 6; + p_ptr->pet_open_doors = FALSE; + p_ptr->pet_pickup_items = FALSE; + + /* Body changing initialisation */ + p_ptr->body_monster = 0; + p_ptr->disembodied = FALSE; + + /* Wipe the bounties */ + total_bounties = 0; + + /* Wipe spells */ + p_ptr->xtra_spells = 0; + + /* Wipe xtra hp */ + p_ptr->hp_mod = 0; + + /* Wipe the monsters */ + wipe_m_list(); + + /* Wipe the doppleganger */ + doppleganger = 0; + + /* Wipe the recall depths */ + for (i = 0; i < max_d_idx; i++) + { + max_dlv[i] = 0; + } + + /* Wipe the known inscription list */ + for (i = 0; i < MAX_INSCRIPTIONS; i++) + { + inscription_info[i].know = FALSE; + } + + /* Wipe the known traps list */ + for (i = 0; i < max_t_idx; i++) + { + t_info[i].known = 0; + t_info[i].ident = FALSE; + } + + /* Reset wild_mode to FALSE */ + p_ptr->wild_mode = FALSE; + p_ptr->old_wild_mode = FALSE; + + /* Initialize allow_one_death */ + p_ptr->allow_one_death = 0; + + p_ptr->loan = p_ptr->loan_time = 0; + + /* Wipe the power list */ + for (i = 0; i < POWER_MAX; i++) + { + p_ptr->powers_mod[i] = 0; + } + + /* No companions killed */ + p_ptr->companion_killed = 0; + + /* Inertia control */ + p_ptr->inertia_controlled_spell = -1; + + /* Automatic stat-gain */ + p_ptr->last_rewarded_level = 1; +} + + +/* Create an object */ +void outfit_obj(int tv, int sv, int pval, int dd, int ds) +{ + object_type forge; + object_type *q_ptr; + + /* Get local object */ + q_ptr = &forge; + q_ptr->pval = 0; + q_ptr->pval2 = 0; + + /* Hack -- Give the player an object */ + object_prep(q_ptr, lookup_kind(tv, sv)); + + if (pval) + q_ptr->pval = pval; + + /* These objects are "storebought" */ + q_ptr->ident |= IDENT_MENTAL; + q_ptr->number = damroll(dd, ds); + + object_aware(q_ptr); + object_known(q_ptr); + (void)inven_carry(q_ptr, FALSE); +} + + +/* + * Give the player an object. + */ +static void player_outfit_object(int qty, int tval, int sval) +{ + object_type forge; + object_type *q_ptr = &forge; + object_prep(q_ptr, lookup_kind(tval, sval)); + q_ptr->number = qty; + object_aware(q_ptr); + object_known(q_ptr); + (void)inven_carry(q_ptr, FALSE); +} + + +/* + * Give player a spell book. + */ +static void player_outfit_spellbook(cptr spell_name) +{ + object_type forge; + object_type *q_ptr = &forge; + object_prep(q_ptr, lookup_kind(TV_BOOK, 255)); + q_ptr->pval = find_spell(spell_name); + q_ptr->ident |= IDENT_MENTAL | IDENT_KNOWN; + inven_carry(q_ptr, FALSE); +} + + +/* + * Init players with some belongings + * + * Having an item makes the player "aware" of its purpose. + */ +static void player_outfit(void) +{ + int i; + cptr class_name = spp_ptr->title + c_name; + cptr subrace_name = rmp_ptr->title + rmp_name; + + /* + * Get an adventurer guide describing a bit of the + * wilderness. + */ + { + /* Hack -- Give the player an adventurer guide */ + player_outfit_object(1, TV_PARCHMENT, 20); + } + + /* + * Provide spell books + */ + if (game_module_idx == MODULE_TOME) + { + if (streq(class_name, "Ranger")) + { + player_outfit_spellbook("Phase Door"); + } + } + if (streq(class_name, "Geomancer")) + { + player_outfit_spellbook("Geyser"); + } + if (streq(class_name, "Priest(Eru)")) + { + player_outfit_spellbook("See the Music"); + } + if (streq(class_name, "Priest(Manwe)")) + { + player_outfit_spellbook("Manwe's Blessing"); + } + if (streq(class_name, "Druid")) + { + player_outfit_spellbook("Charm Animal"); + } + if (streq(class_name, "Dark-Priest")) + { + player_outfit_spellbook("Curse"); + } + if (streq(class_name, "Paladin")) + { + player_outfit_spellbook("Divine Aim"); + } + if (game_module_idx == MODULE_THEME) + { + /* Priests */ + if (streq(class_name, "Stonewright")) + { + player_outfit_spellbook("Firebrand"); + } + if (streq(class_name, "Priest(Varda)")) + { + player_outfit_spellbook("Light of Valinor"); + } + if (streq(class_name, "Priest(Ulmo)")) + { + player_outfit_spellbook("Song of Belegaer"); + } + if (streq(class_name, "Priest(Mandos)")) + { + player_outfit_spellbook("Tears of Luthien"); + } + + /* Dragons */ + if (streq(subrace_name, "Red")) + { + player_outfit_spellbook("Globe of Light"); + } + if (streq(subrace_name, "Black")) + { + player_outfit_spellbook("Geyser"); + } + if (streq(subrace_name, "Green")) + { + player_outfit_spellbook("Noxious Cloud"); + } + if (streq(subrace_name, "Blue")) + { + player_outfit_spellbook("Stone Skin"); + } + if (streq(subrace_name, "White")) + { + player_outfit_spellbook("Sense Monsters"); + } + if (streq(subrace_name, "Ethereal")) + { + player_outfit_spellbook("Recharge"); + } + + /* Demons */ + if (streq(subrace_name, "(Aewrog)")) + { + player_outfit_spellbook("Charm"); + } + if (streq(subrace_name, "(Narrog)")) + { + player_outfit_spellbook("Phase Door"); + } + + /* Peace-mages */ + if (streq(class_name, "Peace-mage")) + { + player_outfit_spellbook("Phase Door"); + } + + /* Wainriders */ + if (streq(class_name, "Wainrider")) + { + player_outfit_spellbook("Curse"); + } + } + + if (streq(class_name, "Mimic")) + { + object_type forge; + object_type *q_ptr = &forge; + + object_prep(q_ptr, lookup_kind(TV_CLOAK, SV_MIMIC_CLOAK)); + q_ptr->pval2 = resolve_mimic_name("Mouse"); + q_ptr->ident |= IDENT_MENTAL | IDENT_KNOWN; + inven_carry(q_ptr, FALSE); + } + + if (game_module_idx == MODULE_THEME) + { + /* Give everyone a scroll of WoR. */ + player_outfit_object(1, TV_SCROLL, SV_SCROLL_WORD_OF_RECALL); + + /* Identify everything in pack. */ + identify_pack_fully(); + } + + if (streq(rmp_ptr->title + rmp_name, "Vampire")) + { + player_gain_corruption(CORRUPT_VAMPIRE_TEETH); + player_gain_corruption(CORRUPT_VAMPIRE_STRENGTH); + player_gain_corruption(CORRUPT_VAMPIRE_VAMPIRE); + } + + process_hooks(HOOK_BIRTH_OBJECTS, "()"); + meta_inertia_control_hook_birth_objects(); + + { + /* Hack -- Give the player some food */ + int qty = (byte)rand_range(3, 7); + player_outfit_object(qty, TV_FOOD, SV_FOOD_RATION); + } + + { + object_type forge; + object_type *q_ptr = &forge; + /* Hack -- Give the player some torches */ + object_prep(q_ptr, lookup_kind(TV_LITE, SV_LITE_TORCH)); + q_ptr->number = (byte)rand_range(3, 7); + q_ptr->timeout = rand_range(3, 7) * 500; + object_aware(q_ptr); + object_known(q_ptr); + (void)inven_carry(q_ptr, FALSE); + } + + /* Rogues have a better knowledge of traps */ + if (has_ability(AB_TRAPPING)) + { + t_info[TRAP_OF_DAGGER_I].known = randint(50) + 50; + t_info[TRAP_OF_POISON_NEEDLE].known = randint(50) + 50; + t_info[TRAP_OF_FIRE_BOLT].known = randint(50) + 50; + t_info[TRAP_OF_DAGGER_I].ident = TRUE; + t_info[TRAP_OF_POISON_NEEDLE].ident = TRUE; + t_info[TRAP_OF_FIRE_BOLT].ident = TRUE; + + /* Hack -- Give the player a some ammo for the traps */ + object_type forge; + object_type *q_ptr = &forge; + object_prep(q_ptr, lookup_kind(TV_SHOT, SV_AMMO_NORMAL)); + q_ptr->number = (byte)rand_range(5, 15); + object_aware(q_ptr); + object_known(q_ptr); + + /* These objects are "storebought" */ + q_ptr->ident |= IDENT_MENTAL; + + (void)inven_carry(q_ptr, FALSE); + } + + /* Hack -- Give the player some useful objects */ + for (i = 0; i < rp_ptr->obj_num; i++) + outfit_obj(rp_ptr->obj_tval[i], rp_ptr->obj_sval[i], rp_ptr->obj_pval[i], rp_ptr->obj_dd[i], rp_ptr->obj_ds[i]); + for (i = 0; i < rmp_ptr->obj_num; i++) + outfit_obj(rmp_ptr->obj_tval[i], rmp_ptr->obj_sval[i], rmp_ptr->obj_pval[i], rmp_ptr->obj_dd[i], rmp_ptr->obj_ds[i]); + for (i = 0; i < cp_ptr->obj_num; i++) + outfit_obj(cp_ptr->obj_tval[i], cp_ptr->obj_sval[i], cp_ptr->obj_pval[i], cp_ptr->obj_dd[i], cp_ptr->obj_ds[i]); + for (i = 0; i < cp_ptr->spec[p_ptr->pspec].obj_num; i++) + outfit_obj(cp_ptr->spec[p_ptr->pspec].obj_tval[i], cp_ptr->spec[p_ptr->pspec].obj_sval[i], cp_ptr->spec[p_ptr->pspec].obj_pval[i], cp_ptr->spec[p_ptr->pspec].obj_dd[i], cp_ptr->spec[p_ptr->pspec].obj_ds[i]); +} + + +/* Possible number(and layout) or random quests */ +#define MAX_RANDOM_QUESTS_TYPES ((8 * 3) + (8 * 1)) +int random_quests_types[MAX_RANDOM_QUESTS_TYPES] = +{ + 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */ + 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */ + 1, 5, 6, 7, 10, 11, 12, 14, /* Princess type */ + 20, 13, 15, 16, 9, 17, 18, 8, /* Hero Sword Quest */ +}; + +/* Enforce OoD monsters until this level */ +#define RQ_LEVEL_CAP 49 + +static void gen_random_quests(int n) +{ + int step, lvl, i, k; + int old_type = dungeon_type; + + /* Factor dlev value by 1000 to keep precision */ + step = (98 * 1000) / n; + + lvl = step / 2; + + quest[QUEST_RANDOM].status = QUEST_STATUS_TAKEN; + + for (i = 0; i < n; i++) + { + monster_race *r_ptr = &r_info[2]; + + int rl = (lvl / 1000) + 1; + + int min_level; + + int tries = 5000; + + random_quest *q_ptr = &random_quests[rl]; + + int j; + + /* Find the appropriate dungeon */ + for (j = 0; j < max_d_idx; j++) + { + dungeon_info_type *d_ptr = &d_info[j]; + + if (!(d_ptr->flags1 & DF1_PRINCIPAL)) continue; + + if ((d_ptr->mindepth <= rl) && (rl <= d_ptr->maxdepth)) + { + dungeon_type = j; + break; + } + } + + q_ptr->type = random_quests_types[rand_int(MAX_RANDOM_QUESTS_TYPES)]; + + /* XXX XXX XXX Try until valid choice is found */ + while (tries) + { + bool_ ok; + + tries--; + + /* Random monster 5 - 10 levels out of depth */ + q_ptr->r_idx = get_mon_num(rl + 4 + randint(6)); + + if (!q_ptr->r_idx) continue; + + r_ptr = &r_info[q_ptr->r_idx]; + + /* Accept only monsters that can be generated */ + if (r_ptr->flags9 & RF9_SPECIAL_GENE) continue; + if (r_ptr->flags9 & RF9_NEVER_GENE) continue; + + /* Accept only monsters that are not breeders */ + if (r_ptr->flags4 & RF4_MULTIPLY) continue; + + /* Forbid joke monsters */ + if (r_ptr->flags8 & RF8_JOKEANGBAND) continue; + + /* Accept only monsters that are not friends */ + if (r_ptr->flags7 & RF7_PET) continue; + + /* Refuse nazguls */ + if (r_ptr->flags7 & RF7_NAZGUL) continue; + + /* Accept only monsters that are not good */ + if (r_ptr->flags3 & RF3_GOOD) continue; + + /* If module says a monster race is friendly, then skip */ + if (modules[game_module_idx].race_status != NULL) + { + s16b *status = (*modules[game_module_idx].race_status)(q_ptr->r_idx); + if ((status != NULL) && (*status >= 0)) + { + continue; + } + } + + /* Assume no explosion attacks */ + ok = TRUE; + + /* Reject monsters with exploding attacks */ + for (k = 0; k < 4; k++) + { + if (r_ptr->blow[k].method == RBM_EXPLODE) ok = FALSE; + } + if (!ok) continue; + + /* No mutliple uniques */ + if ((r_ptr->flags1 & RF1_UNIQUE) && + ((q_ptr->type != 1) || (r_ptr->max_num == -1))) continue; + + /* No single non uniques */ + if ((!(r_ptr->flags1 & RF1_UNIQUE)) && (q_ptr->type == 1)) continue; + + /* Level restriction */ + min_level = (rl > RQ_LEVEL_CAP) ? RQ_LEVEL_CAP : rl; + + /* Accept monsters matching the level restriction */ + if (r_ptr->level > min_level) break; + } + + /* Arg could not find anything ??? */ + if (!tries) + { + if (wizard) + { + message_add(format("Could not find quest monster on lvl %d", rl), TERM_RED); + } + q_ptr->type = 0; + } + else + { + if (r_ptr->flags1 & RF1_UNIQUE) + { + r_ptr->max_num = -1; + } + + q_ptr->done = FALSE; + + if (wizard) + { + message_add(format("Quest for %d on lvl %d", + q_ptr->r_idx, rl), TERM_RED); + } + } + + lvl += step; + } + + dungeon_type = old_type; +} + +int dump_classes(s16b *classes, int sel, u32b *restrictions) +{ + int n = 0; + + char buf[80]; + + cptr str; + + /* Clean up */ + clear_from(12); + + while (classes[n] != -1) + { + cptr mod = ""; + char p2 = ')', p1 = ' '; + + /* Analyze */ + p_ptr->pclass = classes[n]; + cp_ptr = &class_info[p_ptr->pclass]; + str = cp_ptr->title + c_name; + + if (sel == n) + { + p1 = '['; + p2 = ']'; + } + + /* Display */ + strnfmt(buf, 80, "%c%c%c %s%s", p1, + (n <= 25) ? I2A(n) : I2D(n - 26), p2, str, mod); + + /* Print some more info */ + if (sel == n) + { + std::string desc; + + desc += (cp_ptr->desc + c_text); + if (cp_ptr->flags1 & PR1_EXPERIMENTAL) + { + desc += "\nEXPERIMENTAL"; + } + + print_desc(desc.c_str()); + + if (!(restrictions[classes[n] / 32] & BIT(classes[n])) || + cp_ptr->flags1 & PR1_EXPERIMENTAL) + c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); + else + c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); + } + else + { + if (!(restrictions[classes[n] / 32] & BIT(classes[n])) || + cp_ptr->flags1 & PR1_EXPERIMENTAL) + c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); + else + put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4)); + } + n++; + } + + return (n); +} + +int dump_specs(int sel) +{ + int n = 0; + + char buf[80]; + + cptr str; + + /* Clean up */ + clear_from(12); + + for (n = 0; n < MAX_SPEC; n++) + { + char p2 = ')', p1 = ' '; + + /* Found the last one ? */ + if (!class_info[p_ptr->pclass].spec[n].title) break; + + /* Analyze */ + p_ptr->pspec = n; + spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; + str = spp_ptr->title + c_name; + + if (sel == n) + { + p1 = '['; + p2 = ']'; + } + + /* Display */ + strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str); + + /* Print some more info */ + if (sel == n) + { + std::string desc; + + desc += (spp_ptr->desc + c_text); + if (spp_ptr->flags1 & PR1_EXPERIMENTAL) + { + desc += "\nEXPERIMENTAL"; + } + + print_desc(desc.c_str()); + + if (spp_ptr->flags1 & PR1_EXPERIMENTAL) + c_put_str(TERM_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); + else + c_put_str(TERM_L_BLUE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); + } + else + { + if (spp_ptr->flags1 & PR1_EXPERIMENTAL) + c_put_str(TERM_SLATE, buf, 18 + (n / 4), 1 + 20 * (n % 4)); + else + put_str(buf, 18 + (n / 4), 1 + 20 * (n % 4)); + } + } + + return (n); +} + +int dump_races(int sel) +{ + int n = 0; + + char buf[80]; + + cptr str; + + /* Clean up */ + clear_from(12); + + for (n = 0; n < max_rp_idx; n++) + { + char p2 = ')', p1 = ' '; + + /* Analyze */ + p_ptr->prace = n; + rp_ptr = &race_info[p_ptr->prace]; + str = rp_ptr->title + rp_name; + + if (sel == n) + { + p1 = '['; + p2 = ']'; + } + + /* Display */ + strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str); + + /* Print some more info */ + if (sel == n) + { + std::string desc; + + desc += (rp_ptr->desc + rp_text); + if (rp_ptr->flags1 & PR1_EXPERIMENTAL) + { + desc += "\nEXPERIMENTAL"; + } + + print_desc(desc.c_str()); + + if (rp_ptr->flags1 & PR1_EXPERIMENTAL) + c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); + else + c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); + } + else + { + if (rp_ptr->flags1 & PR1_EXPERIMENTAL) + c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); + else + put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5)); + } + } + + return (n); +} + + +int dump_rmods(int sel, int *racem, int max) +{ + int n = 0; + + char buf[80]; + + cptr str; + + /* Clean up */ + clear_from(12); + + /* Dump races */ + for (n = 0; n < max; n++) + { + char p2 = ')', p1 = ' '; + + /* Analyze */ + p_ptr->pracem = racem[n]; + rmp_ptr = &race_mod_info[p_ptr->pracem]; + str = rmp_ptr->title + rmp_name; + + if (sel == n) + { + p1 = '['; + p2 = ']'; + } + + /* Display */ + if (racem[n]) + strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, str); + else + strnfmt(buf, 80, "%c%c%c Classical", p1, I2A(n), p2); + + /* Print some more info */ + if (sel == n) + { + std::string desc; + + desc += (rmp_ptr->desc + rmp_text); + if (rmp_ptr->flags1 & PR1_EXPERIMENTAL) + { + desc += "\nEXPERIMENTAL"; + } + + print_desc(desc.c_str()); + + if (rmp_ptr->flags1 & PR1_EXPERIMENTAL) + c_put_str(TERM_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); + else + c_put_str(TERM_L_BLUE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); + } + else + { + if (rmp_ptr->flags1 & PR1_EXPERIMENTAL) + c_put_str(TERM_SLATE, buf, 18 + (n / 5), 1 + 15 * (n % 5)); + else + put_str(buf, 18 + (n / 5), 1 + 15 * (n % 5)); + } + } + + return (n); +} + +int dump_gods(int sel, int *choice, int max) +{ + int i, j; + char buf[80]; + cptr str; + + /* Clean up */ + clear_from(12); + + Term_putstr(5, 17, -1, TERM_WHITE, + "You can choose to worship a god, some class must start with a god."); + + for (i = 0; i < max; i++) + { + char p2 = ')', p1 = ' '; + int n = choice[i]; + deity_type *g_ptr = &deity_info[0]; + + if (!n) str = "No God"; + else + { + g_ptr = &deity_info[n]; + str = g_ptr->name; + } + + if (sel == i) + { + p1 = '['; + p2 = ']'; + } + + /* Display */ + strnfmt(buf, 80, "%c%c%c %s", p1, I2A(i), p2, str); + + /* Print some more info */ + if (sel == i) + { + if (n) + { + /* Display the first four lines of the god description */ + for (j = 0; j < 4; j++) + if (strcmp(g_ptr->desc[j], "")) + print_desc_aux(g_ptr->desc[j], 12 + j, 1); + } + else print_desc("You can begin as an atheist and still convert to a god later."); + + c_put_str(TERM_L_BLUE, buf, 20 + (i / 4), 1 + 20 * (i % 4)); + } + else + { + put_str(buf, 20 + (i / 4), 1 + 20 * (i % 4)); + } + } + + return (max); +} + + +/* Ask questions */ +static bool_ do_quick_start = FALSE; + +static bool_ player_birth_aux_ask() +{ + int i, k, n, v, sel; + + int racem[100], max_racem = 0; + + u32b restrictions[2]; + + cptr str; + + char c; + + char p2 = ')'; + + char buf[200]; + char inp[200]; + + s16b *class_types; + + /*** Intro ***/ + + /* Clear screen */ + Term_clear(); + + /* Title everything */ + put_str("Name :", 2, 1); + put_str("Sex :", 3, 1); + put_str("Race :", 4, 1); + put_str("Class :", 5, 1); + + /* Dump the default name */ + c_put_str(TERM_L_BLUE, player_name, 2, 9); + + + /*** Instructions ***/ + + /* Display some helpful information */ + Term_putstr(5, 8, -1, TERM_WHITE, + "Please answer the following questions. Most of the questions"); + Term_putstr(5, 9, -1, TERM_WHITE, + "display a set of standard answers, and many will also accept"); + Term_putstr(5, 10, -1, TERM_WHITE, + "some special responses, including 'Q' to quit, 'S' to restart,"); + Term_putstr(5, 11, -1, TERM_WHITE, + "and '?' for help. Note that 'Q' and 'S' must be capitalized."); + + + /*** Quick Start ***/ + + if (previous_char.quick_ok) + { + /* Extra info */ + Term_putstr(1, 15, -1, TERM_WHITE, + "Do you want to use the quick start function(same character as your last one)."); + + /* Choose */ + while (1) + { + put_str("Use quick start (y/n)?", 20, 2); + c = inkey(); + if (c == 'Q') quit(NULL); + else if (c == 'S') return (FALSE); + else if ((c == 'y') || (c == 'Y')) + { + do_quick_start = TRUE; + break; + } + else + { + do_quick_start = FALSE; + break; + } + } + } + + /* Clean up */ + clear_from(15); + + /*** Player sex ***/ + + if (do_quick_start) + { + k = previous_char.sex; + } + else + { + /* Extra info */ + Term_putstr(5, 15, -1, TERM_WHITE, + "Your 'sex' does not have any significant gameplay effects."); + + /* Prompt for "Sex" */ + for (n = 0; n < MAX_SEXES; n++) + { + /* Analyze */ + p_ptr->psex = n; + sp_ptr = &sex_info[p_ptr->psex]; + str = sp_ptr->title; + + /* Display */ + strnfmt(buf, 200, "%c%c %s", I2A(n), p2, str); + put_str(buf, 21 + (n / 5), 2 + 15 * (n % 5)); + } + + /* Choose */ + while (1) + { + strnfmt(buf, 200, "Choose a sex (%c-%c), * for random, = for options: ", I2A(0), I2A(n - 1)); + put_str(buf, 20, 2); + c = inkey(); + if (c == 'Q') quit(NULL); + if (c == 'S') return (FALSE); + if (c == '*') + { + k = rand_int(MAX_SEXES); + break; + } + k = (islower(c) ? A2I(c) : -1); + if ((k >= 0) && (k < n)) break; + if (c == '?') do_cmd_help(); + else if (c == '=') + { + screen_save(); + do_cmd_options_aux(6, "Startup Options", FALSE); + screen_load(); + } + else bell(); + } + } + + /* Set sex */ + p_ptr->psex = k; + sp_ptr = &sex_info[p_ptr->psex]; + str = sp_ptr->title; + + /* Display */ + c_put_str(TERM_L_BLUE, str, 3, 9); + + /* Clean up */ + clear_from(15); + + + /*** Player race ***/ + + if (do_quick_start) + { + k = previous_char.race; + } + else + { + /* Only one choice = instant choice */ + if (max_rp_idx == 1) + k = 0; + else + { + /* Extra info */ + Term_putstr(5, 16, -1, TERM_WHITE, + "Your 'race' determines various intrinsic factors and bonuses."); + + /* Dump races */ + sel = 0; + n = dump_races(sel); + + /* Choose */ + while (1) + { + strnfmt(buf, 200, "Choose a race (%c-%c), * for a random choice, = for options, 8/2/4/6 for movement: ", + I2A(0), I2A(max_rp_idx - 1)); + put_str(buf, 17, 2); + + c = inkey(); + if (c == 'Q') quit(NULL); + if (c == 'S') return (FALSE); + if (c == '*') + { + k = rand_int(max_rp_idx); + break; + } + k = (islower(c) ? A2I(c) : -1); + if ((k >= 0) && (k < n)) break; + if (c == '?') + { + help_race(race_info[sel].title + rp_name); + } + else if (c == '=') + { + screen_save(); + do_cmd_options_aux(6, "Startup Options", FALSE); + screen_load(); + } + else if (c == '2') + { + sel += 5; + if (sel >= n) sel %= 5; + dump_races(sel); + } + else if (c == '8') + { + sel -= 5; + if (sel < 0) sel = n - 1 -( ( -sel) % 5); + /* C's modulus operator does not have defined + results for negative first values. Damn. */ + dump_races(sel); + } + else if (c == '6') + { + sel++; + if (sel >= n) sel = 0; + dump_races(sel); + } + else if (c == '4') + { + sel--; + if (sel < 0) sel = n - 1; + dump_races(sel); + } + else if (c == '\r') + { + k = sel; + break; + } + else bell(); + } + } + } + /* Set race */ + p_ptr->prace = k; + rp_ptr = &race_info[p_ptr->prace]; + str = rp_ptr->title + rp_name; + + /* Display */ + c_put_str(TERM_L_BLUE, str, 4, 9); + + /* Get a random name */ + if (!do_quick_start) create_random_name(p_ptr->prace, player_name); + + /* Display */ + c_put_str(TERM_L_BLUE, player_name, 2, 9); + + /* Clean up */ + clear_from(12); + + + /*** Player race mod ***/ + if (do_quick_start) + { + k = previous_char.rmod; + p_ptr->pracem = k; + rmp_ptr = &race_mod_info[p_ptr->pracem]; + } + else + { + /* Only one choice = instant choice */ + if (max_rmp_idx == 1) + k = 0; + else + { + for (n = 0; n < 100; n++) racem[n] = 0; + + max_racem = 0; + for (n = 0; n < max_rmp_idx; n++) + { + /* Analyze */ + p_ptr->pracem = n; + rmp_ptr = &race_mod_info[p_ptr->pracem]; + + /* Must be an ok choice */ + if (!(BIT(p_ptr->prace) & rmp_ptr->choice[p_ptr->prace / 32])) continue; + + /* Ok thats a possibility */ + racem[max_racem++] = n; + } + + /* Ah ! nothing found, lets use the default */ + if (!max_racem) p_ptr->pracem = 0; + /* Only one ? use it */ + else if (max_racem == 1) p_ptr->pracem = racem[0]; + /* We got to ask the player */ + else + { + /* Extra info */ + Term_putstr(5, 15, -1, TERM_WHITE, + "Your 'race modifier' determines various intrinsic factors and bonuses."); + + /* Dump races */ + sel = 0; + n = dump_rmods(sel, racem, max_racem); + + /* Choose */ + while (1) + { + strnfmt(buf, 200, "Choose a race modifier (%c-%c), * for a random choice, = for options: ", + I2A(0), I2A(max_racem - 1)); + put_str(buf, 17, 2); + c = inkey(); + if (c == 'Q') quit(NULL); + if (c == 'S') return (FALSE); + if (c == '*') + { + do + { + k = rand_int(max_racem); + } + while (!(BIT(racem[k]) & rmp_ptr->choice[racem[k] / 32])); + break; + } + else if (c == '?') + { + help_subrace(race_mod_info[racem[sel]].title + rmp_name); + } + + k = (islower(c) ? A2I(c) : -1); + if ((k >= 0) && (k < max_racem) && + (BIT(p_ptr->prace) & race_mod_info[racem[k]].choice[p_ptr->prace / 32])) break; + + else if (c == '=') + { + screen_save(); + do_cmd_options_aux(6, "Startup Options", FALSE); + screen_load(); + } + else if (c == '2') + { + sel += 5; + if (sel >= n) sel = sel - n + 1; + dump_rmods(sel, racem, max_racem); + } + else if (c == '8') + { + sel -= 5; + if (sel < 0) sel = n - 1 + sel; + dump_rmods(sel, racem, max_racem); + } + else if (c == '6') + { + sel++; + if (sel >= n) sel = 0; + dump_rmods(sel, racem, max_racem); + } + else if (c == '4') + { + sel--; + if (sel < 0) sel = n - 1; + dump_rmods(sel, racem, max_racem); + } + else if (c == '\r') + { + k = sel; + break; + } + else bell(); + } + + /* Set race */ + p_ptr->pracem = racem[k]; + } + rmp_ptr = &race_mod_info[p_ptr->pracem]; + + /* Display */ + c_put_str(TERM_L_BLUE, get_player_race_name(p_ptr->prace, p_ptr->pracem), 4, 9); + } + } + + /* Clean up */ + clear_from(12); + + + /*** Player class ***/ + if (do_quick_start) + { + k = previous_char.pclass; + p_ptr->pclass = k; + cp_ptr = &class_info[p_ptr->pclass]; + k = previous_char.spec; + p_ptr->pspec = k; + spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; + } + else + { + int z; + + for (z = 0; z < 2; z++) + restrictions[z] = (rp_ptr->choice[z] | rmp_ptr->pclass[z]) & (~rmp_ptr->mclass[z]); + + if (max_mc_idx > 1) + { + /* Extra info */ + Term_putstr(5, 13, -1, TERM_WHITE, + "Your 'class' determines various intrinsic abilities and bonuses."); + + /* Get a class type */ + for (i = 0; i < max_mc_idx; i++) + c_put_str(meta_class_info[i].color, format("%c) %s", I2A(i), meta_class_info[i].name), 16 + i, 2); + while (1) + { + strnfmt(buf, 200, "Choose a class type (a-%c), * for random, = for options: ", I2A(max_mc_idx - 1)); + put_str(buf, 15, 2); + c = inkey(); + if (c == 'Q') quit(NULL); + if (c == 'S') return (FALSE); + if (c == '*') + { + k = rand_int(max_mc_idx); + break; + } + k = (islower(c) ? A2I(c) : (D2I(c) + 26)); + if ((k >= 0) && (k < max_mc_idx)) break; + if (c == '?') do_cmd_help(); + else if (c == '=') + { + screen_save(); + do_cmd_options_aux(6, "Startup Options", FALSE); + screen_load(); + } + else bell(); + } + } + else + { + k = 0; + } + class_types = meta_class_info[k].classes; + clear_from(15); + + /* Count classes */ + n = 0; + while (class_types[n] != -1) n++; + + /* Only one choice = instant choice */ + if (n == 1) + k = 0; + else + { + /* Dump classes */ + sel = 0; + n = dump_classes(class_types, sel, restrictions); + + /* Get a class */ + while (1) + { + strnfmt(buf, 200, "Choose a class (%c-%c), * for random, = for options, 8/2/4 for up/down/back: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1)); + put_str(buf, 15, 2); + c = inkey(); + if (c == 'Q') quit(NULL); + if (c == 'S') return (FALSE); + if (c == '*') + { + k = randint(n) - 1; + break; + } + k = (islower(c) ? A2I(c) : (D2I(c) + 26)); + if ((k >= 0) && (k < n)) break; + if (c == '?') + { + help_class(class_info[class_types[sel]].title + c_name); + } + else if (c == '=') + { + screen_save(); + do_cmd_options_aux(6, "Startup Options", FALSE); + screen_load(); + } + else if (c == '2') + { + sel += 4; + if (sel >= n) sel %= 4; + dump_classes(class_types, sel, restrictions); + } + else if (c == '8') + { + sel -= 4; + if (sel < 0) sel = n - 1 -( ( -sel) % 4); + /* C's modulus operator does not have defined + results for negative first values. Damn. */ + dump_classes(class_types, sel, restrictions); + } + else if (c == '6') + { + sel++; + if (sel >= n) sel = 0; + dump_classes(class_types, sel, restrictions); + } + else if (c == '4') + { + sel--; + if (sel < 0) sel = n - 1; + dump_classes(class_types, sel, restrictions); + } + else if (c == '\r') + { + k = sel; + break; + } + else bell(); + } + } + + /* Set class */ + p_ptr->pclass = class_types[k]; + + /* Choose class spec */ + clear_from(15); + + /* Count choices */ + for (n = 0; n < MAX_SPEC; n++) + { + /* Found the last one ? */ + if (!class_info[p_ptr->pclass].spec[n].title) break; + } + + /* Only one choice = auto choice */ + if (n == 1) + k = 0; + else + { + /* Dump classes spec */ + sel = 0; + n = dump_specs(sel); + + /* Get a class */ + while (1) + { + strnfmt(buf, 200, "Choose a class specialisation (%c-%c), * for random, = for options, 8/2/4/6 for up/down/left/right: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1)); + put_str(buf, 15, 2); + c = inkey(); + if (c == 'Q') quit(NULL); + if (c == 'S') return (FALSE); + if (c == '*') + { + k = randint(n) - 1; + break; + } + k = (islower(c) ? A2I(c) : (D2I(c) + 26)); + if ((k >= 0) && (k < n)) break; + if (c == '?') + { + help_class(class_info[p_ptr->pclass].spec[sel].title + c_name); + } + else if (c == '=') + { + screen_save(); + do_cmd_options_aux(6, "Startup Options", FALSE); + screen_load(); + } + else if (c == '2') + { + sel += 4; + if (sel >= n) sel = sel - n + 1; + dump_specs(sel); + } + else if (c == '8') + { + sel -= 4; + if (sel < 0) sel = n - 1 + sel; + dump_specs(sel); + } + else if (c == '6') + { + sel++; + if (sel >= n) sel = 0; + dump_specs(sel); + } + else if (c == '4') + { + sel--; + if (sel < 0) sel = n - 1; + dump_specs(sel); + } + else if (c == '\r') + { + k = sel; + break; + } + else bell(); + } + } + + /* Set class spec */ + p_ptr->pspec = k; + } + cp_ptr = &class_info[p_ptr->pclass]; + spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; + str = spp_ptr->title + c_name; + + /* Display */ + c_put_str(TERM_L_BLUE, str, 5, 9); + + /* Clean up */ + clear_from(15); + + /*** Player god ***/ + if (do_quick_start) + { + k = previous_char.god; + p_ptr->pgod = k; + set_grace(previous_char.grace); + } + else if (PRACE_FLAG(PR1_NO_GOD)) + { + p_ptr->pgod = GOD_NONE; + } + else + { + int choice[MAX_GODS]; + int max = 0; + + /* Get the list of possible gods */ + for (n = 0; n < MAX_GODS; n++) + { + if (god_enabled(&deity_info[n]) && + ((cp_ptr->gods | spp_ptr->gods) & BIT(n))) + { + choice[max++] = n; + } + } + + if (!max) + { + p_ptr->pgod = GOD_NONE; + } + else if (max == 1) + { + p_ptr->pgod = choice[0]; + } + else if (max > 1) + { + sel = 0; + n = dump_gods(sel, choice, max); + + /* Choose */ + while (1) + { + strnfmt(buf, 200, "Choose a god (%c-%c), * for a random choice, " + "= for options, 8/2/4/6 for movement: ", + I2A(0), I2A(max - 1)); + put_str(buf, 19, 2); + + c = inkey(); + if (c == 'Q') quit(NULL); + if (c == 'S') + { + return (FALSE); + } + if (c == '*') + { + k = choice[randint(max) - 1]; + break; + } + k = (islower(c) ? A2I(c) : -1); + if ((k >= 0) && (k < max)) + { + k = choice[k]; + break; + } + if (c == '?') + { + help_god(deity_info[choice[sel]].name); + } + else if (c == '=') + { + screen_save(); + do_cmd_options_aux(6, "Startup Options", FALSE); + screen_load(); + } + else if (c == '2') + { + sel += 4; + if (sel >= n) sel %= 4; + dump_gods(sel, choice, max); + } + else if (c == '8') + { + sel -= 4; + /* C's modulus operator does not have defined + results for negative first values. Damn. */ + if (sel < 0) sel = n - 1 -( ( -sel) % 4); + dump_gods(sel, choice, max); + } + else if (c == '6') + { + sel++; + if (sel >= n) sel = 0; + dump_gods(sel, choice, max); + } + else if (c == '4') + { + sel--; + if (sel < 0) sel = n - 1; + dump_gods(sel, choice, max); + } + else if (c == '\r') + { + k = choice[sel]; + break; + } + else bell(); + } + + /* Set god */ + p_ptr->pgod = k; + p_ptr->grace = 0; + } + + /* A god that like us ? more grace ! */ + if (PRACE_FLAGS(PR1_GOD_FRIEND)) + { + set_grace(200); + } + else + { + set_grace(100); + } + } + + /* Clean up */ + clear_from(12); + + if (!do_quick_start) + { + /* Clear */ + clear_from(15); + + /* */ + if (get_check("Do you want to modify the options")) + { + screen_save(); + do_cmd_options_aux(6, "Startup Options", FALSE); + screen_load(); + } + } + + /* Set birth options: maximize, preserve, sepcial levels and astral */ + p_ptr->maximize = maximize; + p_ptr->preserve = preserve; + p_ptr->special = special_lvls; + p_ptr->astral = (PRACE_FLAG2(PR2_ASTRAL)) ? TRUE : FALSE; + + /* + * A note by pelpel. (remove this please) + * Be it the new Vanilla way (adult vs. birth options) or + * the old one (player_type members), it would be less confusing + * to handle birth-only options in a uniform fashion,the above and + * the following: + * ironman_rooms, + * joke_monsters, + * always_small_level, and + * fate_option + */ + + + /* Set the recall dungeon accordingly */ + dungeon_type = DUNGEON_BASE; + p_ptr->recall_dungeon = dungeon_type; + max_dlv[dungeon_type] = d_info[dungeon_type].mindepth; + + if (p_ptr->astral) + { + /* Somewhere in the misty mountains */ + dungeon_type = DUNGEON_ASTRAL; + p_ptr->wilderness_x = DUNGEON_ASTRAL_WILD_X; + p_ptr->wilderness_y = DUNGEON_ASTRAL_WILD_Y; + } + + /* Clean up */ + clear_from(10); + + /*** User enters number of quests ***/ + /* Heino Vander Sanden and Jimmy De Laet */ + + if (!ironman_rooms) + { + if (do_quick_start) + { + v = previous_char.quests; + } + else + { + /* Extra info */ + Term_putstr(5, 15, -1, TERM_WHITE, + "Select the number of optional random quests you'd like to receive."); + Term_putstr(5, 16, -1, TERM_WHITE, + "If you do not want any optional quests, enter 0."); + + /* Ask the number of additional quests */ + while (TRUE) + { + put_str(format("Number of quests? (0-%u) ", + MAX_RANDOM_QUEST - 1), 20, 2); + + /* Get a the number of additional quest */ + while (TRUE) + { + /* Move the cursor */ + put_str("", 20, 27); + + /* Default */ + strcpy(inp, "20"); + + /* Get a response (or escape) */ + if (!askfor_aux(inp, 2)) inp[0] = '\0'; + if (inp[0] == '*') v = rand_int(MAX_RANDOM_QUEST); + else v = atoi(inp); + + /* Break on valid input */ + if ((v < MAX_RANDOM_QUEST) && ( v >= 0 )) break; + } + break; + } + + /* Clear */ + clear_from(15); + } + } + else + { + /* NO quests for ironman rooms or persistent levels, since they + don't work */ + v = 0; + } + + /* Set the quest monster hook */ + get_mon_num_hook = monster_quest; + + /* Prepare allocation table */ + get_mon_num_prep(); + + /* Generate quests */ + for (i = 0; i < MAX_RANDOM_QUEST; i++) random_quests[i].type = 0; + if (v) gen_random_quests(v); + max_quests = v; + + p_ptr->inside_quest = 0; + + /* Init the plots */ + { + plots[PLOT_MAIN] = QUEST_NECRO; + quest[plots[PLOT_MAIN]].status = QUEST_STATUS_TAKEN; + + plots[PLOT_BREE] = QUEST_THIEVES; + quest[plots[PLOT_BREE]].status = QUEST_STATUS_UNTAKEN; + + plots[PLOT_LORIEN] = QUEST_WOLVES; + quest[plots[PLOT_LORIEN]].status = QUEST_STATUS_UNTAKEN; + + plots[PLOT_GONDOLIN] = QUEST_DRAGONS; + quest[plots[PLOT_GONDOLIN]].status = QUEST_STATUS_UNTAKEN; + + plots[PLOT_MINAS] = QUEST_HAUNTED; + quest[plots[PLOT_MINAS]].status = QUEST_STATUS_UNTAKEN; + + plots[PLOT_KHAZAD] = QUEST_EVIL; + quest[plots[PLOT_KHAZAD]].status = QUEST_STATUS_UNTAKEN; + + plots[PLOT_OTHER] = QUEST_NULL; + } + + quest_random_init_hook(QUEST_RANDOM); + + /* Ok */ + return (TRUE); +} + + + + +/* + * Initial stat costs (initial stats always range from 10 to 18 inclusive). + */ +static const int birth_stat_costs[(18-10) + 1] = +{ + 0, 1, 2, 4, 7, 11, 16, 22, 30 +}; + + +/* + * Helper function for 'player_birth()'. + * + * This function handles "point-based" character creation. + * + * The player selects, for each stat, a value from 10 to 18 (inclusive), + * each costing a certain amount of points (as above), from a pool of 48 + * available points, to which race/class modifiers are then applied. + * + * Each unused point is converted into 100 gold pieces, with a maximum of + * 600 gp at birth. + * + * Taken from V 2.9.0 + */ +static bool_ player_birth_aux_point(void) +{ + int i; + + int row = 3; + + int col = 42; + + int stat = 0; + + int stats[6]; + + int cost; + + char ch; + + char buf[80]; + + int mode = 0; + + + /* Initialize stats */ + for (i = 0; i < 6; i++) + { + /* Initial stats */ + stats[i] = 10; + } + + + /* Roll for base hitpoints */ + get_extra(); + + /* Roll for age/height/weight */ + get_ahw(); + + /* Roll for social class */ + get_history(); + + /*** Generate ***/ + process_hooks(HOOK_BIRTH, "()"); + + /* Get luck */ + p_ptr->luck_base = rp_ptr->luck + rmp_ptr->luck + rand_range( -5, 5); + p_ptr->luck_max = p_ptr->luck_base; + + /* Interact */ + while (1) + { + /* Reset cost */ + cost = 0; + + /* Process stats */ + for (i = 0; i < 6; i++) + { + /* Variable stat maxes */ + if (p_ptr->maximize) + { + /* Reset stats */ + p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stats[i]; + + } + + /* Fixed stat maxes */ + else + { + /* Obtain a "bonus" for "race" and "class" */ + int bonus = rp_ptr->r_adj[i] + cp_ptr->c_adj[i]; + + /* Apply the racial/class bonuses */ + p_ptr->stat_cur[i] = p_ptr->stat_max[i] = + modify_stat_value(stats[i], bonus); + } + + /* Total cost */ + cost += birth_stat_costs[stats[i] - 10]; + } + + /* Restrict cost */ + if (cost > 48) + { + /* Warning */ + bell(); + + /* Reduce stat */ + stats[stat]--; + + /* Recompute costs */ + continue; + } + + /* Gold is inversely proportional to cost */ + p_ptr->au = (100 * (48 - cost)) + 100; + + /* Maximum of 600 gold */ + if (p_ptr->au > 600) p_ptr->au = 600; + + /* Calculate the bonuses and hitpoints */ + p_ptr->update |= (PU_BONUS | PU_HP); + + /* Update stuff */ + update_stuff(); + + /* Fully healed */ + p_ptr->chp = p_ptr->mhp; + + /* Fully rested */ + p_ptr->csp = p_ptr->msp; + + /* Display the player */ + display_player(mode); + + /* Display the costs header */ + put_str("Cost", row - 2, col + 32); + + /* Display the costs */ + for (i = 0; i < 6; i++) + { + /* Display cost */ + strnfmt(buf, 80, "%4d", birth_stat_costs[stats[i] - 10]); + put_str(buf, row + (i - 1), col + 32); + } + + + /* Prompt XXX XXX XXX */ + strnfmt(buf, 80, "Total Cost %2d/48. Use 2/8 to move, 4/6 to modify, ESC to accept.", cost); + prt(buf, 0, 0); + + /* Place cursor just after cost of current stat */ + Term_gotoxy(col + 36, row + stat - 1); + + /* Get key */ + ch = inkey(); + + /* Quit */ + if (ch == 'Q') quit(NULL); + + /* Start over */ + if (ch == 'S') return (FALSE); + + /* Done */ + if (ch == ESCAPE) break; + + /* Prev stat */ + if (ch == '8') + { + stat = (stat + 6 - 1) % 6; + } + + /* Next stat */ + if (ch == '2') + { + stat = (stat + 1) % 6; + } + + /* Decrease stat */ + if ((ch == '4') && (stats[stat] > 10)) + { + stats[stat]--; + } + + /* Increase stat */ + if ((ch == '6') && (stats[stat] < 18)) + { + stats[stat]++; + } + } + + + /* Done */ + return (TRUE); +} + +/* + * Use the autoroller or not to generate a char + */ +static bool_ player_birth_aux_auto() +{ + int i, j, m, v; + + int mode = 0; + + bool_ flag = FALSE; + + bool_ prev = FALSE; + + char c; + + char b1 = '['; + + char b2 = ']'; + + char buf[80]; + + char inp[80]; + + + /* Initialize */ + if (autoroll) + { + int mval[6]; + + + /* Clear fields */ + auto_round = 0L; + last_round = 0L; + + /* Clean up */ + clear_from(10); + + /* Prompt for the minimum stats */ + put_str("Enter minimum attribute for: ", 15, 2); + + /* Output the maximum stats */ + for (i = 0; i < 6; i++) + { + char stat_buf[15]; + + /* Reset the "success" counter */ + stat_match[i] = 0; + + /* Race/Class bonus */ + j = rp_ptr->r_adj[i] + rmp_ptr->r_adj[i] + cp_ptr->c_adj[i]; + + /* Obtain the "maximal" stat */ + m = adjust_stat(17, j, TRUE); + + + /* Save the maximum */ + mval[i] = m; + + /* Extract a textual format */ + cnv_stat(m, stat_buf); + + strnfmt(inp, 80, "(Max of %s):", stat_buf); + + /* Prepare a prompt */ + strnfmt(buf, 80, "%-5s: %-20s", stat_names[i], inp); + + /* Dump the prompt */ + put_str(buf, 16 + i, 5); + } + + /* Input the minimum stats */ + for (i = 0; i < 6; i++) + { + /* Get a minimum stat */ + while (TRUE) + { + char *s; + + /* Move the cursor */ + put_str("", 16 + i, 30); + + /* Default */ + strcpy(inp, ""); + + /* Get a response (or escape) */ + if (!askfor_aux(inp, 8)) inp[0] = '\0'; + + /* Weirdos stat display .. erm .. I mean, original stat display */ + if (!linear_stats) + { + /* Hack -- add a fake slash */ + strcat(inp, "/"); + + /* Hack -- look for the "slash" */ + s = strchr(inp, '/'); + + /* Hack -- Nuke the slash */ + *s++ = '\0'; + + /* Hack -- Extract an input */ + v = atoi(inp) + atoi(s); + } + else + { + int z = atoi(inp); + + if (z <= 18) + v = z; + else + { + int extra = z - 18; + v = 18 + (extra * 10); + } + } + + /* Break on valid input */ + if (v <= mval[i]) break; + } + + /* Save the minimum stat */ + stat_limit[i] = (v > 0) ? v : 0; + } + } + + /* Roll */ + while (TRUE) + { + /* Feedback */ + if (autoroll) + { + Term_clear(); + + put_str("Name :", 2, 1); + put_str("Sex :", 3, 1); + put_str("Race :", 4, 1); + put_str("Class:", 5, 1); + + c_put_str(TERM_L_BLUE, player_name, 2, 9); + c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 9); + strnfmt(buf, 80, "%s", get_player_race_name(p_ptr->prace, p_ptr->pracem)); + c_put_str(TERM_L_BLUE, buf, 4, 9); + c_put_str(TERM_L_BLUE, spp_ptr->title + c_name, 5, 9); + + /* Label stats */ + put_str("STR:", 2 + A_STR, 61); + put_str("INT:", 2 + A_INT, 61); + put_str("WIS:", 2 + A_WIS, 61); + put_str("DEX:", 2 + A_DEX, 61); + put_str("CON:", 2 + A_CON, 61); + put_str("CHR:", 2 + A_CHR, 61); + + /* Note when we started */ + last_round = auto_round; + + /* Indicate the state */ + put_str("(Hit ESC to abort)", 11, 61); + + /* Label count */ + put_str("Round:", 9, 61); + } + + /* Otherwise just get a character */ + else + { + /* Get a new character */ + get_stats(); + } + + /* Auto-roll */ + while (autoroll) + { + bool_ accept = TRUE; + + /* Get a new character */ + get_stats(); + + /* Advance the round */ + auto_round++; + + /* Hack -- Prevent overflow */ + if (auto_round >= 1000000L) break; + + /* Check and count acceptable stats */ + for (i = 0; i < 6; i++) + { + /* This stat is okay */ + if (stat_use[i] >= stat_limit[i]) + { + stat_match[i]++; + } + + /* This stat is not okay */ + else + { + accept = FALSE; + } + } + + /* Break if "happy" */ + if (accept) break; + + /* Take note every 25 rolls */ + flag = (!(auto_round % AUTOROLLER_STEP)); + + /* Update display occasionally */ + if (flag || (auto_round < last_round + 100)) + { + /* Dump data */ + birth_put_stats(); + + /* Dump round */ + put_str(format("%6ld", auto_round), 9, 73); + + /* Make sure they see everything */ + Term_fresh(); + + /* Do not wait for a key */ + inkey_scan = TRUE; + + /* Check for a keypress */ + if (inkey()) break; + } + } + + /* Flush input */ + flush(); + + + /*** Display ***/ + + /* Mode */ + mode = 0; + + /* Roll for base hitpoints */ + get_extra(); + + /* Roll for age/height/weight */ + get_ahw(); + + /* Roll for social class */ + get_history(); + + /* Roll for gold */ + get_money(); + + /*** Generate ***/ + process_hooks(HOOK_BIRTH, "()"); + + /* Input loop */ + while (TRUE) + { + /* Calculate the bonuses and hitpoints */ + p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY); + + /* Update stuff */ + update_stuff(); + + /* Fully healed */ + p_ptr->chp = p_ptr->mhp; + + /* Fully rested */ + p_ptr->csp = p_ptr->msp; + + /* Display the player */ + display_player(mode); + + /* Prepare a prompt (must squeeze everything in) */ + Term_gotoxy(2, 23); + Term_addch(TERM_WHITE, b1); + Term_addstr( -1, TERM_WHITE, "'r' to reroll"); + if (prev) Term_addstr( -1, TERM_WHITE, ", 'p' for prev"); + if (mode) Term_addstr( -1, TERM_WHITE, ", 'h' for Misc."); + else Term_addstr( -1, TERM_WHITE, ", 'h' for History"); + Term_addstr( -1, TERM_WHITE, ", or ESC to accept"); + Term_addch(TERM_WHITE, b2); + + /* Prompt and get a command */ + c = inkey(); + + /* Quit */ + if (c == 'Q') quit(NULL); + + /* Start over */ + if (c == 'S') return (FALSE); + + /* Escape accepts the roll */ + if (c == ESCAPE) break; + + /* Reroll this character */ + if ((c == ' ') || (c == 'r')) break; + + /* Previous character */ + if (prev && (c == 'p')) + { + load_prev_data(TRUE); + continue; + } + + /* Toggle the display */ + if ((c == 'H') || (c == 'h')) + { + mode = ((mode != 0) ? 0 : 1); + continue; + } + + /* Help */ + if (c == '?') + { + do_cmd_help(); + continue; + } + + /* Warning */ + bell(); + } + + /* Are we done? */ + if (c == ESCAPE) break; + + /* Save this for the "previous" character */ + save_prev_data(); + + /* Note that a previous roll exists */ + prev = TRUE; + } + + /* Clear prompt */ + clear_from(23); + + return (TRUE); +} + + +/* + * Helper function for 'player_birth()' + * + * The delay may be reduced, but is recommended to keep players + * from continuously rolling up characters, which can be VERY + * expensive CPU wise. And it cuts down on player stupidity. + */ +static bool_ player_birth_aux() +{ + char c; + + int i, j; + + int y = 0, x = 0; + + char old_history[4][60]; + + /* Ask */ + if (!player_birth_aux_ask()) return (FALSE); + + for (i = 1; i < max_s_idx; i++) + s_info[i].dev = FALSE; + for (i = 1; i < max_s_idx; i++) + { + s32b value = 0, mod = 0; + + compute_skills(&value, &mod, i); + + init_skill(value, mod, i); + + /* Develop only revelant branches */ + if (s_info[i].value || s_info[i].mod) + { + int z = s_info[i].father; + + while (z != -1) + { + s_info[z].dev = TRUE; + z = s_info[z].father; + if (z == 0) + break; + } + } + } + + if (do_quick_start) + { + load_prev_data(FALSE); + + /* Roll for base hitpoints */ + get_extra(); + + /* Calculate the bonuses and hitpoints */ + p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_BODY); + + /* Update stuff */ + update_stuff(); + + /* Fully healed */ + p_ptr->chp = p_ptr->mhp; + + /* Fully rested */ + p_ptr->csp = p_ptr->msp; + } + else + { + /* Point based */ + if (point_based) + { + if (!player_birth_aux_point()) return FALSE; + } + /* Auto-roll */ + else + { + if (!player_birth_aux_auto()) return FALSE; + } + + /* Edit character background */ + for (i = 0; i < 4; i++) + { + strnfmt(old_history[i], 60, "%s", history[i]); + } + /* Turn 0 to space */ + for (i = 0; i < 4; i++) + { + for (j = 0; history[i][j]; j++) /* loop */; + + for (; j < 59; j++) history[i][j] = ' '; + } + display_player(1); + c_put_str(TERM_L_GREEN, "(Character Background - Edit Mode)", 15, 20); + while (TRUE) + { + for (i = 0; i < 4; i++) + { + put_str(history[i], i + 16, 10); + } + c_put_str(TERM_L_BLUE, format("%c", history[y][x]), y + 16, x + 10); + + /* Place cursor just after cost of current stat */ + Term_gotoxy(x + 10, y + 16); + + c = inkey(); + + if (c == '8') + { + y--; + if (y < 0) y = 3; + } + else if (c == '2') + { + y++; + if (y > 3) y = 0; + } + else if (c == '6') + { + x++; + if (x > 59) x = 0; + } + else if (c == '4') + { + x--; + if (x < 0) x = 59; + } + else if (c == '\r') + { + break; + } + else if (c == ESCAPE) + { + for (i = 0; i < 4; i++) + { + strnfmt(history[i], 60, "%s", old_history[i]); + put_str(history[i], i + 16, 10); + } + break; + } + else + { + history[y][x++] = c; + if (x > 58) + { + x = 0; + y++; + if (y > 3) y = 0; + } + } + } + + + /*** Finish up ***/ + + /* Get a name, recolor it, prepare savefile */ + + get_name(); + + + /* Prompt for it */ + prt("['Q' to suicide, 'S' to start over, or ESC to continue]", 23, 10); + + /* Get a key */ + c = inkey(); + + /* Quit */ + if (c == 'Q') quit(NULL); + + /* Start over */ + if (c == 'S') return (FALSE); + } + + /* Save this for the next character */ + previous_char.quick_ok = TRUE; + save_prev_data(); + + /* Accept */ + return (TRUE); +} + + +/* + * Helper function for validate_bg(). + */ +static void validate_bg_aux(int chart, bool_ chart_checked[], char *buf) +{ + char *s; + + int i; + + + /* Assume the chart does not exist */ + bool_ chart_exists = FALSE; + + /* Assume the chart is not complete */ + bool_ chart_complete = FALSE; + + int bg_max = max_bg_idx; + + /* No chart */ + if (!chart) return; + + /* Already saw this chart */ + if (chart_checked[chart]) return; + + /* Build a debug message */ + s = buf + strlen(buf); + + /* XXX XXX XXX */ + (void) strnfmt(s, -1, "%d --> ", chart); + + /* Check each chart */ + for (i = 0; i < bg_max; i++) + { + /* Require same chart */ + if (bg[i].chart != chart) continue; + + /* The chart exists */ + chart_exists = TRUE; + + /* Validate the "next" chart recursively */ + validate_bg_aux(bg[i].next, chart_checked, buf); + + /* Require a terminator */ + if (bg[i].roll != 100) continue; + + /* The chart is complete */ + chart_complete = TRUE; + } + + /* Failed: The chart does not exist */ + if (!chart_exists) + { + quit_fmt("birth.c: bg[] chart %d does not exist\n%s", chart, buf); + } + + /* Failed: The chart is not complete */ + if (!chart_complete) + { + quit_fmt("birth.c: bg[] chart %d is not complete", chart); + } + + /* Remember we saw this chart */ + chart_checked[chart] = TRUE; + + /* Build a debug message */ + *s = 0; +} + + +/* + * Verify that the bg[] table is valid. + */ +static void validate_bg(void) +{ + int i, race; + + bool_ chart_checked[512]; + + char buf[1024]; + + + for (i = 0; i < 512; i++) chart_checked[i] = FALSE; + + /* Check each race */ + for (race = 0; race < max_rp_idx; race++) + { + /* Get the first chart for this race */ + int chart = race_info[race].chart; + + (void) strcpy(buf, ""); + + /* Validate the chart recursively */ + validate_bg_aux(chart, chart_checked, buf); + } +} + +/* + * Initialize a random town + */ +void init_town(int t_idx, int level) +{ + town_type *t_ptr = &town_info[t_idx]; + + /* Mark it as existent */ + t_ptr->flags |= (TOWN_REAL); + + /* Mark it as not found */ + t_ptr->flags &= ~(TOWN_KNOWN); + + /* Generation seed for the town */ + t_ptr->seed = randint(0x10000000); + + /* Total hack and not even used */ + t_ptr->numstores = 8; +} + +/* + * Create a new character. + * + * Note that we may be called with "junk" leftover in the various + * fields, so we must be sure to clear them first. + */ +void player_birth(void) +{ + int i, j, rtown = TOWN_RANDOM; + + /* Validate the bg[] table */ + validate_bg(); + + /* Create a new character */ + while (1) + { + /* Wipe the player */ + player_wipe(); + + /* Roll up a new character */ + if (player_birth_aux()) break; + } + + /* Finish skills */ + p_ptr->skill_points = 0; + p_ptr->skill_last_level = 1; + + recalc_skills(FALSE); + + /* grab level 1 abilities */ + for (i = 0; i < max_ab_idx; i++) + ab_info[i].acquired = FALSE; + apply_level_abilities(1); + + /* Complete the god */ + i = p_ptr->pgod; + p_ptr->pgod = 0; + follow_god(i, TRUE); + + /* Select the default melee type */ + select_default_melee(); + + /* Make a note file if that option is set */ + add_note_type(NOTE_BIRTH); + + /* Note player birth in the message recall */ + message_add(" ", TERM_L_BLUE); + message_add(" ", TERM_L_BLUE); + message_add("====================", TERM_L_BLUE); + message_add(" ", TERM_L_BLUE); + message_add(" ", TERM_L_BLUE); + + /* Hack -- outfit the player */ + player_outfit(); + + /* Initialize random towns in the dungeons */ + for (i = 0; i < max_d_idx; i++) + { + dungeon_info_type *d_ptr = &d_info[i]; + int num = 0, z; + + d_ptr->t_num = 0; + for (z = 0; z < TOWN_DUNGEON; z++) + { + d_ptr->t_idx[z] = 0; + d_ptr->t_level[z] = 0; + } + if (!(d_ptr->flags1 & DF1_RANDOM_TOWNS)) continue; + + /* Can we add a town ? */ + while (magik(TOWN_CHANCE - (num * 10))) + { + int lev; + + d_ptr->t_idx[num] = rtown; + rtown++; + + while (TRUE) + { + int j; + bool_ ok = TRUE; + + lev = rand_range(d_ptr->mindepth, d_ptr->maxdepth - 1); + + /* Be sure it wasnt already used */ + for (j = 0; j < num; j++) + { + if (d_ptr->t_level[j] == lev) ok = FALSE; + } + + /* Ok found one */ + if (ok) break; + } + d_ptr->t_level[num] = lev; + + if (wizard) + { + message_add(format("Random dungeon town: d_idx:%d, lev:%d", i, lev), TERM_WHITE); + } + + /* Create the town */ + init_town(d_ptr->t_idx[num], d_ptr->t_level[num]); + + num++; + + /* No free slots left */ + if (num >= TOWN_DUNGEON) break; + } + + d_ptr->t_num = num; + } + + /* Init the towns */ + for (i = 1; i < max_towns; i++) + { + /* Not destroyed ! yet .. ;) */ + town_info[i].destroyed = FALSE; + + /* Ignore non-existent towns */ + if (!(town_info[i].flags & (TOWN_REAL))) continue; + + create_stores_stock(i); + + /* Init the stores */ + for (j = 0; j < max_st_idx; j++) + { + /* Initialize */ + store_init(i, j); + } + } + + /* Init wilderness seeds */ + for (i = 0; i < max_wild_x; i++) + { + for (j = 0; j < max_wild_y; j++) + { + wild_map[j][i].seed = rand_int(0x10000000); + wild_map[j][i].entrance = 0; + wild_map[j][i].known = FALSE; + } + } + + /* Select bounty monsters. */ + select_bounties(); +} + + + + +char savefile_module[46][80]; +char savefile_names[46][30]; +char savefile_desc[46][80]; +bool_ savefile_alive[46]; +int savefile_idx[46]; + +/* + * Grab all the names from an index + */ +int load_savefile_names() +{ + FILE *fff; + char buf[1024]; + char tmp[50]; + char player_base_save[32]; + int max = 0, fd; + + + /* Build the filename */ + strcpy(tmp, "global.svg"); + path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Read the file */ + fff = my_fopen(buf, "r"); + + /* Failure */ + if (!fff) return (0); + + + /* Save the current 'player_base' */ + strncpy(player_base_save, player_base, 32); + + + /* + * Parse, use '@' intead of ':' as a separator because it cannot exists + * in savefiles + */ + while (0 == my_fgets(fff, buf, 1024)) + { + int i = 0, start, count; + + /* Check for pre-ToME 2.1.2 file */ + count = 0; + i = 0; + while (buf[i] && buf[i] != '\n') + { + if (buf[i] == '@') + ++count; + ++i; + } + + /* Check module if a current svg file */ + start = 0; + i = 0; + if (count > 1) + { + while (buf[i] != '@') + { + savefile_module[max][i - start] = buf[i]; + i++; + } + savefile_module[max][i] = '\0'; + i++; + } + /* Default to ToME for old files */ + else + { + savefile_module[max][0] = 'T'; + savefile_module[max][1] = 'o'; + savefile_module[max][2] = 'M'; + savefile_module[max][3] = 'E'; + savefile_module[max][4] = '\0'; + } + + if (buf[i] == '0') savefile_alive[max] = FALSE; + else if (buf[i] == '1') savefile_alive[max] = TRUE; + + i++; + start = i; + while (buf[i] != '@') + { + savefile_names[max][i - start] = buf[i]; + i++; + } + savefile_names[max][i - start] = '\0'; + i++; + strcpy(savefile_desc[max], buf + i); + + /* Build platform-dependent savefile name */ + strncpy(player_base, savefile_names[max], 32); + process_player_name(TRUE); + + /* File type is 'SAVE' */ + FILE_TYPE(FILE_TYPE_SAVE); + + /* Try to open the savefile */ + fd = fd_open(savefile, O_RDONLY); + + /* Still existing ? */ + if (fd >= 0) + { + fd_close(fd); + max++; + } + } + + my_fclose(fff); + + /* Restore the values of 'player_base' and 'savefile' */ + strncpy(player_base, player_base_save, 32); + process_player_name(TRUE); + + return (max); +} + + +/* + * Save all the names from an index + */ +void save_savefile_names() +{ + FILE *fff; + char buf[1024]; + char tmp[50]; + int max = load_savefile_names(), i; + + + /* Build the filename */ + strcpy(tmp, "global.svg"); + path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Read the file */ + fff = my_fopen(buf, "w"); + + /* Failure */ + if (!fff) return; + + /* + * Save, use '@' intead of ':' as a separator because it cannot exists + * in savefiles + */ + fprintf(fff, "%s@%c%s@%s, the %s %s is %s\n", game_module, + (death) ? '0' : '1', player_base, player_name, + get_player_race_name(p_ptr->prace, p_ptr->pracem), + spp_ptr->title + c_name, + (!death) ? "alive" : "dead"); + + for (i = 0; i < max; i++) + { + if (!strcmp(savefile_names[i], player_base)) continue; + fprintf(fff, "%s@%c%s@%s\n", savefile_module[i], + (savefile_alive[i]) ? '1' : '0', savefile_names[i], savefile_desc[i]); + } + + my_fclose(fff); +} + + +static void dump_savefiles(int sel, int max) +{ + int i; + + char buf[40], pre = ' ', post = ')'; + + char ind; + + + for (i = 0; i < max; i++) + { + ind = I2A(i % 26); + if (i >= 26) ind = toupper(ind); + + if (sel == i) + { + pre = '['; + post = ']'; + } + else + { + pre = ' '; + post = ')'; + } + + if (i == 0) strnfmt(buf, 40, "%c%c%c New Character", pre, ind, post); + else if (i == 1) strnfmt(buf, 40, "%c%c%c Load Savefile", pre, ind, post); + else strnfmt(buf, 40, "%c%c%c %s", pre, ind, post, savefile_names[savefile_idx[i - 2]]); + + if (sel == i) + { + if (i >= 2) + { + if (savefile_alive[i - 2]) c_put_str(TERM_L_GREEN, savefile_desc[savefile_idx[i - 2]], 5, 0); + else c_put_str(TERM_L_RED, savefile_desc[savefile_idx[i - 2]], 5, 0); + } + else if (i == 1) c_put_str(TERM_YELLOW, "Load an existing savefile that is not in the list", 5, 0); + else c_put_str(TERM_YELLOW, "Create a new character", 5, 0); + c_put_str(TERM_L_BLUE, buf, 6 + (i / 4), 20 * (i % 4)); + } + else + put_str(buf, 6 + (i / 4), 20 * (i % 4)); + } +} + + +/* Asks for new game or load game */ +bool_ no_begin_screen = FALSE; + +bool_ begin_screen() +{ + int m, k, sel, max; + +savefile_try_again: + sel = 0; + + /* Grab the savefiles */ + max = load_savefile_names(); + + /* Get only the usable savefiles */ + for (k = 0, m = 0; k < max; k++) + { + s32b can_use; + + can_use = module_savefile_loadable(savefile_module[k]); + if (can_use) + { + savefile_idx[m++] = k; + } + } + max = m + 2; + if (max > 2) sel = 2; + + while (TRUE) + { + /* Clear screen */ + Term_clear(); + + /* Let the user choose */ + c_put_str(TERM_YELLOW, format("Welcome to %s! To play you will need a character.", game_module), 1, 10); + put_str("Press 8/2/4/6 to move, Return to select, Backspace to delete a savefile.", 3, 3); + put_str("and Esc to quit.", 4, 32); + + dump_savefiles(sel, max); + + k = inkey(); + + if (k == ESCAPE) + { + quit(NULL); + } + if (k == '6') + { + sel++; + if (sel >= max) sel = 0; + continue; + } + else if (k == '4') + { + sel--; + if (sel < 0) sel = max - 1; + continue; + } + else if (k == '2') + { + sel += 4; + if (sel >= max) sel = sel % max; + continue; + } + else if (k == '8') + { + sel -= 4; + if (sel < 0) sel = (sel + max - 1) % max; + continue; + } + else if (k == '\r') + { + if (sel < 26) k = I2A(sel); + else k = toupper(I2A(sel)); + } + else if (((k == 0x7F) || (k == '\010')) && (sel >= 2)) + { + char player_base_save[32]; + + if (!get_check(format("Really delete '%s'?", savefile_names[savefile_idx[sel - 2]]))) continue; + + /* Save current 'player_base' */ + strncpy(player_base_save, player_base, 32); + + /* Build platform-dependent save file name */ + strncpy(player_base, savefile_names[savefile_idx[sel - 2]], 32); + process_player_name(TRUE); + + /* Remove the savefile */ + fd_kill(savefile); + + /* Restore 'player_base' and 'savefile' */ + strncpy(player_base, player_base_save, 32); + process_player_name(TRUE); + + /* Reload, gods I hate using goto .. */ + goto savefile_try_again; + + continue; + } + + if (k == 'a') + { + /* Display prompt */ + prt("Enter the name of the savefile that will hold this character: ", 23, 0); + + /* Ask the user for a string */ + if (!askfor_aux(player_base, 15)) continue; + + /* Process the player name */ + process_player_name(TRUE); + + return (TRUE); + } + if (k == 'b') + { + /* Display prompt */ + prt("Enter the name of a savefile: ", 23, 0); + + /* Ask the user for a string */ + if (!askfor_aux(player_base, 15)) continue; + + /* Process the player name */ + process_player_name(TRUE); + + return (FALSE); + } + else + { + int x; + + if (islower(k)) x = A2I(k); + else x = A2I(tolower(k)) + 26; + + if ((x < 2) || (x >= max)) continue; + + strnfmt(player_base, 32, "%s", savefile_names[savefile_idx[x - 2]]); + + /* Process the player name */ + process_player_name(TRUE); + + return (FALSE); + } + } + + /* Shouldnt happen */ + return (FALSE); +} diff --git a/src/bldg.c b/src/bldg.c deleted file mode 100644 index 468e3c1d..00000000 --- a/src/bldg.c +++ /dev/null @@ -1,2234 +0,0 @@ -/* File: bldg.c */ - -/* - * Purpose: Building commands - * Created by Ken Wigle for Kangband - a variant of Angband 2.8.3 - * -KMW- - * - * Rewritten for Kangband 2.8.3i using Kamband's version of - * bldg.c as written by Ivan Tkatchev - * - * Changed for ZAngband by Robert Ruehlmann - * - * Heavily modified for ToME by DarkGod - */ - -#include "angband.h" -#include "hooks.h" -#include "q_library.h" -#include "q_fireprof.h" -#include "q_bounty.h" - -/* hack as in leave_store in store.c */ -static bool_ leave_bldg = FALSE; - -/* remember building location */ -static int building_loc = 0; - - -/* - * A helper function for is_state - */ -bool_ is_state_aux(store_type *s_ptr, int state) -{ - owner_type *ow_ptr = &ow_info[s_ptr->owner]; - - - /* Check race */ - if (ow_ptr->races[state][p_ptr->prace / 32] & (1 << p_ptr->prace)) - { - return (TRUE); - } - - /* Check class */ - if (ow_ptr->classes[state][p_ptr->prace / 32] & (1 << p_ptr->pclass)) - { - return (TRUE); - } - - /* All failed */ - return (FALSE); -} - - -/* - * Test if the state accords with the player - */ -bool_ is_state(store_type *s_ptr, int state) -{ - if (state == STORE_NORMAL) - { - if (is_state_aux(s_ptr, STORE_LIKED)) return (FALSE); - if (is_state_aux(s_ptr, STORE_HATED)) return (FALSE); - return (TRUE); - } - - else - { - return (is_state_aux(s_ptr, state)); - } -} - - -/* - * Clear the building information - */ -static void clear_bldg(int min_row, int max_row) -{ - int i; - - - for (i = min_row; i <= max_row; i++) - { - prt("", i, 0); - } -} - - -/* - * Display a building. - */ -void show_building(store_type *s_ptr) -{ - char buff[20]; - - int i; - - byte action_color; - - char tmp_str[80]; - - store_info_type *st_ptr = &st_info[s_ptr->st_idx]; - - store_action_type *ba_ptr; - - - for (i = 0; i < 6; i++) - { - ba_ptr = &ba_info[st_ptr->actions[i]]; - - if (ba_ptr->letter != '.') - { - if (ba_ptr->action_restr == 0) - { - if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) || - (is_state(s_ptr, STORE_HATED) && (ba_ptr->costs[STORE_HATED] == 0)) || - (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0))) - { - action_color = TERM_WHITE; - buff[0] = '\0'; - } - else if (is_state(s_ptr, STORE_LIKED)) - { - action_color = TERM_L_GREEN; - strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]); - } - else if (is_state(s_ptr, STORE_HATED)) - { - action_color = TERM_RED; - strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_HATED]); - } - else - { - action_color = TERM_YELLOW; - strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]); - } - } - else if (ba_ptr->action_restr == 1) - { - if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) || - (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0))) - { - action_color = TERM_WHITE; - buff[0] = '\0'; - } - else if (is_state(s_ptr, STORE_LIKED)) - { - action_color = TERM_L_GREEN; - strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]); - } - else if (is_state(s_ptr, STORE_HATED)) - { - action_color = TERM_L_DARK; - strnfmt(buff, 20, "(closed)"); - } - else - { - action_color = TERM_YELLOW; - strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]); - } - } - else - { - if (is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) - { - action_color = TERM_WHITE; - buff[0] = '\0'; - } - else if (is_state(s_ptr, STORE_LIKED)) - { - action_color = TERM_L_GREEN; - strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]); - } - else - { - action_color = TERM_L_DARK; - strnfmt(buff, 20, "(closed)"); - } - } - - strnfmt(tmp_str, 80, " %c", ba_ptr->letter); - c_put_str(TERM_YELLOW, tmp_str, 21 + (i / 2), 17 + (30 * (i % 2))); - - strnfmt(tmp_str, 80, ") %s %s", ba_ptr->name + ba_name, buff); - c_put_str(action_color, tmp_str, 21 + (i / 2), 2 + 17 + (30 * (i % 2))); - } - } -} - - -/* reset timed flags */ -static void reset_tim_flags() -{ - p_ptr->fast = 0; /* Timed -- Fast */ - p_ptr->slow = 0; /* Timed -- Slow */ - p_ptr->blind = 0; /* Timed -- Blindness */ - p_ptr->paralyzed = 0; /* Timed -- Paralysis */ - p_ptr->confused = 0; /* Timed -- Confusion */ - p_ptr->afraid = 0; /* Timed -- Fear */ - p_ptr->image = 0; /* Timed -- Hallucination */ - p_ptr->poisoned = 0; /* Timed -- Poisoned */ - p_ptr->cut = 0; /* Timed -- Cut */ - p_ptr->stun = 0; /* Timed -- Stun */ - - p_ptr->protevil = 0; /* Timed -- Protection */ - p_ptr->protgood = 0; /* Timed -- Protection */ - p_ptr->invuln = 0; /* Timed -- Invulnerable */ - p_ptr->hero = 0; /* Timed -- Heroism */ - p_ptr->shero = 0; /* Timed -- Super Heroism */ - p_ptr->shield = 0; /* Timed -- Shield Spell */ - p_ptr->blessed = 0; /* Timed -- Blessed */ - p_ptr->tim_invis = 0; /* Timed -- Invisibility */ - p_ptr->tim_infra = 0; /* Timed -- Infra Vision */ - - p_ptr->oppose_acid = 0; /* Timed -- oppose acid */ - p_ptr->oppose_elec = 0; /* Timed -- oppose lightning */ - p_ptr->oppose_fire = 0; /* Timed -- oppose heat */ - p_ptr->oppose_cold = 0; /* Timed -- oppose cold */ - p_ptr->oppose_pois = 0; /* Timed -- oppose poison */ - - p_ptr->confusing = 0; /* Touch of Confusion */ -} - - -/* - * arena commands - */ -static void arena_comm(int cmd) -{ - char tmp_str[80]; - - monster_race *r_ptr; - - cptr name; - - - switch (cmd) - { - case BACT_ARENA: - { - if (p_ptr->arena_number == MAX_ARENA_MONS) - { - clear_bldg(5, 19); - prt(" Arena Victor!", 5, 0); - prt("Congratulations! You have defeated all before you.", 7, 0); - prt("For that, receive the prize: 10,000 gold pieces", 8, 0); - prt("", 10, 0); - prt("", 11, 0); - p_ptr->au += 10000; - msg_print("Press the space bar to continue"); - msg_print(NULL); - p_ptr->arena_number++; - } - else if (p_ptr->arena_number > MAX_ARENA_MONS) - { - msg_print("You enter the arena briefly and bask in your glory."); - msg_print(NULL); - } - else - { - p_ptr->inside_arena = TRUE; - p_ptr->exit_bldg = FALSE; - reset_tim_flags(); - p_ptr->leaving = TRUE; - p_ptr->oldpx = p_ptr->px; - p_ptr->oldpy = p_ptr->py; - leave_bldg = TRUE; - } - - break; - } - - case BACT_POSTER: - { - if (p_ptr->arena_number == MAX_ARENA_MONS) - msg_print("You are victorious. Enter the arena for the ceremony."); - else if (p_ptr->arena_number > MAX_ARENA_MONS) - msg_print("You have won against all foes."); - else - { - r_ptr = &r_info[arena_monsters[p_ptr->arena_number]]; - name = (r_name + r_ptr->name); - strnfmt(tmp_str, 80, "Do I hear any challenges against: %s", name); - msg_print(tmp_str); - msg_print(NULL); - } - - break; - } - - case BACT_ARENA_RULES: - { - /* Save screen */ - screen_save(); - - /* Peruse the arena help file */ - (void)show_file("arena.txt", NULL, 0, 0); - - /* Load screen */ - screen_load(); - - break; - } - } -} - - -/* - * display fruit for dice slots - */ -static void display_fruit(int row, int col, int fruit) -{ - switch (fruit) - { - case 0: /* lemon */ - { - c_put_str(TERM_YELLOW, " ####.", row, col); - c_put_str(TERM_YELLOW, " # #", row + 1, col); - c_put_str(TERM_YELLOW, " # #", row + 2, col); - c_put_str(TERM_YELLOW, "# #", row + 3, col); - c_put_str(TERM_YELLOW, "# #", row + 4, col); - c_put_str(TERM_YELLOW, "# # ", row + 5, col); - c_put_str(TERM_YELLOW, "# # ", row + 6, col); - c_put_str(TERM_YELLOW, ".#### ", row + 7, col); - prt(" Lemon ", row + 8, col); - - break; - } - - case 1: /* orange */ - { - c_put_str(TERM_ORANGE, " ## ", row, col); - c_put_str(TERM_ORANGE, " #..# ", row + 1, col); - c_put_str(TERM_ORANGE, " #....# ", row + 2, col); - c_put_str(TERM_ORANGE, "#......#", row + 3, col); - c_put_str(TERM_ORANGE, "#......#", row + 4, col); - c_put_str(TERM_ORANGE, " #....# ", row + 5, col); - c_put_str(TERM_ORANGE, " #..# ", row + 6, col); - c_put_str(TERM_ORANGE, " ## ", row + 7, col); - prt(" Orange ", row + 8, col); - - break; - } - - case 2: /* sword */ - { - c_put_str(TERM_SLATE, " /\\ ", row, col); - c_put_str(TERM_SLATE, " ## ", row + 1, col); - c_put_str(TERM_SLATE, " ## ", row + 2, col); - c_put_str(TERM_SLATE, " ## ", row + 3, col); - c_put_str(TERM_SLATE, " ## ", row + 4, col); - c_put_str(TERM_SLATE, " ## ", row + 5, col); - c_put_str(TERM_UMBER, " ###### ", row + 6, col); - c_put_str(TERM_UMBER, " ## ", row + 7, col); - prt(" Sword ", row + 8, col); - - break; - } - - case 3: /* shield */ - { - c_put_str(TERM_SLATE, " ###### ", row, col); - c_put_str(TERM_SLATE, "# #", row + 1, col); - c_put_str(TERM_SLATE, "# ++++ #", row + 2, col); - c_put_str(TERM_SLATE, "# +==+ #", row + 3, col); - c_put_str(TERM_SLATE, "# ++ #", row + 4, col); - c_put_str(TERM_SLATE, " # # ", row + 5, col); - c_put_str(TERM_SLATE, " # # ", row + 6, col); - c_put_str(TERM_SLATE, " ## ", row + 7, col); - prt(" Shield ", row + 8, col); - - break; - } - - case 4: /* plum */ - { - c_put_str(TERM_VIOLET, " ## ", row, col); - c_put_str(TERM_VIOLET, " ###### ", row + 1, col); - c_put_str(TERM_VIOLET, "########", row + 2, col); - c_put_str(TERM_VIOLET, "########", row + 3, col); - c_put_str(TERM_VIOLET, "########", row + 4, col); - c_put_str(TERM_VIOLET, " ###### ", row + 5, col); - c_put_str(TERM_VIOLET, " #### ", row + 6, col); - c_put_str(TERM_VIOLET, " ## ", row + 7, col); - prt(" Plum ", row + 8, col); - - break; - } - - case 5: /* cherry */ - { - c_put_str(TERM_RED, " ##", row, col); - c_put_str(TERM_RED, " ### ", row + 1, col); - c_put_str(TERM_RED, " #..# ", row + 2, col); - c_put_str(TERM_RED, " #..# ", row + 3, col); - c_put_str(TERM_RED, " ###### ", row + 4, col); - c_put_str(TERM_RED, "#..##..#", row + 5, col); - c_put_str(TERM_RED, "#..##..#", row + 6, col); - c_put_str(TERM_RED, " ## ## ", row + 7, col); - prt(" Cherry ", row + 8, col); - - break; - } - } -} - - -/* - * gamble_comm - */ -static bool_ gamble_comm(int cmd) -{ - int roll1, roll2, roll3, choice, odds, win; - - s32b wager; - - s32b maxbet; - - s32b oldgold; - - static const char *fruit[6] = - {"Lemon", "Orange", "Sword", "Shield", "Plum", "Cherry" - }; - - char out_val[160], tmp_str[80], again; - - cptr p; - - - screen_save(); - - if (cmd == BACT_GAMBLE_RULES) - { - /* Peruse the gambling help file */ - (void)show_file("gambling.txt", NULL, 0, 0); - } - else - { - clear_bldg(5, 23); - - /* Set maximum bet */ - if (p_ptr->lev < 10) - maxbet = (p_ptr->lev * 100); - else - maxbet = (p_ptr->lev * 1000); - - /* Get the wager */ - strcpy(out_val, ""); - strnfmt(tmp_str, 80, "Your wager (1-%ld) ? ", maxbet); - get_string(tmp_str, out_val, 32); - - /* Strip spaces */ - for (p = out_val; *p == ' '; p++); - - wager = atol(p); - - if (wager > p_ptr->au) - { - msg_print("Hey! You don't have the gold - get out of here!"); - msg_print(NULL); - screen_load(); - return (FALSE); - } - else if (wager > maxbet) - { - msg_format("I'll take $%ld of that. Keep the rest.", maxbet); - wager = maxbet; - } - else if (wager < 1) - { - msg_print("Ok, we'll start with $1."); - - wager = 1; - } - msg_print(NULL); - win = FALSE; - odds = 0; - oldgold = p_ptr->au; - - strnfmt(tmp_str, 80, "Gold before game: %9ld", oldgold); - prt(tmp_str, 20, 2); - - strnfmt(tmp_str, 80, "Current Wager: %9ld", wager); - prt(tmp_str, 21, 2); - - do - { - switch (cmd) - { - case BACT_IN_BETWEEN: /* Game of In-Between */ - { - c_put_str(TERM_GREEN, "In Between", 5, 2); - odds = 3; - win = FALSE; - roll1 = randint(10); - roll2 = randint(10); - choice = randint(10); - strnfmt(tmp_str, 80, "Black die: %d Black Die: %d", - roll1, roll2); - prt(tmp_str, 8, 3); - strnfmt(tmp_str, 80, "Red die: %d", choice); - prt(tmp_str, 11, 14); - if (((choice > roll1) && (choice < roll2)) || - ((choice < roll1) && (choice > roll2))) - win = TRUE; - - break; - } - case BACT_CRAPS: /* Game of Craps */ - { - c_put_str(TERM_GREEN, "Craps", 5, 2); - win = 3; - odds = 1; - roll1 = randint(6); - roll2 = randint(6); - roll3 = roll1 + roll2; - choice = roll3; - strnfmt(tmp_str, 80, "First roll: %d %d Total: %d", roll1, - roll2, roll3); - prt(tmp_str, 7, 5); - if ((roll3 == 7) || (roll3 == 11)) - win = TRUE; - else if ((roll3 == 2) || (roll3 == 3) || (roll3 == 12)) - win = FALSE; - else - { - do - { - msg_print("Hit any key to roll again"); - msg_print(NULL); - roll1 = randint(6); - roll2 = randint(6); - roll3 = roll1 + roll2; - - strnfmt(tmp_str, 80, "Roll result: %d %d Total: %d", - roll1, roll2, roll3); - prt(tmp_str, 8, 5); - if (roll3 == choice) - win = TRUE; - else if (roll3 == 7) - win = FALSE; - } - while ((win != TRUE) && (win != FALSE)); - } - - break; - } - - case BACT_SPIN_WHEEL: /* Spin the Wheel Game */ - { - win = FALSE; - odds = 10; - c_put_str(TERM_GREEN, "Wheel", 5, 2); - prt("0 1 2 3 4 5 6 7 8 9", 7, 5); - prt("--------------------------------", 8, 3); - strcpy(out_val, ""); - get_string ("Pick a number (1-9): ", out_val, 32); - for (p = out_val; *p == ' '; p++); - choice = atol(p); - if (choice < 0) - { - msg_print("I'll put you down for 0."); - choice = 0; - } - else if (choice > 9) - { - msg_print("Ok, I'll put you down for 9."); - choice = 9; - } - msg_print(NULL); - roll1 = randint(10) - 1; - strnfmt(tmp_str, 80, "The wheel spins to a stop and the winner is %d", - roll1); - prt(tmp_str, 13, 3); - prt("", 9, 0); - prt("*", 9, (3 * roll1 + 5)); - if (roll1 == choice) - win = TRUE; - - break; - } - - case BACT_DICE_SLOTS: /* The Dice Slots */ - { - c_put_str(TERM_GREEN, "Dice Slots", 5, 2); - win = FALSE; - roll1 = randint(6); - roll2 = randint(6); - choice = randint(6); - strnfmt(tmp_str, 80, "%s %s %s", - fruit[roll1 - 1], fruit[roll2 - 1], - fruit[choice - 1]); - prt(tmp_str, 15, 37); - prt("/--------------------------\\", 7, 2); - prt("\\--------------------------/", 17, 2); - display_fruit(8, 3, roll1 - 1); - display_fruit(8, 12, roll2 - 1); - display_fruit(8, 21, choice - 1); - if ((roll1 == roll2) && (roll2 == choice)) - { - win = TRUE; - if (roll1 == 1) - odds = 4; - else if (roll1 == 2) - odds = 6; - else - odds = roll1 * roll1; - } - else if ((roll1 == 6) && (roll2 == 6)) - { - win = TRUE; - odds = choice + 1; - } - - break; - } - } - - if (win) - { - prt("YOU WON", 16, 37); - p_ptr->au = p_ptr->au + (odds * wager); - strnfmt(tmp_str, 80, "Payoff: %d", odds); - prt(tmp_str, 17, 37); - } - else - { - prt("You Lost", 16, 37); - p_ptr->au = p_ptr->au - wager; - prt("", 17, 37); - } - strnfmt(tmp_str, 80, "Current Gold: %9ld", p_ptr->au); - prt(tmp_str, 22, 2); - prt("Again(Y/N)?", 18, 37); - move_cursor(18, 49); - again = inkey(); - if (wager > p_ptr->au) - { - msg_print("Hey! You don't have the gold - get out of here!"); - msg_print(NULL); - screen_load(); - return (FALSE); - /* strnfmt(tmp_str, 80, "Current Wager: %9ld",wager); - prt(tmp_str, 17, 2); */ - } - } - while ((again == 'y') || (again == 'Y')); - - prt("", 18, 37); - if (p_ptr->au >= oldgold) - msg_print("You came out a winner! We'll win next time, I'm sure."); - else - msg_print("You lost gold! Haha, better head home."); - msg_print(NULL); - } - - screen_load(); - - return (TRUE); -} - - -/* - * inn commands - * Note that resting for the night was a perfect way to avoid player - * ghosts in the town *if* you could only make it to the inn in time (-: - * Now that the ghosts are temporarily disabled in 2.8.X, this function - * will not be that useful. I will keep it in the hopes the player - * ghost code does become a reality again. Does help to avoid filthy urchins. - * Resting at night is also a quick way to restock stores -KMW- - */ -static bool_ inn_comm(int cmd) -{ - bool_ vampire; - - - /* Extract race info */ - vampire = ((PRACE_FLAG(PR1_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire"))); - - switch (cmd) - { - case BACT_FOOD: /* Buy food & drink */ - { - if (!vampire) - { - msg_print("The barkeep gives you some gruel and a beer."); - msg_print(NULL); - (void) set_food(PY_FOOD_MAX - 1); - } - else - msg_print("You're a vampire and I don't have any food for you!"); - - break; - } - - /* - * I revamped this... Don't know why normal races didn't get - * mana regenerated. It is the grand tradition of p&p games -- pelpel - */ - case BACT_REST: /* Rest for the night */ - { - bool_ nighttime; - - /* Extract the current time */ - nighttime = ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18)); - - /* Normal races rest at night */ - if (!vampire && !nighttime) - { - msg_print("The rooms are available only at night."); - msg_print(NULL); - return (FALSE); - } - - /* Vampires rest during daytime */ - if (vampire && nighttime) - { - msg_print("The rooms are available only during daylight for your kind."); - msg_print(NULL); - return (FALSE); - } - - /* Must cure HP draining status first */ - if ((p_ptr->poisoned > 0) || (p_ptr->cut > 0)) - { - msg_print("You need a healer, not a room."); - msg_print(NULL); - msg_print("Sorry, but I don't want anyone dying in here."); - return (FALSE); - } - - /* Let the time pass XXX XXX XXX */ - if (vampire) - { - /* Wait for sunset */ - while ((bst(HOUR, turn) >= 6) && (bst(HOUR, turn) < 18)) - { - turn += (10L * MINUTE); - } - } - else - { - /* Wait for sunrise */ - while ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18)) - { - turn += (10L * MINUTE); - } - } - - /* Regen */ - p_ptr->chp = p_ptr->mhp; - p_ptr->csp = p_ptr->msp; - - /* Restore status */ - set_blind(0); - set_confused(0); - p_ptr->stun = 0; - - /* Message */ - if (vampire) msg_print("You awake refreshed for the new night."); - else msg_print("You awake refreshed for the new day."); - - /* Dungeon stuff */ - p_ptr->leaving = TRUE; - p_ptr->oldpx = p_ptr->px; - p_ptr->oldpy = p_ptr->py; - - /* Select new bounties. */ - select_bounties(); - - break; - } - - case BACT_RUMORS: /* Listen for rumors */ - { - char rumor[80]; - - get_rnd_line("rumors.txt", rumor); - msg_format("%s", rumor); - msg_print(NULL); - - break; - } - } - - return (TRUE); -} - - -/* - * Display quest information - */ -static void get_questinfo(int questnum) -{ - int i; - - - /* Print the quest info */ - prt(format("Quest Information (Danger level: %d)", quest[questnum].level), 5, 0); - - prt(quest[questnum].name, 7, 0); - - i = 0; - while ((i < 10) && (quest[questnum].desc[i][0] != '\0')) - { - c_put_str(TERM_YELLOW, quest[questnum].desc[i], i + 8, 0); - i++; - } -} - - -/* - * Request a quest from the Lord. - */ -static bool_ castle_quest(int y, int x) -{ - int plot = 0; - - quest_type *q_ptr; - - - clear_bldg(7, 18); - - /* Current plot of the building */ - plot = cave[y][x].special; - - /* Is there a quest available at the building? */ - if ((!plot) || (plots[plot] == QUEST_NULL)) - { - put_str("I don't have a quest for you at the moment.", 8, 0); - return FALSE; - } - - q_ptr = &quest[plots[plot]]; - - /* Quest is completed */ - if (q_ptr->status == QUEST_STATUS_COMPLETED) - { - /* Rewarded quest */ - q_ptr->status = QUEST_STATUS_FINISHED; - - process_hooks(HOOK_QUEST_FINISH, "(d)", plots[plot]); - - return (TRUE); - } - - /* Quest is still unfinished */ - else if (q_ptr->status == QUEST_STATUS_TAKEN) - { - put_str("You have not completed your current quest yet!", 8, 0); - put_str("Use CTRL-Q to check the status of your quest.", 9, 0); - put_str("Return when you have completed your quest.", 12, 0); - - return (FALSE); - } - /* Failed quest */ - else if (q_ptr->status == QUEST_STATUS_FAILED) - { - /* Mark quest as done (but failed) */ - q_ptr->status = QUEST_STATUS_FAILED_DONE; - - process_hooks(HOOK_QUEST_FAIL, "(d)", plots[plot]); - - return (FALSE); - } - /* No quest yet */ - else if (q_ptr->status == QUEST_STATUS_UNTAKEN) - { - if (process_hooks(HOOK_INIT_QUEST, "(d)", plots[plot])) return (FALSE); - - q_ptr->status = QUEST_STATUS_TAKEN; - - /* Assign a new quest */ - get_questinfo(plots[plot]); - - /* Add the hooks */ - quest[plots[plot]].init(plots[plot]); - - return (TRUE); - } - - return FALSE; -} - -/* - * Displaying town history -KMW- - */ -static void town_history(void) -{ - /* Save screen */ - screen_save(); - - /* Peruse the building help file */ - (void)show_file("bldg.txt", NULL, 0, 0); - - /* Load screen */ - screen_load(); -} - - -/* - * compare_weapon_aux2 -KMW- - */ -static void compare_weapon_aux2(object_type *o_ptr, int numblows, int r, int c, int mult, char attr[80], u32b f1, u32b f2, u32b f3, byte color) -{ - char tmp_str[80]; - - c_put_str(color, attr, r, c); - strnfmt(tmp_str, 80, "Attack: %d-%d damage", - numblows * ((o_ptr->dd * mult) + o_ptr->to_d), - numblows * ((o_ptr->ds * o_ptr->dd * mult) + o_ptr->to_d)); - put_str(tmp_str, r, c + 8); - r++; -} - - -/* - * compare_weapon_aux1 -KMW- - */ -static void compare_weapon_aux1(object_type *o_ptr, int col, int r) -{ - u32b f1, f2, f3, f4, f5, esp; - - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - - if (f1 & (TR1_SLAY_ANIMAL)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Animals:", - f1, f2, f3, TERM_YELLOW); - } - if (f1 & (TR1_SLAY_EVIL)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Evil:", - f1, f2, f3, TERM_YELLOW); - } - if (f1 & (TR1_SLAY_UNDEAD)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Undead:", - f1, f2, f3, TERM_YELLOW); - } - if (f1 & (TR1_SLAY_DEMON)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Demons:", - f1, f2, f3, TERM_YELLOW); - } - if (f1 & (TR1_SLAY_ORC)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Orcs:", - f1, f2, f3, TERM_YELLOW); - } - if (f1 & (TR1_SLAY_TROLL)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Trolls:", - f1, f2, f3, TERM_YELLOW); - } - if (f1 & (TR1_SLAY_GIANT)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Giants:", - f1, f2, f3, TERM_YELLOW); - } - if (f1 & (TR1_SLAY_DRAGON)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Dragons:", - f1, f2, f3, TERM_YELLOW); - } - if (f1 & (TR1_KILL_DRAGON)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 5, "Dragons:", - f1, f2, f3, TERM_YELLOW); - } - if (f1 & (TR1_BRAND_ACID)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Acid:", - f1, f2, f3, TERM_RED); - } - if (f1 & (TR1_BRAND_ELEC)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Elec:", - f1, f2, f3, TERM_RED); - } - if (f1 & (TR1_BRAND_FIRE)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Fire:", - f1, f2, f3, TERM_RED); - } - if (f1 & (TR1_BRAND_COLD)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Cold:", - f1, f2, f3, TERM_RED); - } - if (f1 & (TR1_BRAND_POIS)) - { - compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Poison:", - f1, f2, f3, TERM_RED); - } -} - - -/* - * list_weapon -KMW- - */ -static void list_weapon(object_type *o_ptr, int row, int col) -{ - char o_name[80]; - - char tmp_str[80]; - - - object_desc(o_name, o_ptr, TRUE, 0); - c_put_str(TERM_YELLOW, o_name, row, col); - strnfmt(tmp_str, 80, "To Hit: %d To Damage: %d", o_ptr->to_h, o_ptr->to_d); - put_str(tmp_str, row + 1, col); - strnfmt(tmp_str, 80, "Dice: %d Sides: %d", o_ptr->dd, o_ptr->ds); - put_str(tmp_str, row + 2, col); - strnfmt(tmp_str, 80, "Number of Blows: %d", p_ptr->num_blow); - put_str(tmp_str, row + 3, col); - c_put_str(TERM_YELLOW, "Possible Damage:", row + 5, col); - strnfmt(tmp_str, 80, "One Strike: %d-%d damage", o_ptr->dd + o_ptr->to_d, - (o_ptr->ds*o_ptr->dd) + o_ptr->to_d); - put_str(tmp_str, row + 6, col + 1); - strnfmt(tmp_str, 80, "One Attack: %d-%d damage", p_ptr->num_blow*(o_ptr->dd + o_ptr->to_d), - p_ptr->num_blow*(o_ptr->ds*o_ptr->dd + o_ptr->to_d)); - put_str(tmp_str, row + 7, col + 1); -} - - -/* - * Select melee weapons - */ -static bool_ item_tester_hook_melee_weapon(object_type *o_ptr) -{ - return (wield_slot(o_ptr) == INVEN_WIELD); -} - -/* - * compare_weapons -KMW- - */ -static bool_ compare_weapons(void) -{ - int item, item2, i; - - object_type *o1_ptr, *o2_ptr, *orig_ptr; - - object_type *i_ptr; - - cptr q, s; - - - clear_bldg(6, 18); - - o1_ptr = NULL; - o2_ptr = NULL; - i_ptr = NULL; - - /* Store copy of original wielded weapon in pack slot */ - i_ptr = &p_ptr->inventory[INVEN_WIELD]; - orig_ptr = &p_ptr->inventory[INVEN_PACK]; - object_copy(orig_ptr, i_ptr); - - i = 6; - /* Get first weapon */ - /* Restrict choices to meele weapons */ - item_tester_hook = item_tester_hook_melee_weapon; - - q = "What is your first melee weapon? "; - s = "You have nothing to compare."; - if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) - { - object_wipe(orig_ptr); - return (FALSE); - } - - /* Get the item (in the pack) */ - if (item >= 0) - o1_ptr = &p_ptr->inventory[item]; - - /* Get second weapon */ - /* Restrict choices to melee weapons */ - item_tester_hook = item_tester_hook_melee_weapon; - - q = "What is your second melee weapon? "; - s = "You have nothing to compare."; - if (!get_item(&item2, q, s, (USE_EQUIP | USE_INVEN))) - { - object_wipe(orig_ptr); - return (FALSE); - } - - /* Get the item (in the pack) */ - if (item2 >= 0) o2_ptr = &p_ptr->inventory[item2]; - - put_str("Based on your current abilities, here is what your weapons will do", 4, 2); - - i_ptr = &p_ptr->inventory[INVEN_WIELD]; - object_copy(i_ptr, o1_ptr); - calc_bonuses(TRUE); - - list_weapon(o1_ptr, i, 2); - compare_weapon_aux1(o1_ptr, 2, i + 8); - - i_ptr = &p_ptr->inventory[INVEN_WIELD]; - if (item2 == INVEN_WIELD) - object_copy(i_ptr, orig_ptr); - else - object_copy(i_ptr, o2_ptr); - calc_bonuses(TRUE); - - list_weapon(o2_ptr, i, 40); - compare_weapon_aux1(o2_ptr, 40, i + 8); - - i_ptr = &p_ptr->inventory[INVEN_WIELD]; - object_copy(i_ptr, orig_ptr); - calc_bonuses(TRUE); - - object_wipe(orig_ptr); - - put_str("(Only highest damage applies per monster. Special damage not cumulative)", 20, 0); - - return (TRUE); -} - - -/* - * general all-purpose fixing routine for items from building personnel - * sharpen arrows, repair armor, repair weapon - * -KMW- - */ -static bool_ fix_item(int istart, int iend, int ispecific, bool_ iac, - int ireward, bool_ set_reward) -{ - int i; - - int j = 9; - - int maxenchant = (p_ptr->lev / 5); - - object_type *o_ptr; - - char out_val[80], tmp_str[80]; - - bool_ repaired = FALSE; - - clear_bldg(5, 18); - strnfmt(tmp_str, 80, " Based on your skill, we can improve up to +%d", maxenchant); - prt(tmp_str, 5, 0); - prt("Status", 7, 30); - - for (i = istart; i <= iend; i++) - { - o_ptr = &p_ptr->inventory[i]; - if (ispecific > 0) - { - if (o_ptr->tval != ispecific) - continue; - } - - if (o_ptr->tval) - { - object_desc(tmp_str, o_ptr, FALSE, 1); - - if ((o_ptr->name1 && (o_ptr->ident & 0x08))) - strnfmt(out_val, 80, "%-40s: beyond our skills!", tmp_str); - else if (o_ptr->name1) - strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str); - else - { - if ((iac) && (o_ptr->to_a <= -3)) - { - strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str); - } - else if ((iac) && (o_ptr->to_a < maxenchant)) - { - o_ptr->to_a++; - strnfmt(out_val, 80, "%-40s: polished -> (%d)", tmp_str, o_ptr->to_a); - repaired = TRUE; - } - else if ((!iac) && ((o_ptr->to_h <= -3) || (o_ptr->to_d <= -3))) - { - strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str); - } - /* Sharpen a weapon */ - else if ((!iac) && ((o_ptr->to_h < maxenchant) || - (o_ptr->to_d < maxenchant))) - { - if (o_ptr->to_h < maxenchant) - o_ptr->to_h++; - if (o_ptr->to_d < maxenchant) - o_ptr->to_d++; - strnfmt(out_val, 80, "%-40s: sharpened -> (%d,%d)", tmp_str, - o_ptr->to_h, o_ptr->to_d); - repaired = TRUE; - } - else - strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str); - } - prt(out_val, j++, 0); - } - } - - if (!repaired) - { - msg_print("You don't have anything appropriate."); - msg_print(NULL); - } - else - { - msg_print("Press the spacebar to continue"); - msg_print(NULL); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - p_ptr->update |= (PU_BONUS); - } - clear_bldg(5, 18); - - return (repaired); -} - - -/* - * Research Item - */ -static bool_ research_item(void) -{ - clear_bldg(5, 18); - return (identify_fully()); -} - - -/* - * Show the current quest monster. - */ -static void show_quest_monster(void) -{ - monster_race* r_ptr = &r_info[bounties[0][0]]; - - - msg_format("Quest monster: %s. " - "Need to turn in %d corpse%s to receive reward.", - r_name + r_ptr->name, bounties[0][1], - (bounties[0][1] > 1 ? "s" : "")); - msg_print(NULL); -} - - -/* - * Show the current bounties. - */ -static void show_bounties(void) -{ - int i, j = 6; - - monster_race* r_ptr; - - char buff[80]; - - - clear_bldg(7, 18); - - c_prt(TERM_YELLOW, "Currently active bounties:", 4, 2); - - for (i = 1; i < MAX_BOUNTIES; i++, j++) - { - r_ptr = &r_info[bounties[i][0]]; - - strnfmt(buff, 80, "%-30s (%d gp)", r_name + r_ptr->name, bounties[i][1]); - - prt(buff, j, 2); - - if (j >= 17) - { - msg_print("Press space for more."); - msg_print(NULL); - - clear_bldg(7, 18); - j = 5; - } - } -} - - -/* - * Filter for corpses that currently have a bounty on them. - */ -static bool_ item_tester_hook_bounty(object_type* o_ptr) -{ - int i; - - - if (o_ptr->tval == TV_CORPSE) - { - for (i = 1; i < MAX_BOUNTIES; i++) - { - if (bounties[i][0] == o_ptr->pval2) return (TRUE); - } - } - - return (FALSE); -} - -/* Filter to match the quest monster's corpse. */ -static bool_ item_tester_hook_quest_monster(object_type* o_ptr) -{ - if ((o_ptr->tval == TV_CORPSE) && - (o_ptr->pval2 == bounties[0][0])) return (TRUE); - return (FALSE); -} - - -/* - * Return the boost in the corpse's value depending on how rare the body - * part is. - */ -static int corpse_value_boost(int sval) -{ - switch (sval) - { - case SV_CORPSE_HEAD: - case SV_CORPSE_SKULL: - { - return (1); - } - - /* Default to no boost. */ - default: - { - return (0); - } - } -} - -/* - * Sell a corpse, if there's currently a bounty on it. - */ -static void sell_corpses(void) -{ - object_type* o_ptr; - - int i, boost = 0; - - s16b value; - - int item; - - - /* Set the hook. */ - item_tester_hook = item_tester_hook_bounty; - - /* Select a corpse to sell. */ - if (!get_item(&item, "Sell which corpse", - "You have no corpses you can sell.", USE_INVEN)) return; - - o_ptr = &p_ptr->inventory[item]; - - /* Exotic body parts are worth more. */ - boost = corpse_value_boost(o_ptr->sval); - - /* Try to find a match. */ - for (i = 1; i < MAX_BOUNTIES; i++) - { - if (o_ptr->pval2 == bounties[i][0]) - { - value = bounties[i][1] + boost * (r_info[o_ptr->pval2].level); - - msg_format("Sold for %ld gold pieces.", value); - msg_print(NULL); - p_ptr->au += value; - - /* Increase the number of collected bounties */ - total_bounties++; - - inc_stack_size(item, -1); - - return; - } - } - - msg_print("Sorry, but that monster does not have a bounty on it."); - msg_print(NULL); -} - - - -/* - * Hook for bounty monster selection. - */ -static bool_ mon_hook_bounty(int r_idx) -{ - monster_race* r_ptr = &r_info[r_idx]; - - - /* Reject uniques */ - if (r_ptr->flags1 & RF1_UNIQUE) return (FALSE); - - /* Reject those who cannot leave anything */ - if (!(r_ptr->flags9 & RF9_DROP_CORPSE) && - !(r_ptr->flags9 & RF9_DROP_SKELETON)) return (FALSE); - - /* Reject pets */ - if (r_ptr->flags7 & RF7_PET) return (FALSE); - - /* Reject friendly creatures */ - if (r_ptr->flags7 & RF7_FRIENDLY) return (FALSE); - - /* The rest are acceptable */ - return (TRUE); -} - - -static void select_quest_monster(void) -{ - monster_race* r_ptr; - - int amt; - - - /* - * Set up the hooks -- no bounties on uniques or monsters - * with no corpses - */ - get_mon_num_hook = mon_hook_bounty; - get_mon_num_prep(); - - /* Set up the quest monster. */ - bounties[0][0] = get_mon_num(p_ptr->lev); - - r_ptr = &r_info[bounties[0][0]]; - - /* - * Select the number of monsters needed to kill. Groups and - * breeders require more - */ - amt = randnor(5, 3); - - if (amt < 2) amt = 2; - - if (r_ptr->flags1 & RF1_FRIEND) amt *= 3; amt /= 2; - if (r_ptr->flags1 & RF1_FRIENDS) amt *= 2; - if (r_ptr->flags4 & RF4_MULTIPLY) amt *= 3; - - if (r_ptr->flags7 & RF7_AQUATIC) amt /= 2; - - bounties[0][1] = amt; - - /* Undo the filters */ - get_mon_num_hook = NULL; - get_mon_num_prep(); -} - - - -/* - * Sell a corpse for a reward. - */ -static void sell_quest_monster(void) -{ - object_type* o_ptr; - - int item; - - - /* Set the hook. */ - item_tester_hook = item_tester_hook_quest_monster; - - /* Select a corpse to sell. */ - if (!get_item(&item, "Sell which corpse", - "You have no corpses you can sell.", USE_INVEN)) return; - - o_ptr = &p_ptr->inventory[item]; - - bounties[0][1] -= o_ptr->number; - - /* Completed the quest. */ - if (bounties[0][1] <= 0) - { - int m; - monster_race *r_ptr; - - cmsg_print(TERM_YELLOW, "You have completed your quest!"); - msg_print(NULL); - - /* Give full knowledge */ - - /* Hack -- Maximal info */ - r_ptr = &r_info[bounties[0][0]]; - - msg_print(format("Well done! As a reward I'll teach you everything " - "about the %s, (check your recall)", - r_name + r_ptr->name)); - - r_ptr->r_wake = r_ptr->r_ignore = MAX_UCHAR; - - /* Observe "maximal" attacks */ - for (m = 0; m < 4; m++) - { - /* Examine "actual" blows */ - if (r_ptr->blow[m].effect || r_ptr->blow[m].method) - { - /* Hack -- maximal observations */ - r_ptr->r_blows[m] = MAX_UCHAR; - } - } - - /* Hack -- maximal drops */ - r_ptr->r_drop_gold = r_ptr->r_drop_item = - (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) + - ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) + - ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) + - ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) + - ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) + - ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0)); - - /* Hack -- but only "valid" drops */ - if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0; - if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0; - - /* Hack -- observe many spells */ - r_ptr->r_cast_inate = MAX_UCHAR; - r_ptr->r_cast_spell = MAX_UCHAR; - - /* Hack -- know all the flags */ - r_ptr->r_flags1 = r_ptr->flags1; - r_ptr->r_flags2 = r_ptr->flags2; - r_ptr->r_flags3 = r_ptr->flags3; - r_ptr->r_flags4 = r_ptr->flags4; - r_ptr->r_flags5 = r_ptr->flags5; - r_ptr->r_flags6 = r_ptr->flags6; - r_ptr->r_flags4 = r_ptr->flags7; - r_ptr->r_flags5 = r_ptr->flags8; - r_ptr->r_flags6 = r_ptr->flags9; - - msg_print(NULL); - - select_quest_monster(); - - } - else - { - msg_format("Well done, only %d more to go.", bounties[0][1]); - msg_print(NULL); - } - - inc_stack_size(item, -1); -} - - - -/* - * Fill the bounty list with monsters. - */ -void select_bounties(void) -{ - int i, j; - - - select_quest_monster(); - - /* - * Set up the hooks -- no bounties on uniques or monsters - * with no corpses - */ - get_mon_num_hook = mon_hook_bounty; - get_mon_num_prep(); - - for (i = 1; i < MAX_BOUNTIES; i++) - { - int lev = i * 5 + randnor(0, 2); - monster_race* r_ptr; - s16b r_idx; - s16b val; - - if (lev < 1) lev = 1; - - if (lev >= MAX_DEPTH) lev = MAX_DEPTH - 1; - - /* We don't want to duplicate entries in the list */ - while (TRUE) - { - r_idx = get_mon_num(lev); - - for (j = 0; j < i; j++) - { - if (bounties[j][0] == r_idx) continue; - } - - break; - } - - bounties[i][0] = r_idx; - - r_ptr = &r_info[r_idx]; - - val = r_ptr->mexp + r_ptr->level * 20 + randnor(0, r_ptr->level * 2); - - if (val < 1) val = 1; - - bounties[i][1] = val; - } - - /* Undo the filters. */ - get_mon_num_hook = NULL; - get_mon_num_prep(); -} - -/* - * Execute a building command - */ -bool_ bldg_process_command(store_type *s_ptr, int i) -{ - store_action_type *ba_ptr = &ba_info[st_info[s_ptr->st_idx].actions[i]]; - - int bact = ba_ptr->action; - - int bcost; - - bool_ paid = FALSE; - - bool_ set_reward = FALSE; - - bool_ recreate = FALSE; - - - if (is_state(s_ptr, STORE_LIKED)) - { - bcost = ba_ptr->costs[STORE_LIKED]; - } - else if (is_state(s_ptr, STORE_HATED)) - { - bcost = ba_ptr->costs[STORE_HATED]; - } - else - { - bcost = ba_ptr->costs[STORE_NORMAL]; - } - - /* action restrictions */ - if (((ba_ptr->action_restr == 1) && (is_state(s_ptr, STORE_LIKED))) || - ((ba_ptr->action_restr == 2) && (!is_state(s_ptr, STORE_LIKED)))) - { - msg_print("You have no right to choose that!"); - msg_print(NULL); - return FALSE; - } - - /* If player has loan and the time is out, few things work in stores */ - if (p_ptr->loan && !p_ptr->loan_time) - { - if ((bact != BACT_SELL) && (bact != BACT_VIEW_BOUNTIES) && - (bact != BACT_SELL_CORPSES) && - (bact != BACT_VIEW_QUEST_MON) && - (bact != BACT_SELL_QUEST_MON) && - (bact != BACT_EXAMINE) && (bact != BACT_STEAL) && - (bact != BACT_PAY_BACK_LOAN)) - { - msg_print("You are not allowed to do that until you have paid back your loan."); - msg_print(NULL); - return FALSE; - } - } - - /* check gold */ - if (bcost > p_ptr->au) - { - msg_print("You do not have the gold!"); - msg_print(NULL); - return FALSE; - } - - if (!bcost) set_reward = TRUE; - - switch (bact) - { - case BACT_RESEARCH_ITEM: - { - paid = research_item(); - break; - } - - case BACT_TOWN_HISTORY: - { - town_history(); - break; - } - - case BACT_RACE_LEGENDS: - { - race_legends(); - break; - } - - case BACT_QUEST1: - case BACT_QUEST2: - case BACT_QUEST3: - case BACT_QUEST4: - { - int y = 1, x = 1; - bool_ ok = FALSE; - - while ((x < cur_wid - 1) && !ok) - { - y = 1; - while ((y < cur_hgt - 1) && !ok) - { - /* Found the location of the quest info ? */ - if (bact - BACT_QUEST1 + FEAT_QUEST1 == cave[y][x].feat) - { - /* Stop the loop */ - ok = TRUE; - } - y++; - } - x++; - } - - if (ok) - { - recreate = castle_quest(y - 1, x - 1); - ; - } - else - { - msg_format("ERROR: no quest info feature found: %d", bact - BACT_QUEST1 + FEAT_QUEST1); - } - break; - } - - case BACT_KING_LEGENDS: - case BACT_ARENA_LEGENDS: - case BACT_LEGENDS: - { - show_highclass(building_loc); - break; - } - - case BACT_POSTER: - case BACT_ARENA_RULES: - case BACT_ARENA: - { - arena_comm(bact); - break; - } - - case BACT_IN_BETWEEN: - case BACT_CRAPS: - case BACT_SPIN_WHEEL: - case BACT_DICE_SLOTS: - case BACT_GAMBLE_RULES: - { - gamble_comm(bact); - break; - } - - case BACT_REST: - case BACT_RUMORS: - case BACT_FOOD: - { - paid = inn_comm(bact); - break; - } - - case BACT_RESEARCH_MONSTER: - { - paid = !research_mon(); - break; - } - - case BACT_COMPARE_WEAPONS: - { - paid = compare_weapons(); - break; - } - - case BACT_ENCHANT_WEAPON: - { - paid = fix_item(INVEN_WIELD, INVEN_WIELD, 0, FALSE, - BACT_ENCHANT_WEAPON, set_reward); - break; - } - - case BACT_ENCHANT_ARMOR: - { - paid = fix_item(INVEN_BODY, INVEN_FEET, 0, TRUE, - BACT_ENCHANT_ARMOR, set_reward); - break; - } - - /* needs work */ - case BACT_RECHARGE: - { - if (recharge(80)) paid = TRUE; - break; - } - - /* needs work */ - case BACT_IDENTS: - { - identify_pack(); - msg_print("Your possessions have been identified."); - msg_print(NULL); - paid = TRUE; - break; - } - - /* needs work */ - case BACT_STAR_HEAL: - { - hp_player(200); - set_poisoned(0); - set_blind(0); - set_confused(0); - set_cut(0); - set_stun(0); - if (p_ptr->black_breath) - { - msg_print("The hold of the Black Breath on you is broken!"); - p_ptr->black_breath = FALSE; - } - paid = TRUE; - break; - } - - /* needs work */ - case BACT_HEALING: - { - hp_player(200); - set_poisoned(0); - set_blind(0); - set_confused(0); - set_cut(0); - set_stun(0); - paid = TRUE; - break; - } - - /* needs work */ - case BACT_RESTORE: - { - if (do_res_stat(A_STR, TRUE)) paid = TRUE; - if (do_res_stat(A_INT, TRUE)) paid = TRUE; - if (do_res_stat(A_WIS, TRUE)) paid = TRUE; - if (do_res_stat(A_DEX, TRUE)) paid = TRUE; - if (do_res_stat(A_CON, TRUE)) paid = TRUE; - if (do_res_stat(A_CHR, TRUE)) paid = TRUE; - break; - } - - case BACT_ENCHANT_ARROWS: - { - paid = fix_item(0, INVEN_WIELD, TV_ARROW, FALSE, - BACT_ENCHANT_ARROWS, set_reward); - break; - } - - case BACT_ENCHANT_BOW: - { - paid = fix_item(INVEN_BOW, INVEN_BOW, TV_BOW, FALSE, - BACT_ENCHANT_BOW, set_reward); - break; - } - - case BACT_RECALL: - { - p_ptr->word_recall = 1; - msg_print("The air about you becomes charged..."); - paid = TRUE; - break; - } - - case BACT_TELEPORT_LEVEL: - { - if (reset_recall(FALSE)) - { - p_ptr->word_recall = 1; - msg_print("The air about you becomes charged..."); - paid = TRUE; - } - break; - } - - case BACT_MIMIC_NORMAL: - { - set_mimic(0, 0, 0); - paid = TRUE; - break; - } - - case BACT_VIEW_BOUNTIES: - { - show_bounties(); - break; - } - - case BACT_VIEW_QUEST_MON: - { - show_quest_monster(); - break; - } - - case BACT_SELL_QUEST_MON: - { - sell_quest_monster(); - break; - } - - case BACT_SELL_CORPSES: - { - sell_corpses(); - break; - } - - case BACT_DIVINATION: - { - int i, count = 0; - bool_ something = FALSE; - - while (count < 1000) - { - count++; - i = rand_int(MAX_FATES); - if (!fates[i].fate) continue; - if (fates[i].know) continue; - msg_print("You know a little more of your fate."); - - fates[i].know = TRUE; - something = TRUE; - break; - } - - if (!something) msg_print("Well, you have no fate, but I'll keep your money anyway!"); - - paid = TRUE; - break; - - } - - case BACT_BUY: - { - store_purchase(); - break; - } - - case BACT_SELL: - { - store_sell(); - break; - } - - case BACT_EXAMINE: - { - store_examine(); - break; - } - - case BACT_STEAL: - { - store_stole(); - break; - } - - case BACT_REQUEST_ITEM: - { - store_request_item(); - paid = TRUE; - break; - } - - case BACT_GET_LOAN: - { - s32b i, req; - char prompt[80]; - - if (p_ptr->loan) - { - msg_print("You already have a loan!"); - break; - } - - req = p_ptr->au; - - for (i = 0; i < INVEN_TOTAL; i++) - req += object_value_real(&p_ptr->inventory[i]); - - if (req > 100000) req = 100000; - if ((req + p_ptr->au) > PY_MAX_GOLD) req = PY_MAX_GOLD - p_ptr->au; - - strnfmt(prompt, sizeof (prompt), - "How much would you like to get (0-%ld) ?", req); - - req = get_quantity(prompt, req); - - if (req) - { - p_ptr->loan += req; - p_ptr->au += req; - p_ptr->loan_time += req; - - msg_format("You receive %i gold pieces", req); - - paid = TRUE; - } - else - msg_format("You did not request any money!"); - - break; - } - - case BACT_PAY_BACK_LOAN: - { - s32b req; - char prompt[80]; - - if (p_ptr->loan) - { - msg_format("You have nothing to payback!"); - break; - } - - msg_format("You have a loan of %i.", p_ptr->loan); - - req = ((p_ptr->loan + bcost) > p_ptr->au) ? p_ptr->au - bcost : p_ptr->loan; - - strnfmt(prompt, sizeof (prompt), - "How much would you like to pay back (0-%ld) ?", req); - - req = get_quantity(prompt, req); - - p_ptr->loan -= req; - p_ptr->au -= req; - - if (!p_ptr->loan) p_ptr->loan_time = 0; - - msg_format("You pay back %i gold pieces", req); - paid = TRUE; - break; - } - - case BACT_DROP_ITEM: - { - quest_bounty_drop_item(); - break; - } - - case BACT_GET_ITEM: - { - quest_bounty_get_item(); - break; - } - - case BACT_LIBRARY_QUEST: - { - quest_library_building(&paid, &recreate); - break; - } - - case BACT_FIREPROOF_QUEST: - { - quest_fireproof_building(&paid, &recreate); - break; - } - - case BACT_EREBOR_KEY: - { - msg_print("You will need Thorin's Key and Thrain's Map" - " to get anywhere in Erebor. One may be found" - " in the Barrow-Downs. The other, in Mirkwood."); - break; - } - - default: - { - if (process_hooks_ret(HOOK_BUILDING_ACTION, "dd", "(d)", bact)) - { - paid = process_hooks_return[0].num; - recreate = process_hooks_return[1].num; - } - break; - } - } - - if (paid) - { - p_ptr->au -= bcost; - - /* Display the current gold */ - store_prt_gold(); - } - - return (recreate); -} - - -/* - * Enter quest level - */ -void enter_quest(void) -{ - if (!(cave[p_ptr->py][p_ptr->px].feat == FEAT_QUEST_ENTER)) - { - msg_print("You see no quest level here."); - return; - } - else - { - /* Player enters a new quest */ - p_ptr->oldpy = p_ptr->py; - p_ptr->oldpx = p_ptr->px; - - leaving_quest = p_ptr->inside_quest; - - p_ptr->inside_quest = cave[p_ptr->py][p_ptr->px].special; - dun_level = 1; - p_ptr->leaving = TRUE; - p_ptr->oldpx = p_ptr->px; - p_ptr->oldpy = p_ptr->py; - } -} - - -/* - * Do building commands - */ -void do_cmd_bldg(void) -{ - int i, which, x = p_ptr->px, y = p_ptr->py; - - char command; - - bool_ validcmd; - - store_type *s_ptr; - - store_action_type *ba_ptr; - - - if (cave[p_ptr->py][p_ptr->px].feat != FEAT_SHOP) - { - msg_print("You see no building here."); - return; - } - - which = cave[p_ptr->py][p_ptr->px].special; - building_loc = which; - - s_ptr = &town_info[p_ptr->town_num].store[which]; - - p_ptr->oldpy = p_ptr->py; - p_ptr->oldpx = p_ptr->px; - - /* Forget the lite */ - /* forget_lite(); */ - - /* Forget the view */ - forget_view(); - - /* Hack -- Increase "icky" depth */ - character_icky++; - - command_arg = 0; - command_rep = 0; - command_new = 0; - - show_building(s_ptr); - leave_bldg = FALSE; - - while (!leave_bldg) - { - validcmd = FALSE; - prt("", 1, 0); - command = inkey(); - - if (command == ESCAPE) - { - leave_bldg = TRUE; - p_ptr->inside_arena = FALSE; - break; - } - - for (i = 0; i < 6; i++) - { - ba_ptr = &ba_info[st_info->actions[i]]; - - if (ba_ptr->letter) - { - if (ba_ptr->letter == command) - { - validcmd = TRUE; - break; - } - } - if (ba_ptr->letter_aux) - { - if (ba_ptr->letter_aux == command) - { - validcmd = TRUE; - break; - } - } - } - - if (validcmd) - bldg_process_command(s_ptr, i); - - /* Notice stuff */ - notice_stuff(); - - /* Handle stuff */ - handle_stuff(); - } - - /* Flush messages XXX XXX XXX */ - msg_print(NULL); - - /* Reinit wilderness to activate quests ... */ - wilderness_gen(TRUE); - p_ptr->py = y; - p_ptr->px = x; - - /* Hack -- Decrease "icky" depth */ - character_icky--; - - /* Clear the screen */ - Term_clear(); - - /* Update the visuals */ - p_ptr->update |= (PU_VIEW | PU_MON_LITE | PU_MONSTERS | PU_BONUS); - - /* Redraw entire screen */ - p_ptr->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP); - - /* Window stuff */ - p_ptr->window |= (PW_OVERHEAD); -} - - diff --git a/src/bldg.cc b/src/bldg.cc new file mode 100644 index 00000000..2a87f035 --- /dev/null +++ b/src/bldg.cc @@ -0,0 +1,2234 @@ +/* File: bldg.c */ + +/* + * Purpose: Building commands + * Created by Ken Wigle for Kangband - a variant of Angband 2.8.3 + * -KMW- + * + * Rewritten for Kangband 2.8.3i using Kamband's version of + * bldg.c as written by Ivan Tkatchev + * + * Changed for ZAngband by Robert Ruehlmann + * + * Heavily modified for ToME by DarkGod + */ + +#include "angband.h" +#include "hooks.h" +#include "q_library.h" +#include "q_fireprof.h" +#include "q_bounty.h" + +/* hack as in leave_store in store.c */ +static bool_ leave_bldg = FALSE; + +/* remember building location */ +static int building_loc = 0; + + +/* + * A helper function for is_state + */ +bool_ is_state_aux(store_type *s_ptr, int state) +{ + owner_type *ow_ptr = &ow_info[s_ptr->owner]; + + + /* Check race */ + if (ow_ptr->races[state][p_ptr->prace / 32] & (1 << p_ptr->prace)) + { + return (TRUE); + } + + /* Check class */ + if (ow_ptr->classes[state][p_ptr->prace / 32] & (1 << p_ptr->pclass)) + { + return (TRUE); + } + + /* All failed */ + return (FALSE); +} + + +/* + * Test if the state accords with the player + */ +bool_ is_state(store_type *s_ptr, int state) +{ + if (state == STORE_NORMAL) + { + if (is_state_aux(s_ptr, STORE_LIKED)) return (FALSE); + if (is_state_aux(s_ptr, STORE_HATED)) return (FALSE); + return (TRUE); + } + + else + { + return (is_state_aux(s_ptr, state)); + } +} + + +/* + * Clear the building information + */ +static void clear_bldg(int min_row, int max_row) +{ + int i; + + + for (i = min_row; i <= max_row; i++) + { + prt("", i, 0); + } +} + + +/* + * Display a building. + */ +void show_building(store_type *s_ptr) +{ + char buff[20]; + + int i; + + byte action_color; + + char tmp_str[80]; + + store_info_type *st_ptr = &st_info[s_ptr->st_idx]; + + store_action_type *ba_ptr; + + + for (i = 0; i < 6; i++) + { + ba_ptr = &ba_info[st_ptr->actions[i]]; + + if (ba_ptr->letter != '.') + { + if (ba_ptr->action_restr == 0) + { + if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) || + (is_state(s_ptr, STORE_HATED) && (ba_ptr->costs[STORE_HATED] == 0)) || + (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0))) + { + action_color = TERM_WHITE; + buff[0] = '\0'; + } + else if (is_state(s_ptr, STORE_LIKED)) + { + action_color = TERM_L_GREEN; + strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]); + } + else if (is_state(s_ptr, STORE_HATED)) + { + action_color = TERM_RED; + strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_HATED]); + } + else + { + action_color = TERM_YELLOW; + strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]); + } + } + else if (ba_ptr->action_restr == 1) + { + if ((is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) || + (is_state(s_ptr, STORE_NORMAL) && (ba_ptr->costs[STORE_NORMAL] == 0))) + { + action_color = TERM_WHITE; + buff[0] = '\0'; + } + else if (is_state(s_ptr, STORE_LIKED)) + { + action_color = TERM_L_GREEN; + strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]); + } + else if (is_state(s_ptr, STORE_HATED)) + { + action_color = TERM_L_DARK; + strnfmt(buff, 20, "(closed)"); + } + else + { + action_color = TERM_YELLOW; + strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_NORMAL]); + } + } + else + { + if (is_state(s_ptr, STORE_LIKED) && (ba_ptr->costs[STORE_LIKED] == 0)) + { + action_color = TERM_WHITE; + buff[0] = '\0'; + } + else if (is_state(s_ptr, STORE_LIKED)) + { + action_color = TERM_L_GREEN; + strnfmt(buff, 20, "(%dgp)", ba_ptr->costs[STORE_LIKED]); + } + else + { + action_color = TERM_L_DARK; + strnfmt(buff, 20, "(closed)"); + } + } + + strnfmt(tmp_str, 80, " %c", ba_ptr->letter); + c_put_str(TERM_YELLOW, tmp_str, 21 + (i / 2), 17 + (30 * (i % 2))); + + strnfmt(tmp_str, 80, ") %s %s", ba_ptr->name + ba_name, buff); + c_put_str(action_color, tmp_str, 21 + (i / 2), 2 + 17 + (30 * (i % 2))); + } + } +} + + +/* reset timed flags */ +static void reset_tim_flags() +{ + p_ptr->fast = 0; /* Timed -- Fast */ + p_ptr->slow = 0; /* Timed -- Slow */ + p_ptr->blind = 0; /* Timed -- Blindness */ + p_ptr->paralyzed = 0; /* Timed -- Paralysis */ + p_ptr->confused = 0; /* Timed -- Confusion */ + p_ptr->afraid = 0; /* Timed -- Fear */ + p_ptr->image = 0; /* Timed -- Hallucination */ + p_ptr->poisoned = 0; /* Timed -- Poisoned */ + p_ptr->cut = 0; /* Timed -- Cut */ + p_ptr->stun = 0; /* Timed -- Stun */ + + p_ptr->protevil = 0; /* Timed -- Protection */ + p_ptr->protgood = 0; /* Timed -- Protection */ + p_ptr->invuln = 0; /* Timed -- Invulnerable */ + p_ptr->hero = 0; /* Timed -- Heroism */ + p_ptr->shero = 0; /* Timed -- Super Heroism */ + p_ptr->shield = 0; /* Timed -- Shield Spell */ + p_ptr->blessed = 0; /* Timed -- Blessed */ + p_ptr->tim_invis = 0; /* Timed -- Invisibility */ + p_ptr->tim_infra = 0; /* Timed -- Infra Vision */ + + p_ptr->oppose_acid = 0; /* Timed -- oppose acid */ + p_ptr->oppose_elec = 0; /* Timed -- oppose lightning */ + p_ptr->oppose_fire = 0; /* Timed -- oppose heat */ + p_ptr->oppose_cold = 0; /* Timed -- oppose cold */ + p_ptr->oppose_pois = 0; /* Timed -- oppose poison */ + + p_ptr->confusing = 0; /* Touch of Confusion */ +} + + +/* + * arena commands + */ +static void arena_comm(int cmd) +{ + char tmp_str[80]; + + monster_race *r_ptr; + + cptr name; + + + switch (cmd) + { + case BACT_ARENA: + { + if (p_ptr->arena_number == MAX_ARENA_MONS) + { + clear_bldg(5, 19); + prt(" Arena Victor!", 5, 0); + prt("Congratulations! You have defeated all before you.", 7, 0); + prt("For that, receive the prize: 10,000 gold pieces", 8, 0); + prt("", 10, 0); + prt("", 11, 0); + p_ptr->au += 10000; + msg_print("Press the space bar to continue"); + msg_print(NULL); + p_ptr->arena_number++; + } + else if (p_ptr->arena_number > MAX_ARENA_MONS) + { + msg_print("You enter the arena briefly and bask in your glory."); + msg_print(NULL); + } + else + { + p_ptr->inside_arena = TRUE; + p_ptr->exit_bldg = FALSE; + reset_tim_flags(); + p_ptr->leaving = TRUE; + p_ptr->oldpx = p_ptr->px; + p_ptr->oldpy = p_ptr->py; + leave_bldg = TRUE; + } + + break; + } + + case BACT_POSTER: + { + if (p_ptr->arena_number == MAX_ARENA_MONS) + msg_print("You are victorious. Enter the arena for the ceremony."); + else if (p_ptr->arena_number > MAX_ARENA_MONS) + msg_print("You have won against all foes."); + else + { + r_ptr = &r_info[arena_monsters[p_ptr->arena_number]]; + name = (r_name + r_ptr->name); + strnfmt(tmp_str, 80, "Do I hear any challenges against: %s", name); + msg_print(tmp_str); + msg_print(NULL); + } + + break; + } + + case BACT_ARENA_RULES: + { + /* Save screen */ + screen_save(); + + /* Peruse the arena help file */ + (void)show_file("arena.txt", NULL, 0, 0); + + /* Load screen */ + screen_load(); + + break; + } + } +} + + +/* + * display fruit for dice slots + */ +static void display_fruit(int row, int col, int fruit) +{ + switch (fruit) + { + case 0: /* lemon */ + { + c_put_str(TERM_YELLOW, " ####.", row, col); + c_put_str(TERM_YELLOW, " # #", row + 1, col); + c_put_str(TERM_YELLOW, " # #", row + 2, col); + c_put_str(TERM_YELLOW, "# #", row + 3, col); + c_put_str(TERM_YELLOW, "# #", row + 4, col); + c_put_str(TERM_YELLOW, "# # ", row + 5, col); + c_put_str(TERM_YELLOW, "# # ", row + 6, col); + c_put_str(TERM_YELLOW, ".#### ", row + 7, col); + prt(" Lemon ", row + 8, col); + + break; + } + + case 1: /* orange */ + { + c_put_str(TERM_ORANGE, " ## ", row, col); + c_put_str(TERM_ORANGE, " #..# ", row + 1, col); + c_put_str(TERM_ORANGE, " #....# ", row + 2, col); + c_put_str(TERM_ORANGE, "#......#", row + 3, col); + c_put_str(TERM_ORANGE, "#......#", row + 4, col); + c_put_str(TERM_ORANGE, " #....# ", row + 5, col); + c_put_str(TERM_ORANGE, " #..# ", row + 6, col); + c_put_str(TERM_ORANGE, " ## ", row + 7, col); + prt(" Orange ", row + 8, col); + + break; + } + + case 2: /* sword */ + { + c_put_str(TERM_SLATE, " /\\ ", row, col); + c_put_str(TERM_SLATE, " ## ", row + 1, col); + c_put_str(TERM_SLATE, " ## ", row + 2, col); + c_put_str(TERM_SLATE, " ## ", row + 3, col); + c_put_str(TERM_SLATE, " ## ", row + 4, col); + c_put_str(TERM_SLATE, " ## ", row + 5, col); + c_put_str(TERM_UMBER, " ###### ", row + 6, col); + c_put_str(TERM_UMBER, " ## ", row + 7, col); + prt(" Sword ", row + 8, col); + + break; + } + + case 3: /* shield */ + { + c_put_str(TERM_SLATE, " ###### ", row, col); + c_put_str(TERM_SLATE, "# #", row + 1, col); + c_put_str(TERM_SLATE, "# ++++ #", row + 2, col); + c_put_str(TERM_SLATE, "# +==+ #", row + 3, col); + c_put_str(TERM_SLATE, "# ++ #", row + 4, col); + c_put_str(TERM_SLATE, " # # ", row + 5, col); + c_put_str(TERM_SLATE, " # # ", row + 6, col); + c_put_str(TERM_SLATE, " ## ", row + 7, col); + prt(" Shield ", row + 8, col); + + break; + } + + case 4: /* plum */ + { + c_put_str(TERM_VIOLET, " ## ", row, col); + c_put_str(TERM_VIOLET, " ###### ", row + 1, col); + c_put_str(TERM_VIOLET, "########", row + 2, col); + c_put_str(TERM_VIOLET, "########", row + 3, col); + c_put_str(TERM_VIOLET, "########", row + 4, col); + c_put_str(TERM_VIOLET, " ###### ", row + 5, col); + c_put_str(TERM_VIOLET, " #### ", row + 6, col); + c_put_str(TERM_VIOLET, " ## ", row + 7, col); + prt(" Plum ", row + 8, col); + + break; + } + + case 5: /* cherry */ + { + c_put_str(TERM_RED, " ##", row, col); + c_put_str(TERM_RED, " ### ", row + 1, col); + c_put_str(TERM_RED, " #..# ", row + 2, col); + c_put_str(TERM_RED, " #..# ", row + 3, col); + c_put_str(TERM_RED, " ###### ", row + 4, col); + c_put_str(TERM_RED, "#..##..#", row + 5, col); + c_put_str(TERM_RED, "#..##..#", row + 6, col); + c_put_str(TERM_RED, " ## ## ", row + 7, col); + prt(" Cherry ", row + 8, col); + + break; + } + } +} + + +/* + * gamble_comm + */ +static bool_ gamble_comm(int cmd) +{ + int roll1, roll2, roll3, choice, odds, win; + + s32b wager; + + s32b maxbet; + + s32b oldgold; + + static const char *fruit[6] = + {"Lemon", "Orange", "Sword", "Shield", "Plum", "Cherry" + }; + + char out_val[160], tmp_str[80], again; + + cptr p; + + + screen_save(); + + if (cmd == BACT_GAMBLE_RULES) + { + /* Peruse the gambling help file */ + (void)show_file("gambling.txt", NULL, 0, 0); + } + else + { + clear_bldg(5, 23); + + /* Set maximum bet */ + if (p_ptr->lev < 10) + maxbet = (p_ptr->lev * 100); + else + maxbet = (p_ptr->lev * 1000); + + /* Get the wager */ + strcpy(out_val, ""); + strnfmt(tmp_str, 80, "Your wager (1-%ld) ? ", maxbet); + get_string(tmp_str, out_val, 32); + + /* Strip spaces */ + for (p = out_val; *p == ' '; p++); + + wager = atol(p); + + if (wager > p_ptr->au) + { + msg_print("Hey! You don't have the gold - get out of here!"); + msg_print(NULL); + screen_load(); + return (FALSE); + } + else if (wager > maxbet) + { + msg_format("I'll take $%ld of that. Keep the rest.", maxbet); + wager = maxbet; + } + else if (wager < 1) + { + msg_print("Ok, we'll start with $1."); + + wager = 1; + } + msg_print(NULL); + win = FALSE; + odds = 0; + oldgold = p_ptr->au; + + strnfmt(tmp_str, 80, "Gold before game: %9ld", oldgold); + prt(tmp_str, 20, 2); + + strnfmt(tmp_str, 80, "Current Wager: %9ld", wager); + prt(tmp_str, 21, 2); + + do + { + switch (cmd) + { + case BACT_IN_BETWEEN: /* Game of In-Between */ + { + c_put_str(TERM_GREEN, "In Between", 5, 2); + odds = 3; + win = FALSE; + roll1 = randint(10); + roll2 = randint(10); + choice = randint(10); + strnfmt(tmp_str, 80, "Black die: %d Black Die: %d", + roll1, roll2); + prt(tmp_str, 8, 3); + strnfmt(tmp_str, 80, "Red die: %d", choice); + prt(tmp_str, 11, 14); + if (((choice > roll1) && (choice < roll2)) || + ((choice < roll1) && (choice > roll2))) + win = TRUE; + + break; + } + case BACT_CRAPS: /* Game of Craps */ + { + c_put_str(TERM_GREEN, "Craps", 5, 2); + win = 3; + odds = 1; + roll1 = randint(6); + roll2 = randint(6); + roll3 = roll1 + roll2; + choice = roll3; + strnfmt(tmp_str, 80, "First roll: %d %d Total: %d", roll1, + roll2, roll3); + prt(tmp_str, 7, 5); + if ((roll3 == 7) || (roll3 == 11)) + win = TRUE; + else if ((roll3 == 2) || (roll3 == 3) || (roll3 == 12)) + win = FALSE; + else + { + do + { + msg_print("Hit any key to roll again"); + msg_print(NULL); + roll1 = randint(6); + roll2 = randint(6); + roll3 = roll1 + roll2; + + strnfmt(tmp_str, 80, "Roll result: %d %d Total: %d", + roll1, roll2, roll3); + prt(tmp_str, 8, 5); + if (roll3 == choice) + win = TRUE; + else if (roll3 == 7) + win = FALSE; + } + while ((win != TRUE) && (win != FALSE)); + } + + break; + } + + case BACT_SPIN_WHEEL: /* Spin the Wheel Game */ + { + win = FALSE; + odds = 10; + c_put_str(TERM_GREEN, "Wheel", 5, 2); + prt("0 1 2 3 4 5 6 7 8 9", 7, 5); + prt("--------------------------------", 8, 3); + strcpy(out_val, ""); + get_string ("Pick a number (1-9): ", out_val, 32); + for (p = out_val; *p == ' '; p++); + choice = atol(p); + if (choice < 0) + { + msg_print("I'll put you down for 0."); + choice = 0; + } + else if (choice > 9) + { + msg_print("Ok, I'll put you down for 9."); + choice = 9; + } + msg_print(NULL); + roll1 = randint(10) - 1; + strnfmt(tmp_str, 80, "The wheel spins to a stop and the winner is %d", + roll1); + prt(tmp_str, 13, 3); + prt("", 9, 0); + prt("*", 9, (3 * roll1 + 5)); + if (roll1 == choice) + win = TRUE; + + break; + } + + case BACT_DICE_SLOTS: /* The Dice Slots */ + { + c_put_str(TERM_GREEN, "Dice Slots", 5, 2); + win = FALSE; + roll1 = randint(6); + roll2 = randint(6); + choice = randint(6); + strnfmt(tmp_str, 80, "%s %s %s", + fruit[roll1 - 1], fruit[roll2 - 1], + fruit[choice - 1]); + prt(tmp_str, 15, 37); + prt("/--------------------------\\", 7, 2); + prt("\\--------------------------/", 17, 2); + display_fruit(8, 3, roll1 - 1); + display_fruit(8, 12, roll2 - 1); + display_fruit(8, 21, choice - 1); + if ((roll1 == roll2) && (roll2 == choice)) + { + win = TRUE; + if (roll1 == 1) + odds = 4; + else if (roll1 == 2) + odds = 6; + else + odds = roll1 * roll1; + } + else if ((roll1 == 6) && (roll2 == 6)) + { + win = TRUE; + odds = choice + 1; + } + + break; + } + } + + if (win) + { + prt("YOU WON", 16, 37); + p_ptr->au = p_ptr->au + (odds * wager); + strnfmt(tmp_str, 80, "Payoff: %d", odds); + prt(tmp_str, 17, 37); + } + else + { + prt("You Lost", 16, 37); + p_ptr->au = p_ptr->au - wager; + prt("", 17, 37); + } + strnfmt(tmp_str, 80, "Current Gold: %9ld", p_ptr->au); + prt(tmp_str, 22, 2); + prt("Again(Y/N)?", 18, 37); + move_cursor(18, 49); + again = inkey(); + if (wager > p_ptr->au) + { + msg_print("Hey! You don't have the gold - get out of here!"); + msg_print(NULL); + screen_load(); + return (FALSE); + /* strnfmt(tmp_str, 80, "Current Wager: %9ld",wager); + prt(tmp_str, 17, 2); */ + } + } + while ((again == 'y') || (again == 'Y')); + + prt("", 18, 37); + if (p_ptr->au >= oldgold) + msg_print("You came out a winner! We'll win next time, I'm sure."); + else + msg_print("You lost gold! Haha, better head home."); + msg_print(NULL); + } + + screen_load(); + + return (TRUE); +} + + +/* + * inn commands + * Note that resting for the night was a perfect way to avoid player + * ghosts in the town *if* you could only make it to the inn in time (-: + * Now that the ghosts are temporarily disabled in 2.8.X, this function + * will not be that useful. I will keep it in the hopes the player + * ghost code does become a reality again. Does help to avoid filthy urchins. + * Resting at night is also a quick way to restock stores -KMW- + */ +static bool_ inn_comm(int cmd) +{ + bool_ vampire; + + + /* Extract race info */ + vampire = ((PRACE_FLAG(PR1_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire"))); + + switch (cmd) + { + case BACT_FOOD: /* Buy food & drink */ + { + if (!vampire) + { + msg_print("The barkeep gives you some gruel and a beer."); + msg_print(NULL); + (void) set_food(PY_FOOD_MAX - 1); + } + else + msg_print("You're a vampire and I don't have any food for you!"); + + break; + } + + /* + * I revamped this... Don't know why normal races didn't get + * mana regenerated. It is the grand tradition of p&p games -- pelpel + */ + case BACT_REST: /* Rest for the night */ + { + bool_ nighttime; + + /* Extract the current time */ + nighttime = ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18)); + + /* Normal races rest at night */ + if (!vampire && !nighttime) + { + msg_print("The rooms are available only at night."); + msg_print(NULL); + return (FALSE); + } + + /* Vampires rest during daytime */ + if (vampire && nighttime) + { + msg_print("The rooms are available only during daylight for your kind."); + msg_print(NULL); + return (FALSE); + } + + /* Must cure HP draining status first */ + if ((p_ptr->poisoned > 0) || (p_ptr->cut > 0)) + { + msg_print("You need a healer, not a room."); + msg_print(NULL); + msg_print("Sorry, but I don't want anyone dying in here."); + return (FALSE); + } + + /* Let the time pass XXX XXX XXX */ + if (vampire) + { + /* Wait for sunset */ + while ((bst(HOUR, turn) >= 6) && (bst(HOUR, turn) < 18)) + { + turn += (10L * MINUTE); + } + } + else + { + /* Wait for sunrise */ + while ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18)) + { + turn += (10L * MINUTE); + } + } + + /* Regen */ + p_ptr->chp = p_ptr->mhp; + p_ptr->csp = p_ptr->msp; + + /* Restore status */ + set_blind(0); + set_confused(0); + p_ptr->stun = 0; + + /* Message */ + if (vampire) msg_print("You awake refreshed for the new night."); + else msg_print("You awake refreshed for the new day."); + + /* Dungeon stuff */ + p_ptr->leaving = TRUE; + p_ptr->oldpx = p_ptr->px; + p_ptr->oldpy = p_ptr->py; + + /* Select new bounties. */ + select_bounties(); + + break; + } + + case BACT_RUMORS: /* Listen for rumors */ + { + char rumor[80]; + + get_rnd_line("rumors.txt", rumor); + msg_format("%s", rumor); + msg_print(NULL); + + break; + } + } + + return (TRUE); +} + + +/* + * Display quest information + */ +static void get_questinfo(int questnum) +{ + int i; + + + /* Print the quest info */ + prt(format("Quest Information (Danger level: %d)", quest[questnum].level), 5, 0); + + prt(quest[questnum].name, 7, 0); + + i = 0; + while ((i < 10) && (quest[questnum].desc[i][0] != '\0')) + { + c_put_str(TERM_YELLOW, quest[questnum].desc[i], i + 8, 0); + i++; + } +} + + +/* + * Request a quest from the Lord. + */ +static bool_ castle_quest(int y, int x) +{ + int plot = 0; + + quest_type *q_ptr; + + + clear_bldg(7, 18); + + /* Current plot of the building */ + plot = cave[y][x].special; + + /* Is there a quest available at the building? */ + if ((!plot) || (plots[plot] == QUEST_NULL)) + { + put_str("I don't have a quest for you at the moment.", 8, 0); + return FALSE; + } + + q_ptr = &quest[plots[plot]]; + + /* Quest is completed */ + if (q_ptr->status == QUEST_STATUS_COMPLETED) + { + /* Rewarded quest */ + q_ptr->status = QUEST_STATUS_FINISHED; + + process_hooks(HOOK_QUEST_FINISH, "(d)", plots[plot]); + + return (TRUE); + } + + /* Quest is still unfinished */ + else if (q_ptr->status == QUEST_STATUS_TAKEN) + { + put_str("You have not completed your current quest yet!", 8, 0); + put_str("Use CTRL-Q to check the status of your quest.", 9, 0); + put_str("Return when you have completed your quest.", 12, 0); + + return (FALSE); + } + /* Failed quest */ + else if (q_ptr->status == QUEST_STATUS_FAILED) + { + /* Mark quest as done (but failed) */ + q_ptr->status = QUEST_STATUS_FAILED_DONE; + + process_hooks(HOOK_QUEST_FAIL, "(d)", plots[plot]); + + return (FALSE); + } + /* No quest yet */ + else if (q_ptr->status == QUEST_STATUS_UNTAKEN) + { + if (process_hooks(HOOK_INIT_QUEST, "(d)", plots[plot])) return (FALSE); + + q_ptr->status = QUEST_STATUS_TAKEN; + + /* Assign a new quest */ + get_questinfo(plots[plot]); + + /* Add the hooks */ + quest[plots[plot]].init(plots[plot]); + + return (TRUE); + } + + return FALSE; +} + +/* + * Displaying town history -KMW- + */ +static void town_history(void) +{ + /* Save screen */ + screen_save(); + + /* Peruse the building help file */ + (void)show_file("bldg.txt", NULL, 0, 0); + + /* Load screen */ + screen_load(); +} + + +/* + * compare_weapon_aux2 -KMW- + */ +static void compare_weapon_aux2(object_type *o_ptr, int numblows, int r, int c, int mult, const char *attr, u32b f1, u32b f2, u32b f3, byte color) +{ + char tmp_str[80]; + + c_put_str(color, attr, r, c); + strnfmt(tmp_str, 80, "Attack: %d-%d damage", + numblows * ((o_ptr->dd * mult) + o_ptr->to_d), + numblows * ((o_ptr->ds * o_ptr->dd * mult) + o_ptr->to_d)); + put_str(tmp_str, r, c + 8); + r++; +} + + +/* + * compare_weapon_aux1 -KMW- + */ +static void compare_weapon_aux1(object_type *o_ptr, int col, int r) +{ + u32b f1, f2, f3, f4, f5, esp; + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + + if (f1 & (TR1_SLAY_ANIMAL)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Animals:", + f1, f2, f3, TERM_YELLOW); + } + if (f1 & (TR1_SLAY_EVIL)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 2, "Evil:", + f1, f2, f3, TERM_YELLOW); + } + if (f1 & (TR1_SLAY_UNDEAD)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Undead:", + f1, f2, f3, TERM_YELLOW); + } + if (f1 & (TR1_SLAY_DEMON)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Demons:", + f1, f2, f3, TERM_YELLOW); + } + if (f1 & (TR1_SLAY_ORC)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Orcs:", + f1, f2, f3, TERM_YELLOW); + } + if (f1 & (TR1_SLAY_TROLL)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Trolls:", + f1, f2, f3, TERM_YELLOW); + } + if (f1 & (TR1_SLAY_GIANT)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Giants:", + f1, f2, f3, TERM_YELLOW); + } + if (f1 & (TR1_SLAY_DRAGON)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Dragons:", + f1, f2, f3, TERM_YELLOW); + } + if (f1 & (TR1_KILL_DRAGON)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 5, "Dragons:", + f1, f2, f3, TERM_YELLOW); + } + if (f1 & (TR1_BRAND_ACID)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Acid:", + f1, f2, f3, TERM_RED); + } + if (f1 & (TR1_BRAND_ELEC)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Elec:", + f1, f2, f3, TERM_RED); + } + if (f1 & (TR1_BRAND_FIRE)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Fire:", + f1, f2, f3, TERM_RED); + } + if (f1 & (TR1_BRAND_COLD)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Cold:", + f1, f2, f3, TERM_RED); + } + if (f1 & (TR1_BRAND_POIS)) + { + compare_weapon_aux2(o_ptr, p_ptr->num_blow, r++, col, 3, "Poison:", + f1, f2, f3, TERM_RED); + } +} + + +/* + * list_weapon -KMW- + */ +static void list_weapon(object_type *o_ptr, int row, int col) +{ + char o_name[80]; + + char tmp_str[80]; + + + object_desc(o_name, o_ptr, TRUE, 0); + c_put_str(TERM_YELLOW, o_name, row, col); + strnfmt(tmp_str, 80, "To Hit: %d To Damage: %d", o_ptr->to_h, o_ptr->to_d); + put_str(tmp_str, row + 1, col); + strnfmt(tmp_str, 80, "Dice: %d Sides: %d", o_ptr->dd, o_ptr->ds); + put_str(tmp_str, row + 2, col); + strnfmt(tmp_str, 80, "Number of Blows: %d", p_ptr->num_blow); + put_str(tmp_str, row + 3, col); + c_put_str(TERM_YELLOW, "Possible Damage:", row + 5, col); + strnfmt(tmp_str, 80, "One Strike: %d-%d damage", o_ptr->dd + o_ptr->to_d, + (o_ptr->ds*o_ptr->dd) + o_ptr->to_d); + put_str(tmp_str, row + 6, col + 1); + strnfmt(tmp_str, 80, "One Attack: %d-%d damage", p_ptr->num_blow*(o_ptr->dd + o_ptr->to_d), + p_ptr->num_blow*(o_ptr->ds*o_ptr->dd + o_ptr->to_d)); + put_str(tmp_str, row + 7, col + 1); +} + + +/* + * Select melee weapons + */ +static bool_ item_tester_hook_melee_weapon(object_type *o_ptr) +{ + return (wield_slot(o_ptr) == INVEN_WIELD); +} + +/* + * compare_weapons -KMW- + */ +static bool_ compare_weapons(void) +{ + int item, item2, i; + + object_type *o1_ptr, *o2_ptr, *orig_ptr; + + object_type *i_ptr; + + cptr q, s; + + + clear_bldg(6, 18); + + o1_ptr = NULL; + o2_ptr = NULL; + i_ptr = NULL; + + /* Store copy of original wielded weapon in pack slot */ + i_ptr = &p_ptr->inventory[INVEN_WIELD]; + orig_ptr = &p_ptr->inventory[INVEN_PACK]; + object_copy(orig_ptr, i_ptr); + + i = 6; + /* Get first weapon */ + /* Restrict choices to meele weapons */ + item_tester_hook = item_tester_hook_melee_weapon; + + q = "What is your first melee weapon? "; + s = "You have nothing to compare."; + if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) + { + object_wipe(orig_ptr); + return (FALSE); + } + + /* Get the item (in the pack) */ + if (item >= 0) + o1_ptr = &p_ptr->inventory[item]; + + /* Get second weapon */ + /* Restrict choices to melee weapons */ + item_tester_hook = item_tester_hook_melee_weapon; + + q = "What is your second melee weapon? "; + s = "You have nothing to compare."; + if (!get_item(&item2, q, s, (USE_EQUIP | USE_INVEN))) + { + object_wipe(orig_ptr); + return (FALSE); + } + + /* Get the item (in the pack) */ + if (item2 >= 0) o2_ptr = &p_ptr->inventory[item2]; + + put_str("Based on your current abilities, here is what your weapons will do", 4, 2); + + i_ptr = &p_ptr->inventory[INVEN_WIELD]; + object_copy(i_ptr, o1_ptr); + calc_bonuses(TRUE); + + list_weapon(o1_ptr, i, 2); + compare_weapon_aux1(o1_ptr, 2, i + 8); + + i_ptr = &p_ptr->inventory[INVEN_WIELD]; + if (item2 == INVEN_WIELD) + object_copy(i_ptr, orig_ptr); + else + object_copy(i_ptr, o2_ptr); + calc_bonuses(TRUE); + + list_weapon(o2_ptr, i, 40); + compare_weapon_aux1(o2_ptr, 40, i + 8); + + i_ptr = &p_ptr->inventory[INVEN_WIELD]; + object_copy(i_ptr, orig_ptr); + calc_bonuses(TRUE); + + object_wipe(orig_ptr); + + put_str("(Only highest damage applies per monster. Special damage not cumulative)", 20, 0); + + return (TRUE); +} + + +/* + * general all-purpose fixing routine for items from building personnel + * sharpen arrows, repair armor, repair weapon + * -KMW- + */ +static bool_ fix_item(int istart, int iend, int ispecific, bool_ iac, + int ireward, bool_ set_reward) +{ + int i; + + int j = 9; + + int maxenchant = (p_ptr->lev / 5); + + object_type *o_ptr; + + char out_val[80], tmp_str[80]; + + bool_ repaired = FALSE; + + clear_bldg(5, 18); + strnfmt(tmp_str, 80, " Based on your skill, we can improve up to +%d", maxenchant); + prt(tmp_str, 5, 0); + prt("Status", 7, 30); + + for (i = istart; i <= iend; i++) + { + o_ptr = &p_ptr->inventory[i]; + if (ispecific > 0) + { + if (o_ptr->tval != ispecific) + continue; + } + + if (o_ptr->tval) + { + object_desc(tmp_str, o_ptr, FALSE, 1); + + if ((o_ptr->name1 && (o_ptr->ident & 0x08))) + strnfmt(out_val, 80, "%-40s: beyond our skills!", tmp_str); + else if (o_ptr->name1) + strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str); + else + { + if ((iac) && (o_ptr->to_a <= -3)) + { + strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str); + } + else if ((iac) && (o_ptr->to_a < maxenchant)) + { + o_ptr->to_a++; + strnfmt(out_val, 80, "%-40s: polished -> (%d)", tmp_str, o_ptr->to_a); + repaired = TRUE; + } + else if ((!iac) && ((o_ptr->to_h <= -3) || (o_ptr->to_d <= -3))) + { + strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str); + } + /* Sharpen a weapon */ + else if ((!iac) && ((o_ptr->to_h < maxenchant) || + (o_ptr->to_d < maxenchant))) + { + if (o_ptr->to_h < maxenchant) + o_ptr->to_h++; + if (o_ptr->to_d < maxenchant) + o_ptr->to_d++; + strnfmt(out_val, 80, "%-40s: sharpened -> (%d,%d)", tmp_str, + o_ptr->to_h, o_ptr->to_d); + repaired = TRUE; + } + else + strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str); + } + prt(out_val, j++, 0); + } + } + + if (!repaired) + { + msg_print("You don't have anything appropriate."); + msg_print(NULL); + } + else + { + msg_print("Press the spacebar to continue"); + msg_print(NULL); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + p_ptr->update |= (PU_BONUS); + } + clear_bldg(5, 18); + + return (repaired); +} + + +/* + * Research Item + */ +static bool_ research_item(void) +{ + clear_bldg(5, 18); + return (identify_fully()); +} + + +/* + * Show the current quest monster. + */ +static void show_quest_monster(void) +{ + monster_race* r_ptr = &r_info[bounties[0][0]]; + + + msg_format("Quest monster: %s. " + "Need to turn in %d corpse%s to receive reward.", + r_name + r_ptr->name, bounties[0][1], + (bounties[0][1] > 1 ? "s" : "")); + msg_print(NULL); +} + + +/* + * Show the current bounties. + */ +static void show_bounties(void) +{ + int i, j = 6; + + monster_race* r_ptr; + + char buff[80]; + + + clear_bldg(7, 18); + + c_prt(TERM_YELLOW, "Currently active bounties:", 4, 2); + + for (i = 1; i < MAX_BOUNTIES; i++, j++) + { + r_ptr = &r_info[bounties[i][0]]; + + strnfmt(buff, 80, "%-30s (%d gp)", r_name + r_ptr->name, bounties[i][1]); + + prt(buff, j, 2); + + if (j >= 17) + { + msg_print("Press space for more."); + msg_print(NULL); + + clear_bldg(7, 18); + j = 5; + } + } +} + + +/* + * Filter for corpses that currently have a bounty on them. + */ +static bool_ item_tester_hook_bounty(object_type* o_ptr) +{ + int i; + + + if (o_ptr->tval == TV_CORPSE) + { + for (i = 1; i < MAX_BOUNTIES; i++) + { + if (bounties[i][0] == o_ptr->pval2) return (TRUE); + } + } + + return (FALSE); +} + +/* Filter to match the quest monster's corpse. */ +static bool_ item_tester_hook_quest_monster(object_type* o_ptr) +{ + if ((o_ptr->tval == TV_CORPSE) && + (o_ptr->pval2 == bounties[0][0])) return (TRUE); + return (FALSE); +} + + +/* + * Return the boost in the corpse's value depending on how rare the body + * part is. + */ +static int corpse_value_boost(int sval) +{ + switch (sval) + { + case SV_CORPSE_HEAD: + case SV_CORPSE_SKULL: + { + return (1); + } + + /* Default to no boost. */ + default: + { + return (0); + } + } +} + +/* + * Sell a corpse, if there's currently a bounty on it. + */ +static void sell_corpses(void) +{ + object_type* o_ptr; + + int i, boost = 0; + + s16b value; + + int item; + + + /* Set the hook. */ + item_tester_hook = item_tester_hook_bounty; + + /* Select a corpse to sell. */ + if (!get_item(&item, "Sell which corpse", + "You have no corpses you can sell.", USE_INVEN)) return; + + o_ptr = &p_ptr->inventory[item]; + + /* Exotic body parts are worth more. */ + boost = corpse_value_boost(o_ptr->sval); + + /* Try to find a match. */ + for (i = 1; i < MAX_BOUNTIES; i++) + { + if (o_ptr->pval2 == bounties[i][0]) + { + value = bounties[i][1] + boost * (r_info[o_ptr->pval2].level); + + msg_format("Sold for %ld gold pieces.", value); + msg_print(NULL); + p_ptr->au += value; + + /* Increase the number of collected bounties */ + total_bounties++; + + inc_stack_size(item, -1); + + return; + } + } + + msg_print("Sorry, but that monster does not have a bounty on it."); + msg_print(NULL); +} + + + +/* + * Hook for bounty monster selection. + */ +static bool_ mon_hook_bounty(int r_idx) +{ + monster_race* r_ptr = &r_info[r_idx]; + + + /* Reject uniques */ + if (r_ptr->flags1 & RF1_UNIQUE) return (FALSE); + + /* Reject those who cannot leave anything */ + if (!(r_ptr->flags9 & RF9_DROP_CORPSE) && + !(r_ptr->flags9 & RF9_DROP_SKELETON)) return (FALSE); + + /* Reject pets */ + if (r_ptr->flags7 & RF7_PET) return (FALSE); + + /* Reject friendly creatures */ + if (r_ptr->flags7 & RF7_FRIENDLY) return (FALSE); + + /* The rest are acceptable */ + return (TRUE); +} + + +static void select_quest_monster(void) +{ + monster_race* r_ptr; + + int amt; + + + /* + * Set up the hooks -- no bounties on uniques or monsters + * with no corpses + */ + get_mon_num_hook = mon_hook_bounty; + get_mon_num_prep(); + + /* Set up the quest monster. */ + bounties[0][0] = get_mon_num(p_ptr->lev); + + r_ptr = &r_info[bounties[0][0]]; + + /* + * Select the number of monsters needed to kill. Groups and + * breeders require more + */ + amt = randnor(5, 3); + + if (amt < 2) amt = 2; + + if (r_ptr->flags1 & RF1_FRIEND) amt *= 3; amt /= 2; + if (r_ptr->flags1 & RF1_FRIENDS) amt *= 2; + if (r_ptr->flags4 & RF4_MULTIPLY) amt *= 3; + + if (r_ptr->flags7 & RF7_AQUATIC) amt /= 2; + + bounties[0][1] = amt; + + /* Undo the filters */ + get_mon_num_hook = NULL; + get_mon_num_prep(); +} + + + +/* + * Sell a corpse for a reward. + */ +static void sell_quest_monster(void) +{ + object_type* o_ptr; + + int item; + + + /* Set the hook. */ + item_tester_hook = item_tester_hook_quest_monster; + + /* Select a corpse to sell. */ + if (!get_item(&item, "Sell which corpse", + "You have no corpses you can sell.", USE_INVEN)) return; + + o_ptr = &p_ptr->inventory[item]; + + bounties[0][1] -= o_ptr->number; + + /* Completed the quest. */ + if (bounties[0][1] <= 0) + { + int m; + monster_race *r_ptr; + + cmsg_print(TERM_YELLOW, "You have completed your quest!"); + msg_print(NULL); + + /* Give full knowledge */ + + /* Hack -- Maximal info */ + r_ptr = &r_info[bounties[0][0]]; + + msg_print(format("Well done! As a reward I'll teach you everything " + "about the %s, (check your recall)", + r_name + r_ptr->name)); + + r_ptr->r_wake = r_ptr->r_ignore = MAX_UCHAR; + + /* Observe "maximal" attacks */ + for (m = 0; m < 4; m++) + { + /* Examine "actual" blows */ + if (r_ptr->blow[m].effect || r_ptr->blow[m].method) + { + /* Hack -- maximal observations */ + r_ptr->r_blows[m] = MAX_UCHAR; + } + } + + /* Hack -- maximal drops */ + r_ptr->r_drop_gold = r_ptr->r_drop_item = + (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) + + ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) + + ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) + + ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) + + ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) + + ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0)); + + /* Hack -- but only "valid" drops */ + if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0; + if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0; + + /* Hack -- observe many spells */ + r_ptr->r_cast_inate = MAX_UCHAR; + r_ptr->r_cast_spell = MAX_UCHAR; + + /* Hack -- know all the flags */ + r_ptr->r_flags1 = r_ptr->flags1; + r_ptr->r_flags2 = r_ptr->flags2; + r_ptr->r_flags3 = r_ptr->flags3; + r_ptr->r_flags4 = r_ptr->flags4; + r_ptr->r_flags5 = r_ptr->flags5; + r_ptr->r_flags6 = r_ptr->flags6; + r_ptr->r_flags4 = r_ptr->flags7; + r_ptr->r_flags5 = r_ptr->flags8; + r_ptr->r_flags6 = r_ptr->flags9; + + msg_print(NULL); + + select_quest_monster(); + + } + else + { + msg_format("Well done, only %d more to go.", bounties[0][1]); + msg_print(NULL); + } + + inc_stack_size(item, -1); +} + + + +/* + * Fill the bounty list with monsters. + */ +void select_bounties(void) +{ + int i, j; + + + select_quest_monster(); + + /* + * Set up the hooks -- no bounties on uniques or monsters + * with no corpses + */ + get_mon_num_hook = mon_hook_bounty; + get_mon_num_prep(); + + for (i = 1; i < MAX_BOUNTIES; i++) + { + int lev = i * 5 + randnor(0, 2); + monster_race* r_ptr; + s16b r_idx; + s16b val; + + if (lev < 1) lev = 1; + + if (lev >= MAX_DEPTH) lev = MAX_DEPTH - 1; + + /* We don't want to duplicate entries in the list */ + while (TRUE) + { + r_idx = get_mon_num(lev); + + for (j = 0; j < i; j++) + { + if (bounties[j][0] == r_idx) continue; + } + + break; + } + + bounties[i][0] = r_idx; + + r_ptr = &r_info[r_idx]; + + val = r_ptr->mexp + r_ptr->level * 20 + randnor(0, r_ptr->level * 2); + + if (val < 1) val = 1; + + bounties[i][1] = val; + } + + /* Undo the filters. */ + get_mon_num_hook = NULL; + get_mon_num_prep(); +} + +/* + * Execute a building command + */ +bool_ bldg_process_command(store_type *s_ptr, int i) +{ + store_action_type *ba_ptr = &ba_info[st_info[s_ptr->st_idx].actions[i]]; + + int bact = ba_ptr->action; + + int bcost; + + bool_ paid = FALSE; + + bool_ set_reward = FALSE; + + bool_ recreate = FALSE; + + + if (is_state(s_ptr, STORE_LIKED)) + { + bcost = ba_ptr->costs[STORE_LIKED]; + } + else if (is_state(s_ptr, STORE_HATED)) + { + bcost = ba_ptr->costs[STORE_HATED]; + } + else + { + bcost = ba_ptr->costs[STORE_NORMAL]; + } + + /* action restrictions */ + if (((ba_ptr->action_restr == 1) && (is_state(s_ptr, STORE_LIKED))) || + ((ba_ptr->action_restr == 2) && (!is_state(s_ptr, STORE_LIKED)))) + { + msg_print("You have no right to choose that!"); + msg_print(NULL); + return FALSE; + } + + /* If player has loan and the time is out, few things work in stores */ + if (p_ptr->loan && !p_ptr->loan_time) + { + if ((bact != BACT_SELL) && (bact != BACT_VIEW_BOUNTIES) && + (bact != BACT_SELL_CORPSES) && + (bact != BACT_VIEW_QUEST_MON) && + (bact != BACT_SELL_QUEST_MON) && + (bact != BACT_EXAMINE) && (bact != BACT_STEAL) && + (bact != BACT_PAY_BACK_LOAN)) + { + msg_print("You are not allowed to do that until you have paid back your loan."); + msg_print(NULL); + return FALSE; + } + } + + /* check gold */ + if (bcost > p_ptr->au) + { + msg_print("You do not have the gold!"); + msg_print(NULL); + return FALSE; + } + + if (!bcost) set_reward = TRUE; + + switch (bact) + { + case BACT_RESEARCH_ITEM: + { + paid = research_item(); + break; + } + + case BACT_TOWN_HISTORY: + { + town_history(); + break; + } + + case BACT_RACE_LEGENDS: + { + race_legends(); + break; + } + + case BACT_QUEST1: + case BACT_QUEST2: + case BACT_QUEST3: + case BACT_QUEST4: + { + int y = 1, x = 1; + bool_ ok = FALSE; + + while ((x < cur_wid - 1) && !ok) + { + y = 1; + while ((y < cur_hgt - 1) && !ok) + { + /* Found the location of the quest info ? */ + if (bact - BACT_QUEST1 + FEAT_QUEST1 == cave[y][x].feat) + { + /* Stop the loop */ + ok = TRUE; + } + y++; + } + x++; + } + + if (ok) + { + recreate = castle_quest(y - 1, x - 1); + ; + } + else + { + msg_format("ERROR: no quest info feature found: %d", bact - BACT_QUEST1 + FEAT_QUEST1); + } + break; + } + + case BACT_KING_LEGENDS: + case BACT_ARENA_LEGENDS: + case BACT_LEGENDS: + { + show_highclass(building_loc); + break; + } + + case BACT_POSTER: + case BACT_ARENA_RULES: + case BACT_ARENA: + { + arena_comm(bact); + break; + } + + case BACT_IN_BETWEEN: + case BACT_CRAPS: + case BACT_SPIN_WHEEL: + case BACT_DICE_SLOTS: + case BACT_GAMBLE_RULES: + { + gamble_comm(bact); + break; + } + + case BACT_REST: + case BACT_RUMORS: + case BACT_FOOD: + { + paid = inn_comm(bact); + break; + } + + case BACT_RESEARCH_MONSTER: + { + paid = !research_mon(); + break; + } + + case BACT_COMPARE_WEAPONS: + { + paid = compare_weapons(); + break; + } + + case BACT_ENCHANT_WEAPON: + { + paid = fix_item(INVEN_WIELD, INVEN_WIELD, 0, FALSE, + BACT_ENCHANT_WEAPON, set_reward); + break; + } + + case BACT_ENCHANT_ARMOR: + { + paid = fix_item(INVEN_BODY, INVEN_FEET, 0, TRUE, + BACT_ENCHANT_ARMOR, set_reward); + break; + } + + /* needs work */ + case BACT_RECHARGE: + { + if (recharge(80)) paid = TRUE; + break; + } + + /* needs work */ + case BACT_IDENTS: + { + identify_pack(); + msg_print("Your possessions have been identified."); + msg_print(NULL); + paid = TRUE; + break; + } + + /* needs work */ + case BACT_STAR_HEAL: + { + hp_player(200); + set_poisoned(0); + set_blind(0); + set_confused(0); + set_cut(0); + set_stun(0); + if (p_ptr->black_breath) + { + msg_print("The hold of the Black Breath on you is broken!"); + p_ptr->black_breath = FALSE; + } + paid = TRUE; + break; + } + + /* needs work */ + case BACT_HEALING: + { + hp_player(200); + set_poisoned(0); + set_blind(0); + set_confused(0); + set_cut(0); + set_stun(0); + paid = TRUE; + break; + } + + /* needs work */ + case BACT_RESTORE: + { + if (do_res_stat(A_STR, TRUE)) paid = TRUE; + if (do_res_stat(A_INT, TRUE)) paid = TRUE; + if (do_res_stat(A_WIS, TRUE)) paid = TRUE; + if (do_res_stat(A_DEX, TRUE)) paid = TRUE; + if (do_res_stat(A_CON, TRUE)) paid = TRUE; + if (do_res_stat(A_CHR, TRUE)) paid = TRUE; + break; + } + + case BACT_ENCHANT_ARROWS: + { + paid = fix_item(0, INVEN_WIELD, TV_ARROW, FALSE, + BACT_ENCHANT_ARROWS, set_reward); + break; + } + + case BACT_ENCHANT_BOW: + { + paid = fix_item(INVEN_BOW, INVEN_BOW, TV_BOW, FALSE, + BACT_ENCHANT_BOW, set_reward); + break; + } + + case BACT_RECALL: + { + p_ptr->word_recall = 1; + msg_print("The air about you becomes charged..."); + paid = TRUE; + break; + } + + case BACT_TELEPORT_LEVEL: + { + if (reset_recall(FALSE)) + { + p_ptr->word_recall = 1; + msg_print("The air about you becomes charged..."); + paid = TRUE; + } + break; + } + + case BACT_MIMIC_NORMAL: + { + set_mimic(0, 0, 0); + paid = TRUE; + break; + } + + case BACT_VIEW_BOUNTIES: + { + show_bounties(); + break; + } + + case BACT_VIEW_QUEST_MON: + { + show_quest_monster(); + break; + } + + case BACT_SELL_QUEST_MON: + { + sell_quest_monster(); + break; + } + + case BACT_SELL_CORPSES: + { + sell_corpses(); + break; + } + + case BACT_DIVINATION: + { + int i, count = 0; + bool_ something = FALSE; + + while (count < 1000) + { + count++; + i = rand_int(MAX_FATES); + if (!fates[i].fate) continue; + if (fates[i].know) continue; + msg_print("You know a little more of your fate."); + + fates[i].know = TRUE; + something = TRUE; + break; + } + + if (!something) msg_print("Well, you have no fate, but I'll keep your money anyway!"); + + paid = TRUE; + break; + + } + + case BACT_BUY: + { + store_purchase(); + break; + } + + case BACT_SELL: + { + store_sell(); + break; + } + + case BACT_EXAMINE: + { + store_examine(); + break; + } + + case BACT_STEAL: + { + store_stole(); + break; + } + + case BACT_REQUEST_ITEM: + { + store_request_item(); + paid = TRUE; + break; + } + + case BACT_GET_LOAN: + { + s32b i, req; + char prompt[80]; + + if (p_ptr->loan) + { + msg_print("You already have a loan!"); + break; + } + + req = p_ptr->au; + + for (i = 0; i < INVEN_TOTAL; i++) + req += object_value_real(&p_ptr->inventory[i]); + + if (req > 100000) req = 100000; + if ((req + p_ptr->au) > PY_MAX_GOLD) req = PY_MAX_GOLD - p_ptr->au; + + strnfmt(prompt, sizeof (prompt), + "How much would you like to get (0-%ld) ?", req); + + req = get_quantity(prompt, req); + + if (req) + { + p_ptr->loan += req; + p_ptr->au += req; + p_ptr->loan_time += req; + + msg_format("You receive %i gold pieces", req); + + paid = TRUE; + } + else + msg_format("You did not request any money!"); + + break; + } + + case BACT_PAY_BACK_LOAN: + { + s32b req; + char prompt[80]; + + if (p_ptr->loan) + { + msg_format("You have nothing to payback!"); + break; + } + + msg_format("You have a loan of %i.", p_ptr->loan); + + req = ((p_ptr->loan + bcost) > p_ptr->au) ? p_ptr->au - bcost : p_ptr->loan; + + strnfmt(prompt, sizeof (prompt), + "How much would you like to pay back (0-%ld) ?", req); + + req = get_quantity(prompt, req); + + p_ptr->loan -= req; + p_ptr->au -= req; + + if (!p_ptr->loan) p_ptr->loan_time = 0; + + msg_format("You pay back %i gold pieces", req); + paid = TRUE; + break; + } + + case BACT_DROP_ITEM: + { + quest_bounty_drop_item(); + break; + } + + case BACT_GET_ITEM: + { + quest_bounty_get_item(); + break; + } + + case BACT_LIBRARY_QUEST: + { + quest_library_building(&paid, &recreate); + break; + } + + case BACT_FIREPROOF_QUEST: + { + quest_fireproof_building(&paid, &recreate); + break; + } + + case BACT_EREBOR_KEY: + { + msg_print("You will need Thorin's Key and Thrain's Map" + " to get anywhere in Erebor. One may be found" + " in the Barrow-Downs. The other, in Mirkwood."); + break; + } + + default: + { + if (process_hooks_ret(HOOK_BUILDING_ACTION, "dd", "(d)", bact)) + { + paid = process_hooks_return[0].num; + recreate = process_hooks_return[1].num; + } + break; + } + } + + if (paid) + { + p_ptr->au -= bcost; + + /* Display the current gold */ + store_prt_gold(); + } + + return (recreate); +} + + +/* + * Enter quest level + */ +void enter_quest(void) +{ + if (!(cave[p_ptr->py][p_ptr->px].feat == FEAT_QUEST_ENTER)) + { + msg_print("You see no quest level here."); + return; + } + else + { + /* Player enters a new quest */ + p_ptr->oldpy = p_ptr->py; + p_ptr->oldpx = p_ptr->px; + + leaving_quest = p_ptr->inside_quest; + + p_ptr->inside_quest = cave[p_ptr->py][p_ptr->px].special; + dun_level = 1; + p_ptr->leaving = TRUE; + p_ptr->oldpx = p_ptr->px; + p_ptr->oldpy = p_ptr->py; + } +} + + +/* + * Do building commands + */ +void do_cmd_bldg(void) +{ + int i, which, x = p_ptr->px, y = p_ptr->py; + + char command; + + bool_ validcmd; + + store_type *s_ptr; + + store_action_type *ba_ptr; + + + if (cave[p_ptr->py][p_ptr->px].feat != FEAT_SHOP) + { + msg_print("You see no building here."); + return; + } + + which = cave[p_ptr->py][p_ptr->px].special; + building_loc = which; + + s_ptr = &town_info[p_ptr->town_num].store[which]; + + p_ptr->oldpy = p_ptr->py; + p_ptr->oldpx = p_ptr->px; + + /* Forget the lite */ + /* forget_lite(); */ + + /* Forget the view */ + forget_view(); + + /* Hack -- Increase "icky" depth */ + character_icky++; + + command_arg = 0; + command_rep = 0; + command_new = 0; + + show_building(s_ptr); + leave_bldg = FALSE; + + while (!leave_bldg) + { + validcmd = FALSE; + prt("", 1, 0); + command = inkey(); + + if (command == ESCAPE) + { + leave_bldg = TRUE; + p_ptr->inside_arena = FALSE; + break; + } + + for (i = 0; i < 6; i++) + { + ba_ptr = &ba_info[st_info->actions[i]]; + + if (ba_ptr->letter) + { + if (ba_ptr->letter == command) + { + validcmd = TRUE; + break; + } + } + if (ba_ptr->letter_aux) + { + if (ba_ptr->letter_aux == command) + { + validcmd = TRUE; + break; + } + } + } + + if (validcmd) + bldg_process_command(s_ptr, i); + + /* Notice stuff */ + notice_stuff(); + + /* Handle stuff */ + handle_stuff(); + } + + /* Flush messages XXX XXX XXX */ + msg_print(NULL); + + /* Reinit wilderness to activate quests ... */ + wilderness_gen(TRUE); + p_ptr->py = y; + p_ptr->px = x; + + /* Hack -- Decrease "icky" depth */ + character_icky--; + + /* Clear the screen */ + Term_clear(); + + /* Update the visuals */ + p_ptr->update |= (PU_VIEW | PU_MON_LITE | PU_MONSTERS | PU_BONUS); + + /* Redraw entire screen */ + p_ptr->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP); + + /* Window stuff */ + p_ptr->window |= (PW_OVERHEAD); +} + + diff --git a/src/cave.c b/src/cave.c deleted file mode 100644 index 2a02f017..00000000 --- a/src/cave.c +++ /dev/null @@ -1,5022 +0,0 @@ -/* File: cave.c */ - -/* Purpose: low level dungeon routines -BEN- */ - - -#include "angband.h" -#include "q_rand.h" - - -/* - * Support for Adam Bolt's tileset, lighting and transparency effects - * by Robert Ruehlmann (rr9@angband.org) - */ - - -/* - * Approximate Distance between two points. - * - * When either the X or Y component dwarfs the other component, - * this function is almost perfect, and otherwise, it tends to - * over-estimate about one grid per fifteen grids of distance. - * - * Algorithm: hypot(dy,dx) = max(dy,dx) + min(dy,dx) / 2 - */ -int distance(int y1, int x1, int y2, int x2) -{ - int dy, dx, d; - - - /* Find the absolute y/x distance components */ - dy = (y1 > y2) ? (y1 - y2) : (y2 - y1); - dx = (x1 > x2) ? (x1 - x2) : (x2 - x1); - - /* Hack -- approximate the distance */ - d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1)); - - /* Return the distance */ - return (d); -} - - -/* - * Returns TRUE if a grid is considered to be a wall for the purpose - * of magic mapping / clairvoyance - */ -static bool_ is_wall(cave_type *c_ptr) -{ - byte feat; - - - /* Handle feature mimics */ - if (c_ptr->mimic) feat = c_ptr->mimic; - else feat = c_ptr->feat; - - /* Paranoia */ - if (feat >= max_f_idx) return FALSE; - - /* Vanilla floors and doors aren't considered to be walls */ - if (feat < FEAT_SECRET) return FALSE; - - /* Exception #1: a glass wall is a wall but doesn't prevent LOS */ - if (feat == FEAT_GLASS_WALL) return FALSE; - - /* Exception #2: an illusion wall is not a wall but obstructs view */ - if (feat == FEAT_ILLUS_WALL) return TRUE; - - /* Exception #3: a small tree is a floor but obstructs view */ - if (feat == FEAT_SMALL_TREES) return TRUE; - - /* Normal cases: use the WALL flag in f_info.txt */ - return (f_info[feat].flags1 & FF1_WALL) ? TRUE : FALSE; -} - - -/* - * A simple, fast, integer-based line-of-sight algorithm. By Joseph Hall, - * 4116 Brewster Drive, Raleigh NC 27606. Email to jnh@ecemwl.ncsu.edu. - * - * Returns TRUE if a line of sight can be traced from (x1,y1) to (x2,y2). - * - * The LOS begins at the center of the tile (x1,y1) and ends at the center of - * the tile (x2,y2). If los() is to return TRUE, all of the tiles this line - * passes through must be floor tiles, except for (x1,y1) and (x2,y2). - * - * We assume that the "mathematical corner" of a non-floor tile does not - * block line of sight. - * - * Because this function uses (short) ints for all calculations, overflow may - * occur if dx and dy exceed 90. - * - * Once all the degenerate cases are eliminated, the values "qx", "qy", and - * "m" are multiplied by a scale factor "f1 = abs(dx * dy * 2)", so that - * we can use integer arithmetic. - * - * We travel from start to finish along the longer axis, starting at the border - * between the first and second tiles, where the y offset = .5 * slope, taking - * into account the scale factor. See below. - * - * Also note that this function and the "move towards target" code do NOT - * share the same properties. Thus, you can see someone, target them, and - * then fire a bolt at them, but the bolt may hit a wall, not them. However, - * by clever choice of target locations, you can sometimes throw a "curve". - * - * Note that "line of sight" is not "reflexive" in all cases. - * - * Use the "projectable()" routine to test "spell/missile line of sight". - * - * Use the "update_view()" function to determine player line-of-sight. - */ -bool_ los(int y1, int x1, int y2, int x2) -{ - /* Delta */ - int dx, dy; - - /* Absolute */ - int ax, ay; - - /* Signs */ - int sx, sy; - - /* Fractions */ - int qx, qy; - - /* Scanners */ - int tx, ty; - - /* Scale factors */ - int f1, f2; - - /* Slope, or 1/Slope, of LOS */ - int m; - - - /* Extract the offset */ - dy = y2 - y1; - dx = x2 - x1; - - /* Extract the absolute offset */ - ay = ABS(dy); - ax = ABS(dx); - - - /* Handle adjacent (or identical) grids */ - if ((ax < 2) && (ay < 2)) return (TRUE); - - - /* Paranoia -- require "safe" origin */ - /* if (!in_bounds(y1, x1)) return (FALSE); */ - - - /* Directly South/North */ - if (!dx) - { - /* South -- check for walls */ - if (dy > 0) - { - for (ty = y1 + 1; ty < y2; ty++) - { - if (!cave_sight_bold(ty, x1)) return (FALSE); - } - } - - /* North -- check for walls */ - else - { - for (ty = y1 - 1; ty > y2; ty--) - { - if (!cave_sight_bold(ty, x1)) return (FALSE); - } - } - - /* Assume los */ - return (TRUE); - } - - /* Directly East/West */ - if (!dy) - { - /* East -- check for walls */ - if (dx > 0) - { - for (tx = x1 + 1; tx < x2; tx++) - { - if (!cave_sight_bold(y1, tx)) return (FALSE); - } - } - - /* West -- check for walls */ - else - { - for (tx = x1 - 1; tx > x2; tx--) - { - if (!cave_sight_bold(y1, tx)) return (FALSE); - } - } - - /* Assume los */ - return (TRUE); - } - - - /* Extract some signs */ - sx = (dx < 0) ? -1 : 1; - sy = (dy < 0) ? -1 : 1; - - - /* Vertical "knights" */ - if (ax == 1) - { - if (ay == 2) - { - if (cave_sight_bold(y1 + sy, x1)) return (TRUE); - } - } - - /* Horizontal "knights" */ - else if (ay == 1) - { - if (ax == 2) - { - if (cave_sight_bold(y1, x1 + sx)) return (TRUE); - } - } - - - /* Calculate scale factor div 2 */ - f2 = (ax * ay); - - /* Calculate scale factor */ - f1 = f2 << 1; - - - /* Travel horizontally */ - if (ax >= ay) - { - /* Let m = dy / dx * 2 * (dy * dx) = 2 * dy * dy */ - qy = ay * ay; - m = qy << 1; - - tx = x1 + sx; - - /* Consider the special case where slope == 1. */ - if (qy == f2) - { - ty = y1 + sy; - qy -= f1; - } - else - { - ty = y1; - } - - /* Note (below) the case (qy == f2), where */ - /* the LOS exactly meets the corner of a tile. */ - while (x2 - tx) - { - if (!cave_sight_bold(ty, tx)) return (FALSE); - - qy += m; - - if (qy < f2) - { - tx += sx; - } - else if (qy > f2) - { - ty += sy; - if (!cave_sight_bold(ty, tx)) return (FALSE); - qy -= f1; - tx += sx; - } - else - { - ty += sy; - qy -= f1; - tx += sx; - } - } - } - - /* Travel vertically */ - else - { - /* Let m = dx / dy * 2 * (dx * dy) = 2 * dx * dx */ - qx = ax * ax; - m = qx << 1; - - ty = y1 + sy; - - if (qx == f2) - { - tx = x1 + sx; - qx -= f1; - } - else - { - tx = x1; - } - - /* Note (below) the case (qx == f2), where */ - /* the LOS exactly meets the corner of a tile. */ - while (y2 - ty) - { - if (!cave_sight_bold(ty, tx)) return (FALSE); - - qx += m; - - if (qx < f2) - { - ty += sy; - } - else if (qx > f2) - { - tx += sx; - if (!cave_sight_bold(ty, tx)) return (FALSE); - qx -= f1; - ty += sy; - } - else - { - tx += sx; - qx -= f1; - ty += sy; - } - } - } - - /* Assume los */ - return (TRUE); -} - - - -/* - * Returns true if the player's grid is dark - */ -bool_ no_lite(void) -{ - return (!player_can_see_bold(p_ptr->py, p_ptr->px)); -} - - - -/* - * Determine if a given location may be "destroyed" - * - * Used by destruction spells, and for placing stairs, etc. - */ -bool_ cave_valid_bold(int y, int x) -{ - cave_type *c_ptr = &cave[y][x]; - - s16b this_o_idx, next_o_idx = 0; - - - /* Forbid perma-grids */ - if (cave_perma_grid(c_ptr)) return (FALSE); - - /* Check objects */ - for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) - { - object_type * o_ptr; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Forbid artifact grids */ - if ((o_ptr->art_name) || artifact_p(o_ptr)) return (FALSE); - } - - /* Accept */ - return (TRUE); -} - - - - -/* - * Hack -- Legal monster codes - */ -static cptr image_monster_hack = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -/* - * Hack -- Legal monster codes for IBM pseudo-graphics - * - * Dropped. Although this option has long left unmaintained, hardcoding - * code points makes it impossible to update the font and prf files - * flexibly. And the normal graphics code still works with it -- pelpel - */ - -/* - * Mega-Hack -- Hallucinatory monster - */ -static void image_monster(byte *ap, char *cp) -{ - int n; - - switch (graphics_mode) - { - /* Text mode */ - case GRAPHICS_NONE: - { - n = strlen(image_monster_hack); - - /* Random symbol from set above */ - *cp = (image_monster_hack[rand_int(n)]); - - /* Random color */ - *ap = randint(15); - - break; - } - - /* Normal graphics */ - default: - { - /* Avoid player ghost */ - n = randint(max_r_idx); - - *cp = r_info[n].x_char; - - *ap = r_info[n].x_attr; - - break; - } - } -} - - - - -/* - * Hack -- Legal object codes - */ -static cptr image_object_hack = "?/|\\\"!$()_-=[]{},~"; - -/* - * Hardcoded IBM pseudo-graphics code points have been removed - * for the same reason as stated above -- pelpel - */ - -/* - * Mega-Hack -- Hallucinatory object - */ -static void image_object(byte *ap, char *cp) -{ - int n; - - switch (graphics_mode) - { - /* Text mode */ - case GRAPHICS_NONE: - { - n = strlen(image_object_hack); - - /* Random symbol from set above */ - *cp = (image_object_hack[rand_int(n)]); - - /* Random color */ - *ap = randint(15); - - /* Done */ - break; - } - - /* Normal graphics */ - default: - { - n = randint(max_k_idx - 1); - - *cp = k_info[n].x_char; - *ap = k_info[n].x_attr; - - break; - } - } -} - - -/* - * Hack -- Random hallucination - */ -static void image_random(byte *ap, char *cp) -{ - /* Normally, assume monsters */ - if (rand_int(100) < 75) - { - image_monster(ap, cp); - } - - /* Otherwise, assume objects */ - else - { - image_object(ap, cp); - } -} - - -/* - * The 16x16 tile of the terrain supports lighting - */ -static bool_ feat_supports_lighting(byte feat) -{ - return (f_info[feat].flags1 & FF1_SUPPORT_LIGHT) != 0; -} - - -char get_shimmer_color() -{ - switch (randint(7)) - { - case 1: - return (TERM_RED); - case 2: - return (TERM_L_RED); - case 3: - return (TERM_WHITE); - case 4: - return (TERM_L_GREEN); - case 5: - return (TERM_BLUE); - case 6: - return (TERM_L_DARK); - case 7: - return (TERM_GREEN); - } - - return (TERM_VIOLET); -} - - -/* - * Table of breath colors. Must match listings in a single set of - * monster spell flags. - * - * The value "255" is special. Monsters with that kind of breath - * may be any color. - */ -static byte breath_to_attr[32][2] = -{ - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { TERM_SLATE, TERM_L_DARK }, /* RF4_BRTH_ACID */ - { TERM_BLUE, TERM_L_BLUE }, /* RF4_BRTH_ELEC */ - { TERM_RED, TERM_L_RED }, /* RF4_BRTH_FIRE */ - { TERM_WHITE, TERM_L_WHITE }, /* RF4_BRTH_COLD */ - { TERM_GREEN, TERM_L_GREEN }, /* RF4_BRTH_POIS */ - { TERM_L_GREEN, TERM_GREEN }, /* RF4_BRTH_NETHR */ - { TERM_YELLOW, TERM_ORANGE }, /* RF4_BRTH_LITE */ - { TERM_L_DARK, TERM_SLATE }, /* RF4_BRTH_DARK */ - { TERM_L_UMBER, TERM_UMBER }, /* RF4_BRTH_CONFU */ - { TERM_YELLOW, TERM_L_UMBER }, /* RF4_BRTH_SOUND */ - { 255, 255 }, /* (any color) */ /* RF4_BRTH_CHAOS */ - { TERM_VIOLET, TERM_VIOLET }, /* RF4_BRTH_DISEN */ - { TERM_L_RED, TERM_VIOLET }, /* RF4_BRTH_NEXUS */ - { TERM_L_BLUE, TERM_L_BLUE }, /* RF4_BRTH_TIME */ - { TERM_L_WHITE, TERM_SLATE }, /* RF4_BRTH_INER */ - { TERM_L_WHITE, TERM_SLATE }, /* RF4_BRTH_GRAV */ - { TERM_UMBER, TERM_L_UMBER }, /* RF4_BRTH_SHARD */ - { TERM_ORANGE, TERM_RED }, /* RF4_BRTH_PLAS */ - { TERM_UMBER, TERM_L_UMBER }, /* RF4_BRTH_FORCE */ - { TERM_L_BLUE, TERM_WHITE }, /* RF4_BRTH_MANA */ - { 0, 0 }, /* */ - { TERM_GREEN, TERM_L_GREEN }, /* RF4_BRTH_NUKE */ - { 0, 0 }, /* */ - { TERM_WHITE, TERM_L_RED }, /* RF4_BRTH_DISINT */ -}; - - -/* - * Multi-hued monsters shimmer acording to their breaths. - * - * If a monster has only one kind of breath, it uses both colors - * associated with that breath. Otherwise, it just uses the first - * color for any of its breaths. - * - * If a monster does not breath anything, it can be any color. - */ -static byte multi_hued_attr(monster_race *r_ptr) -{ - byte allowed_attrs[15]; - - int i, j; - - int stored_colors = 0; - - int breaths = 0; - - int first_color = 0; - - int second_color = 0; - - - /* Monsters with no ranged attacks can be any color */ - if (!r_ptr->freq_inate) return (get_shimmer_color()); - - /* Check breaths */ - for (i = 0; i < 32; i++) - { - bool_ stored = FALSE; - - /* Don't have that breath */ - if (!(r_ptr->flags4 & (1L << i))) continue; - - /* Get the first color of this breath */ - first_color = breath_to_attr[i][0]; - - /* Breath has no color associated with it */ - if (first_color == 0) continue; - - /* Monster can be of any color */ - if (first_color == 255) return (randint(15)); - - - /* Increment the number of breaths */ - breaths++; - - /* Monsters with lots of breaths may be any color. */ - if (breaths == 6) return (randint(15)); - - - /* Always store the first color */ - for (j = 0; j < stored_colors; j++) - { - /* Already stored */ - if (allowed_attrs[j] == first_color) stored = TRUE; - } - if (!stored) - { - allowed_attrs[stored_colors] = first_color; - stored_colors++; - } - - /* - * Remember (but do not immediately store) the second color - * of the first breath. - */ - if (breaths == 1) - { - second_color = breath_to_attr[i][1]; - } - } - - /* Monsters with no breaths may be of any color. */ - if (breaths == 0) return (get_shimmer_color()); - - /* If monster has one breath, store the second color too. */ - if (breaths == 1) - { - allowed_attrs[stored_colors] = second_color; - stored_colors++; - } - - /* Pick a color at random */ - return (allowed_attrs[rand_int(stored_colors)]); -} - - -/* - * Extract the attr/char to display at the given (legal) map location - * - * Note that this function, since it is called by "lite_spot()" which - * is called by "update_view()", is a major efficiency concern. - * - * Basically, we examine each "layer" of the world (terrain, objects, - * monsters/players), from the bottom up, extracting a new attr/char - * if necessary at each layer, and defaulting to "darkness". This is - * not the fastest method, but it is very simple, and it is about as - * fast as it could be for grids which contain no "marked" objects or - * "visible" monsters. - * - * We apply the effects of hallucination during each layer. Objects will - * always appear as random "objects", monsters will always appear as random - * "monsters", and normal grids occasionally appear as random "monsters" or - * "objects", but note that these random "monsters" and "objects" are really - * just "colored ascii symbols" (which may look silly on some machines). - * - * The hallucination functions avoid taking any pointers to local variables - * because some compilers refuse to use registers for any local variables - * whose address is taken anywhere in the function. - * - * As an optimization, we can handle the "player" grid as a special case. - * - * Note that the memorization of "objects" and "monsters" is not related - * to the memorization of "terrain". This allows the player to memorize - * the terrain of a grid without memorizing any objects in that grid, and - * to detect monsters without detecting anything about the terrain of the - * grid containing the monster. - * - * The fact that all interesting "objects" and "terrain features" are - * memorized as soon as they become visible for the first time means - * that we only have to check the "CAVE_SEEN" flag for "boring" grids. - * - * Note that bizarre things must be done when the "attr" and/or "char" - * codes have the "high-bit" set, since these values are used to encode - * various "special" pictures in some versions, and certain situations, - * such as "multi-hued" or "clear" monsters, cause the attr/char codes - * to be "scrambled" in various ways. - * - * Note that the "zero" entry in the feature/object/monster arrays are - * used to provide "special" attr/char codes, with "monster zero" being - * used for the player attr/char, "object zero" being used for the "stack" - * attr/char, and "feature zero" being used for the "nothing" attr/char. - * - * Note that eventually we may want to use the "&" symbol for embedded - * treasure, and use the "*" symbol to indicate multiple objects, but - * currently, we simply use the attr/char of the first "marked" object - * in the stack, if any, and so "object zero" is unused. XXX XXX XXX - * - * Note the assumption that doing "x_ptr = &x_info[x]" plus a few of - * "x_ptr->xxx", is quicker than "x_info[x].xxx", even if "x" is a fixed - * constant. If this is incorrect then a lot of code should be changed. - * - * - * Some comments on the "terrain" layer... - * - * Note that "boring" grids (floors, invisible traps, and any illegal grids) - * are very different from "interesting" grids (all other terrain features), - * and the two types of grids are handled completely separately. The most - * important distinction is that "boring" grids may or may not be memorized - * when they are first encountered, and so we must use the "CAVE_SEEN" flag - * to see if they are "see-able". - * - * - * Some comments on the "terrain" layer (boring grids)... - * - * Note that "boring" grids are always drawn using the picture for "empty - * floors", which is stored in "f_info[FEAT_FLOOR]". Sometimes, special - * lighting effects may cause this picture to be modified. - * - * Note that "invisible traps" are always displayes exactly like "empty - * floors", which prevents various forms of "cheating", with no loss of - * efficiency. There are still a few ways to "guess" where traps may be - * located, for example, objects will never fall into a grid containing - * an invisible trap. XXX XXX - * - * To determine if a "boring" grid should be displayed, we simply check to - * see if it is either memorized ("CAVE_MARK"), or currently "see-able" by - * the player ("CAVE_SEEN"). Note that "CAVE_SEEN" is now maintained by the - * "update_view()" function. - * - * Note the "special lighting effects" which can be activated for "boring" - * grids using the "view_special_lite" option, causing certain such grids - * to be displayed using special colors. If the grid is "see-able" by - * the player, we will use the normal (except that, if the "view_yellow_lite" - * option is set, and the grid is *only* "see-able" because of the player's - * torch, then we will use "yellow"), else if the player is "blind", we will - * use greyscale, else if the grid is not "illuminated", we will use "dark - * gray", if the "view_bright_lite" option is set, we will use "darker" colour - * else we will use the normal colour. - * - * - * Some comments on the "terrain" layer (non-boring grids)... - * - * Note the use of the "mimic" field in the "terrain feature" processing, - * which allows any feature to "pretend" to be another feature. This is - * used to "hide" secret doors, and to make all "doors" appear the same, - * and all "walls" appear the same, and "hidden" treasure stay hidden. - * Note that it is possible to use this field to make a feature "look" - * like a floor, but the "view_special_lite" flag only affects actual - * "boring" grids. - * - * Since "interesting" grids are always memorized as soon as they become - * "see-able" by the player ("CAVE_SEEN"), such a grid only needs to be - * displayed if it is memorized ("CAVE_MARK"). Most "interesting" grids - * are in fact non-memorized, non-see-able, wall grids, so the fact that - * we do not have to check the "CAVE_SEEN" flag adds some efficiency, at - * the cost of *forcing* the memorization of all "interesting" grids when - * they are first seen. Since the "CAVE_SEEN" flag is now maintained by - * the "update_view()" function, this efficiency is not as significant as - * it was in previous versions, and could perhaps be removed. - * (so I removed this to simplify the terrain feature handling -- pelpel) - * - * Note the "special lighting effects" which can be activated for "wall" - * grids using the "view_granite_lite" option, causing certain such grids - * to be displayed using special colors. - * If the grid is "see-able" by the player, we will use the normal colour - * else if the player is "blind", we will use grey scale, else if the - * "view_bright_lite" option is set, we will use reduced colour, else we - * will use the normal one. - * - * Note that "wall" grids are more complicated than "boring" grids, due to - * the fact that "CAVE_GLOW" for a "wall" grid means that the grid *might* - * be glowing, depending on where the player is standing in relation to the - * wall. In particular, the wall of an illuminated room should look just - * like any other (dark) wall unless the player is actually inside the room. - * - * Thus, we do not support as many visual special effects for "wall" grids - * as we do for "boring" grids, since many of them would give the player - * information about the "CAVE_GLOW" flag of the wall grid, in particular, - * it would allow the player to notice the walls of illuminated rooms from - * a dark hallway that happened to run beside the room. - * - * - * Some comments on the "object" layer... - * - * Currently, we do nothing with multi-hued objects, because there are - * not any. If there were, they would have to set "shimmer_objects" - * when they were created, and then new "shimmer" code in "dungeon.c" - * would have to be created handle the "shimmer" effect, and the code - * in "cave.c" would have to be updated to create the shimmer effect. - * This did not seem worth the effort. XXX XXX - * - * - * Some comments on the "monster"/"player" layer... - * - * Note that monsters can have some "special" flags, including "ATTR_MULTI", - * which means their color changes, and "ATTR_CLEAR", which means they take - * the color of whatever is under them, and "CHAR_CLEAR", which means that - * they take the symbol of whatever is under them. Technically, the flag - * "CHAR_MULTI" is supposed to indicate that a monster looks strange when - * examined, but this flag is currently ignored. All of these flags are - * ignored if the "avoid_other" option is set, since checking for these - * conditions is expensive (and annoying) on some systems. - * - * Normally, players could be handled just like monsters, except that the - * concept of the "torch lite" of others player would add complications. - * For efficiency, however, we handle the (only) player first, since the - * "player" symbol always "pre-empts" any other facts about the grid. - * - * The "hidden_player" efficiency option, which only makes sense with a - * single player, allows the player symbol to be hidden while running. - */ - -/* - * Alternative colours for unseen grids - * - * Reduced colours - remembered interesting grids and perma-lit floors - * B&W - currently only used by blindness effect - */ - -/* Colour */ -static byte dark_attrs[16] = -{ - TERM_DARK, TERM_L_WHITE, TERM_L_DARK, TERM_ORANGE, - TERM_RED, TERM_GREEN, TERM_BLUE, TERM_UMBER, - TERM_L_DARK, TERM_SLATE, TERM_VIOLET, TERM_YELLOW, - TERM_RED, TERM_GREEN, TERM_BLUE, TERM_UMBER -}; - -/* B&W */ -static byte darker_attrs[16] = -{ - TERM_DARK, TERM_L_WHITE, TERM_L_DARK, TERM_SLATE, - TERM_L_DARK, TERM_L_DARK, TERM_L_DARK, TERM_L_DARK, - TERM_L_DARK, TERM_SLATE, TERM_L_DARK, TERM_SLATE, - TERM_SLATE, TERM_SLATE, TERM_SLATE, TERM_SLATE -}; - - -void map_info(int y, int x, byte *ap, char *cp, byte *tap, char *tcp, - byte *eap, char *ecp) -{ - cave_type *c_ptr; - - feature_type *f_ptr; - - s16b this_o_idx, next_o_idx = 0; - - u16b info; - - s16b t_idx; - - byte feat; - - byte a; - - byte c; - - /* - * This means that a port supports graphics overlay as well as lighting - * effects. See the step 3 below for the detailed information about - * lighting. Basically, it requires "darker" tiles for those terrain - * features with SUPPORT_LIGHT flag set, and they must be arranged - * this way: - * col col+1 col+2 - * row base darker brighter - */ - bool_ graf_new = ((graphics_mode == GRAPHICS_ISO) || - (graphics_mode == GRAPHICS_NEW)); - - /* - * I never understand why some coders like shimmering so much. - * It just serves to hurt my eyes, IMHO. If one feels like to show off, - * go for better graphics support... Anyway this means a port allows - * changing attr independently from its char -- pelpel - */ - bool_ attr_mutable = (!use_graphics || - (graphics_mode == GRAPHICS_IBM)); - - - /**** Preparation ****/ - - /* Access the grid */ - c_ptr = &cave[y][x]; - - - /* Cache some frequently used values */ - - /* Grid info */ - info = c_ptr->info; - - /* Feature code */ - feat = c_ptr->feat; - - /* Apply "mimic" field */ - if (c_ptr->mimic) - { - feat = c_ptr->mimic; - } - else - { - feat = f_info[feat].mimic; - } - - /* Access floor */ - f_ptr = &f_info[feat]; - - - /* Reset attr/char */ - *eap = 0; - *ecp = 0; - - - /**** Layer 1 -- Terrain feature ****/ - - /* Only memorised or visible grids are displayed */ - if (info & (CAVE_MARK | CAVE_SEEN)) - { - /**** Step 1 -- Retrieve base attr/char ****/ - - /* 'Sane' terrain features */ - if (feat != FEAT_SHOP) - { - /* Normal char */ - c = f_ptr->x_char; - - /* Normal attr */ - a = f_ptr->x_attr; - } - - /* Mega-Hack 1 -- Building don't conform to f_info */ - else - { - c = st_info[c_ptr->special].x_char; - a = st_info[c_ptr->special].x_attr; - } - - /* Mega-Hack 2 -- stair to dungeon branch are purple */ - if (c_ptr->special && attr_mutable && - ((feat == FEAT_MORE) || (feat == FEAT_LESS))) - { - a = TERM_VIOLET; - } - - /* Mega-Hack 3 -- Traps don't have f_info entries either */ - if ((info & (CAVE_TRDT)) && (feat != FEAT_ILLUS_WALL)) - { - /* Trap index */ - t_idx = c_ptr->t_idx; - - if (use_graphics && - (t_info[t_idx].g_attr != 0) && - (t_info[t_idx].g_char != 0)) - { - - if (graf_new) - { - *eap = t_info[t_idx].g_attr; - *ecp = t_info[t_idx].g_char; - } - else - { - a = t_info[t_idx].g_attr; - c = t_info[t_idx].g_char; - } - - } - else - { - /* - * If trap is set on a floor grid that is not - * one of "interesting" features, use a special - * symbol to display it. Check for doors is no longer - * necessary because they have REMEMBER flag now. - * - * Cave macros cannot be used safely here, because of - * c_ptr->mimic XXX XXX - */ - if (!attr_mutable) - { - a = f_info[FEAT_TRAP].x_attr; - c = f_info[FEAT_TRAP].x_char; - } - else - { - if ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR) - { - c = f_info[FEAT_TRAP].x_char; - } - - /* Add attr XXX XXX XXX */ - a = t_info[t_idx].color; - - /* Get a new color with a strange formula :) XXX XXX XXX */ - if (t_info[t_idx].flags & FTRAP_CHANGE) - { - s32b tmp; - - tmp = dun_level + dungeon_type + feat; - - a = tmp % 16; - } - } - } - } - - - /**** Step 2 -- Apply special random effects ****/ - if (!avoid_other && !avoid_shimmer && attr_mutable) - { - /* Special terrain effect */ - if (c_ptr->effect) - { - a = spell_color(effects[c_ptr->effect].type); - } - - /* Multi-hued attr */ - else if (f_ptr->flags1 & FF1_ATTR_MULTI) - { - a = f_ptr->shimmer[rand_int(7)]; - } - } - - - /* - * Step 3 - * - * Special lighting effects, if specified and applicable - * This will never happen for - * - any grids in the overhead map - * - traps - * - (graphics modes) terrain features without corresponding - * "darker" tiles. - * - * Note the use of f_ptr->flags1 to avoid problems with - * c_ptr->mimic. - */ - - /* view_special_lite: lighting effects for boring features */ - if (view_special_lite && - ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR)) - { - if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)) && - (attr_mutable || (graf_new && feat_supports_lighting(feat)))) - { - /* Handle "seen" grids */ - if (info & (CAVE_SEEN)) - { - /* Only lit by "torch" light */ - if (view_yellow_lite && !(info & (CAVE_GLOW))) - { - if (graf_new) - { - /* Use a brightly lit tile */ - c += 2; - } - else - { - /* Use "yellow" */ - a = TERM_YELLOW; - } - } - } - - /* Handle "blind" */ - else if (p_ptr->blind) - { - if (graf_new) - { - /* Use a dark tile */ - c++; - } - else - { - /* Use darker colour */ - a = darker_attrs[a & 0xF]; - } - } - - /* Handle "dark" grids */ - else if (!(info & (CAVE_GLOW))) - { - if (graf_new) - { - /* Use a dark tile */ - c++; - } - else - { - /* Use darkest colour */ - a = TERM_L_DARK; - } - } - - /* "Out-of-sight" glowing grids -- handle "view_bright_lite" */ - else if (view_bright_lite) - { - if (graf_new) - { - /* Use a dark tile */ - c++; - } - else - { - /* Use darker colour */ - a = dark_attrs[a & 0xF]; - } - } - } - } - - /* view_granite_lite: lighting effects for walls and doors */ - else if (view_granite_lite && - (f_ptr->flags1 & (FF1_NO_VISION | FF1_DOOR))) - { - if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)) && - (attr_mutable || (graf_new && feat_supports_lighting(feat)))) - { - /* Handle "seen" grids */ - if (info & (CAVE_SEEN)) - { - /* Do nothing */ - } - - /* Handle "blind" */ - else if (p_ptr->blind) - { - if (graf_new) - { - /* Use a dark tile */ - c++; - } - else - { - /* Use darker colour */ - a = darker_attrs[a & 0xF]; - } - } - - /* Handle "view_bright_lite" */ - else if (view_bright_lite) - { - if (graf_new) - { - /* Use a dark tile */ - c++; - } - else - { - /* Use darker colour */ - a = dark_attrs[a & 0xF]; - } - } - - else - { - if (graf_new) - { - /* Use a brightly lit tile */ - c += 2; - } - else - { - /* Use normal colour */ - } - } - } - } - } - - /* Unknown grids */ - else - { - /* Access darkness */ - f_ptr = &f_info[FEAT_NONE]; - - /* Normal attr */ - a = f_ptr->x_attr; - - /* Normal char */ - c = f_ptr->x_char; - } - - /* - * Hack -- rare random hallucination - * Because we cannot be sure which is outer dungeon walls, - * the check for 'feat' has been removed - */ - if (p_ptr->image && (rand_int(256) == 0)) - { - /* Hallucinate */ - image_random(ap, cp); - } - - /* Save the terrain info for the transparency effects */ - *tap = a; - *tcp = c; - - /* Save the info */ - *ap = a; - *cp = c; - - - /**** Layer 2 -- Objects ****/ - - if (feat != FEAT_MON_TRAP) - { - for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) - { - object_type * o_ptr; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Memorized objects */ - if (o_ptr->marked) - { - /* Normal char */ - *cp = object_char(o_ptr); - - /* Normal attr */ - *ap = object_attr(o_ptr); - - /* Multi-hued attr */ - if (!avoid_other && attr_mutable && - (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI)) - { - *ap = get_shimmer_color(); - } - - /* Hack -- hallucination */ - if (p_ptr->image) image_object(ap, cp); - - /* Done */ - break; - } - } - } - - - /**** Layer 3 -- Handle monsters ****/ - - if (c_ptr->m_idx) - { - monster_type *m_ptr = &m_list[c_ptr->m_idx]; - monster_race *r_ptr = race_inf(m_ptr); - - if (r_ptr->flags9 & RF9_MIMIC) - { - object_type *o_ptr; - - /* Acquire object */ - o_ptr = &o_list[m_ptr->hold_o_idx]; - - /* Memorized objects */ - if (o_ptr->marked) - { - /* Normal char */ - *cp = object_char(o_ptr); - - /* Normal attr */ - *ap = object_attr(o_ptr); - - /* Multi-hued attr */ - if (!avoid_other && attr_mutable && - (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI)) - { - *ap = get_shimmer_color(); - } - - /* Hack -- hallucination */ - if (p_ptr->image) image_object(ap, cp); - } - } - else - { - /* Visible monster */ - if (m_ptr->ml) - { - monster_race *r_ptr = race_inf(m_ptr); - - /* Reset attr/char */ - *eap = 0; - *ecp = 0; - - if (use_graphics) - { - - if (graf_new) - { - monster_ego *re_ptr = &re_info[m_ptr->ego]; - - /* Desired attr */ - *eap = re_ptr->g_attr; - - /* Desired char */ - *ecp = re_ptr->g_char; - } - - /* Use base monster */ - r_ptr = &r_info[m_ptr->r_idx]; - } - - /* Desired attr/char */ - c = r_ptr->x_char; - a = r_ptr->x_attr; - - /* Ignore weird codes */ - if (avoid_other) - { - /* Use char */ - *cp = c; - - /* Use attr */ - *ap = a; - } - - /* Special attr/char codes */ - else if (!attr_mutable) - { - /* Use char */ - *cp = c; - - /* Use attr */ - *ap = a; - } - - /* Multi-hued monster */ - else if (r_ptr->flags1 & (RF1_ATTR_MULTI)) - { - /* Is it a shapechanger? */ - if (r_ptr->flags2 & (RF2_SHAPECHANGER)) - { - image_random(ap, cp); - } - else - *cp = c; - - /* Multi-hued attr */ - if (r_ptr->flags2 & (RF2_ATTR_ANY)) - { - *ap = randint(15); - } - else - { - *ap = multi_hued_attr(r_ptr); - } - } - - /* Normal monster (not "clear" in any way) */ - else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR | RF1_CHAR_CLEAR))) - { - /* Use char */ - *cp = c; - - /* Use attr */ - *ap = a; - } - - /* - * Hack -- Bizarre grid under monster - * WAS: else if (*ap & 0x80) || (*cp & 0x80) -- pelpel - */ - else if (*ap & 0x80) - { - /* Use char */ - *cp = c; - - /* Use attr */ - *ap = a; - } - - /* Normal */ - else - { - /* Normal (non-clear char) monster */ - if (!(r_ptr->flags1 & (RF1_CHAR_CLEAR))) - { - /* Normal char */ - *cp = c; - } - - /* Normal (non-clear attr) monster */ - else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR))) - { - /* Normal attr */ - *ap = a; - } - } - - /* Hack -- hallucination */ - if (p_ptr->image) - { - /* Hallucinatory monster */ - image_monster(ap, cp); - } - } - } - } - - /* Handle "player" */ - if ((y == p_ptr->py) && (x == p_ptr->px) && - (!p_ptr->invis || p_ptr->see_inv)) - { - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - /* Reset attr/char */ - *eap = 0; - *ecp = 0; - - /* Get the "player" attr */ - if (!avoid_other && attr_mutable && (r_ptr->flags1 & RF1_ATTR_MULTI)) - { - a = get_shimmer_color(); - } - else - { - a = r_ptr->x_attr; - } - - /* Get the "player" char */ - c = r_ptr->x_char; - - - /* Mega-Hack -- Apply modifications to player graphics XXX XXX XXX */ - switch (graphics_mode) - { - case GRAPHICS_NONE: - case GRAPHICS_IBM: - { - if (player_char_health) - { - int percent = p_ptr->chp * 10 / p_ptr->mhp; - - if (percent < 7) - { - c = I2D(percent); - if (percent < 3) a = TERM_L_RED; - } - } - - break; - } - - case GRAPHICS_OLD: - { - if (player_symbols) - { - a = BMP_FIRST_PC_CLASS + p_ptr->pclass; - c = BMP_FIRST_PC_RACE + p_ptr->prace; - } - - break; - } - - case GRAPHICS_ISO: - case GRAPHICS_NEW: - { - if (p_ptr->pracem) - { - player_race_mod *rmp_ptr = &race_mod_info[p_ptr->pracem]; - - /* Desired attr */ - *eap = rmp_ptr->g_attr; - - /* Desired char */ - *ecp = rmp_ptr->g_char; - } - - /* +AKH 20020421 - Health dispay for graphics, too */ - if (player_char_health && (graphics_mode == GRAPHICS_NEW)) - { - int percent = p_ptr->chp * 14 / p_ptr->mhp; - - if (percent < 10) - { - *eap = 10; - *ecp = 32 + 14 - percent; - } - } - - break; - } - - } - - /* Save the info */ - *ap = a; - *cp = c; - - } -} - - -/* - * Special version of map_info, for use by cmovie and HTML converter - * to obtain pure-ASCII image of dungeon map - */ -void map_info_default(int y, int x, byte *ap, char *cp) -{ - cave_type *c_ptr; - - feature_type *f_ptr; - - s16b this_o_idx, next_o_idx = 0; - - u16b info; - - s16b t_idx; - - byte feat; - - byte a; - - byte c; - - bool_ use_graphics_hack = use_graphics; - byte graphics_mode_hack = graphics_mode; - - - /* Temporarily disable graphics mode -- for some random effects XXX */ - use_graphics = FALSE; - graphics_mode = GRAPHICS_NONE; - - /**** Preparation ****/ - - /* Access the grid */ - c_ptr = &cave[y][x]; - - - /* Cache some frequently used values */ - - /* Grid info */ - info = c_ptr->info; - - /* Feature code */ - feat = c_ptr->feat; - - /* Apply "mimic" field */ - if (c_ptr->mimic) - { - feat = c_ptr->mimic; - } - else - { - feat = f_info[feat].mimic; - } - - /* Access floor */ - f_ptr = &f_info[feat]; - - - /**** Layer 1 -- Terrain feature ****/ - - /* Only memorised or visible grids are displayed */ - if (info & (CAVE_MARK | CAVE_SEEN)) - { - /**** Step 1 -- Retrieve base attr/char ****/ - - /* 'Sane' terrain features */ - if (feat != FEAT_SHOP) - { - /* Default char */ - c = f_ptr->d_char; - - /* Default attr */ - a = f_ptr->d_attr; - } - - /* Mega-Hack 1 -- Building don't conform to f_info */ - else - { - c = st_info[c_ptr->special].d_char; - a = st_info[c_ptr->special].d_attr; - } - - /* Mega-Hack 2 -- stair to dungeon branch are purple */ - if (c_ptr->special && - ((feat == FEAT_MORE) || (feat == FEAT_LESS))) - { - a = TERM_VIOLET; - } - - /* Mega-Hack 3 -- Traps don't have f_info entries either */ - if ((info & (CAVE_TRDT)) && (feat != FEAT_ILLUS_WALL)) - { - /* Trap index */ - t_idx = c_ptr->t_idx; - - /* - * If trap is set on a floor grid that is not - * one of "interesting" features, use a special - * symbol to display it. Check for doors is no longer - * necessary because they have REMEMBER flag now. - * - * Cave macros cannot be used safely here, because of - * c_ptr->mimic XXX XXX - */ - if ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR) - { - c = f_info[FEAT_TRAP].d_char; - } - - /* Add attr */ - a = t_info[t_idx].color; - - /* Get a new color with a strange formula :) */ - if (t_info[t_idx].flags & FTRAP_CHANGE) - { - s32b tmp; - - tmp = dun_level + dungeon_type + feat; - - a = tmp % 16; - } - } - - - /**** Step 2 -- Apply special random effects ****/ - if (!avoid_other) - { - /* Special terrain effect */ - if (c_ptr->effect) - { - a = spell_color(effects[c_ptr->effect].type); - } - - /* Multi-hued attr */ - else if (f_ptr->flags1 & FF1_ATTR_MULTI) - { - a = f_ptr->shimmer[rand_int(7)]; - } - } - - - /* - * Step 3 - * - * Special lighting effects, if specified and applicable - * This will never happen for - * - any grids in the overhead map - * - traps - * - (graphics modes) terrain features without corresponding - * "darker" tiles. - * - * All the if's here are flag checks, so changed order shouldn't - * affect performance a lot, I hope... - */ - - /* view_special_lite: lighting effects for boring features */ - if (view_special_lite && - ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR)) - { - if (!p_ptr->wild_mode && !(info & (CAVE_TRDT))) - { - /* Handle "seen" grids */ - if (info & (CAVE_SEEN)) - { - /* Only lit by "torch" light */ - if (view_yellow_lite && !(info & (CAVE_GLOW))) - { - /* Use "yellow" */ - a = TERM_YELLOW; - } - } - - /* Handle "blind" */ - else if (p_ptr->blind) - { - /* Use darker colour */ - a = darker_attrs[a & 0xF]; - } - - /* Handle "dark" grids */ - else if (!(info & (CAVE_GLOW))) - { - /* Use darkest colour */ - a = TERM_L_DARK; - } - - /* "Out-of-sight" glowing grids -- handle "view_bright_lite" */ - else if (view_bright_lite) - { - /* Use darker colour */ - a = dark_attrs[a & 0xF]; - } - } - } - - /* view_granite_lite: lighting effects for walls and doors */ - else if (view_granite_lite && - (f_ptr->flags1 & (FF1_NO_VISION | FF1_DOOR))) - { - if (!p_ptr->wild_mode && !(info & (CAVE_TRDT))) - { - /* Handle "seen" grids */ - if (info & (CAVE_SEEN)) - { - /* Do nothing */ - } - - /* Handle "blind" */ - else if (p_ptr->blind) - { - /* Use darker colour */ - a = darker_attrs[a & 0xF]; - } - - /* Handle "view_bright_lite" */ - else if (view_bright_lite) - { - /* Use darker colour */ - a = dark_attrs[a & 0xF]; - } - } - } - } - - /* Unknown grids */ - else - { - /* Access darkness */ - f_ptr = &f_info[FEAT_NONE]; - - /* Default attr */ - a = f_ptr->d_attr; - - /* Default char */ - c = f_ptr->d_char; - } - - /* - * Hack -- rare random hallucination - * Because we cannot be sure which is outer dungeon walls, - * the check for 'feat' has been removed - */ - if (p_ptr->image && (rand_int(256) == 0)) - { - /* Hallucinate */ - image_random(ap, cp); - } - - /* Save the info */ - *ap = a; - *cp = c; - - - /**** Layer 2 -- Objects ****/ - - if (feat != FEAT_MON_TRAP) - { - for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) - { - object_type * o_ptr; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Memorized objects */ - if (o_ptr->marked) - { - /* Normal char */ - *cp = object_char_default(o_ptr); - - /* Normal attr */ - *ap = object_attr_default(o_ptr); - - /* Multi-hued attr */ - if (!avoid_other && - (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI)) - { - *ap = get_shimmer_color(); - } - - /* Hack -- hallucination */ - if (p_ptr->image) image_object(ap, cp); - - /* Done */ - break; - } - } - } - - - /**** Layer 3 -- Handle monsters ****/ - - if (c_ptr->m_idx) - { - monster_type *m_ptr = &m_list[c_ptr->m_idx]; - monster_race *r_ptr = race_inf(m_ptr); - - if (r_ptr->flags9 & RF9_MIMIC) - { - object_type *o_ptr; - - /* Acquire object */ - o_ptr = &o_list[m_ptr->hold_o_idx]; - - /* Memorized objects */ - if (o_ptr->marked) - { - /* Normal char */ - *cp = object_char_default(o_ptr); - - /* Normal attr */ - *ap = object_attr_default(o_ptr); - - /* Multi-hued attr */ - if (!avoid_other && !use_graphics && - (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI)) - { - *ap = get_shimmer_color(); - } - - /* Hack -- hallucination */ - if (p_ptr->image) image_object(ap, cp); - } - } - else - { - /* Visible monster */ - if (m_ptr->ml) - { - monster_race *r_ptr = race_inf(m_ptr); - - /* Default attr/char */ - c = r_ptr->d_char; - a = r_ptr->d_attr; - - /* Ignore weird codes */ - if (avoid_other) - { - /* Use char */ - *cp = c; - - /* Use attr */ - *ap = a; - } - - /* Multi-hued monster */ - else if (r_ptr->flags1 & (RF1_ATTR_MULTI)) - { - /* Is it a shapechanger? */ - if (r_ptr->flags2 & (RF2_SHAPECHANGER)) - { - image_random(ap, cp); - } - else - *cp = c; - - /* Multi-hued attr */ - if (r_ptr->flags2 & (RF2_ATTR_ANY)) - { - *ap = randint(15); - } - else - { - *ap = multi_hued_attr(r_ptr); - } - } - - /* Normal monster (not "clear" in any way) */ - else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR | RF1_CHAR_CLEAR))) - { - /* Use char */ - *cp = c; - - /* Use attr */ - *ap = a; - } - - /* Hack -- Bizarre grid under monster */ - else if ((*ap & 0x80) || (*cp & 0x80)) - { - /* Use char */ - *cp = c; - - /* Use attr */ - *ap = a; - } - - /* Normal */ - else - { - /* Normal (non-clear char) monster */ - if (!(r_ptr->flags1 & (RF1_CHAR_CLEAR))) - { - /* Normal char */ - *cp = c; - } - - /* Normal (non-clear attr) monster */ - else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR))) - { - /* Normal attr */ - *ap = a; - } - } - - /* Hack -- hallucination */ - if (p_ptr->image) - { - /* Hallucinatory monster */ - image_monster(ap, cp); - } - } - } - } - - - /* Handle "player" */ - if ((y == p_ptr->py) && (x == p_ptr->px) && - (!p_ptr->invis || - (p_ptr->invis && p_ptr->see_inv))) - { - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - /* Get the "player" attr */ - if (!avoid_other && (r_ptr->flags1 & RF1_ATTR_MULTI)) - { - a = get_shimmer_color(); - } - else - { - a = r_ptr->d_attr; - } - - /* Get the "player" char */ - c = r_ptr->d_char; - - /* Save the info */ - *ap = a; - *cp = c; - - } - - /* XXX Restore the graphics mode */ - use_graphics = use_graphics_hack; - graphics_mode = graphics_mode_hack; -} - - -/* - * Calculate panel colum of a location in the map - */ -static int panel_col_of(int col) -{ - col -= panel_col_min; - if (use_bigtile) col *= 2; - return col + COL_MAP; -} - - - -/* - * Moves the cursor to a given MAP (y,x) location - */ -void move_cursor_relative(int row, int col) -{ - /* Real co-ords convert to screen positions */ - row -= panel_row_prt; - - /* Go there */ - Term_gotoxy(panel_col_of(col), row); -} - - - -/* - * Place an attr/char pair at the given map coordinate, if legal. - */ -void print_rel(char c, byte a, int y, int x) -{ - /* Paranoia -- Only do "legal" locations */ - if (!panel_contains(y, x)) return; - - /* Draw the char using the attr */ - Term_draw(panel_col_of(x), y - panel_row_prt, a, c); - - if (use_bigtile) - { - char c2; - byte a2; - - if (a & 0x80) - { - a2 = 255; - c2 = 255; - } - else - { - a2 = TERM_WHITE; - c2 = ' '; - } - Term_draw(panel_col_of(x) + 1, y - panel_row_prt, a2, c2); - } -} - - - - - -/* - * Memorize interesting viewable object/features in the given grid - * - * This function should only be called on "legal" grids. - * - * This function will memorize the object and/or feature in the given - * grid, if they are (1) viewable and (2) interesting. Note that all - * objects are interesting, all terrain features except floors (and - * invisible traps) are interesting, and floors (and invisible traps) - * are interesting sometimes (depending on various options involving - * the illumination of floor grids). - * - * The automatic memorization of all objects and non-floor terrain - * features as soon as they are displayed allows incredible amounts - * of optimization in various places, especially "map_info()". - * - * Note that the memorization of objects is completely separate from - * the memorization of terrain features, preventing annoying floor - * memorization when a detected object is picked up from a dark floor, - * and object memorization when an object is dropped into a floor grid - * which is memorized but out-of-sight. - * - * This function should be called every time the "memorization" of - * a grid (or the object in a grid) is called into question, such - * as when an object is created in a grid, when a terrain feature - * "changes" from "floor" to "non-floor", when any grid becomes - * "illuminated" or "viewable", and when a "floor" grid becomes - * "torch-lit". - * - * Note the relatively efficient use of this function by the various - * "update_view()" and "update_lite()" calls, to allow objects and - * terrain features to be memorized (and drawn) whenever they become - * viewable or illuminated in any way, but not when they "maintain" - * or "lose" their previous viewability or illumination. - * - * Note the butchered "internal" version of "player_can_see_bold()", - * optimized primarily for the most common cases, that is, for the - * non-marked floor grids. - */ -void note_spot(int y, int x) -{ - cave_type *c_ptr = &cave[y][x]; - - u16b info = c_ptr->info; - - s16b this_o_idx, next_o_idx = 0; - - - /* Require "seen" flag */ - if (!(info & (CAVE_SEEN))) return; - - - /* Hack -- memorize objects */ - for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) - { - object_type * o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Memorize objects */ - o_ptr->marked = TRUE; - } - - if (c_ptr->m_idx) - { - monster_type *m_ptr = &m_list[c_ptr->m_idx]; - monster_race *r_ptr = race_inf(m_ptr); - - if (r_ptr->flags9 & RF9_MIMIC) - { - object_type *o_ptr = &o_list[m_ptr->hold_o_idx]; - - o_ptr->marked = TRUE; - } - } - - - /* Hack -- memorize grids */ - if (!(info & (CAVE_MARK))) - { - /* Memorise some "boring" grids */ - if (cave_plain_floor_grid(c_ptr)) - { - /* Option -- memorise certain floors */ - if ((info & (CAVE_TRDT)) || - ((info & (CAVE_GLOW)) && view_perma_grids ) || - view_torch_grids) - { - /* Memorize */ - c_ptr->info |= (CAVE_MARK); - } - } - - /* Memorise all "interesting" grids */ - else - { - /* Memorize */ - c_ptr->info |= (CAVE_MARK); - } - } -} - - -/* - * Redraw (on the screen) a given MAP location - * - * This function should only be called on "legal" grids - */ -void lite_spot(int y, int x) -{ - byte a, a2; - byte c, c2; - - byte ta; - char tc; - - byte ea; - char ec; - - - /* Redraw if on screen */ - if (panel_contains(y, x)) - { - /* Examine the grid */ - map_info(y, x, &a, (char*)&c, &ta, &tc, &ea, &ec); - - /* Hack -- Queue it */ - Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc, ea, ec); - if (use_bigtile) - { - if (a & 0x80) - { - a2 = 255; - c2 = 255; - } - else - { - a2 = TERM_WHITE; - c2 = ' '; - } - Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0, 0, 0); - } - - } -} - - - - -/* - * Prints the map of the dungeon - * - * Note that, for efficiency, we contain an "optimized" version - * of both "lite_spot()" and "print_rel()", and that we use the - * "lite_spot()" function to display the player grid, if needed. - */ -void prt_map(void) -{ - int x, y; - - int v; - - /* Access the cursor state */ - (void)Term_get_cursor(&v); - - /* Hide the cursor */ - (void)Term_set_cursor(0); - - /* Dump the map */ - for (y = panel_row_min; y <= panel_row_max; y++) - { - /* Scan the columns of row "y" */ - for (x = panel_col_min; x <= panel_col_max; x++) - { - byte a, a2; - char c, c2; - - byte ta; - char tc; - byte ea; - char ec; - - /* Determine what is there */ - map_info(y, x, &a, &c, &ta, &tc, &ea, &ec); - - /* Efficiency -- Redraw that grid of the map */ - Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc, ea, ec); - if (use_bigtile) - { - if (a & 0x80) - { - a2 = 255; - c2 = 255; - } - else - { - a2 = TERM_WHITE; - c2 = ' '; - } - Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0, 0, 0); - } - } - } - - /* Display player */ - lite_spot(p_ptr->py, p_ptr->px); - - /* Restore the cursor */ - (void)Term_set_cursor(v); -} - - - - - -/* - * Display highest priority object in the RATIO by RATIO area - */ - -/* - * Display the entire map - */ -#define MAP_HGT (MAX_HGT / RATIO) -#define MAP_WID (MAX_WID / RATIO) - -/* - * Hack -- priority array (see below) - * - * Note that all "walls" always look like "secret doors" (see "map_info()"). - */ -static byte priority_table[][2] = -{ - /* Dark */ - { FEAT_NONE, 2 }, - - /* Floors */ - { FEAT_FLOOR, 5 }, - - /* Walls */ - { FEAT_SECRET, 10 }, - - /* Quartz */ - { FEAT_QUARTZ, 11 }, - - /* Magma */ - { FEAT_MAGMA, 12 }, - - /* Rubble */ - { FEAT_RUBBLE, 13 }, - - /* Sandwall */ - { FEAT_SANDWALL, 14 }, - - /* Open doors */ - { FEAT_OPEN, 15 }, - { FEAT_BROKEN, 15 }, - - /* Closed doors */ - { FEAT_DOOR_HEAD + 0x00, 17 }, - - /* Hidden gold */ - { FEAT_QUARTZ_K, 19 }, - { FEAT_MAGMA_K, 19 }, - { FEAT_SANDWALL_K, 19 }, - - /* water, lava, & trees oh my! -KMW- */ - { FEAT_DEEP_WATER, 20 }, - { FEAT_SHAL_WATER, 20 }, - { FEAT_DEEP_LAVA, 20 }, - { FEAT_SHAL_LAVA, 20 }, - { FEAT_DIRT, 20 }, - { FEAT_GRASS, 20 }, - { FEAT_DARK_PIT, 20 }, - { FEAT_TREES, 20 }, - { FEAT_MOUNTAIN, 20 }, - { FEAT_ICE, 20}, - { FEAT_SAND, 20}, - { FEAT_DEAD_TREE, 20}, - { FEAT_ASH, 20}, - { FEAT_MUD, 20}, - - /* Fountain */ - { FEAT_FOUNTAIN, 22 }, - { FEAT_EMPTY_FOUNTAIN, 22 }, - - /* Stairs */ - { FEAT_LESS, 25 }, - { FEAT_MORE, 25 }, - - /* Stairs */ - { FEAT_WAY_LESS, 25 }, - { FEAT_WAY_MORE, 25 }, - - { FEAT_SHAFT_UP, 25 }, - { FEAT_SHAFT_DOWN, 25 }, - - /* End */ - { 0, 0 } -}; - - -/* - * Hack -- a priority function (see below) - */ -static byte priority(byte a, char c) -{ - int i, p0, p1; - - feature_type *f_ptr; - - /* Scan the table */ - for (i = 0; TRUE; i++) - { - /* Priority level */ - p1 = priority_table[i][1]; - - /* End of table */ - if (!p1) break; - - /* Feature index */ - p0 = priority_table[i][0]; - - /* Access the feature */ - f_ptr = &f_info[p0]; - - /* Check character and attribute, accept matches */ - if ((f_ptr->x_char == c) && (f_ptr->x_attr == a)) return (p1); - } - - /* Default */ - return (20); -} - - -/* - * Display a "small-scale" map of the dungeon in the active Term - * - * Note that the "map_info()" function must return fully colorized - * data or this function will not work correctly. - * - * Note that this function must "disable" the special lighting - * effects so that the "priority" function will work. - * - * Note the use of a specialized "priority" function to allow this - * function to work with any graphic attr/char mappings, and the - * attempts to optimize this function where possible. - */ -void display_map(int *cy, int *cx) -{ - int i, j, x, y; - - byte ta; - char tc; - - byte tp; - - byte **ma; - char **mc; - - byte **mp; - - bool_ old_view_special_lite; - bool_ old_view_granite_lite; - - int hgt, wid, yrat, xrat, yfactor, xfactor; - - - /* Obtain current size of the Angband window */ - Term_get_size(&wid, &hgt); - - /* Use two characters as one tile in Bigtile mode */ - if (use_bigtile) wid /= 2; - - /* - * Calculate the size of the dungeon map area - */ - hgt -= ROW_MAP + 2; - wid -= COL_MAP + 1; - - /* Paranoia */ - if ((hgt < 3) || (wid < 3)) - { - /* Map is too small, but place the player anyway */ - *cy = ROW_MAP; - *cx = COL_MAP; - - return; - } - - - /* Save lighting effects */ - old_view_special_lite = view_special_lite; - old_view_granite_lite = view_granite_lite; - - /* Disable lighting effects */ - view_special_lite = FALSE; - view_granite_lite = FALSE; - - - /* Allocate temporary memory for the maps */ - C_MAKE(ma, hgt + 2, byte *); - C_MAKE(mc, hgt + 2, char *); - C_MAKE(mp, hgt + 2, byte *); - - /* Allocate each line in the maps */ - for (i = 0; i < hgt + 2; i++) - { - C_MAKE(ma[i], wid + 2, byte); - C_MAKE(mc[i], wid + 2, char); - C_MAKE(mp[i], wid + 2, byte); - } - - /* Clear the chars and attributes */ - for (y = 0; y < hgt + 2; ++y) - { - for (x = 0; x < wid + 2; ++x) - { - /* Nothing here */ - ma[y][x] = TERM_WHITE; - mc[y][x] = ' '; - - /* No priority */ - mp[y][x] = 0; - } - } - - /* Calculate scaling factors */ - yfactor = ((cur_hgt / hgt < 4) && (cur_hgt > hgt)) ? 10 : 1; - xfactor = ((cur_wid / wid < 4) && (cur_wid > wid)) ? 10 : 1; - - yrat = (cur_hgt * yfactor + (hgt - 1)) / hgt; - xrat = (cur_wid * xfactor + (wid - 1)) / wid; - - /* Fill in the map */ - for (j = 0; j < cur_hgt; ++j) - { - for (i = 0; i < cur_wid; ++i) - { - /* Location */ - y = j * yfactor / yrat + 1; - x = i * xfactor / xrat + 1; - - /* Extract the current attr/char at that map location */ - map_info(j, i, &ta, &tc, &ta, &tc, &ta, &tc); - - /* Extract the priority of that attr/char */ - tp = priority(ta, tc); - - /* Player location has the highest priority */ - if ((p_ptr->py == j) && (p_ptr->px == i)) tp = 255; - - /* Save "best" */ - if (mp[y][x] < tp) - { - /* Save the char */ - mc[y][x] = tc; - - /* Save the attr */ - ma[y][x] = ta; - - /* Save priority */ - mp[y][x] = tp; - } - } - } - - - /* Corners */ - y = hgt + 1; - x = wid + 1; - - /* Draw the corners */ - mc[0][0] = mc[0][x] = mc[y][0] = mc[y][x] = '+'; - - /* Draw the horizontal edges */ - for (x = 1; x <= wid; x++) mc[0][x] = mc[y][x] = '-'; - - /* Draw the vertical edges */ - for (y = 1; y <= hgt; y++) mc[y][0] = mc[y][x] = '|'; - - - /* Display each map line in order */ - for (y = 0; y < hgt + 2; ++y) - { - /* Start a new line */ - Term_gotoxy(COL_MAP - 1, y); - - /* Display the line */ - for (x = 0; x < wid + 2; ++x) - { - ta = ma[y][x]; - tc = mc[y][x]; - - /* Add the character */ - Term_addch(ta, tc); - - /* Double width tile mode requires filler */ - if (use_bigtile) - { - byte a2; - char c2; - - if (ta & 0x80) - { - /* Mega-Hack */ - a2 = 255; - c2 = 255; - } - else - { - a2 = TERM_WHITE; - c2 = ' '; - } - - Term_addch(a2, c2); - } - } - } - - /* Player location in dungeon */ - *cy = p_ptr->py * yfactor / yrat + ROW_MAP; - if (!use_bigtile) - { - *cx = p_ptr->px * xfactor / xrat + COL_MAP; - } - else - { - *cx = (p_ptr->px * xfactor / xrat + 1) * 2 - 1 + COL_MAP; - } - - /* Free each line in the maps */ - for (i = 0; i < hgt + 2; i++) - { - C_FREE(ma[i], wid + 2, byte); - C_FREE(mc[i], wid + 2, char); - C_FREE(mp[i], wid + 2, byte); - } - - /* Allocate temporary memory for the maps */ - C_FREE(ma, hgt + 2, byte *); - C_FREE(mc, hgt + 2, char *); - C_FREE(mp, hgt + 2, byte *); - - - /* Restore lighting effects */ - view_special_lite = old_view_special_lite; - view_granite_lite = old_view_granite_lite; -} - - -/* - * Display a "small-scale" map of the dungeon for the player - * - * Currently, the "player" is displayed on the map. XXX XXX XXX - */ -void do_cmd_view_map(void) -{ - int cy, cx; - int wid, hgt; - - /* Retrive current screen size */ - Term_get_size(&wid, &hgt); - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - /* Note */ - prt("Please wait...", 0, 0); - - /* Flush */ - Term_fresh(); - - /* Clear the screen */ - Term_clear(); - - /* Display the map */ - display_map(&cy, &cx); - - /* Wait for it */ - put_str("Hit any key to continue", hgt - 1, (wid - COL_MAP) / 2); - - /* Hilite the player */ - move_cursor(cy, cx); - - /* Get any key */ - inkey(); - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; -} - - - - - - -/* - * Some comments on the dungeon related data structures and functions... - * - * Angband is primarily a dungeon exploration game, and it should come as - * no surprise that the internal representation of the dungeon has evolved - * over time in much the same way as the game itself, to provide semantic - * changes to the game itself, to make the code simpler to understand, and - * to make the executable itself faster or more efficient in various ways. - * - * There are a variety of dungeon related data structures, and associated - * functions, which store information about the dungeon, and provide methods - * by which this information can be accessed or modified. - * - * Some of this information applies to the dungeon as a whole, such as the - * list of unique monsters which are still alive. Some of this information - * only applies to the current dungeon level, such as the current depth, or - * the list of monsters currently inhabiting the level. And some of the - * information only applies to a single grid of the current dungeon level, - * such as whether the grid is illuminated, or whether the grid contains a - * monster, or whether the grid can be seen by the player. If Angband was - * to be turned into a multi-player game, some of the information currently - * associated with the dungeon should really be associated with the player, - * such as whether a given grid is viewable by a given player. - * - * One of the major bottlenecks in ancient versions of Angband was in the - * calculation of "line of sight" from the player to various grids, such - * as those containing monsters, using the relatively expensive "los()" - * function. This was such a nasty bottleneck that a lot of silly things - * were done to reduce the dependancy on "line of sight", for example, you - * could not "see" any grids in a lit room until you actually entered the - * room, at which point every grid in the room became "illuminated" and - * all of the grids in the room were "memorized" forever. Other major - * bottlenecks involved the determination of whether a grid was lit by the - * player's torch, and whether a grid blocked the player's line of sight. - * These bottlenecks led to the development of special new functions to - * optimize issues involved with "line of sight" and "torch lit grids". - * These optimizations led to entirely new additions to the game, such as - * the ability to display the player's entire field of view using different - * colors than were used for the "memorized" portions of the dungeon, and - * the ability to memorize dark floor grids, but to indicate by the way in - * which they are displayed that they are not actually illuminated. And - * of course many of them simply made the game itself faster or more fun. - * Also, over time, the definition of "line of sight" has been relaxed to - * allow the player to see a wider "field of view", which is slightly more - * realistic, and only slightly more expensive to maintain. - * - * Currently, a lot of the information about the dungeon is stored in ways - * that make it very efficient to access or modify the information, while - * still attempting to be relatively conservative about memory usage, even - * if this means that some information is stored in multiple places, or in - * ways which require the use of special code idioms. For example, each - * monster record in the monster array contains the location of the monster, - * and each cave grid has an index into the monster array, or a zero if no - * monster is in the grid. This allows the monster code to efficiently see - * where the monster is located, while allowing the dungeon code to quickly - * determine not only if a monster is present in a given grid, but also to - * find out which monster. The extra space used to store the information - * twice is inconsequential compared to the speed increase. - * - * Some of the information about the dungeon is used by functions which can - * constitute the "critical efficiency path" of the game itself, and so the - * way in which they are stored and accessed has been optimized in order to - * optimize the game itself. For example, the "update_view()" function was - * originally created to speed up the game itself (when the player was not - * running), but then it took on extra responsibility as the provider of the - * new "special effects lighting code", and became one of the most important - * bottlenecks when the player was running. So many rounds of optimization - * were performed on both the function itself, and the data structures which - * it uses, resulting eventually in a function which not only made the game - * faster than before, but which was responsible for even more calculations - * (including the determination of which grids are "viewable" by the player, - * which grids are illuminated by the player's torch, and which grids can be - * "seen" in some way by the player), as well as for providing the guts of - * the special effects lighting code, and for the efficient redisplay of any - * grids whose visual representation may have changed. - * - * Several pieces of information about each cave grid are stored in various - * two dimensional arrays, with one unit of information for each grid in the - * dungeon. Some of these arrays have been intentionally expanded by a small - * factor to make the two dimensional array accesses faster by allowing the - * use of shifting instead of multiplication. - * - * Several pieces of information about each cave grid are stored in the - * "cave_info" array, which is a special two dimensional array of bytes, - * one for each cave grid, each containing eight separate "flags" which - * describe some property of the cave grid. These flags can be checked and - * modified extremely quickly, especially when special idioms are used to - * force the compiler to keep a local register pointing to the base of the - * array. Special location offset macros can be used to minimize the number - * of computations which must be performed at runtime. Note that using a - * byte for each flag set may be slightly more efficient than using a larger - * unit, so if another flag (or two) is needed later, and it must be fast, - * then the two existing flags which do not have to be fast should be moved - * out into some other data structure and the new flags should take their - * place. This may require a few minor changes in the savefile code. - * - * The "CAVE_ROOM" flag is saved in the savefile and is used to determine - * which grids are part of "rooms", and thus which grids are affected by - * "illumination" spells. This flag does not have to be very fast. - * - * The "CAVE_ICKY" flag is saved in the savefile and is used to determine - * which grids are part of "vaults", and thus which grids cannot serve as - * the destinations of player teleportation. This flag does not have to - * be very fast. - * - * The "CAVE_MARK" flag is saved in the savefile and is used to determine - * which grids have been "memorized" by the player. This flag is used by - * the "map_info()" function to determine if a grid should be displayed. - * This flag is used in a few other places to determine if the player can - * "know" about a given grid. This flag must be very fast. - * - * The "CAVE_GLOW" flag is saved in the savefile and is used to determine - * which grids are "permanently illuminated". This flag is used by the - * "update_view()" function to help determine which viewable flags may - * be "seen" by the player. This flag is used by the "map_info" function - * to determine if a grid is only lit by the player's torch. This flag - * has special semantics for wall grids (see "update_view()"). This flag - * must be very fast. - * - * The "CAVE_WALL" flag is used to determine which grids block the player's - * line of sight. This flag is used by the "update_view()" function to - * determine which grids block line of sight, and to help determine which - * grids can be "seen" by the player. This flag must be very fast. - * - * The "CAVE_VIEW" flag is used to determine which grids are currently in - * line of sight of the player. This flag is set by (and used by) the - * "update_view()" function. This flag is used by any code which needs to - * know if the player can "view" a given grid. This flag is used by the - * "map_info()" function for some optional special lighting effects. The - * "player_has_los_bold()" macro wraps an abstraction around this flag, but - * certain code idioms are much more efficient. This flag is used to check - * if a modification to a terrain feature might affect the player's field of - * view. This flag is used to see if certain monsters are "visible" to the - * player. This flag is used to allow any monster in the player's field of - * view to "sense" the presence of the player. This flag must be very fast. - * - * The "CAVE_SEEN" flag is used to determine which grids are currently in - * line of sight of the player and also illuminated in some way. This flag - * is set by the "update_view()" function, using computations based on the - * "CAVE_VIEW" and "CAVE_WALL" and "CAVE_GLOW" flags of various grids. This - * flag is used by any code which needs to know if the player can "see" a - * given grid. This flag is used by the "map_info()" function both to see - * if a given "boring" grid can be seen by the player, and for some optional - * special lighting effects. The "player_can_see_bold()" macro wraps an - * abstraction around this flag, but certain code idioms are much more - * efficient. This flag is used to see if certain monsters are "visible" to - * the player. This flag is never set for a grid unless "CAVE_VIEW" is also - * set for the grid. Whenever the "CAVE_WALL" or "CAVE_GLOW" flag changes - * for a grid which has the "CAVE_VIEW" flag set, the "CAVE_SEEN" flag must - * be recalculated. The simplest way to do this is to call "forget_view()" - * and "update_view()" whenever the "CAVE_WALL" or "CAVE_GLOW" flags change - * for a grid which has "CAVE_VIEW" set. This flag must be very fast. - * - * The "CAVE_TEMP" flag is used for a variety of temporary purposes. This - * flag is used to determine if the "CAVE_SEEN" flag for a grid has changed - * during the "update_view()" function. This flag is used to "spread" light - * or darkness through a room. This flag is used by the "monster flow code". - * This flag must always be cleared by any code which sets it, often, this - * can be optimized by the use of the special "temp_g", "temp_y", "temp_x" - * arrays (and the special "temp_n" global). This flag must be very fast. - * - * Note that the "CAVE_MARK" flag is used for many reasons, some of which - * are strictly for optimization purposes. The "CAVE_MARK" flag means that - * even if the player cannot "see" the grid, he "knows" about the terrain in - * that grid. This is used to "memorize" grids when they are first "seen" by - * the player, and to allow certain grids to be "detected" by certain magic. - * Note that most grids are always memorized when they are first "seen", but - * "boring" grids (floor grids) are only memorized if the "view_torch_grids" - * option is set, or if the "view_perma_grids" option is set, and the grid - * in question has the "CAVE_GLOW" flag set. - * - * Objects are "memorized" in a different way, using a special "marked" flag - * on the object itself, which is set when an object is observed or detected. - * This allows objects to be "memorized" independant of the terrain features. - * - * The "update_view()" function is an extremely important function. It is - * called only when the player moves, significant terrain changes, or the - * player's blindness or torch radius changes. Note that when the player - * is resting, or performing any repeated actions (like digging, disarming, - * farming, etc), there is no need to call the "update_view()" function, so - * even if it was not very efficient, this would really only matter when the - * player was "running" through the dungeon. It sets the "CAVE_VIEW" flag - * on every cave grid in the player's field of view, and maintains an array - * of all such grids in the global "view_g" array. It also checks the torch - * radius of the player, and sets the "CAVE_SEEN" flag for every grid which - * is in the "field of view" of the player and which is also "illuminated", - * either by the players torch (if any) or by any permanent light source. - * It could use and help maintain information about multiple light sources, - * which would be helpful in a multi-player version of Angband. - * - * The "update_view()" function maintains the special "view_g" array, which - * contains exactly those grids which have the "CAVE_VIEW" flag set. This - * array is used by "update_view()" to (only) memorize grids which become - * newly "seen", and to (only) redraw grids whose "seen" value changes, which - * allows the use of some interesting (and very efficient) "special lighting - * effects". In addition, this array could be used elsewhere to quickly scan - * through all the grids which are in the player's field of view. - * - * Note that the "update_view()" function allows, among other things, a room - * to be "partially" seen as the player approaches it, with a growing cone - * of floor appearing as the player gets closer to the door. Also, by not - * turning on the "memorize perma-lit grids" option, the player will only - * "see" those floor grids which are actually in line of sight. And best - * of all, you can now activate the special lighting effects to indicate - * which grids are actually in the player's field of view by using dimmer - * colors for grids which are not in the player's field of view, and/or to - * indicate which grids are illuminated only by the player's torch by using - * the color yellow for those grids. - * - * The old "update_view()" algorithm uses the special "CAVE_EASY" flag as a - * temporary internal flag to mark those grids which are not only in view, - * but which are also "easily" in line of sight of the player. This flag - * is actually just the "CAVE_SEEN" flag, and the "update_view()" function - * makes sure to clear it for all old "CAVE_SEEN" grids, and then use it in - * the algorithm as "CAVE_EASY", and then clear it for all "CAVE_EASY" grids, - * and then reset it as appropriate for all new "CAVE_SEEN" grids. This is - * kind of messy, but it works. The old algorithm may disappear eventually. - * - * The new "update_view()" algorithm uses a faster and more mathematically - * correct algorithm, assisted by a large machine generated static array, to - * determine the "CAVE_VIEW" and "CAVE_SEEN" flags simultaneously. See below. - * - * It seems as though slight modifications to the "update_view()" functions - * would allow us to determine "reverse" line-of-sight as well as "normal" - * line-of-sight", which would allow monsters to have a more "correct" way - * to determine if they can "see" the player, since right now, they "cheat" - * somewhat and assume that if the player has "line of sight" to them, then - * they can "pretend" that they have "line of sight" to the player. But if - * such a change was attempted, the monsters would actually start to exhibit - * some undesirable behavior, such as "freezing" near the entrances to long - * hallways containing the player, and code would have to be added to make - * the monsters move around even if the player was not detectable, and to - * "remember" where the player was last seen, to avoid looking stupid. - * - * Note that the "CAVE_GLOW" flag means that a grid is permanently lit in - * some way. However, for the player to "see" the grid, as determined by - * the "CAVE_SEEN" flag, the player must not be blind, the grid must have - * the "CAVE_VIEW" flag set, and if the grid is a "wall" grid, and it is - * not lit by the player's torch, then it must touch a grid which does not - * have the "CAVE_WALL" flag set, but which does have both the "CAVE_GLOW" - * and "CAVE_VIEW" flags set. This last part about wall grids is induced - * by the semantics of "CAVE_GLOW" as applied to wall grids, and checking - * the technical requirements can be very expensive, especially since the - * grid may be touching some "illegal" grids. Luckily, it is more or less - * correct to restrict the "touching" grids from the eight "possible" grids - * to the (at most) three grids which are touching the grid, and which are - * closer to the player than the grid itself, which eliminates more than - * half of the work, including all of the potentially "illegal" grids, if - * at most one of the three grids is a "diagonal" grid. In addition, in - * almost every situation, it is possible to ignore the "CAVE_VIEW" flag - * on these three "touching" grids, for a variety of technical reasons. - * Finally, note that in most situations, it is only necessary to check - * a single "touching" grid, in fact, the grid which is strictly closest - * to the player of all the touching grids, and in fact, it is normally - * only necessary to check the "CAVE_GLOW" flag of that grid, again, for - * various technical reasons. However, one of the situations which does - * not work with this last reduction is the very common one in which the - * player approaches an illuminated room from a dark hallway, in which the - * two wall grids which form the "entrance" to the room would not be marked - * as "CAVE_SEEN", since of the three "touching" grids nearer to the player - * than each wall grid, only the farthest of these grids is itself marked - * "CAVE_GLOW". - * - * - * Here are some pictures of the legal "light source" radius values, in - * which the numbers indicate the "order" in which the grids could have - * been calculated, if desired. Note that the code will work with larger - * radiuses, though currently yields such a radius, and the game would - * become slower in some situations if it did. - * - * Rad=0 Rad=1 Rad=2 Rad=3 - * No-Lite Torch,etc Lantern Artifacts - * - * 333 - * 333 43334 - * 212 32123 3321233 - * @ 1@1 31@13 331@133 - * 212 32123 3321233 - * 333 43334 - * 333 - * - * - * Here is an illustration of the two different "update_view()" algorithms, - * in which the grids marked "%" are pillars, and the grids marked "?" are - * not in line of sight of the player. - * - * - * Sample situation - * - * ##################### - * ############.%.%.%.%# - * #...@..#####........# - * #............%.%.%.%# - * #......#####........# - * ############........# - * ##################### - * - * - * New Algorithm Old Algorithm - * - * ########????????????? ########????????????? - * #...@..#????????????? #...@..#????????????? - * #...........????????? #.........??????????? - * #......#####.....???? #......####?????????? - * ########?????????...# ########????????????? - * - * ########????????????? ########????????????? - * #.@....#????????????? #.@....#????????????? - * #............%??????? #...........????????? - * #......#####........? #......#####????????? - * ########??????????..# ########????????????? - * - * ########????????????? ########?????%??????? - * #......#####........# #......#####..??????? - * #.@..........%??????? #.@..........%??????? - * #......#####........# #......#####..??????? - * ########????????????? ########????????????? - * - * ########??????????..# ########????????????? - * #......#####........? #......#####????????? - * #............%??????? #...........????????? - * #.@....#????????????? #.@....#????????????? - * ########????????????? ########????????????? - * - * ########?????????%??? ########????????????? - * #......#####.....???? #......####?????????? - * #...........????????? #.........??????????? - * #...@..#????????????? #...@..#????????????? - * ########????????????? ########????????????? - */ - - - - -/* - * Maximum number of grids in a single octant - */ -#define VINFO_MAX_GRIDS 161 - - -/* - * Maximum number of slopes in a single octant - */ -#define VINFO_MAX_SLOPES 126 - - -/* - * Mask of bits used in a single octant - */ -#define VINFO_BITS_3 0x3FFFFFFF -#define VINFO_BITS_2 0xFFFFFFFF -#define VINFO_BITS_1 0xFFFFFFFF -#define VINFO_BITS_0 0xFFFFFFFF - - -/* - * Forward declare - */ -typedef struct vinfo_type vinfo_type; - - -/* - * The 'vinfo_type' structure - */ -struct vinfo_type -{ - s16b grid_y[8]; - s16b grid_x[8]; - - u32b bits_3; - u32b bits_2; - u32b bits_1; - u32b bits_0; - - vinfo_type *next_0; - vinfo_type *next_1; - - byte y; - byte x; - byte d; - byte r; -}; - - - -/* - * The array of "vinfo" objects, initialized by "vinfo_init()" - */ -static vinfo_type vinfo[VINFO_MAX_GRIDS]; - - - - -/* - * Slope scale factor - */ -#define SCALE 100000L - - -/* - * The actual slopes (for reference) - */ - -/* Bit : Slope Grids */ -/* --- : ----- ----- */ -/* 0 : 2439 21 */ -/* 1 : 2564 21 */ -/* 2 : 2702 21 */ -/* 3 : 2857 21 */ -/* 4 : 3030 21 */ -/* 5 : 3225 21 */ -/* 6 : 3448 21 */ -/* 7 : 3703 21 */ -/* 8 : 4000 21 */ -/* 9 : 4347 21 */ -/* 10 : 4761 21 */ -/* 11 : 5263 21 */ -/* 12 : 5882 21 */ -/* 13 : 6666 21 */ -/* 14 : 7317 22 */ -/* 15 : 7692 20 */ -/* 16 : 8108 21 */ -/* 17 : 8571 21 */ -/* 18 : 9090 20 */ -/* 19 : 9677 21 */ -/* 20 : 10344 21 */ -/* 21 : 11111 20 */ -/* 22 : 12000 21 */ -/* 23 : 12820 22 */ -/* 24 : 13043 22 */ -/* 25 : 13513 22 */ -/* 26 : 14285 20 */ -/* 27 : 15151 22 */ -/* 28 : 15789 22 */ -/* 29 : 16129 22 */ -/* 30 : 17241 22 */ -/* 31 : 17647 22 */ -/* 32 : 17948 23 */ -/* 33 : 18518 22 */ -/* 34 : 18918 22 */ -/* 35 : 20000 19 */ -/* 36 : 21212 22 */ -/* 37 : 21739 22 */ -/* 38 : 22580 22 */ -/* 39 : 23076 22 */ -/* 40 : 23809 22 */ -/* 41 : 24137 22 */ -/* 42 : 24324 23 */ -/* 43 : 25714 23 */ -/* 44 : 25925 23 */ -/* 45 : 26315 23 */ -/* 46 : 27272 22 */ -/* 47 : 28000 23 */ -/* 48 : 29032 23 */ -/* 49 : 29411 23 */ -/* 50 : 29729 24 */ -/* 51 : 30434 23 */ -/* 52 : 31034 23 */ -/* 53 : 31428 23 */ -/* 54 : 33333 18 */ -/* 55 : 35483 23 */ -/* 56 : 36000 23 */ -/* 57 : 36842 23 */ -/* 58 : 37142 24 */ -/* 59 : 37931 24 */ -/* 60 : 38461 24 */ -/* 61 : 39130 24 */ -/* 62 : 39393 24 */ -/* 63 : 40740 24 */ -/* 64 : 41176 24 */ -/* 65 : 41935 24 */ -/* 66 : 42857 23 */ -/* 67 : 44000 24 */ -/* 68 : 44827 24 */ -/* 69 : 45454 23 */ -/* 70 : 46666 24 */ -/* 71 : 47368 24 */ -/* 72 : 47826 24 */ -/* 73 : 48148 24 */ -/* 74 : 48387 24 */ -/* 75 : 51515 25 */ -/* 76 : 51724 25 */ -/* 77 : 52000 25 */ -/* 78 : 52380 25 */ -/* 79 : 52941 25 */ -/* 80 : 53846 25 */ -/* 81 : 54838 25 */ -/* 82 : 55555 24 */ -/* 83 : 56521 25 */ -/* 84 : 57575 26 */ -/* 85 : 57894 25 */ -/* 86 : 58620 25 */ -/* 87 : 60000 23 */ -/* 88 : 61290 25 */ -/* 89 : 61904 25 */ -/* 90 : 62962 25 */ -/* 91 : 63636 25 */ -/* 92 : 64705 25 */ -/* 93 : 65217 25 */ -/* 94 : 65517 25 */ -/* 95 : 67741 26 */ -/* 96 : 68000 26 */ -/* 97 : 68421 26 */ -/* 98 : 69230 26 */ -/* 99 : 70370 26 */ -/* 100 : 71428 25 */ -/* 101 : 72413 26 */ -/* 102 : 73333 26 */ -/* 103 : 73913 26 */ -/* 104 : 74193 27 */ -/* 105 : 76000 26 */ -/* 106 : 76470 26 */ -/* 107 : 77777 25 */ -/* 108 : 78947 26 */ -/* 109 : 79310 26 */ -/* 110 : 80952 26 */ -/* 111 : 81818 26 */ -/* 112 : 82608 26 */ -/* 113 : 84000 26 */ -/* 114 : 84615 26 */ -/* 115 : 85185 26 */ -/* 116 : 86206 27 */ -/* 117 : 86666 27 */ -/* 118 : 88235 27 */ -/* 119 : 89473 27 */ -/* 120 : 90476 27 */ -/* 121 : 91304 27 */ -/* 122 : 92000 27 */ -/* 123 : 92592 27 */ -/* 124 : 93103 28 */ -/* 125 : 100000 13 */ - - - -/* - * Forward declare - */ -typedef struct vinfo_hack vinfo_hack; - - -/* - * Temporary data used by "vinfo_init()" - * - * - Number of grids - * - * - Number of slopes - * - * - Slope values - * - * - Slope range per grid - */ -struct vinfo_hack -{ - - int num_slopes; - - long slopes[VINFO_MAX_SLOPES]; - - long slopes_min[MAX_SIGHT + 1][MAX_SIGHT + 1]; - long slopes_max[MAX_SIGHT + 1][MAX_SIGHT + 1]; -}; - - - -/* - * Sorting hook -- comp function -- array of long's (see below) - * - * We use "u" to point to an array of long integers. - */ -static bool_ ang_sort_comp_hook_longs(vptr u, vptr v, int a, int b) -{ - long *x = (long*)(u); - - return (x[a] <= x[b]); -} - - -/* - * Sorting hook -- comp function -- array of long's (see below) - * - * We use "u" to point to an array of long integers. - */ -static void ang_sort_swap_hook_longs(vptr u, vptr v, int a, int b) -{ - long *x = (long*)(u); - - long temp; - - /* Swap */ - temp = x[a]; - x[a] = x[b]; - x[b] = temp; -} - - - -/* - * Save a slope - */ -static void vinfo_init_aux(vinfo_hack *hack, int y, int x, long m) -{ - int i; - - /* Handle "legal" slopes */ - if ((m > 0) && (m <= SCALE)) - { - /* Look for that slope */ - for (i = 0; i < hack->num_slopes; i++) - { - if (hack->slopes[i] == m) break; - } - - /* New slope */ - if (i == hack->num_slopes) - { - /* Paranoia */ - if (hack->num_slopes >= VINFO_MAX_SLOPES) - { - quit_fmt("Too many slopes (%d)!", - VINFO_MAX_SLOPES); - } - - /* Save the slope, and advance */ - hack->slopes[hack->num_slopes++] = m; - } - } - - /* Track slope range */ - if (hack->slopes_min[y][x] > m) hack->slopes_min[y][x] = m; - if (hack->slopes_max[y][x] < m) hack->slopes_max[y][x] = m; -} - - - -/* - * Initialize the "vinfo" array - * - * Full Octagon (radius 20), Grids=1149 - * - * Quadrant (south east), Grids=308, Slopes=251 - * - * Octant (east then south), Grids=161, Slopes=126 - * - * This function assumes that VINFO_MAX_GRIDS and VINFO_MAX_SLOPES - * have the correct values, which can be derived by setting them to - * a number which is too high, running this function, and using the - * error messages to obtain the correct values. - */ -errr vinfo_init(void) -{ - int i, y, x; - - long m; - - vinfo_hack *hack; - - int num_grids = 0; - - int queue_head = 0; - int queue_tail = 0; - vinfo_type *queue[VINFO_MAX_GRIDS*2]; - - - /* Make hack */ - MAKE(hack, vinfo_hack); - - - /* Analyze grids */ - for (y = 0; y <= MAX_SIGHT; ++y) - { - for (x = y; x <= MAX_SIGHT; ++x) - { - /* Skip grids which are out of sight range */ - if (distance(0, 0, y, x) > MAX_SIGHT) continue; - - /* Default slope range */ - hack->slopes_min[y][x] = 999999999; - hack->slopes_max[y][x] = 0; - - /* Paranoia */ - if (num_grids >= VINFO_MAX_GRIDS) - { - quit_fmt("Too many grids (%d >= %d)!", - num_grids, VINFO_MAX_GRIDS); - } - - /* Count grids */ - num_grids++; - - /* Slope to the top right corner */ - m = SCALE * (1000L * y - 500) / (1000L * x + 500); - - /* Handle "legal" slopes */ - vinfo_init_aux(hack, y, x, m); - - /* Slope to top left corner */ - m = SCALE * (1000L * y - 500) / (1000L * x - 500); - - /* Handle "legal" slopes */ - vinfo_init_aux(hack, y, x, m); - - /* Slope to bottom right corner */ - m = SCALE * (1000L * y + 500) / (1000L * x + 500); - - /* Handle "legal" slopes */ - vinfo_init_aux(hack, y, x, m); - - /* Slope to bottom left corner */ - m = SCALE * (1000L * y + 500) / (1000L * x - 500); - - /* Handle "legal" slopes */ - vinfo_init_aux(hack, y, x, m); - } - } - - - /* Enforce maximal efficiency */ - if (num_grids < VINFO_MAX_GRIDS) - { - quit_fmt("Too few grids (%d < %d)!", - num_grids, VINFO_MAX_GRIDS); - } - - /* Enforce maximal efficiency */ - if (hack->num_slopes < VINFO_MAX_SLOPES) - { - quit_fmt("Too few slopes (%d < %d)!", - hack->num_slopes, VINFO_MAX_SLOPES); - } - - - /* Sort slopes numerically */ - ang_sort_comp = ang_sort_comp_hook_longs; - - /* Sort slopes numerically */ - ang_sort_swap = ang_sort_swap_hook_longs; - - /* Sort the (unique) slopes */ - ang_sort(hack->slopes, NULL, hack->num_slopes); - - - - /* Enqueue player grid */ - queue[queue_tail++] = &vinfo[0]; - - /* Process queue */ - while (queue_head < queue_tail) - { - int e; - - /* Index */ - e = queue_head; - - /* Dequeue next grid */ - queue_head++; - - /* Location of main grid */ - y = vinfo[e].grid_y[0]; - x = vinfo[e].grid_x[0]; - - - /* Compute grid offsets */ - vinfo[e].grid_y[0] = + y; - vinfo[e].grid_x[0] = + x; - vinfo[e].grid_y[1] = + x; - vinfo[e].grid_x[1] = + y; - vinfo[e].grid_y[2] = + x; - vinfo[e].grid_x[2] = -y; - vinfo[e].grid_y[3] = + y; - vinfo[e].grid_x[3] = -x; - vinfo[e].grid_y[4] = -y; - vinfo[e].grid_x[4] = -x; - vinfo[e].grid_y[5] = -x; - vinfo[e].grid_x[5] = -y; - vinfo[e].grid_y[6] = -x; - vinfo[e].grid_x[6] = + y; - vinfo[e].grid_y[7] = -y; - vinfo[e].grid_x[7] = + x; - - - /* Analyze slopes */ - for (i = 0; i < hack->num_slopes; ++i) - { - m = hack->slopes[i]; - - /* Memorize intersection slopes (for non-player-grids) */ - if ((e > 0) && - (hack->slopes_min[y][x] < m) && - (m < hack->slopes_max[y][x])) - { - switch (i / 32) - { - case 3: - vinfo[e].bits_3 |= (1L << (i % 32)); - break; - case 2: - vinfo[e].bits_2 |= (1L << (i % 32)); - break; - case 1: - vinfo[e].bits_1 |= (1L << (i % 32)); - break; - case 0: - vinfo[e].bits_0 |= (1L << (i % 32)); - break; - } - } - } - - - /* Default */ - vinfo[e].next_0 = &vinfo[0]; - - /* Grid next child */ - if (distance(0, 0, y, x + 1) <= MAX_SIGHT) - { - if ((queue[queue_tail - 1]->grid_y[0] != y) || - (queue[queue_tail - 1]->grid_x[0] != x + 1)) - { - vinfo[queue_tail].grid_y[0] = y; - vinfo[queue_tail].grid_x[0] = x + 1; - queue[queue_tail] = &vinfo[queue_tail]; - queue_tail++; - } - - vinfo[e].next_0 = &vinfo[queue_tail - 1]; - } - - - /* Default */ - vinfo[e].next_1 = &vinfo[0]; - - /* Grid diag child */ - if (distance(0, 0, y + 1, x + 1) <= MAX_SIGHT) - { - if ((queue[queue_tail - 1]->grid_y[0] != y + 1) || - (queue[queue_tail - 1]->grid_x[0] != x + 1)) - { - vinfo[queue_tail].grid_y[0] = y + 1; - vinfo[queue_tail].grid_x[0] = x + 1; - queue[queue_tail] = &vinfo[queue_tail]; - queue_tail++; - } - - vinfo[e].next_1 = &vinfo[queue_tail - 1]; - } - - - /* Hack -- main diagonal has special children */ - if (y == x) vinfo[e].next_0 = vinfo[e].next_1; - - - /* Extra values */ - vinfo[e].y = y; - vinfo[e].x = x; - vinfo[e].d = ((y > x) ? (y + x / 2) : (x + y / 2)); - vinfo[e].r = ((!y) ? x : (!x) ? y : (y == x) ? y : 0); - } - - - /* Verify maximal bits XXX XXX XXX */ - if (((vinfo[1].bits_3 | vinfo[2].bits_3) != VINFO_BITS_3) || - ((vinfo[1].bits_2 | vinfo[2].bits_2) != VINFO_BITS_2) || - ((vinfo[1].bits_1 | vinfo[2].bits_1) != VINFO_BITS_1) || - ((vinfo[1].bits_0 | vinfo[2].bits_0) != VINFO_BITS_0)) - { - quit("Incorrect bit masks!"); - } - - - /* Kill hack */ - KILL(hack, vinfo_hack); - - - /* Success */ - return (0); -} - - - -/* - * Forget the "CAVE_VIEW" grids, redrawing as needed - */ -void forget_view(void) -{ - int i; - - int fast_view_n = view_n; - - cave_type *c_ptr; - - - /* None to forget */ - if (!fast_view_n) return; - - /* Clear them all */ - for (i = 0; i < fast_view_n; i++) - { - int y = view_y[i]; - int x = view_x[i]; - - /* Access the grid */ - c_ptr = &cave[y][x]; - - /* Clear "CAVE_VIEW", "CAVE_SEEN" and player torch flags */ - c_ptr->info &= ~(CAVE_VIEW | CAVE_SEEN | CAVE_PLIT); - - /* Redraw */ - lite_spot(y, x); - } - - /* None left */ - view_n = 0; -} - - - -/* - * Calculate the complete field of view using a new algorithm - * - * If "view_y/x" and "temp_y/x" were global pointers to arrays of grids, as - * opposed to actual arrays of grids, then we could be more efficient by - * using "pointer swapping". - * - * Normally, vision along the major axes is more likely than vision - * along the diagonal axes, so we check the bits corresponding to - * the lines of sight near the major axes first. - * - * We use the "temp_y/x" array (and the "CAVE_TEMP" flag) to keep track of - * which grids were previously marked "CAVE_SEEN", since only those grids - * whose "CAVE_SEEN" value changes during this routine must be redrawn. - * - * This function is now responsible for maintaining the "CAVE_SEEN" - * flags as well as the "CAVE_VIEW" flags, which is good, because - * the only grids which normally need to be memorized and/or redrawn - * are the ones whose "CAVE_SEEN" flag changes during this routine. - * - * Basically, this function divides the "octagon of view" into octants of - * grids (where grids on the main axes and diagonal axes are "shared" by - * two octants), and processes each octant one at a time, processing each - * octant one grid at a time, processing only those grids which "might" be - * viewable, and setting the "CAVE_VIEW" flag for each grid for which there - * is an (unobstructed) line of sight from the center of the player grid to - * any internal point in the grid (and collecting these "CAVE_VIEW" grids - * into the "view_y/x" array), and setting the "CAVE_SEEN" flag for the grid - * if, in addition, the grid is "illuminated" in some way. - * - * This function relies on a theorem (suggested and proven by Mat Hostetter) - * which states that in each octant of a field of view, a given grid will - * be "intersected" by one or more unobstructed "lines of sight" from the - * center of the player grid if and only if it is "intersected" by at least - * one such unobstructed "line of sight" which passes directly through some - * corner of some grid in the octant which is not shared by any other octant. - * The proof is based on the fact that there are at least three significant - * lines of sight involving any non-shared grid in any octant, one which - * intersects the grid and passes though the corner of the grid closest to - * the player, and two which "brush" the grid, passing through the "outer" - * corners of the grid, and that any line of sight which intersects a grid - * without passing through the corner of a grid in the octant can be "slid" - * slowly towards the corner of the grid closest to the player, until it - * either reaches it or until it brushes the corner of another grid which - * is closer to the player, and in either case, the existanc of a suitable - * line of sight is thus demonstrated. - * - * It turns out that in each octant of the radius 20 "octagon of view", - * there are 161 grids (with 128 not shared by any other octant), and there - * are exactly 126 distinct "lines of sight" passing from the center of the - * player grid through any corner of any non-shared grid in the octant. To - * determine if a grid is "viewable" by the player, therefore, you need to - * simply show that one of these 126 lines of sight intersects the grid but - * does not intersect any wall grid closer to the player. So we simply use - * a bit vector with 126 bits to represent the set of interesting lines of - * sight which have not yet been obstructed by wall grids, and then we scan - * all the grids in the octant, moving outwards from the player grid. For - * each grid, if any of the lines of sight which intersect that grid have not - * yet been obstructed, then the grid is viewable. Furthermore, if the grid - * is a wall grid, then all of the lines of sight which intersect the grid - * should be marked as obstructed for future reference. Also, we only need - * to check those grids for whom at least one of the "parents" was a viewable - * non-wall grid, where the parents include the two grids touching the grid - * but closer to the player grid (one adjacent, and one diagonal). For the - * bit vector, we simply use 4 32-bit integers. All of the static values - * which are needed by this function are stored in the large "vinfo" array - * (above), which is machine generated by another program. XXX XXX XXX - * - * Hack -- The queue must be able to hold more than VINFO_MAX_GRIDS grids - * because the grids at the edge of the field of view use "grid zero" as - * their children, and the queue must be able to hold several of these - * special grids. Because the actual number of required grids is bizarre, - * we simply allocate twice as many as we would normally need. XXX XXX XXX - */ -void update_view(void) -{ - int i, o; - int y, x; - - int radius; - - int fast_view_n = view_n; - - int fast_temp_n = 0; - - cave_type *c_ptr; - - u16b info; - - - /*** Step 0 -- Begin ***/ - - /* Save the old "view" grids for later */ - for (i = 0; i < fast_view_n; i++) - { - /* Location */ - y = view_y[i]; - x = view_x[i]; - - /* Grid */ - c_ptr = &cave[y][x]; - - /* Get grid info */ - info = c_ptr->info; - ; - - /* Save "CAVE_SEEN" grids */ - if (info & (CAVE_SEEN)) - { - /* Set "CAVE_TEMP" flag */ - info |= (CAVE_TEMP); - - /* Save grid for later */ - temp_y[fast_temp_n] = y; - temp_x[fast_temp_n++] = x; - } - - /* Clear "CAVE_VIEW", "CAVE_SEEN" and player torch flags */ - info &= ~(CAVE_VIEW | CAVE_SEEN | CAVE_PLIT); - - /* Save cave info */ - c_ptr->info = info; - } - - /* Reset the "view" array */ - fast_view_n = 0; - - /* Extract "radius" value */ - radius = p_ptr->cur_lite; - - /* Handle real light */ - if (radius > 0) ++radius; - - - /*** Step 1 -- player grid ***/ - - /* Player grid */ - c_ptr = &cave[p_ptr->py][p_ptr->px]; - - /* Get grid info */ - info = c_ptr->info; - - /* Assume viewable */ - info |= (CAVE_VIEW); - - /* Torch-lit grid */ - if (0 < radius) - { - /* Mark as "CAVE_SEEN" and torch-lit */ - info |= (CAVE_SEEN | CAVE_PLIT); - } - - - /* Perma-lit grid */ - else if (info & (CAVE_GLOW)) - { - /* Mark as "CAVE_SEEN" */ - info |= (CAVE_SEEN); - } - - /* Save cave info */ - c_ptr->info = info; - - /* Save in array */ - view_y[fast_view_n] = p_ptr->py; - view_x[fast_view_n++] = p_ptr->px; - - - /*** Step 2 -- octants ***/ - - /* Scan each octant */ - for (o = 0; o < 8; o++) - { - vinfo_type *p; - - /* Last added */ - vinfo_type *last = &vinfo[0]; - - /* Grid queue */ - int queue_head = 0; - int queue_tail = 0; - vinfo_type *queue[VINFO_MAX_GRIDS*2]; - - /* Slope bit vector */ - u32b bits0 = VINFO_BITS_0; - u32b bits1 = VINFO_BITS_1; - u32b bits2 = VINFO_BITS_2; - u32b bits3 = VINFO_BITS_3; - - /* Reset queue */ - queue_head = queue_tail = 0; - - /* Initial grids */ - queue[queue_tail++] = &vinfo[1]; - queue[queue_tail++] = &vinfo[2]; - - /* Process queue */ - while (queue_head < queue_tail) - { - /* Dequeue next grid */ - p = queue[queue_head++]; - - /* Check bits */ - if ((bits0 & (p->bits_0)) || - (bits1 & (p->bits_1)) || - (bits2 & (p->bits_2)) || - (bits3 & (p->bits_3))) - { - /* Extract coordinate value */ - y = p_ptr->py + p->grid_y[o]; - x = p_ptr->px + p->grid_x[o]; - - /* Access the grid */ - c_ptr = &cave[y][x]; - - /* Get grid info */ - info = c_ptr->info; - - /* Handle wall */ - if (info & (CAVE_WALL)) - { - /* Clear bits */ - bits0 &= ~(p->bits_0); - bits1 &= ~(p->bits_1); - bits2 &= ~(p->bits_2); - bits3 &= ~(p->bits_3); - - /* Newly viewable wall */ - if (!(info & (CAVE_VIEW))) - { - /* Mark as viewable */ - info |= (CAVE_VIEW); - - /* Torch-lit grids */ - if (p->d < radius) - { - /* Mark as "CAVE_SEEN" and torch-lit */ - info |= (CAVE_SEEN | CAVE_PLIT); - } - - /* Monster-lit grids */ - else if (info & (CAVE_MLIT)) - { - /* Mark as "CAVE_SEEN" */ - info |= (CAVE_SEEN); - } - - /* Perma-lit grids */ - else if (info & (CAVE_GLOW)) - { - /* Hack -- move towards player */ - int yy = (y < p_ptr->py) ? (y + 1) : (y > p_ptr->py) ? (y - 1) : y; - int xx = (x < p_ptr->px) ? (x + 1) : (x > p_ptr->px) ? (x - 1) : x; - - /* Check for "simple" illumination */ - if (cave[yy][xx].info & (CAVE_GLOW)) - { - /* Mark as seen */ - info |= (CAVE_SEEN); - } - } - - /* Save cave info */ - c_ptr->info = info; - - /* Save in array */ - view_y[fast_view_n] = y; - view_x[fast_view_n++] = x; - } - } - - /* Handle non-wall */ - else - { - /* Enqueue child */ - if (last != p->next_0) - { - queue[queue_tail++] = last = p->next_0; - } - - /* Enqueue child */ - if (last != p->next_1) - { - queue[queue_tail++] = last = p->next_1; - } - - /* Newly viewable non-wall */ - if (!(info & (CAVE_VIEW))) - { - /* Mark as "viewable" */ - info |= (CAVE_VIEW); - - /* Torch-lit grids */ - if (p->d < radius) - { - /* Mark as "CAVE_SEEN" and torch-lit */ - info |= (CAVE_SEEN | CAVE_PLIT); - } - - /* Perma-lit or monster-lit grids */ - else if (info & (CAVE_GLOW | CAVE_MLIT)) - { - /* Mark as "CAVE_SEEN" */ - info |= (CAVE_SEEN); - } - - /* Save cave info */ - c_ptr->info = info; - - /* Save in array */ - view_y[fast_view_n] = y; - view_x[fast_view_n++] = x; - } - } - } - } - } - - - /*** Step 3 -- Complete the algorithm ***/ - - /* Handle blindness */ - if (p_ptr->blind) - { - /* Process "new" grids */ - for (i = 0; i < fast_view_n; i++) - { - /* Location */ - y = view_y[i]; - x = view_x[i]; - - /* Grid cannot be "CAVE_SEEN" */ - cave[y][x].info &= ~(CAVE_SEEN); - } - } - - /* Process "new" grids */ - for (i = 0; i < fast_view_n; i++) - { - /* Location */ - y = view_y[i]; - x = view_x[i]; - - /* Get grid info */ - info = cave[y][x].info; - - /* Was not "CAVE_SEEN", is now "CAVE_SEEN" */ - if ((info & (CAVE_SEEN)) && !(info & (CAVE_TEMP))) - { - /* Note */ - note_spot(y, x); - - /* Redraw */ - lite_spot(y, x); - } - } - - /* Process "old" grids */ - for (i = 0; i < fast_temp_n; i++) - { - /* Location */ - y = temp_y[i]; - x = temp_x[i]; - - /* Grid */ - c_ptr = &cave[y][x]; - - /* Get grid info */ - info = c_ptr->info; - - /* Clear "CAVE_TEMP" flag */ - info &= ~(CAVE_TEMP); - - /* Save cave info */ - c_ptr->info = info; - - /* Was "CAVE_SEEN", is now not "CAVE_SEEN" */ - if (!(info & (CAVE_SEEN))) - { - /* Redraw */ - lite_spot(y, x); - } - } - - - /* Save 'view_n' */ - view_n = fast_view_n; -} - - -/* - * Clear monster light - */ -void forget_mon_lite(void) -{ - int i, y, x; - - /* Process all the monster-lit grids */ - for (i = 0; i < lite_n; i++) - { - /* Access location */ - y = lite_y[i]; - x = lite_x[i]; - - /* Clear monster light flag */ - cave[y][x].info &= ~(CAVE_MLIT); - } - - /* Forget light array */ - lite_n = 0; -} - - -/* - * Update squares illuminated by monsters - * - * Code taken from Steven Fuerst's work for ZAngband, without support - * for multiple lite radii, and with necessary modifications for different - * internal representation of dungeon/wilderness. Other minor changes - * are mine... - * - * I'm not sure if I can handle wide radius well. Consider the following - * example, with p carrying a radius 3 light source: - * - * ##%# - * .x.. - * p##@ - * - * % should be illuminated, although the beam path is entirely out of - * player's los (because of grid-based nature of cave representation)... - * And I'm extremely reluctant to introduce symmetrical los. The current - * asymmetrical system has its own merit, and all the rules of games are - * asymmetrical, in some way or another... - * - * The code below exploits special characteristics of radius one light - * where one can fairly safely use light source's visibility (in terms of los) - * to determine if we can illuminate walls XXX - * - * This function works within the current player's field of view - * calculated by update_view(), so it should normally be called - * whenever FoV is updated (== PU_VIEW | PU_MON_LITE). The other - * case is when RF9_HAS_LITE monsters have moved or dead. Monster - * creation occurs out of LoS, so I chose not to take this into - * consideration. - * - * The CAVE_TEMP flag is used by the function to remember "old" monster-lit - * grids so that it can only redraw squares whose visibility has changed. - * - * Doing this in the update_view() order (update "new" grids, then "old") - * would result in bizarre lighting effects XXX XXX - * - * It has been made possible again to draw torch/monster-lit grids in - * different colours, even when they are in permanently lit locations - * by using (CAVE_PLIT|CAVE_MLIT) as if it were old CAVE_LITE, but I don't - * think it's appropriate for torch lights to be visible under the Sun :) - * or brighter light, and it doesn't work well with PernAngband's already - * colourful terrain features in aesthetically pleasing ways... -- pelpel - */ -void update_mon_lite(void) -{ - int i, y, x, d; - int fy, fx; - - cave_type *c_ptr; - u16b info; - - bool_ invis; - - s16b fast_lite_n = lite_n; - s16b fast_temp_n; - - - /* Mega-Hack -- It's unnecessary there */ - if (p_ptr->wild_mode) return; - - /* Handle special case -- Blindness */ - if (p_ptr->blind) - { - for (i = 0; i < fast_lite_n; i++) - { - /* Light location */ - y = lite_y[i]; - x = lite_x[i]; - - /* Forget monster light and view */ - cave[y][x].info &= ~(CAVE_MLIT | CAVE_SEEN); - - /* Redraw spot */ - /* lite_spot(y, x); */ - } - - /* Clear the light list */ - lite_n = 0; - - /* Done */ - return; - } - - - /* Remember and clear all monster-lit grids */ - for (i = 0; i < fast_lite_n; i++) - { - /* Lit location */ - y = lite_y[i]; - x = lite_x[i]; - - /* Access grid */ - c_ptr = &cave[y][x]; - - /* Access cave info of the grid */ - info = c_ptr->info; - - /* Remember it, by setting the CAVE_TEMP flag */ - info |= (CAVE_TEMP); - - /* Forget monster light */ - info &= ~(CAVE_MLIT); - - /* Unseen unless it's glowing or illuminated by player light source */ - if (!(info & (CAVE_GLOW | CAVE_PLIT))) - { - info &= ~(CAVE_SEEN); - } - - /* Save cave info flags */ - c_ptr->info = info; - } - - - /* Clear the temp list */ - fast_temp_n = 0; - - /* Loop through monsters, adding newly lit grids to changes list */ - for (i = 1; i < m_max; i++) - { - monster_type *m_ptr = &m_list[i]; - monster_race *r_ptr; - - /* Skip dead monsters */ - if (!m_ptr->r_idx) continue; - - /* Skip out-of-sight monsters (MAX_SIGHT + max radius) */ - if (m_ptr->cdis > MAX_SIGHT + 1) continue; - - /* Access monster race info (with possible ego mods) */ - r_ptr = race_info_idx(m_ptr->r_idx, m_ptr->ego); - - /* Skip monsters not carrying light source */ - if (!(r_ptr->flags9 & RF9_HAS_LITE)) continue; - - /* Access the location */ - fy = m_ptr->fy; - fx = m_ptr->fx; - - /* Extract monster grid visibility */ - invis = !player_has_los_bold(fy, fx); - - /* Nested loops may be a bad idea here XXX */ - for (d = 0; d < 9; d++) - { - y = fy + ddy_ddd[d]; - x = fx + ddx_ddd[d]; - - /* Paranoia */ - /* if (!in_bounds(y, x)) continue; */ - - /* Access the grid */ - c_ptr = &cave[y][x]; - - /* Access cave info flags */ - info = c_ptr->info; - - /* Don't care grids out of player's los */ - if (!(info & (CAVE_VIEW))) continue; - - /* - * Avoid processing already monster-lit grids, - * for efficiency and to avoid temp array overflow - */ - if (info & (CAVE_MLIT)) continue; - - /* - * Hack XXX XXX -- light shouldn't penetrate walls - * - * OK NG - * .#. p#. | p. .p. p.. - * p.@ ..@ | .# .#. .#. - * | .@ .@. ..@ - * - * So if a monster carrying light source is out of player LoS, - * walls aren't illuminated. - * - * CAVEAT: % will be illuminated in cases like this: - * - * #%..@ - * p.... - * - * We don't have four sides for a wall grid, so... - */ - if (invis && (f_info[c_ptr->feat].flags1 & FF1_NO_VISION)) continue; - - /* Give monster light to the location */ - c_ptr->info |= (CAVE_MLIT | CAVE_SEEN); - - /* Save the location */ - temp_y[fast_temp_n] = y; - temp_x[fast_temp_n] = x; - fast_temp_n++; - } - } - - /* Process old grids */ - for (i = 0; i < fast_lite_n; i++) - { - /* Access location */ - y = lite_y[i]; - x = lite_x[i]; - - /* Access grid */ - c_ptr = &cave[y][x]; - - /* Was lit, is no longer lit */ - if (!(c_ptr->info & (CAVE_MLIT))) - { - /* Clear the temp flag */ - c_ptr->info &= ~(CAVE_TEMP); - - /* See if there was a visible monster */ - if (player_has_los_bold(y, x) && c_ptr->m_idx) - { - /* Hide the monster */ - update_mon(c_ptr->m_idx, FALSE); - } - else - { - /* Redraw */ - lite_spot(y, x); - } - } - } - - /* Copy the temp array into the light array */ - for (i = 0; i < fast_temp_n; i++) - { - /* Access location */ - y = temp_y[i]; - x = temp_x[i]; - - /* Access grid */ - c_ptr = &cave[y][x]; - - - /* No changes in illumination */ - if (c_ptr->info & (CAVE_TEMP)) - { - /* Clear the temp flag */ - c_ptr->info &= ~(CAVE_TEMP); - } - - /* Was not lit, is now lit */ - else - { - /* Remember the location, if appropriate */ - note_spot(y, x); - - /* See if there is a monster */ - if (c_ptr->m_idx) - { - /* Show it */ - update_mon(c_ptr->m_idx, FALSE); - } - else - { - /* Redraw */ - lite_spot(y, x); - } - } - - - /* Save the location */ - lite_y[i] = y; - lite_x[i] = x; - } - - /* Save lite_n */ - lite_n = fast_temp_n; - - /* Forget temp */ - temp_n = 0; -} - - - - - - -/* - * Hack -- provide some "speed" for the "flow" code - * This entry is the "current index" for the "when" field - * Note that a "when" value of "zero" means "not used". - * - * Note that the "cost" indexes from 1 to 127 are for - * "old" data, and from 128 to 255 are for "new" data. - * - * This means that as long as the player does not "teleport", - * then any monster up to 128 + MONSTER_FLOW_DEPTH will be - * able to track down the player, and in general, will be - * able to track down either the player or a position recently - * occupied by the player. - */ -static int flow_n = 0; - - -/* - * Hack -- forget the "flow" information - */ -void forget_flow(void) -{ - int x, y; - - /* Nothing to forget */ - if (!flow_n) return; - - /* Check the entire dungeon */ - for (y = 0; y < cur_hgt; y++) - { - for (x = 0; x < cur_wid; x++) - { - /* Forget the old data */ - cave[y][x].cost = 0; - cave[y][x].when = 0; - } - } - - /* Start over */ - flow_n = 0; -} - - -/* - * Hack -- Allow us to treat the "seen" array as a queue - */ -static int flow_head = 0; -static int flow_tail = 0; - - -/* - * Take note of a reachable grid. Assume grid is legal. - */ -static void update_flow_aux(int y, int x, int n) -{ - cave_type *c_ptr; - - int old_head = flow_head; - - - /* Get the grid */ - c_ptr = &cave[y][x]; - - /* Ignore "pre-stamped" entries */ - if (c_ptr->when == flow_n) return; - - /* Ignore "walls" and "rubble" */ - if (c_ptr->feat >= FEAT_RUBBLE) return; - - /* Save the time-stamp */ - c_ptr->when = flow_n; - - /* Save the flow cost */ - c_ptr->cost = n; - - /* Hack -- limit flow depth */ - if (n == MONSTER_FLOW_DEPTH) return; - - /* Enqueue that entry */ - temp_y[flow_head] = y; - temp_x[flow_head] = x; - - /* Advance the queue */ - if (++flow_head == TEMP_MAX) flow_head = 0; - - /* Hack -- notice overflow by forgetting new entry */ - if (flow_head == flow_tail) flow_head = old_head; -} - - -/* - * Hack -- fill in the "cost" field of every grid that the player - * can "reach" with the number of steps needed to reach that grid. - * This also yields the "distance" of the player from every grid. - * - * In addition, mark the "when" of the grids that can reach - * the player with the incremented value of "flow_n". - * - * Hack -- use the "seen" array as a "circular queue". - * - * We do not need a priority queue because the cost from grid - * to grid is always "one" and we process them in order. - */ -void update_flow(void) -{ - int x, y, d; - - /* Hack -- disabled */ - if (!flow_by_sound) return; - - /* Paranoia -- make sure the array is empty */ - if (temp_n) return; - - /* Cycle the old entries (once per 128 updates) */ - if (flow_n == 255) - { - /* Rotate the time-stamps */ - for (y = 0; y < cur_hgt; y++) - { - for (x = 0; x < cur_wid; x++) - { - int w = cave[y][x].when; - cave[y][x].when = (w > 128) ? (w - 128) : 0; - } - } - - /* Restart */ - flow_n = 127; - } - - /* Start a new flow (never use "zero") */ - flow_n++; - - - /* Reset the "queue" */ - flow_head = flow_tail = 0; - - /* Add the player's grid to the queue */ - update_flow_aux(p_ptr->py, p_ptr->px, 0); - - /* Now process the queue */ - while (flow_head != flow_tail) - { - /* Extract the next entry */ - y = temp_y[flow_tail]; - x = temp_x[flow_tail]; - - /* Forget that entry */ - if (++flow_tail == TEMP_MAX) flow_tail = 0; - - /* Add the "children" */ - for (d = 0; d < 8; d++) - { - /* Add that child if "legal" */ - update_flow_aux(y + ddy_ddd[d], x + ddx_ddd[d], cave[y][x].cost + 1); - } - } - - /* Forget the flow info */ - flow_head = flow_tail = 0; -} - - - - - - - -/* - * Hack -- map the current panel (plus some) ala "magic mapping" - */ -void map_area(void) -{ - int i, x, y, y1, y2, x1, x2; - - cave_type *c_ptr; - - - /* Pick an area to map */ - y1 = panel_row_min - randint(10); - y2 = panel_row_max + randint(10); - x1 = panel_col_min - randint(20); - x2 = panel_col_max + randint(20); - - /* Speed -- shrink to fit legal bounds */ - if (y1 < 1) y1 = 1; - if (y2 > cur_hgt - 2) y2 = cur_hgt - 2; - if (x1 < 1) x1 = 1; - if (x2 > cur_wid - 2) x2 = cur_wid - 2; - - /* Scan that area */ - for (y = y1; y <= y2; y++) - { - for (x = x1; x <= x2; x++) - { - c_ptr = &cave[y][x]; - - /* All non-walls are "checked" */ - if (!is_wall(c_ptr)) - { - /* Memorize normal features */ - if (!cave_plain_floor_grid(c_ptr)) - { - /* Memorize the object */ - c_ptr->info |= (CAVE_MARK); - } - - /* Memorize known walls */ - for (i = 0; i < 8; i++) - { - c_ptr = &cave[y + ddy_ddd[i]][x + ddx_ddd[i]]; - - /* Memorize walls (etc) */ - if (is_wall(c_ptr)) - { - /* Memorize the walls */ - c_ptr->info |= (CAVE_MARK); - } - } - } - } - } - - /* Redraw map */ - p_ptr->redraw |= (PR_MAP); - - /* Window stuff */ - p_ptr->window |= (PW_OVERHEAD); -} - - - -/* - * Light up the dungeon using "clairvoyance" - * - * This function "illuminates" every grid in the dungeon, memorizes all - * "objects", memorizes all grids as with magic mapping, and, under the - * standard option settings (view_perma_grids but not view_torch_grids) - * memorizes all floor grids too. - * - * Note that if "view_perma_grids" is not set, we do not memorize floor - * grids, since this would defeat the purpose of "view_perma_grids", not - * that anyone seems to play without this option. - * - * Note that if "view_torch_grids" is set, we do not memorize floor grids, - * since this would prevent the use of "view_torch_grids" as a method to - * keep track of what grids have been observed directly. - */ -void wiz_lite(void) -{ - int i, y, x; - - - /* Memorize objects */ - for (i = 1; i < o_max; i++) - { - object_type *o_ptr = &o_list[i]; - - /* Skip dead objects */ - if (!o_ptr->k_idx) continue; - - /* Skip held objects */ - if (o_ptr->held_m_idx) continue; - - /* Memorize */ - o_ptr->marked = TRUE; - } - - /* Scan all normal grids */ - for (y = 1; y < cur_hgt - 1; y++) - { - /* Scan all normal grids */ - for (x = 1; x < cur_wid - 1; x++) - { - cave_type *c_ptr = &cave[y][x]; - - if (c_ptr->m_idx) - { - monster_type *m_ptr = &m_list[c_ptr->m_idx]; - monster_race *r_ptr = race_inf(m_ptr); - - if (r_ptr->flags9 & RF9_MIMIC) - { - object_type *o_ptr = &o_list[m_ptr->hold_o_idx]; - - o_ptr->marked = TRUE; - } - } - - /* Process all non-walls */ - /* if (c_ptr->feat < FEAT_SECRET) */ - { - /* Scan all neighbors */ - for (i = 0; i < 9; i++) - { - int yy = y + ddy_ddd[i]; - int xx = x + ddx_ddd[i]; - - /* Get the grid */ - c_ptr = &cave[yy][xx]; - - /* Perma-lite the grid */ - c_ptr->info |= (CAVE_GLOW); - - /* Memorize normal features */ - if (!cave_plain_floor_grid(c_ptr)) - { - /* Memorize the grid */ - c_ptr->info |= (CAVE_MARK); - } - - /* Normally, memorize floors (see above) */ - if (view_perma_grids && !view_torch_grids) - { - /* Memorize the grid */ - c_ptr->info |= (CAVE_MARK); - } - } - } - } - } - - /* Fully update the visuals */ - p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE); - - /* Redraw map */ - p_ptr->redraw |= (PR_MAP); - - /* Window stuff */ - p_ptr->window |= (PW_OVERHEAD); -} - -void wiz_lite_extra(void) -{ - int y, x; - for (y = 0; y < cur_hgt; y++) - { - for (x = 0; x < cur_wid; x++) - { - cave[y][x].info |= (CAVE_GLOW | CAVE_MARK); - } - } - wiz_lite(); -} - -/* - * Forget the dungeon map (ala "Thinking of Maud..."). - */ -void wiz_dark(void) -{ - int i, y, x; - - - /* Forget every grid */ - for (y = 0; y < cur_hgt; y++) - { - for (x = 0; x < cur_wid; x++) - { - cave_type *c_ptr = &cave[y][x]; - - /* Process the grid */ - c_ptr->info &= ~(CAVE_MARK); - } - } - - /* Forget all objects */ - for (i = 1; i < o_max; i++) - { - object_type *o_ptr = &o_list[i]; - - /* Skip dead objects */ - if (!o_ptr->k_idx) continue; - - /* Skip held objects */ - if (o_ptr->held_m_idx) continue; - - /* Forget the object */ - o_ptr->marked = FALSE; - } - - /* Fully update the visuals */ - p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE); - - /* Redraw map */ - p_ptr->redraw |= (PR_MAP); - - /* Window stuff */ - p_ptr->window |= (PW_OVERHEAD); -} - - - - - -/* - * Change the "feat" flag for a grid, and notice/redraw the grid - */ -void cave_set_feat(int y, int x, int feat) -{ - cave_type *c_ptr = &cave[y][x]; - - /* Change the feature */ - c_ptr->feat = feat; - - /* - * Handle "wall/door" grids - * - * XXX XXX XXX This assumes c_ptr->mimic doesn't mimic terrain - * features whose LoS behaviour is different from its own, in - * most cases. Level boundaries are the most notable exception, - * where "real" terrain is always FEAT_PERM_SOLID, and the fact - * is (ab)used to prevent out-of-range access to the cave array. - * If we were going to implement an evil dungeon type in which - * everything is mimicked, then this function, los(), projectable(), - * project_path() and maybe some functions in melee2.c might - * better use c_ptr->mimic when it's set -- pelpel - */ - if (!cave_sight_grid(c_ptr)) - { - c_ptr->info |= (CAVE_WALL); - } - - /* Handle "floor"/etc grids */ - else - { - c_ptr->info &= ~(CAVE_WALL); - } - - /* Notice & Redraw */ - if (character_dungeon) - { - /* Notice */ - note_spot(y, x); - - /* Redraw */ - lite_spot(y, x); - } -} - - -/* - * Place floor terrain at (y, x) according to dungeon info - */ -void place_floor(int y, int x) -{ - cave_set_feat(y, x, floor_type[rand_int(100)]); -} - -/* - * This routine is used when the current feature gets convert to a floor and - * the possible floor types include glass which is permanent. An unpassable - * feature is undesirable, so the glass gets convert to molten glass which - * is passable. - */ -void place_floor_convert_glass(int y, int x) -{ - place_floor(y, x); - - if (cave[y][x].feat == 188) cave[y][x].feat = 103; -} - -/* - * Place a cave filler at (y, x) - */ -void place_filler(int y, int x) -{ - cave_set_feat(y, x, fill_type[rand_int(100)]); -} - - -/* - * Calculate "incremental motion". Used by project() and shoot(). - * Assumes that (*y,*x) lies on the path from (y1,x1) to (y2,x2). - */ -void mmove2(int *y, int *x, int y1, int x1, int y2, int x2) -{ - int dy, dx, dist, shift; - - /* Extract the distance travelled */ - dy = (*y < y1) ? y1 - *y : *y - y1; - dx = (*x < x1) ? x1 - *x : *x - x1; - - /* Number of steps */ - dist = (dy > dx) ? dy : dx; - - /* We are calculating the next location */ - dist++; - - - /* Calculate the total distance along each axis */ - dy = (y2 < y1) ? (y1 - y2) : (y2 - y1); - dx = (x2 < x1) ? (x1 - x2) : (x2 - x1); - - /* Paranoia -- Hack -- no motion */ - if (!dy && !dx) return; - - - /* Move mostly vertically */ - if (dy > dx) - { - /* Extract a shift factor */ - shift = (dist * dx + (dy - 1) / 2) / dy; - - /* Sometimes move along the minor axis */ - (*x) = (x2 < x1) ? (x1 - shift) : (x1 + shift); - - /* Always move along major axis */ - (*y) = (y2 < y1) ? (y1 - dist) : (y1 + dist); - } - - /* Move mostly horizontally */ - else - { - /* Extract a shift factor */ - shift = (dist * dy + (dx - 1) / 2) / dx; - - /* Sometimes move along the minor axis */ - (*y) = (y2 < y1) ? (y1 - shift) : (y1 + shift); - - /* Always move along major axis */ - (*x) = (x2 < x1) ? (x1 - dist) : (x1 + dist); - } -} - - - -/* - * Determine if a bolt spell cast from (y1,x1) to (y2,x2) will arrive - * at the final destination, assuming no monster gets in the way. - * - * This is slightly (but significantly) different from "los(y1,x1,y2,x2)". - */ -bool_ projectable(int y1, int x1, int y2, int x2) -{ - int dist, y, x; - - /* Start at the initial location */ - y = y1, x = x1; - - /* See "project()" */ - for (dist = 0; dist <= MAX_RANGE; dist++) - { - /* Check for arrival at "final target" */ - /* - * NB: this check was AFTER the 'never pass - * thru walls' clause, below. Switching them - * lets monsters shoot a the player if s/he is - * visible but in a wall - */ - if ((x == x2) && (y == y2)) return (TRUE); - - /* Never pass through walls */ - if (dist && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) break; - - /* Calculate the new location */ - mmove2(&y, &x, y1, x1, y2, x2); - } - - - /* Assume obstruction */ - return (FALSE); -} - - - - -/* - * Standard "find me a location" function - * - * Obtains a legal location within the given distance of the initial - * location, and with "los()" from the source to destination location. - * - * This function is often called from inside a loop which searches for - * locations while increasing the "d" distance. - * - * Currently the "m" parameter is unused. - */ -void scatter(int *yp, int *xp, int y, int x, int d) -{ - int nx, ny; - int attempts_left = 5000; - - /* Pick a location */ - while (--attempts_left) - { - /* Pick a new location */ - ny = rand_spread(y, d); - nx = rand_spread(x, d); - - /* Ignore illegal locations and outer walls */ - if (!in_bounds(ny, nx)) continue; - - /* Ignore "excessively distant" locations */ - if ((d > 1) && (distance(y, x, ny, nx) > d)) continue; - - /* Require "line of sight" */ - if (los(y, x, ny, nx)) break; - } - - if (attempts_left > 0) - { - /* Save the location */ - (*yp) = ny; - (*xp) = nx; - } -} - - - - -/* - * Track a new monster - */ -void health_track(int m_idx) -{ - /* Track a new guy */ - health_who = m_idx; - - /* Redraw (later) */ - p_ptr->redraw |= (PR_HEALTH); -} - - - -/* - * Hack -- track the given monster race - */ -void monster_race_track(int r_idx, int ego) -{ - /* Save this monster ID */ - monster_race_idx = r_idx; - monster_ego_idx = ego; - - /* Window stuff */ - p_ptr->window |= (PW_MONSTER); -} - - - -/* - * Hack -- track the given object kind - */ -void object_track(object_type *o_ptr) -{ - /* Save this monster ID */ - tracked_object = o_ptr; - - /* Window stuff */ - p_ptr->window |= (PW_OBJECT); -} - - - -/* - * Something has happened to disturb the player. - * - * The first arg indicates a major disturbance, which affects search. - * - * All disturbance cancels repeated commands, resting, and running. - */ -void disturb(int stop_search) -{ - /* Cancel auto-commands */ - /* command_new = 0; */ - - /* Cancel repeated commands */ - if (command_rep) - { - /* Cancel */ - command_rep = 0; - - /* Redraw the state (later) */ - p_ptr->redraw |= (PR_STATE); - } - - /* Cancel Resting */ - if (resting) - { - /* Cancel */ - resting = 0; - - /* Redraw the state (later) */ - p_ptr->redraw |= (PR_STATE); - } - - /* Cancel running */ - if (running) - { - /* Cancel */ - running = 0; - - /* Calculate torch radius */ - p_ptr->update |= (PU_TORCH); - } - - /* Cancel searching if requested */ - if (stop_search && p_ptr->searching) - { - /* Cancel */ - p_ptr->searching = FALSE; - - /* Recalculate bonuses */ - p_ptr->update |= (PU_BONUS); - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - } - - /* Flush the input if requested */ - if (flush_disturb) flush(); -} - - -/* - * Hack -- Check if a level is a "quest" level - */ -int is_quest(int level) -{ - int i = random_quest_number(); - - /* Check quests */ - if (p_ptr->inside_quest) - return (p_ptr->inside_quest); - - if (i) return (QUEST_RANDOM); - - /* Nope */ - return (0); -} - - -/* - * Return the index of the random quest on this level - * (or zero) - */ -int random_quest_number() -{ - if ((dun_level >= 1) && (dun_level < MAX_RANDOM_QUEST) && - (dungeon_flags1 & DF1_PRINCIPAL) && - (random_quests[dun_level].type) && - (!random_quests[dun_level].done) && - (!is_randhero(dun_level))) - { - return dun_level; - } - - /* Nope */ - return 0; -} - - -/* - * handle spell effects - */ -int effect_pop() -{ - int i; - - for (i = 1; i < MAX_EFFECTS; i++) - if (!effects[i].time) - return i; - return -1; -} - -int new_effect(int type, int dam, int time, int cy, int cx, int rad, s32b flags) -{ - int i; - - if ((i = effect_pop()) == -1) return -1; - - effects[i].type = type; - effects[i].dam = dam; - effects[i].time = time; - effects[i].flags = flags; - effects[i].cx = cx; - effects[i].cy = cy; - effects[i].rad = rad; - return i; -} diff --git a/src/cave.cc b/src/cave.cc new file mode 100644 index 00000000..a0b8c7fc --- /dev/null +++ b/src/cave.cc @@ -0,0 +1,4952 @@ +/* File: cave.c */ + +/* Purpose: low level dungeon routines -BEN- */ + + +#include "angband.h" +#include "q_rand.h" + +#include +#include +#include + +/* + * Support for Adam Bolt's tileset, lighting and transparency effects + * by Robert Ruehlmann (rr9@angband.org) + */ + + +/* + * Approximate Distance between two points. + * + * When either the X or Y component dwarfs the other component, + * this function is almost perfect, and otherwise, it tends to + * over-estimate about one grid per fifteen grids of distance. + * + * Algorithm: hypot(dy,dx) = max(dy,dx) + min(dy,dx) / 2 + */ +int distance(int y1, int x1, int y2, int x2) +{ + int dy, dx, d; + + + /* Find the absolute y/x distance components */ + dy = (y1 > y2) ? (y1 - y2) : (y2 - y1); + dx = (x1 > x2) ? (x1 - x2) : (x2 - x1); + + /* Hack -- approximate the distance */ + d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1)); + + /* Return the distance */ + return (d); +} + + +/* + * Returns TRUE if a grid is considered to be a wall for the purpose + * of magic mapping / clairvoyance + */ +static bool_ is_wall(cave_type *c_ptr) +{ + byte feat; + + + /* Handle feature mimics */ + if (c_ptr->mimic) feat = c_ptr->mimic; + else feat = c_ptr->feat; + + /* Paranoia */ + if (feat >= max_f_idx) return FALSE; + + /* Vanilla floors and doors aren't considered to be walls */ + if (feat < FEAT_SECRET) return FALSE; + + /* Exception #1: a glass wall is a wall but doesn't prevent LOS */ + if (feat == FEAT_GLASS_WALL) return FALSE; + + /* Exception #2: an illusion wall is not a wall but obstructs view */ + if (feat == FEAT_ILLUS_WALL) return TRUE; + + /* Exception #3: a small tree is a floor but obstructs view */ + if (feat == FEAT_SMALL_TREES) return TRUE; + + /* Normal cases: use the WALL flag in f_info.txt */ + return (f_info[feat].flags1 & FF1_WALL) ? TRUE : FALSE; +} + + +/* + * A simple, fast, integer-based line-of-sight algorithm. By Joseph Hall, + * 4116 Brewster Drive, Raleigh NC 27606. Email to jnh@ecemwl.ncsu.edu. + * + * Returns TRUE if a line of sight can be traced from (x1,y1) to (x2,y2). + * + * The LOS begins at the center of the tile (x1,y1) and ends at the center of + * the tile (x2,y2). If los() is to return TRUE, all of the tiles this line + * passes through must be floor tiles, except for (x1,y1) and (x2,y2). + * + * We assume that the "mathematical corner" of a non-floor tile does not + * block line of sight. + * + * Because this function uses (short) ints for all calculations, overflow may + * occur if dx and dy exceed 90. + * + * Once all the degenerate cases are eliminated, the values "qx", "qy", and + * "m" are multiplied by a scale factor "f1 = abs(dx * dy * 2)", so that + * we can use integer arithmetic. + * + * We travel from start to finish along the longer axis, starting at the border + * between the first and second tiles, where the y offset = .5 * slope, taking + * into account the scale factor. See below. + * + * Also note that this function and the "move towards target" code do NOT + * share the same properties. Thus, you can see someone, target them, and + * then fire a bolt at them, but the bolt may hit a wall, not them. However, + * by clever choice of target locations, you can sometimes throw a "curve". + * + * Note that "line of sight" is not "reflexive" in all cases. + * + * Use the "projectable()" routine to test "spell/missile line of sight". + * + * Use the "update_view()" function to determine player line-of-sight. + */ +bool_ los(int y1, int x1, int y2, int x2) +{ + /* Delta */ + int dx, dy; + + /* Absolute */ + int ax, ay; + + /* Signs */ + int sx, sy; + + /* Fractions */ + int qx, qy; + + /* Scanners */ + int tx, ty; + + /* Scale factors */ + int f1, f2; + + /* Slope, or 1/Slope, of LOS */ + int m; + + + /* Extract the offset */ + dy = y2 - y1; + dx = x2 - x1; + + /* Extract the absolute offset */ + ay = ABS(dy); + ax = ABS(dx); + + + /* Handle adjacent (or identical) grids */ + if ((ax < 2) && (ay < 2)) return (TRUE); + + + /* Paranoia -- require "safe" origin */ + /* if (!in_bounds(y1, x1)) return (FALSE); */ + + + /* Directly South/North */ + if (!dx) + { + /* South -- check for walls */ + if (dy > 0) + { + for (ty = y1 + 1; ty < y2; ty++) + { + if (!cave_sight_bold(ty, x1)) return (FALSE); + } + } + + /* North -- check for walls */ + else + { + for (ty = y1 - 1; ty > y2; ty--) + { + if (!cave_sight_bold(ty, x1)) return (FALSE); + } + } + + /* Assume los */ + return (TRUE); + } + + /* Directly East/West */ + if (!dy) + { + /* East -- check for walls */ + if (dx > 0) + { + for (tx = x1 + 1; tx < x2; tx++) + { + if (!cave_sight_bold(y1, tx)) return (FALSE); + } + } + + /* West -- check for walls */ + else + { + for (tx = x1 - 1; tx > x2; tx--) + { + if (!cave_sight_bold(y1, tx)) return (FALSE); + } + } + + /* Assume los */ + return (TRUE); + } + + + /* Extract some signs */ + sx = (dx < 0) ? -1 : 1; + sy = (dy < 0) ? -1 : 1; + + + /* Vertical "knights" */ + if (ax == 1) + { + if (ay == 2) + { + if (cave_sight_bold(y1 + sy, x1)) return (TRUE); + } + } + + /* Horizontal "knights" */ + else if (ay == 1) + { + if (ax == 2) + { + if (cave_sight_bold(y1, x1 + sx)) return (TRUE); + } + } + + + /* Calculate scale factor div 2 */ + f2 = (ax * ay); + + /* Calculate scale factor */ + f1 = f2 << 1; + + + /* Travel horizontally */ + if (ax >= ay) + { + /* Let m = dy / dx * 2 * (dy * dx) = 2 * dy * dy */ + qy = ay * ay; + m = qy << 1; + + tx = x1 + sx; + + /* Consider the special case where slope == 1. */ + if (qy == f2) + { + ty = y1 + sy; + qy -= f1; + } + else + { + ty = y1; + } + + /* Note (below) the case (qy == f2), where */ + /* the LOS exactly meets the corner of a tile. */ + while (x2 - tx) + { + if (!cave_sight_bold(ty, tx)) return (FALSE); + + qy += m; + + if (qy < f2) + { + tx += sx; + } + else if (qy > f2) + { + ty += sy; + if (!cave_sight_bold(ty, tx)) return (FALSE); + qy -= f1; + tx += sx; + } + else + { + ty += sy; + qy -= f1; + tx += sx; + } + } + } + + /* Travel vertically */ + else + { + /* Let m = dx / dy * 2 * (dx * dy) = 2 * dx * dx */ + qx = ax * ax; + m = qx << 1; + + ty = y1 + sy; + + if (qx == f2) + { + tx = x1 + sx; + qx -= f1; + } + else + { + tx = x1; + } + + /* Note (below) the case (qx == f2), where */ + /* the LOS exactly meets the corner of a tile. */ + while (y2 - ty) + { + if (!cave_sight_bold(ty, tx)) return (FALSE); + + qx += m; + + if (qx < f2) + { + ty += sy; + } + else if (qx > f2) + { + tx += sx; + if (!cave_sight_bold(ty, tx)) return (FALSE); + qx -= f1; + ty += sy; + } + else + { + tx += sx; + qx -= f1; + ty += sy; + } + } + } + + /* Assume los */ + return (TRUE); +} + + + +/* + * Returns true if the player's grid is dark + */ +bool_ no_lite(void) +{ + return (!player_can_see_bold(p_ptr->py, p_ptr->px)); +} + + + +/* + * Determine if a given location may be "destroyed" + * + * Used by destruction spells, and for placing stairs, etc. + */ +bool_ cave_valid_bold(int y, int x) +{ + cave_type *c_ptr = &cave[y][x]; + + s16b this_o_idx, next_o_idx = 0; + + + /* Forbid perma-grids */ + if (cave_perma_grid(c_ptr)) return (FALSE); + + /* Check objects */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Forbid artifact grids */ + if ((o_ptr->art_name) || artifact_p(o_ptr)) return (FALSE); + } + + /* Accept */ + return (TRUE); +} + + + + +/* + * Hack -- Legal monster codes + */ +static cptr image_monster_hack = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/* + * Hack -- Legal monster codes for IBM pseudo-graphics + * + * Dropped. Although this option has long left unmaintained, hardcoding + * code points makes it impossible to update the font and prf files + * flexibly. And the normal graphics code still works with it -- pelpel + */ + +/* + * Mega-Hack -- Hallucinatory monster + */ +static void image_monster(byte *ap, char *cp) +{ + int n; + + switch (graphics_mode) + { + /* Text mode */ + case GRAPHICS_NONE: + { + n = strlen(image_monster_hack); + + /* Random symbol from set above */ + *cp = (image_monster_hack[rand_int(n)]); + + /* Random color */ + *ap = randint(15); + + break; + } + + /* Normal graphics */ + default: + { + /* Avoid player ghost */ + n = randint(max_r_idx); + + *cp = r_info[n].x_char; + + *ap = r_info[n].x_attr; + + break; + } + } +} + + + + +/* + * Hack -- Legal object codes + */ +static cptr image_object_hack = "?/|\\\"!$()_-=[]{},~"; + +/* + * Hardcoded IBM pseudo-graphics code points have been removed + * for the same reason as stated above -- pelpel + */ + +/* + * Mega-Hack -- Hallucinatory object + */ +static void image_object(byte *ap, char *cp) +{ + int n; + + switch (graphics_mode) + { + /* Text mode */ + case GRAPHICS_NONE: + { + n = strlen(image_object_hack); + + /* Random symbol from set above */ + *cp = (image_object_hack[rand_int(n)]); + + /* Random color */ + *ap = randint(15); + + /* Done */ + break; + } + + /* Normal graphics */ + default: + { + n = randint(max_k_idx - 1); + + *cp = k_info[n].x_char; + *ap = k_info[n].x_attr; + + break; + } + } +} + + +/* + * Hack -- Random hallucination + */ +static void image_random(byte *ap, char *cp) +{ + /* Normally, assume monsters */ + if (rand_int(100) < 75) + { + image_monster(ap, cp); + } + + /* Otherwise, assume objects */ + else + { + image_object(ap, cp); + } +} + + +/* + * The 16x16 tile of the terrain supports lighting + */ +static bool_ feat_supports_lighting(byte feat) +{ + return (f_info[feat].flags1 & FF1_SUPPORT_LIGHT) != 0; +} + + +char get_shimmer_color() +{ + switch (randint(7)) + { + case 1: + return (TERM_RED); + case 2: + return (TERM_L_RED); + case 3: + return (TERM_WHITE); + case 4: + return (TERM_L_GREEN); + case 5: + return (TERM_BLUE); + case 6: + return (TERM_L_DARK); + case 7: + return (TERM_GREEN); + } + + return (TERM_VIOLET); +} + + +/* + * Table of breath colors. Must match listings in a single set of + * monster spell flags. + * + * The value "255" is special. Monsters with that kind of breath + * may be any color. + */ +static byte breath_to_attr[32][2] = +{ + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { TERM_SLATE, TERM_L_DARK }, /* RF4_BRTH_ACID */ + { TERM_BLUE, TERM_L_BLUE }, /* RF4_BRTH_ELEC */ + { TERM_RED, TERM_L_RED }, /* RF4_BRTH_FIRE */ + { TERM_WHITE, TERM_L_WHITE }, /* RF4_BRTH_COLD */ + { TERM_GREEN, TERM_L_GREEN }, /* RF4_BRTH_POIS */ + { TERM_L_GREEN, TERM_GREEN }, /* RF4_BRTH_NETHR */ + { TERM_YELLOW, TERM_ORANGE }, /* RF4_BRTH_LITE */ + { TERM_L_DARK, TERM_SLATE }, /* RF4_BRTH_DARK */ + { TERM_L_UMBER, TERM_UMBER }, /* RF4_BRTH_CONFU */ + { TERM_YELLOW, TERM_L_UMBER }, /* RF4_BRTH_SOUND */ + { 255, 255 }, /* (any color) */ /* RF4_BRTH_CHAOS */ + { TERM_VIOLET, TERM_VIOLET }, /* RF4_BRTH_DISEN */ + { TERM_L_RED, TERM_VIOLET }, /* RF4_BRTH_NEXUS */ + { TERM_L_BLUE, TERM_L_BLUE }, /* RF4_BRTH_TIME */ + { TERM_L_WHITE, TERM_SLATE }, /* RF4_BRTH_INER */ + { TERM_L_WHITE, TERM_SLATE }, /* RF4_BRTH_GRAV */ + { TERM_UMBER, TERM_L_UMBER }, /* RF4_BRTH_SHARD */ + { TERM_ORANGE, TERM_RED }, /* RF4_BRTH_PLAS */ + { TERM_UMBER, TERM_L_UMBER }, /* RF4_BRTH_FORCE */ + { TERM_L_BLUE, TERM_WHITE }, /* RF4_BRTH_MANA */ + { 0, 0 }, /* */ + { TERM_GREEN, TERM_L_GREEN }, /* RF4_BRTH_NUKE */ + { 0, 0 }, /* */ + { TERM_WHITE, TERM_L_RED }, /* RF4_BRTH_DISINT */ +}; + + +/* + * Multi-hued monsters shimmer acording to their breaths. + * + * If a monster has only one kind of breath, it uses both colors + * associated with that breath. Otherwise, it just uses the first + * color for any of its breaths. + * + * If a monster does not breath anything, it can be any color. + */ +static byte multi_hued_attr(monster_race *r_ptr) +{ + byte allowed_attrs[15]; + + int i, j; + + int stored_colors = 0; + + int breaths = 0; + + int first_color = 0; + + int second_color = 0; + + + /* Monsters with no ranged attacks can be any color */ + if (!r_ptr->freq_inate) return (get_shimmer_color()); + + /* Check breaths */ + for (i = 0; i < 32; i++) + { + bool_ stored = FALSE; + + /* Don't have that breath */ + if (!(r_ptr->flags4 & (1L << i))) continue; + + /* Get the first color of this breath */ + first_color = breath_to_attr[i][0]; + + /* Breath has no color associated with it */ + if (first_color == 0) continue; + + /* Monster can be of any color */ + if (first_color == 255) return (randint(15)); + + + /* Increment the number of breaths */ + breaths++; + + /* Monsters with lots of breaths may be any color. */ + if (breaths == 6) return (randint(15)); + + + /* Always store the first color */ + for (j = 0; j < stored_colors; j++) + { + /* Already stored */ + if (allowed_attrs[j] == first_color) stored = TRUE; + } + if (!stored) + { + allowed_attrs[stored_colors] = first_color; + stored_colors++; + } + + /* + * Remember (but do not immediately store) the second color + * of the first breath. + */ + if (breaths == 1) + { + second_color = breath_to_attr[i][1]; + } + } + + /* Monsters with no breaths may be of any color. */ + if (breaths == 0) return (get_shimmer_color()); + + /* If monster has one breath, store the second color too. */ + if (breaths == 1) + { + allowed_attrs[stored_colors] = second_color; + stored_colors++; + } + + /* Pick a color at random */ + return (allowed_attrs[rand_int(stored_colors)]); +} + + +/* + * Extract the attr/char to display at the given (legal) map location + * + * Note that this function, since it is called by "lite_spot()" which + * is called by "update_view()", is a major efficiency concern. + * + * Basically, we examine each "layer" of the world (terrain, objects, + * monsters/players), from the bottom up, extracting a new attr/char + * if necessary at each layer, and defaulting to "darkness". This is + * not the fastest method, but it is very simple, and it is about as + * fast as it could be for grids which contain no "marked" objects or + * "visible" monsters. + * + * We apply the effects of hallucination during each layer. Objects will + * always appear as random "objects", monsters will always appear as random + * "monsters", and normal grids occasionally appear as random "monsters" or + * "objects", but note that these random "monsters" and "objects" are really + * just "colored ascii symbols" (which may look silly on some machines). + * + * The hallucination functions avoid taking any pointers to local variables + * because some compilers refuse to use registers for any local variables + * whose address is taken anywhere in the function. + * + * As an optimization, we can handle the "player" grid as a special case. + * + * Note that the memorization of "objects" and "monsters" is not related + * to the memorization of "terrain". This allows the player to memorize + * the terrain of a grid without memorizing any objects in that grid, and + * to detect monsters without detecting anything about the terrain of the + * grid containing the monster. + * + * The fact that all interesting "objects" and "terrain features" are + * memorized as soon as they become visible for the first time means + * that we only have to check the "CAVE_SEEN" flag for "boring" grids. + * + * Note that bizarre things must be done when the "attr" and/or "char" + * codes have the "high-bit" set, since these values are used to encode + * various "special" pictures in some versions, and certain situations, + * such as "multi-hued" or "clear" monsters, cause the attr/char codes + * to be "scrambled" in various ways. + * + * Note that the "zero" entry in the feature/object/monster arrays are + * used to provide "special" attr/char codes, with "monster zero" being + * used for the player attr/char, "object zero" being used for the "stack" + * attr/char, and "feature zero" being used for the "nothing" attr/char. + * + * Note that eventually we may want to use the "&" symbol for embedded + * treasure, and use the "*" symbol to indicate multiple objects, but + * currently, we simply use the attr/char of the first "marked" object + * in the stack, if any, and so "object zero" is unused. XXX XXX XXX + * + * Note the assumption that doing "x_ptr = &x_info[x]" plus a few of + * "x_ptr->xxx", is quicker than "x_info[x].xxx", even if "x" is a fixed + * constant. If this is incorrect then a lot of code should be changed. + * + * + * Some comments on the "terrain" layer... + * + * Note that "boring" grids (floors, invisible traps, and any illegal grids) + * are very different from "interesting" grids (all other terrain features), + * and the two types of grids are handled completely separately. The most + * important distinction is that "boring" grids may or may not be memorized + * when they are first encountered, and so we must use the "CAVE_SEEN" flag + * to see if they are "see-able". + * + * + * Some comments on the "terrain" layer (boring grids)... + * + * Note that "boring" grids are always drawn using the picture for "empty + * floors", which is stored in "f_info[FEAT_FLOOR]". Sometimes, special + * lighting effects may cause this picture to be modified. + * + * Note that "invisible traps" are always displayes exactly like "empty + * floors", which prevents various forms of "cheating", with no loss of + * efficiency. There are still a few ways to "guess" where traps may be + * located, for example, objects will never fall into a grid containing + * an invisible trap. XXX XXX + * + * To determine if a "boring" grid should be displayed, we simply check to + * see if it is either memorized ("CAVE_MARK"), or currently "see-able" by + * the player ("CAVE_SEEN"). Note that "CAVE_SEEN" is now maintained by the + * "update_view()" function. + * + * Note the "special lighting effects" which can be activated for "boring" + * grids using the "view_special_lite" option, causing certain such grids + * to be displayed using special colors. If the grid is "see-able" by + * the player, we will use the normal (except that, if the "view_yellow_lite" + * option is set, and the grid is *only* "see-able" because of the player's + * torch, then we will use "yellow"), else if the player is "blind", we will + * use greyscale, else if the grid is not "illuminated", we will use "dark + * gray", if the "view_bright_lite" option is set, we will use "darker" colour + * else we will use the normal colour. + * + * + * Some comments on the "terrain" layer (non-boring grids)... + * + * Note the use of the "mimic" field in the "terrain feature" processing, + * which allows any feature to "pretend" to be another feature. This is + * used to "hide" secret doors, and to make all "doors" appear the same, + * and all "walls" appear the same, and "hidden" treasure stay hidden. + * Note that it is possible to use this field to make a feature "look" + * like a floor, but the "view_special_lite" flag only affects actual + * "boring" grids. + * + * Since "interesting" grids are always memorized as soon as they become + * "see-able" by the player ("CAVE_SEEN"), such a grid only needs to be + * displayed if it is memorized ("CAVE_MARK"). Most "interesting" grids + * are in fact non-memorized, non-see-able, wall grids, so the fact that + * we do not have to check the "CAVE_SEEN" flag adds some efficiency, at + * the cost of *forcing* the memorization of all "interesting" grids when + * they are first seen. Since the "CAVE_SEEN" flag is now maintained by + * the "update_view()" function, this efficiency is not as significant as + * it was in previous versions, and could perhaps be removed. + * (so I removed this to simplify the terrain feature handling -- pelpel) + * + * Note the "special lighting effects" which can be activated for "wall" + * grids using the "view_granite_lite" option, causing certain such grids + * to be displayed using special colors. + * If the grid is "see-able" by the player, we will use the normal colour + * else if the player is "blind", we will use grey scale, else if the + * "view_bright_lite" option is set, we will use reduced colour, else we + * will use the normal one. + * + * Note that "wall" grids are more complicated than "boring" grids, due to + * the fact that "CAVE_GLOW" for a "wall" grid means that the grid *might* + * be glowing, depending on where the player is standing in relation to the + * wall. In particular, the wall of an illuminated room should look just + * like any other (dark) wall unless the player is actually inside the room. + * + * Thus, we do not support as many visual special effects for "wall" grids + * as we do for "boring" grids, since many of them would give the player + * information about the "CAVE_GLOW" flag of the wall grid, in particular, + * it would allow the player to notice the walls of illuminated rooms from + * a dark hallway that happened to run beside the room. + * + * + * Some comments on the "object" layer... + * + * Currently, we do nothing with multi-hued objects, because there are + * not any. If there were, they would have to set "shimmer_objects" + * when they were created, and then new "shimmer" code in "dungeon.c" + * would have to be created handle the "shimmer" effect, and the code + * in "cave.c" would have to be updated to create the shimmer effect. + * This did not seem worth the effort. XXX XXX + * + * + * Some comments on the "monster"/"player" layer... + * + * Note that monsters can have some "special" flags, including "ATTR_MULTI", + * which means their color changes, and "ATTR_CLEAR", which means they take + * the color of whatever is under them, and "CHAR_CLEAR", which means that + * they take the symbol of whatever is under them. Technically, the flag + * "CHAR_MULTI" is supposed to indicate that a monster looks strange when + * examined, but this flag is currently ignored. All of these flags are + * ignored if the "avoid_other" option is set, since checking for these + * conditions is expensive (and annoying) on some systems. + * + * Normally, players could be handled just like monsters, except that the + * concept of the "torch lite" of others player would add complications. + * For efficiency, however, we handle the (only) player first, since the + * "player" symbol always "pre-empts" any other facts about the grid. + * + * The "hidden_player" efficiency option, which only makes sense with a + * single player, allows the player symbol to be hidden while running. + */ + +/* + * Alternative colours for unseen grids + * + * Reduced colours - remembered interesting grids and perma-lit floors + * B&W - currently only used by blindness effect + */ + +/* Colour */ +static byte dark_attrs[16] = +{ + TERM_DARK, TERM_L_WHITE, TERM_L_DARK, TERM_ORANGE, + TERM_RED, TERM_GREEN, TERM_BLUE, TERM_UMBER, + TERM_L_DARK, TERM_SLATE, TERM_VIOLET, TERM_YELLOW, + TERM_RED, TERM_GREEN, TERM_BLUE, TERM_UMBER +}; + +/* B&W */ +static byte darker_attrs[16] = +{ + TERM_DARK, TERM_L_WHITE, TERM_L_DARK, TERM_SLATE, + TERM_L_DARK, TERM_L_DARK, TERM_L_DARK, TERM_L_DARK, + TERM_L_DARK, TERM_SLATE, TERM_L_DARK, TERM_SLATE, + TERM_SLATE, TERM_SLATE, TERM_SLATE, TERM_SLATE +}; + + +void map_info(int y, int x, byte *ap, char *cp, byte *tap, char *tcp, + byte *eap, char *ecp) +{ + cave_type *c_ptr; + + feature_type *f_ptr; + + s16b this_o_idx, next_o_idx = 0; + + u16b info; + + s16b t_idx; + + byte feat; + + byte a; + + byte c; + + /* + * This means that a port supports graphics overlay as well as lighting + * effects. See the step 3 below for the detailed information about + * lighting. Basically, it requires "darker" tiles for those terrain + * features with SUPPORT_LIGHT flag set, and they must be arranged + * this way: + * col col+1 col+2 + * row base darker brighter + */ + bool_ graf_new = ((graphics_mode == GRAPHICS_ISO) || + (graphics_mode == GRAPHICS_NEW)); + + /* + * I never understand why some coders like shimmering so much. + * It just serves to hurt my eyes, IMHO. If one feels like to show off, + * go for better graphics support... Anyway this means a port allows + * changing attr independently from its char -- pelpel + */ + bool_ attr_mutable = (!use_graphics || + (graphics_mode == GRAPHICS_IBM)); + + + /**** Preparation ****/ + + /* Access the grid */ + c_ptr = &cave[y][x]; + + + /* Cache some frequently used values */ + + /* Grid info */ + info = c_ptr->info; + + /* Feature code */ + feat = c_ptr->feat; + + /* Apply "mimic" field */ + if (c_ptr->mimic) + { + feat = c_ptr->mimic; + } + else + { + feat = f_info[feat].mimic; + } + + /* Access floor */ + f_ptr = &f_info[feat]; + + + /* Reset attr/char */ + *eap = 0; + *ecp = 0; + + + /**** Layer 1 -- Terrain feature ****/ + + /* Only memorised or visible grids are displayed */ + if (info & (CAVE_MARK | CAVE_SEEN)) + { + /**** Step 1 -- Retrieve base attr/char ****/ + + /* 'Sane' terrain features */ + if (feat != FEAT_SHOP) + { + /* Normal char */ + c = f_ptr->x_char; + + /* Normal attr */ + a = f_ptr->x_attr; + } + + /* Mega-Hack 1 -- Building don't conform to f_info */ + else + { + c = st_info[c_ptr->special].x_char; + a = st_info[c_ptr->special].x_attr; + } + + /* Mega-Hack 2 -- stair to dungeon branch are purple */ + if (c_ptr->special && attr_mutable && + ((feat == FEAT_MORE) || (feat == FEAT_LESS))) + { + a = TERM_VIOLET; + } + + /* Mega-Hack 3 -- Traps don't have f_info entries either */ + if ((info & (CAVE_TRDT)) && (feat != FEAT_ILLUS_WALL)) + { + /* Trap index */ + t_idx = c_ptr->t_idx; + + if (use_graphics && + (t_info[t_idx].g_attr != 0) && + (t_info[t_idx].g_char != 0)) + { + + if (graf_new) + { + *eap = t_info[t_idx].g_attr; + *ecp = t_info[t_idx].g_char; + } + else + { + a = t_info[t_idx].g_attr; + c = t_info[t_idx].g_char; + } + + } + else + { + /* + * If trap is set on a floor grid that is not + * one of "interesting" features, use a special + * symbol to display it. Check for doors is no longer + * necessary because they have REMEMBER flag now. + * + * Cave macros cannot be used safely here, because of + * c_ptr->mimic XXX XXX + */ + if (!attr_mutable) + { + a = f_info[FEAT_TRAP].x_attr; + c = f_info[FEAT_TRAP].x_char; + } + else + { + if ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR) + { + c = f_info[FEAT_TRAP].x_char; + } + + /* Add attr XXX XXX XXX */ + a = t_info[t_idx].color; + + /* Get a new color with a strange formula :) XXX XXX XXX */ + if (t_info[t_idx].flags & FTRAP_CHANGE) + { + s32b tmp; + + tmp = dun_level + dungeon_type + feat; + + a = tmp % 16; + } + } + } + } + + + /**** Step 2 -- Apply special random effects ****/ + if (!avoid_other && !avoid_shimmer && attr_mutable) + { + /* Special terrain effect */ + if (c_ptr->effect) + { + a = spell_color(effects[c_ptr->effect].type); + } + + /* Multi-hued attr */ + else if (f_ptr->flags1 & FF1_ATTR_MULTI) + { + a = f_ptr->shimmer[rand_int(7)]; + } + } + + + /* + * Step 3 + * + * Special lighting effects, if specified and applicable + * This will never happen for + * - any grids in the overhead map + * - traps + * - (graphics modes) terrain features without corresponding + * "darker" tiles. + * + * Note the use of f_ptr->flags1 to avoid problems with + * c_ptr->mimic. + */ + + /* view_special_lite: lighting effects for boring features */ + if (view_special_lite && + ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR)) + { + if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)) && + (attr_mutable || (graf_new && feat_supports_lighting(feat)))) + { + /* Handle "seen" grids */ + if (info & (CAVE_SEEN)) + { + /* Only lit by "torch" light */ + if (view_yellow_lite && !(info & (CAVE_GLOW))) + { + if (graf_new) + { + /* Use a brightly lit tile */ + c += 2; + } + else + { + /* Use "yellow" */ + a = TERM_YELLOW; + } + } + } + + /* Handle "blind" */ + else if (p_ptr->blind) + { + if (graf_new) + { + /* Use a dark tile */ + c++; + } + else + { + /* Use darker colour */ + a = darker_attrs[a & 0xF]; + } + } + + /* Handle "dark" grids */ + else if (!(info & (CAVE_GLOW))) + { + if (graf_new) + { + /* Use a dark tile */ + c++; + } + else + { + /* Use darkest colour */ + a = TERM_L_DARK; + } + } + + /* "Out-of-sight" glowing grids -- handle "view_bright_lite" */ + else if (view_bright_lite) + { + if (graf_new) + { + /* Use a dark tile */ + c++; + } + else + { + /* Use darker colour */ + a = dark_attrs[a & 0xF]; + } + } + } + } + + /* view_granite_lite: lighting effects for walls and doors */ + else if (view_granite_lite && + (f_ptr->flags1 & (FF1_NO_VISION | FF1_DOOR))) + { + if (!p_ptr->wild_mode && !(info & (CAVE_TRDT)) && + (attr_mutable || (graf_new && feat_supports_lighting(feat)))) + { + /* Handle "seen" grids */ + if (info & (CAVE_SEEN)) + { + /* Do nothing */ + } + + /* Handle "blind" */ + else if (p_ptr->blind) + { + if (graf_new) + { + /* Use a dark tile */ + c++; + } + else + { + /* Use darker colour */ + a = darker_attrs[a & 0xF]; + } + } + + /* Handle "view_bright_lite" */ + else if (view_bright_lite) + { + if (graf_new) + { + /* Use a dark tile */ + c++; + } + else + { + /* Use darker colour */ + a = dark_attrs[a & 0xF]; + } + } + + else + { + if (graf_new) + { + /* Use a brightly lit tile */ + c += 2; + } + else + { + /* Use normal colour */ + } + } + } + } + } + + /* Unknown grids */ + else + { + /* Access darkness */ + f_ptr = &f_info[FEAT_NONE]; + + /* Normal attr */ + a = f_ptr->x_attr; + + /* Normal char */ + c = f_ptr->x_char; + } + + /* + * Hack -- rare random hallucination + * Because we cannot be sure which is outer dungeon walls, + * the check for 'feat' has been removed + */ + if (p_ptr->image && (rand_int(256) == 0)) + { + /* Hallucinate */ + image_random(ap, cp); + } + + /* Save the terrain info for the transparency effects */ + *tap = a; + *tcp = c; + + /* Save the info */ + *ap = a; + *cp = c; + + + /**** Layer 2 -- Objects ****/ + + if (feat != FEAT_MON_TRAP) + { + for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Memorized objects */ + if (o_ptr->marked) + { + /* Normal char */ + *cp = object_char(o_ptr); + + /* Normal attr */ + *ap = object_attr(o_ptr); + + /* Multi-hued attr */ + if (!avoid_other && attr_mutable && + (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI)) + { + *ap = get_shimmer_color(); + } + + /* Hack -- hallucination */ + if (p_ptr->image) image_object(ap, cp); + + /* Done */ + break; + } + } + } + + + /**** Layer 3 -- Handle monsters ****/ + + if (c_ptr->m_idx) + { + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + monster_race *r_ptr = race_inf(m_ptr); + + if (r_ptr->flags9 & RF9_MIMIC) + { + object_type *o_ptr; + + /* Acquire object */ + o_ptr = &o_list[m_ptr->hold_o_idx]; + + /* Memorized objects */ + if (o_ptr->marked) + { + /* Normal char */ + *cp = object_char(o_ptr); + + /* Normal attr */ + *ap = object_attr(o_ptr); + + /* Multi-hued attr */ + if (!avoid_other && attr_mutable && + (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI)) + { + *ap = get_shimmer_color(); + } + + /* Hack -- hallucination */ + if (p_ptr->image) image_object(ap, cp); + } + } + else + { + /* Visible monster */ + if (m_ptr->ml) + { + monster_race *r_ptr = race_inf(m_ptr); + + /* Reset attr/char */ + *eap = 0; + *ecp = 0; + + if (use_graphics) + { + + if (graf_new) + { + monster_ego *re_ptr = &re_info[m_ptr->ego]; + + /* Desired attr */ + *eap = re_ptr->g_attr; + + /* Desired char */ + *ecp = re_ptr->g_char; + } + + /* Use base monster */ + r_ptr = &r_info[m_ptr->r_idx]; + } + + /* Desired attr/char */ + c = r_ptr->x_char; + a = r_ptr->x_attr; + + /* Ignore weird codes */ + if (avoid_other) + { + /* Use char */ + *cp = c; + + /* Use attr */ + *ap = a; + } + + /* Special attr/char codes */ + else if (!attr_mutable) + { + /* Use char */ + *cp = c; + + /* Use attr */ + *ap = a; + } + + /* Multi-hued monster */ + else if (r_ptr->flags1 & (RF1_ATTR_MULTI)) + { + /* Is it a shapechanger? */ + if (r_ptr->flags2 & (RF2_SHAPECHANGER)) + { + image_random(ap, cp); + } + else + *cp = c; + + /* Multi-hued attr */ + if (r_ptr->flags2 & (RF2_ATTR_ANY)) + { + *ap = randint(15); + } + else + { + *ap = multi_hued_attr(r_ptr); + } + } + + /* Normal monster (not "clear" in any way) */ + else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR | RF1_CHAR_CLEAR))) + { + /* Use char */ + *cp = c; + + /* Use attr */ + *ap = a; + } + + /* + * Hack -- Bizarre grid under monster + * WAS: else if (*ap & 0x80) || (*cp & 0x80) -- pelpel + */ + else if (*ap & 0x80) + { + /* Use char */ + *cp = c; + + /* Use attr */ + *ap = a; + } + + /* Normal */ + else + { + /* Normal (non-clear char) monster */ + if (!(r_ptr->flags1 & (RF1_CHAR_CLEAR))) + { + /* Normal char */ + *cp = c; + } + + /* Normal (non-clear attr) monster */ + else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR))) + { + /* Normal attr */ + *ap = a; + } + } + + /* Hack -- hallucination */ + if (p_ptr->image) + { + /* Hallucinatory monster */ + image_monster(ap, cp); + } + } + } + } + + /* Handle "player" */ + if ((y == p_ptr->py) && (x == p_ptr->px) && + (!p_ptr->invis || p_ptr->see_inv)) + { + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + /* Reset attr/char */ + *eap = 0; + *ecp = 0; + + /* Get the "player" attr */ + if (!avoid_other && attr_mutable && (r_ptr->flags1 & RF1_ATTR_MULTI)) + { + a = get_shimmer_color(); + } + else + { + a = r_ptr->x_attr; + } + + /* Get the "player" char */ + c = r_ptr->x_char; + + + /* Mega-Hack -- Apply modifications to player graphics XXX XXX XXX */ + switch (graphics_mode) + { + case GRAPHICS_NONE: + case GRAPHICS_IBM: + { + if (player_char_health) + { + int percent = p_ptr->chp * 10 / p_ptr->mhp; + + if (percent < 7) + { + c = I2D(percent); + if (percent < 3) a = TERM_L_RED; + } + } + + break; + } + + case GRAPHICS_OLD: + { + if (player_symbols) + { + a = BMP_FIRST_PC_CLASS + p_ptr->pclass; + c = BMP_FIRST_PC_RACE + p_ptr->prace; + } + + break; + } + + case GRAPHICS_ISO: + case GRAPHICS_NEW: + { + if (p_ptr->pracem) + { + player_race_mod *rmp_ptr = &race_mod_info[p_ptr->pracem]; + + /* Desired attr */ + *eap = rmp_ptr->g_attr; + + /* Desired char */ + *ecp = rmp_ptr->g_char; + } + + /* +AKH 20020421 - Health dispay for graphics, too */ + if (player_char_health && (graphics_mode == GRAPHICS_NEW)) + { + int percent = p_ptr->chp * 14 / p_ptr->mhp; + + if (percent < 10) + { + *eap = 10; + *ecp = 32 + 14 - percent; + } + } + + break; + } + + } + + /* Save the info */ + *ap = a; + *cp = c; + + } +} + + +/* + * Special version of map_info, for use by cmovie and HTML converter + * to obtain pure-ASCII image of dungeon map + */ +void map_info_default(int y, int x, byte *ap, char *cp) +{ + cave_type *c_ptr; + + feature_type *f_ptr; + + s16b this_o_idx, next_o_idx = 0; + + u16b info; + + s16b t_idx; + + byte feat; + + byte a; + + byte c; + + bool_ use_graphics_hack = use_graphics; + byte graphics_mode_hack = graphics_mode; + + + /* Temporarily disable graphics mode -- for some random effects XXX */ + use_graphics = FALSE; + graphics_mode = GRAPHICS_NONE; + + /**** Preparation ****/ + + /* Access the grid */ + c_ptr = &cave[y][x]; + + + /* Cache some frequently used values */ + + /* Grid info */ + info = c_ptr->info; + + /* Feature code */ + feat = c_ptr->feat; + + /* Apply "mimic" field */ + if (c_ptr->mimic) + { + feat = c_ptr->mimic; + } + else + { + feat = f_info[feat].mimic; + } + + /* Access floor */ + f_ptr = &f_info[feat]; + + + /**** Layer 1 -- Terrain feature ****/ + + /* Only memorised or visible grids are displayed */ + if (info & (CAVE_MARK | CAVE_SEEN)) + { + /**** Step 1 -- Retrieve base attr/char ****/ + + /* 'Sane' terrain features */ + if (feat != FEAT_SHOP) + { + /* Default char */ + c = f_ptr->d_char; + + /* Default attr */ + a = f_ptr->d_attr; + } + + /* Mega-Hack 1 -- Building don't conform to f_info */ + else + { + c = st_info[c_ptr->special].d_char; + a = st_info[c_ptr->special].d_attr; + } + + /* Mega-Hack 2 -- stair to dungeon branch are purple */ + if (c_ptr->special && + ((feat == FEAT_MORE) || (feat == FEAT_LESS))) + { + a = TERM_VIOLET; + } + + /* Mega-Hack 3 -- Traps don't have f_info entries either */ + if ((info & (CAVE_TRDT)) && (feat != FEAT_ILLUS_WALL)) + { + /* Trap index */ + t_idx = c_ptr->t_idx; + + /* + * If trap is set on a floor grid that is not + * one of "interesting" features, use a special + * symbol to display it. Check for doors is no longer + * necessary because they have REMEMBER flag now. + * + * Cave macros cannot be used safely here, because of + * c_ptr->mimic XXX XXX + */ + if ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR) + { + c = f_info[FEAT_TRAP].d_char; + } + + /* Add attr */ + a = t_info[t_idx].color; + + /* Get a new color with a strange formula :) */ + if (t_info[t_idx].flags & FTRAP_CHANGE) + { + s32b tmp; + + tmp = dun_level + dungeon_type + feat; + + a = tmp % 16; + } + } + + + /**** Step 2 -- Apply special random effects ****/ + if (!avoid_other) + { + /* Special terrain effect */ + if (c_ptr->effect) + { + a = spell_color(effects[c_ptr->effect].type); + } + + /* Multi-hued attr */ + else if (f_ptr->flags1 & FF1_ATTR_MULTI) + { + a = f_ptr->shimmer[rand_int(7)]; + } + } + + + /* + * Step 3 + * + * Special lighting effects, if specified and applicable + * This will never happen for + * - any grids in the overhead map + * - traps + * - (graphics modes) terrain features without corresponding + * "darker" tiles. + * + * All the if's here are flag checks, so changed order shouldn't + * affect performance a lot, I hope... + */ + + /* view_special_lite: lighting effects for boring features */ + if (view_special_lite && + ((f_ptr->flags1 & (FF1_FLOOR | FF1_REMEMBER)) == FF1_FLOOR)) + { + if (!p_ptr->wild_mode && !(info & (CAVE_TRDT))) + { + /* Handle "seen" grids */ + if (info & (CAVE_SEEN)) + { + /* Only lit by "torch" light */ + if (view_yellow_lite && !(info & (CAVE_GLOW))) + { + /* Use "yellow" */ + a = TERM_YELLOW; + } + } + + /* Handle "blind" */ + else if (p_ptr->blind) + { + /* Use darker colour */ + a = darker_attrs[a & 0xF]; + } + + /* Handle "dark" grids */ + else if (!(info & (CAVE_GLOW))) + { + /* Use darkest colour */ + a = TERM_L_DARK; + } + + /* "Out-of-sight" glowing grids -- handle "view_bright_lite" */ + else if (view_bright_lite) + { + /* Use darker colour */ + a = dark_attrs[a & 0xF]; + } + } + } + + /* view_granite_lite: lighting effects for walls and doors */ + else if (view_granite_lite && + (f_ptr->flags1 & (FF1_NO_VISION | FF1_DOOR))) + { + if (!p_ptr->wild_mode && !(info & (CAVE_TRDT))) + { + /* Handle "seen" grids */ + if (info & (CAVE_SEEN)) + { + /* Do nothing */ + } + + /* Handle "blind" */ + else if (p_ptr->blind) + { + /* Use darker colour */ + a = darker_attrs[a & 0xF]; + } + + /* Handle "view_bright_lite" */ + else if (view_bright_lite) + { + /* Use darker colour */ + a = dark_attrs[a & 0xF]; + } + } + } + } + + /* Unknown grids */ + else + { + /* Access darkness */ + f_ptr = &f_info[FEAT_NONE]; + + /* Default attr */ + a = f_ptr->d_attr; + + /* Default char */ + c = f_ptr->d_char; + } + + /* + * Hack -- rare random hallucination + * Because we cannot be sure which is outer dungeon walls, + * the check for 'feat' has been removed + */ + if (p_ptr->image && (rand_int(256) == 0)) + { + /* Hallucinate */ + image_random(ap, cp); + } + + /* Save the info */ + *ap = a; + *cp = c; + + + /**** Layer 2 -- Objects ****/ + + if (feat != FEAT_MON_TRAP) + { + for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Memorized objects */ + if (o_ptr->marked) + { + /* Normal char */ + *cp = object_char_default(o_ptr); + + /* Normal attr */ + *ap = object_attr_default(o_ptr); + + /* Multi-hued attr */ + if (!avoid_other && + (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI)) + { + *ap = get_shimmer_color(); + } + + /* Hack -- hallucination */ + if (p_ptr->image) image_object(ap, cp); + + /* Done */ + break; + } + } + } + + + /**** Layer 3 -- Handle monsters ****/ + + if (c_ptr->m_idx) + { + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + monster_race *r_ptr = race_inf(m_ptr); + + if (r_ptr->flags9 & RF9_MIMIC) + { + object_type *o_ptr; + + /* Acquire object */ + o_ptr = &o_list[m_ptr->hold_o_idx]; + + /* Memorized objects */ + if (o_ptr->marked) + { + /* Normal char */ + *cp = object_char_default(o_ptr); + + /* Normal attr */ + *ap = object_attr_default(o_ptr); + + /* Multi-hued attr */ + if (!avoid_other && !use_graphics && + (k_info[o_ptr->k_idx].flags5 & TR5_ATTR_MULTI)) + { + *ap = get_shimmer_color(); + } + + /* Hack -- hallucination */ + if (p_ptr->image) image_object(ap, cp); + } + } + else + { + /* Visible monster */ + if (m_ptr->ml) + { + monster_race *r_ptr = race_inf(m_ptr); + + /* Default attr/char */ + c = r_ptr->d_char; + a = r_ptr->d_attr; + + /* Ignore weird codes */ + if (avoid_other) + { + /* Use char */ + *cp = c; + + /* Use attr */ + *ap = a; + } + + /* Multi-hued monster */ + else if (r_ptr->flags1 & (RF1_ATTR_MULTI)) + { + /* Is it a shapechanger? */ + if (r_ptr->flags2 & (RF2_SHAPECHANGER)) + { + image_random(ap, cp); + } + else + *cp = c; + + /* Multi-hued attr */ + if (r_ptr->flags2 & (RF2_ATTR_ANY)) + { + *ap = randint(15); + } + else + { + *ap = multi_hued_attr(r_ptr); + } + } + + /* Normal monster (not "clear" in any way) */ + else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR | RF1_CHAR_CLEAR))) + { + /* Use char */ + *cp = c; + + /* Use attr */ + *ap = a; + } + + /* Hack -- Bizarre grid under monster */ + else if ((*ap & 0x80) || (*cp & 0x80)) + { + /* Use char */ + *cp = c; + + /* Use attr */ + *ap = a; + } + + /* Normal */ + else + { + /* Normal (non-clear char) monster */ + if (!(r_ptr->flags1 & (RF1_CHAR_CLEAR))) + { + /* Normal char */ + *cp = c; + } + + /* Normal (non-clear attr) monster */ + else if (!(r_ptr->flags1 & (RF1_ATTR_CLEAR))) + { + /* Normal attr */ + *ap = a; + } + } + + /* Hack -- hallucination */ + if (p_ptr->image) + { + /* Hallucinatory monster */ + image_monster(ap, cp); + } + } + } + } + + + /* Handle "player" */ + if ((y == p_ptr->py) && (x == p_ptr->px) && + (!p_ptr->invis || + (p_ptr->invis && p_ptr->see_inv))) + { + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + /* Get the "player" attr */ + if (!avoid_other && (r_ptr->flags1 & RF1_ATTR_MULTI)) + { + a = get_shimmer_color(); + } + else + { + a = r_ptr->d_attr; + } + + /* Get the "player" char */ + c = r_ptr->d_char; + + /* Save the info */ + *ap = a; + *cp = c; + + } + + /* XXX Restore the graphics mode */ + use_graphics = use_graphics_hack; + graphics_mode = graphics_mode_hack; +} + + +/* + * Calculate panel colum of a location in the map + */ +static int panel_col_of(int col) +{ + col -= panel_col_min; + if (use_bigtile) col *= 2; + return col + COL_MAP; +} + + + +/* + * Moves the cursor to a given MAP (y,x) location + */ +void move_cursor_relative(int row, int col) +{ + /* Real co-ords convert to screen positions */ + row -= panel_row_prt; + + /* Go there */ + Term_gotoxy(panel_col_of(col), row); +} + + + +/* + * Place an attr/char pair at the given map coordinate, if legal. + */ +void print_rel(char c, byte a, int y, int x) +{ + /* Paranoia -- Only do "legal" locations */ + if (!panel_contains(y, x)) return; + + /* Draw the char using the attr */ + Term_draw(panel_col_of(x), y - panel_row_prt, a, c); + + if (use_bigtile) + { + char c2; + byte a2; + + if (a & 0x80) + { + a2 = 255; + c2 = 255; + } + else + { + a2 = TERM_WHITE; + c2 = ' '; + } + Term_draw(panel_col_of(x) + 1, y - panel_row_prt, a2, c2); + } +} + + + + + +/* + * Memorize interesting viewable object/features in the given grid + * + * This function should only be called on "legal" grids. + * + * This function will memorize the object and/or feature in the given + * grid, if they are (1) viewable and (2) interesting. Note that all + * objects are interesting, all terrain features except floors (and + * invisible traps) are interesting, and floors (and invisible traps) + * are interesting sometimes (depending on various options involving + * the illumination of floor grids). + * + * The automatic memorization of all objects and non-floor terrain + * features as soon as they are displayed allows incredible amounts + * of optimization in various places, especially "map_info()". + * + * Note that the memorization of objects is completely separate from + * the memorization of terrain features, preventing annoying floor + * memorization when a detected object is picked up from a dark floor, + * and object memorization when an object is dropped into a floor grid + * which is memorized but out-of-sight. + * + * This function should be called every time the "memorization" of + * a grid (or the object in a grid) is called into question, such + * as when an object is created in a grid, when a terrain feature + * "changes" from "floor" to "non-floor", when any grid becomes + * "illuminated" or "viewable", and when a "floor" grid becomes + * "torch-lit". + * + * Note the relatively efficient use of this function by the various + * "update_view()" and "update_lite()" calls, to allow objects and + * terrain features to be memorized (and drawn) whenever they become + * viewable or illuminated in any way, but not when they "maintain" + * or "lose" their previous viewability or illumination. + * + * Note the butchered "internal" version of "player_can_see_bold()", + * optimized primarily for the most common cases, that is, for the + * non-marked floor grids. + */ +void note_spot(int y, int x) +{ + cave_type *c_ptr = &cave[y][x]; + + u16b info = c_ptr->info; + + s16b this_o_idx, next_o_idx = 0; + + + /* Require "seen" flag */ + if (!(info & (CAVE_SEEN))) return; + + + /* Hack -- memorize objects */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) + { + object_type * o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Memorize objects */ + o_ptr->marked = TRUE; + } + + if (c_ptr->m_idx) + { + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + monster_race *r_ptr = race_inf(m_ptr); + + if (r_ptr->flags9 & RF9_MIMIC) + { + object_type *o_ptr = &o_list[m_ptr->hold_o_idx]; + + o_ptr->marked = TRUE; + } + } + + + /* Hack -- memorize grids */ + if (!(info & (CAVE_MARK))) + { + /* Memorise some "boring" grids */ + if (cave_plain_floor_grid(c_ptr)) + { + /* Option -- memorise certain floors */ + if ((info & (CAVE_TRDT)) || + ((info & (CAVE_GLOW)) && view_perma_grids ) || + view_torch_grids) + { + /* Memorize */ + c_ptr->info |= (CAVE_MARK); + } + } + + /* Memorise all "interesting" grids */ + else + { + /* Memorize */ + c_ptr->info |= (CAVE_MARK); + } + } +} + + +/* + * Redraw (on the screen) a given MAP location + * + * This function should only be called on "legal" grids + */ +void lite_spot(int y, int x) +{ + byte a, a2; + byte c, c2; + + byte ta; + char tc; + + byte ea; + char ec; + + + /* Redraw if on screen */ + if (panel_contains(y, x)) + { + /* Examine the grid */ + map_info(y, x, &a, (char*)&c, &ta, &tc, &ea, &ec); + + /* Hack -- Queue it */ + Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc, ea, ec); + if (use_bigtile) + { + if (a & 0x80) + { + a2 = 255; + c2 = 255; + } + else + { + a2 = TERM_WHITE; + c2 = ' '; + } + Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0, 0, 0); + } + + } +} + + + + +/* + * Prints the map of the dungeon + * + * Note that, for efficiency, we contain an "optimized" version + * of both "lite_spot()" and "print_rel()", and that we use the + * "lite_spot()" function to display the player grid, if needed. + */ +void prt_map(void) +{ + int x, y; + + int v; + + /* Access the cursor state */ + (void)Term_get_cursor(&v); + + /* Hide the cursor */ + (void)Term_set_cursor(0); + + /* Dump the map */ + for (y = panel_row_min; y <= panel_row_max; y++) + { + /* Scan the columns of row "y" */ + for (x = panel_col_min; x <= panel_col_max; x++) + { + byte a, a2; + char c, c2; + + byte ta; + char tc; + byte ea; + char ec; + + /* Determine what is there */ + map_info(y, x, &a, &c, &ta, &tc, &ea, &ec); + + /* Efficiency -- Redraw that grid of the map */ + Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c, ta, tc, ea, ec); + if (use_bigtile) + { + if (a & 0x80) + { + a2 = 255; + c2 = 255; + } + else + { + a2 = TERM_WHITE; + c2 = ' '; + } + Term_queue_char(panel_col_of(x) + 1, y - panel_row_prt, a2, c2, 0, 0, 0, 0); + } + } + } + + /* Display player */ + lite_spot(p_ptr->py, p_ptr->px); + + /* Restore the cursor */ + (void)Term_set_cursor(v); +} + + + + + +/* + * Display highest priority object in the RATIO by RATIO area + */ + +/* + * Display the entire map + */ +#define MAP_HGT (MAX_HGT / RATIO) +#define MAP_WID (MAX_WID / RATIO) + +/* + * Hack -- priority array (see below) + * + * Note that all "walls" always look like "secret doors" (see "map_info()"). + */ +static byte priority_table[][2] = +{ + /* Dark */ + { FEAT_NONE, 2 }, + + /* Floors */ + { FEAT_FLOOR, 5 }, + + /* Walls */ + { FEAT_SECRET, 10 }, + + /* Quartz */ + { FEAT_QUARTZ, 11 }, + + /* Magma */ + { FEAT_MAGMA, 12 }, + + /* Rubble */ + { FEAT_RUBBLE, 13 }, + + /* Sandwall */ + { FEAT_SANDWALL, 14 }, + + /* Open doors */ + { FEAT_OPEN, 15 }, + { FEAT_BROKEN, 15 }, + + /* Closed doors */ + { FEAT_DOOR_HEAD + 0x00, 17 }, + + /* Hidden gold */ + { FEAT_QUARTZ_K, 19 }, + { FEAT_MAGMA_K, 19 }, + { FEAT_SANDWALL_K, 19 }, + + /* water, lava, & trees oh my! -KMW- */ + { FEAT_DEEP_WATER, 20 }, + { FEAT_SHAL_WATER, 20 }, + { FEAT_DEEP_LAVA, 20 }, + { FEAT_SHAL_LAVA, 20 }, + { FEAT_DIRT, 20 }, + { FEAT_GRASS, 20 }, + { FEAT_DARK_PIT, 20 }, + { FEAT_TREES, 20 }, + { FEAT_MOUNTAIN, 20 }, + { FEAT_ICE, 20}, + { FEAT_SAND, 20}, + { FEAT_DEAD_TREE, 20}, + { FEAT_ASH, 20}, + { FEAT_MUD, 20}, + + /* Fountain */ + { FEAT_FOUNTAIN, 22 }, + { FEAT_EMPTY_FOUNTAIN, 22 }, + + /* Stairs */ + { FEAT_LESS, 25 }, + { FEAT_MORE, 25 }, + + /* Stairs */ + { FEAT_WAY_LESS, 25 }, + { FEAT_WAY_MORE, 25 }, + + { FEAT_SHAFT_UP, 25 }, + { FEAT_SHAFT_DOWN, 25 }, + + /* End */ + { 0, 0 } +}; + + +/* + * Hack -- a priority function (see below) + */ +static byte priority(byte a, char c) +{ + int i, p0, p1; + + feature_type *f_ptr; + + /* Scan the table */ + for (i = 0; TRUE; i++) + { + /* Priority level */ + p1 = priority_table[i][1]; + + /* End of table */ + if (!p1) break; + + /* Feature index */ + p0 = priority_table[i][0]; + + /* Access the feature */ + f_ptr = &f_info[p0]; + + /* Check character and attribute, accept matches */ + if ((f_ptr->x_char == c) && (f_ptr->x_attr == a)) return (p1); + } + + /* Default */ + return (20); +} + + +/* + * Display a "small-scale" map of the dungeon in the active Term + * + * Note that the "map_info()" function must return fully colorized + * data or this function will not work correctly. + * + * Note that this function must "disable" the special lighting + * effects so that the "priority" function will work. + * + * Note the use of a specialized "priority" function to allow this + * function to work with any graphic attr/char mappings, and the + * attempts to optimize this function where possible. + */ +void display_map(int *cy, int *cx) +{ + int i, j, x, y; + + byte ta; + char tc; + + byte tp; + + bool_ old_view_special_lite; + bool_ old_view_granite_lite; + + int hgt, wid, yrat, xrat, yfactor, xfactor; + + + /* Obtain current size of the Angband window */ + Term_get_size(&wid, &hgt); + + /* Use two characters as one tile in Bigtile mode */ + if (use_bigtile) wid /= 2; + + /* + * Calculate the size of the dungeon map area + */ + hgt -= ROW_MAP + 2; + wid -= COL_MAP + 1; + + /* Paranoia */ + if ((hgt < 3) || (wid < 3)) + { + /* Map is too small, but place the player anyway */ + *cy = ROW_MAP; + *cx = COL_MAP; + + return; + } + + + /* Save lighting effects */ + old_view_special_lite = view_special_lite; + old_view_granite_lite = view_granite_lite; + + /* Disable lighting effects */ + view_special_lite = FALSE; + view_granite_lite = FALSE; + + + /* Set up initial maps */ + std::vector> ma; + std::vector> mc; + std::vector> mp; + for (i = 0; i < hgt + 2; i++) + { + // Nothing there. + ma.push_back(std::vector(wid + 2, TERM_WHITE)); + mc.push_back(std::vector(wid + 2, ' ')); + + // No priority. + mp.push_back(std::vector(wid + 2, 0)); + } + assert(ma.size() == hgt + 2); + assert(mc.size() == hgt + 2); + assert(mp.size() == hgt + 2); + + /* Calculate scaling factors */ + yfactor = ((cur_hgt / hgt < 4) && (cur_hgt > hgt)) ? 10 : 1; + xfactor = ((cur_wid / wid < 4) && (cur_wid > wid)) ? 10 : 1; + + yrat = (cur_hgt * yfactor + (hgt - 1)) / hgt; + xrat = (cur_wid * xfactor + (wid - 1)) / wid; + + /* Fill in the map */ + for (j = 0; j < cur_hgt; ++j) + { + for (i = 0; i < cur_wid; ++i) + { + /* Location */ + y = j * yfactor / yrat + 1; + x = i * xfactor / xrat + 1; + + /* Extract the current attr/char at that map location */ + map_info(j, i, &ta, &tc, &ta, &tc, &ta, &tc); + + /* Extract the priority of that attr/char */ + tp = priority(ta, tc); + + /* Player location has the highest priority */ + if ((p_ptr->py == j) && (p_ptr->px == i)) tp = 255; + + /* Save "best" */ + if (mp[y][x] < tp) + { + /* Save the char */ + mc[y][x] = tc; + + /* Save the attr */ + ma[y][x] = ta; + + /* Save priority */ + mp[y][x] = tp; + } + } + } + + + /* Corners */ + y = hgt + 1; + x = wid + 1; + + /* Draw the corners */ + mc[0][0] = mc[0][x] = mc[y][0] = mc[y][x] = '+'; + + /* Draw the horizontal edges */ + for (x = 1; x <= wid; x++) mc[0][x] = mc[y][x] = '-'; + + /* Draw the vertical edges */ + for (y = 1; y <= hgt; y++) mc[y][0] = mc[y][x] = '|'; + + + /* Display each map line in order */ + for (y = 0; y < hgt + 2; ++y) + { + /* Start a new line */ + Term_gotoxy(COL_MAP - 1, y); + + /* Display the line */ + for (x = 0; x < wid + 2; ++x) + { + ta = ma[y][x]; + tc = mc[y][x]; + + /* Add the character */ + Term_addch(ta, tc); + + /* Double width tile mode requires filler */ + if (use_bigtile) + { + byte a2; + char c2; + + if (ta & 0x80) + { + /* Mega-Hack */ + a2 = 255; + c2 = 255; + } + else + { + a2 = TERM_WHITE; + c2 = ' '; + } + + Term_addch(a2, c2); + } + } + } + + /* Player location in dungeon */ + *cy = p_ptr->py * yfactor / yrat + ROW_MAP; + if (!use_bigtile) + { + *cx = p_ptr->px * xfactor / xrat + COL_MAP; + } + else + { + *cx = (p_ptr->px * xfactor / xrat + 1) * 2 - 1 + COL_MAP; + } + + /* Restore lighting effects */ + view_special_lite = old_view_special_lite; + view_granite_lite = old_view_granite_lite; +} + + +/* + * Display a "small-scale" map of the dungeon for the player + * + * Currently, the "player" is displayed on the map. XXX XXX XXX + */ +void do_cmd_view_map(void) +{ + int cy, cx; + int wid, hgt; + + /* Retrive current screen size */ + Term_get_size(&wid, &hgt); + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + /* Note */ + prt("Please wait...", 0, 0); + + /* Flush */ + Term_fresh(); + + /* Clear the screen */ + Term_clear(); + + /* Display the map */ + display_map(&cy, &cx); + + /* Wait for it */ + put_str("Hit any key to continue", hgt - 1, (wid - COL_MAP) / 2); + + /* Hilite the player */ + move_cursor(cy, cx); + + /* Get any key */ + inkey(); + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; +} + + + + + + +/* + * Some comments on the dungeon related data structures and functions... + * + * Angband is primarily a dungeon exploration game, and it should come as + * no surprise that the internal representation of the dungeon has evolved + * over time in much the same way as the game itself, to provide semantic + * changes to the game itself, to make the code simpler to understand, and + * to make the executable itself faster or more efficient in various ways. + * + * There are a variety of dungeon related data structures, and associated + * functions, which store information about the dungeon, and provide methods + * by which this information can be accessed or modified. + * + * Some of this information applies to the dungeon as a whole, such as the + * list of unique monsters which are still alive. Some of this information + * only applies to the current dungeon level, such as the current depth, or + * the list of monsters currently inhabiting the level. And some of the + * information only applies to a single grid of the current dungeon level, + * such as whether the grid is illuminated, or whether the grid contains a + * monster, or whether the grid can be seen by the player. If Angband was + * to be turned into a multi-player game, some of the information currently + * associated with the dungeon should really be associated with the player, + * such as whether a given grid is viewable by a given player. + * + * One of the major bottlenecks in ancient versions of Angband was in the + * calculation of "line of sight" from the player to various grids, such + * as those containing monsters, using the relatively expensive "los()" + * function. This was such a nasty bottleneck that a lot of silly things + * were done to reduce the dependancy on "line of sight", for example, you + * could not "see" any grids in a lit room until you actually entered the + * room, at which point every grid in the room became "illuminated" and + * all of the grids in the room were "memorized" forever. Other major + * bottlenecks involved the determination of whether a grid was lit by the + * player's torch, and whether a grid blocked the player's line of sight. + * These bottlenecks led to the development of special new functions to + * optimize issues involved with "line of sight" and "torch lit grids". + * These optimizations led to entirely new additions to the game, such as + * the ability to display the player's entire field of view using different + * colors than were used for the "memorized" portions of the dungeon, and + * the ability to memorize dark floor grids, but to indicate by the way in + * which they are displayed that they are not actually illuminated. And + * of course many of them simply made the game itself faster or more fun. + * Also, over time, the definition of "line of sight" has been relaxed to + * allow the player to see a wider "field of view", which is slightly more + * realistic, and only slightly more expensive to maintain. + * + * Currently, a lot of the information about the dungeon is stored in ways + * that make it very efficient to access or modify the information, while + * still attempting to be relatively conservative about memory usage, even + * if this means that some information is stored in multiple places, or in + * ways which require the use of special code idioms. For example, each + * monster record in the monster array contains the location of the monster, + * and each cave grid has an index into the monster array, or a zero if no + * monster is in the grid. This allows the monster code to efficiently see + * where the monster is located, while allowing the dungeon code to quickly + * determine not only if a monster is present in a given grid, but also to + * find out which monster. The extra space used to store the information + * twice is inconsequential compared to the speed increase. + * + * Some of the information about the dungeon is used by functions which can + * constitute the "critical efficiency path" of the game itself, and so the + * way in which they are stored and accessed has been optimized in order to + * optimize the game itself. For example, the "update_view()" function was + * originally created to speed up the game itself (when the player was not + * running), but then it took on extra responsibility as the provider of the + * new "special effects lighting code", and became one of the most important + * bottlenecks when the player was running. So many rounds of optimization + * were performed on both the function itself, and the data structures which + * it uses, resulting eventually in a function which not only made the game + * faster than before, but which was responsible for even more calculations + * (including the determination of which grids are "viewable" by the player, + * which grids are illuminated by the player's torch, and which grids can be + * "seen" in some way by the player), as well as for providing the guts of + * the special effects lighting code, and for the efficient redisplay of any + * grids whose visual representation may have changed. + * + * Several pieces of information about each cave grid are stored in various + * two dimensional arrays, with one unit of information for each grid in the + * dungeon. Some of these arrays have been intentionally expanded by a small + * factor to make the two dimensional array accesses faster by allowing the + * use of shifting instead of multiplication. + * + * Several pieces of information about each cave grid are stored in the + * "cave_info" array, which is a special two dimensional array of bytes, + * one for each cave grid, each containing eight separate "flags" which + * describe some property of the cave grid. These flags can be checked and + * modified extremely quickly, especially when special idioms are used to + * force the compiler to keep a local register pointing to the base of the + * array. Special location offset macros can be used to minimize the number + * of computations which must be performed at runtime. Note that using a + * byte for each flag set may be slightly more efficient than using a larger + * unit, so if another flag (or two) is needed later, and it must be fast, + * then the two existing flags which do not have to be fast should be moved + * out into some other data structure and the new flags should take their + * place. This may require a few minor changes in the savefile code. + * + * The "CAVE_ROOM" flag is saved in the savefile and is used to determine + * which grids are part of "rooms", and thus which grids are affected by + * "illumination" spells. This flag does not have to be very fast. + * + * The "CAVE_ICKY" flag is saved in the savefile and is used to determine + * which grids are part of "vaults", and thus which grids cannot serve as + * the destinations of player teleportation. This flag does not have to + * be very fast. + * + * The "CAVE_MARK" flag is saved in the savefile and is used to determine + * which grids have been "memorized" by the player. This flag is used by + * the "map_info()" function to determine if a grid should be displayed. + * This flag is used in a few other places to determine if the player can + * "know" about a given grid. This flag must be very fast. + * + * The "CAVE_GLOW" flag is saved in the savefile and is used to determine + * which grids are "permanently illuminated". This flag is used by the + * "update_view()" function to help determine which viewable flags may + * be "seen" by the player. This flag is used by the "map_info" function + * to determine if a grid is only lit by the player's torch. This flag + * has special semantics for wall grids (see "update_view()"). This flag + * must be very fast. + * + * The "CAVE_WALL" flag is used to determine which grids block the player's + * line of sight. This flag is used by the "update_view()" function to + * determine which grids block line of sight, and to help determine which + * grids can be "seen" by the player. This flag must be very fast. + * + * The "CAVE_VIEW" flag is used to determine which grids are currently in + * line of sight of the player. This flag is set by (and used by) the + * "update_view()" function. This flag is used by any code which needs to + * know if the player can "view" a given grid. This flag is used by the + * "map_info()" function for some optional special lighting effects. The + * "player_has_los_bold()" macro wraps an abstraction around this flag, but + * certain code idioms are much more efficient. This flag is used to check + * if a modification to a terrain feature might affect the player's field of + * view. This flag is used to see if certain monsters are "visible" to the + * player. This flag is used to allow any monster in the player's field of + * view to "sense" the presence of the player. This flag must be very fast. + * + * The "CAVE_SEEN" flag is used to determine which grids are currently in + * line of sight of the player and also illuminated in some way. This flag + * is set by the "update_view()" function, using computations based on the + * "CAVE_VIEW" and "CAVE_WALL" and "CAVE_GLOW" flags of various grids. This + * flag is used by any code which needs to know if the player can "see" a + * given grid. This flag is used by the "map_info()" function both to see + * if a given "boring" grid can be seen by the player, and for some optional + * special lighting effects. The "player_can_see_bold()" macro wraps an + * abstraction around this flag, but certain code idioms are much more + * efficient. This flag is used to see if certain monsters are "visible" to + * the player. This flag is never set for a grid unless "CAVE_VIEW" is also + * set for the grid. Whenever the "CAVE_WALL" or "CAVE_GLOW" flag changes + * for a grid which has the "CAVE_VIEW" flag set, the "CAVE_SEEN" flag must + * be recalculated. The simplest way to do this is to call "forget_view()" + * and "update_view()" whenever the "CAVE_WALL" or "CAVE_GLOW" flags change + * for a grid which has "CAVE_VIEW" set. This flag must be very fast. + * + * The "CAVE_TEMP" flag is used for a variety of temporary purposes. This + * flag is used to determine if the "CAVE_SEEN" flag for a grid has changed + * during the "update_view()" function. This flag is used to "spread" light + * or darkness through a room. This flag is used by the "monster flow code". + * This flag must always be cleared by any code which sets it, often, this + * can be optimized by the use of the special "temp_g", "temp_y", "temp_x" + * arrays (and the special "temp_n" global). This flag must be very fast. + * + * Note that the "CAVE_MARK" flag is used for many reasons, some of which + * are strictly for optimization purposes. The "CAVE_MARK" flag means that + * even if the player cannot "see" the grid, he "knows" about the terrain in + * that grid. This is used to "memorize" grids when they are first "seen" by + * the player, and to allow certain grids to be "detected" by certain magic. + * Note that most grids are always memorized when they are first "seen", but + * "boring" grids (floor grids) are only memorized if the "view_torch_grids" + * option is set, or if the "view_perma_grids" option is set, and the grid + * in question has the "CAVE_GLOW" flag set. + * + * Objects are "memorized" in a different way, using a special "marked" flag + * on the object itself, which is set when an object is observed or detected. + * This allows objects to be "memorized" independant of the terrain features. + * + * The "update_view()" function is an extremely important function. It is + * called only when the player moves, significant terrain changes, or the + * player's blindness or torch radius changes. Note that when the player + * is resting, or performing any repeated actions (like digging, disarming, + * farming, etc), there is no need to call the "update_view()" function, so + * even if it was not very efficient, this would really only matter when the + * player was "running" through the dungeon. It sets the "CAVE_VIEW" flag + * on every cave grid in the player's field of view, and maintains an array + * of all such grids in the global "view_g" array. It also checks the torch + * radius of the player, and sets the "CAVE_SEEN" flag for every grid which + * is in the "field of view" of the player and which is also "illuminated", + * either by the players torch (if any) or by any permanent light source. + * It could use and help maintain information about multiple light sources, + * which would be helpful in a multi-player version of Angband. + * + * The "update_view()" function maintains the special "view_g" array, which + * contains exactly those grids which have the "CAVE_VIEW" flag set. This + * array is used by "update_view()" to (only) memorize grids which become + * newly "seen", and to (only) redraw grids whose "seen" value changes, which + * allows the use of some interesting (and very efficient) "special lighting + * effects". In addition, this array could be used elsewhere to quickly scan + * through all the grids which are in the player's field of view. + * + * Note that the "update_view()" function allows, among other things, a room + * to be "partially" seen as the player approaches it, with a growing cone + * of floor appearing as the player gets closer to the door. Also, by not + * turning on the "memorize perma-lit grids" option, the player will only + * "see" those floor grids which are actually in line of sight. And best + * of all, you can now activate the special lighting effects to indicate + * which grids are actually in the player's field of view by using dimmer + * colors for grids which are not in the player's field of view, and/or to + * indicate which grids are illuminated only by the player's torch by using + * the color yellow for those grids. + * + * The old "update_view()" algorithm uses the special "CAVE_EASY" flag as a + * temporary internal flag to mark those grids which are not only in view, + * but which are also "easily" in line of sight of the player. This flag + * is actually just the "CAVE_SEEN" flag, and the "update_view()" function + * makes sure to clear it for all old "CAVE_SEEN" grids, and then use it in + * the algorithm as "CAVE_EASY", and then clear it for all "CAVE_EASY" grids, + * and then reset it as appropriate for all new "CAVE_SEEN" grids. This is + * kind of messy, but it works. The old algorithm may disappear eventually. + * + * The new "update_view()" algorithm uses a faster and more mathematically + * correct algorithm, assisted by a large machine generated static array, to + * determine the "CAVE_VIEW" and "CAVE_SEEN" flags simultaneously. See below. + * + * It seems as though slight modifications to the "update_view()" functions + * would allow us to determine "reverse" line-of-sight as well as "normal" + * line-of-sight", which would allow monsters to have a more "correct" way + * to determine if they can "see" the player, since right now, they "cheat" + * somewhat and assume that if the player has "line of sight" to them, then + * they can "pretend" that they have "line of sight" to the player. But if + * such a change was attempted, the monsters would actually start to exhibit + * some undesirable behavior, such as "freezing" near the entrances to long + * hallways containing the player, and code would have to be added to make + * the monsters move around even if the player was not detectable, and to + * "remember" where the player was last seen, to avoid looking stupid. + * + * Note that the "CAVE_GLOW" flag means that a grid is permanently lit in + * some way. However, for the player to "see" the grid, as determined by + * the "CAVE_SEEN" flag, the player must not be blind, the grid must have + * the "CAVE_VIEW" flag set, and if the grid is a "wall" grid, and it is + * not lit by the player's torch, then it must touch a grid which does not + * have the "CAVE_WALL" flag set, but which does have both the "CAVE_GLOW" + * and "CAVE_VIEW" flags set. This last part about wall grids is induced + * by the semantics of "CAVE_GLOW" as applied to wall grids, and checking + * the technical requirements can be very expensive, especially since the + * grid may be touching some "illegal" grids. Luckily, it is more or less + * correct to restrict the "touching" grids from the eight "possible" grids + * to the (at most) three grids which are touching the grid, and which are + * closer to the player than the grid itself, which eliminates more than + * half of the work, including all of the potentially "illegal" grids, if + * at most one of the three grids is a "diagonal" grid. In addition, in + * almost every situation, it is possible to ignore the "CAVE_VIEW" flag + * on these three "touching" grids, for a variety of technical reasons. + * Finally, note that in most situations, it is only necessary to check + * a single "touching" grid, in fact, the grid which is strictly closest + * to the player of all the touching grids, and in fact, it is normally + * only necessary to check the "CAVE_GLOW" flag of that grid, again, for + * various technical reasons. However, one of the situations which does + * not work with this last reduction is the very common one in which the + * player approaches an illuminated room from a dark hallway, in which the + * two wall grids which form the "entrance" to the room would not be marked + * as "CAVE_SEEN", since of the three "touching" grids nearer to the player + * than each wall grid, only the farthest of these grids is itself marked + * "CAVE_GLOW". + * + * + * Here are some pictures of the legal "light source" radius values, in + * which the numbers indicate the "order" in which the grids could have + * been calculated, if desired. Note that the code will work with larger + * radiuses, though currently yields such a radius, and the game would + * become slower in some situations if it did. + * + * Rad=0 Rad=1 Rad=2 Rad=3 + * No-Lite Torch,etc Lantern Artifacts + * + * 333 + * 333 43334 + * 212 32123 3321233 + * @ 1@1 31@13 331@133 + * 212 32123 3321233 + * 333 43334 + * 333 + * + * + * Here is an illustration of the two different "update_view()" algorithms, + * in which the grids marked "%" are pillars, and the grids marked "?" are + * not in line of sight of the player. + * + * + * Sample situation + * + * ##################### + * ############.%.%.%.%# + * #...@..#####........# + * #............%.%.%.%# + * #......#####........# + * ############........# + * ##################### + * + * + * New Algorithm Old Algorithm + * + * ########????????????? ########????????????? + * #...@..#????????????? #...@..#????????????? + * #...........????????? #.........??????????? + * #......#####.....???? #......####?????????? + * ########?????????...# ########????????????? + * + * ########????????????? ########????????????? + * #.@....#????????????? #.@....#????????????? + * #............%??????? #...........????????? + * #......#####........? #......#####????????? + * ########??????????..# ########????????????? + * + * ########????????????? ########?????%??????? + * #......#####........# #......#####..??????? + * #.@..........%??????? #.@..........%??????? + * #......#####........# #......#####..??????? + * ########????????????? ########????????????? + * + * ########??????????..# ########????????????? + * #......#####........? #......#####????????? + * #............%??????? #...........????????? + * #.@....#????????????? #.@....#????????????? + * ########????????????? ########????????????? + * + * ########?????????%??? ########????????????? + * #......#####.....???? #......####?????????? + * #...........????????? #.........??????????? + * #...@..#????????????? #...@..#????????????? + * ########????????????? ########????????????? + */ + + + + +/* + * Maximum number of grids in a single octant + */ +#define VINFO_MAX_GRIDS 161 + + +/* + * Maximum number of slopes in a single octant + */ +#define VINFO_MAX_SLOPES 126 + + +/* + * Mask of bits used in a single octant + */ +#define VINFO_BITS_3 0x3FFFFFFF +#define VINFO_BITS_2 0xFFFFFFFF +#define VINFO_BITS_1 0xFFFFFFFF +#define VINFO_BITS_0 0xFFFFFFFF + + +/* + * Forward declare + */ +typedef struct vinfo_type vinfo_type; + + +/* + * The 'vinfo_type' structure + */ +struct vinfo_type +{ + s16b grid_y[8]; + s16b grid_x[8]; + + u32b bits_3; + u32b bits_2; + u32b bits_1; + u32b bits_0; + + vinfo_type *next_0; + vinfo_type *next_1; + + byte y; + byte x; + byte d; + byte r; +}; + + + +/* + * The array of "vinfo" objects, initialized by "vinfo_init()" + */ +static vinfo_type vinfo[VINFO_MAX_GRIDS]; + + + + +/* + * Slope scale factor + */ +#define SCALE 100000L + + +/* + * The actual slopes (for reference) + */ + +/* Bit : Slope Grids */ +/* --- : ----- ----- */ +/* 0 : 2439 21 */ +/* 1 : 2564 21 */ +/* 2 : 2702 21 */ +/* 3 : 2857 21 */ +/* 4 : 3030 21 */ +/* 5 : 3225 21 */ +/* 6 : 3448 21 */ +/* 7 : 3703 21 */ +/* 8 : 4000 21 */ +/* 9 : 4347 21 */ +/* 10 : 4761 21 */ +/* 11 : 5263 21 */ +/* 12 : 5882 21 */ +/* 13 : 6666 21 */ +/* 14 : 7317 22 */ +/* 15 : 7692 20 */ +/* 16 : 8108 21 */ +/* 17 : 8571 21 */ +/* 18 : 9090 20 */ +/* 19 : 9677 21 */ +/* 20 : 10344 21 */ +/* 21 : 11111 20 */ +/* 22 : 12000 21 */ +/* 23 : 12820 22 */ +/* 24 : 13043 22 */ +/* 25 : 13513 22 */ +/* 26 : 14285 20 */ +/* 27 : 15151 22 */ +/* 28 : 15789 22 */ +/* 29 : 16129 22 */ +/* 30 : 17241 22 */ +/* 31 : 17647 22 */ +/* 32 : 17948 23 */ +/* 33 : 18518 22 */ +/* 34 : 18918 22 */ +/* 35 : 20000 19 */ +/* 36 : 21212 22 */ +/* 37 : 21739 22 */ +/* 38 : 22580 22 */ +/* 39 : 23076 22 */ +/* 40 : 23809 22 */ +/* 41 : 24137 22 */ +/* 42 : 24324 23 */ +/* 43 : 25714 23 */ +/* 44 : 25925 23 */ +/* 45 : 26315 23 */ +/* 46 : 27272 22 */ +/* 47 : 28000 23 */ +/* 48 : 29032 23 */ +/* 49 : 29411 23 */ +/* 50 : 29729 24 */ +/* 51 : 30434 23 */ +/* 52 : 31034 23 */ +/* 53 : 31428 23 */ +/* 54 : 33333 18 */ +/* 55 : 35483 23 */ +/* 56 : 36000 23 */ +/* 57 : 36842 23 */ +/* 58 : 37142 24 */ +/* 59 : 37931 24 */ +/* 60 : 38461 24 */ +/* 61 : 39130 24 */ +/* 62 : 39393 24 */ +/* 63 : 40740 24 */ +/* 64 : 41176 24 */ +/* 65 : 41935 24 */ +/* 66 : 42857 23 */ +/* 67 : 44000 24 */ +/* 68 : 44827 24 */ +/* 69 : 45454 23 */ +/* 70 : 46666 24 */ +/* 71 : 47368 24 */ +/* 72 : 47826 24 */ +/* 73 : 48148 24 */ +/* 74 : 48387 24 */ +/* 75 : 51515 25 */ +/* 76 : 51724 25 */ +/* 77 : 52000 25 */ +/* 78 : 52380 25 */ +/* 79 : 52941 25 */ +/* 80 : 53846 25 */ +/* 81 : 54838 25 */ +/* 82 : 55555 24 */ +/* 83 : 56521 25 */ +/* 84 : 57575 26 */ +/* 85 : 57894 25 */ +/* 86 : 58620 25 */ +/* 87 : 60000 23 */ +/* 88 : 61290 25 */ +/* 89 : 61904 25 */ +/* 90 : 62962 25 */ +/* 91 : 63636 25 */ +/* 92 : 64705 25 */ +/* 93 : 65217 25 */ +/* 94 : 65517 25 */ +/* 95 : 67741 26 */ +/* 96 : 68000 26 */ +/* 97 : 68421 26 */ +/* 98 : 69230 26 */ +/* 99 : 70370 26 */ +/* 100 : 71428 25 */ +/* 101 : 72413 26 */ +/* 102 : 73333 26 */ +/* 103 : 73913 26 */ +/* 104 : 74193 27 */ +/* 105 : 76000 26 */ +/* 106 : 76470 26 */ +/* 107 : 77777 25 */ +/* 108 : 78947 26 */ +/* 109 : 79310 26 */ +/* 110 : 80952 26 */ +/* 111 : 81818 26 */ +/* 112 : 82608 26 */ +/* 113 : 84000 26 */ +/* 114 : 84615 26 */ +/* 115 : 85185 26 */ +/* 116 : 86206 27 */ +/* 117 : 86666 27 */ +/* 118 : 88235 27 */ +/* 119 : 89473 27 */ +/* 120 : 90476 27 */ +/* 121 : 91304 27 */ +/* 122 : 92000 27 */ +/* 123 : 92592 27 */ +/* 124 : 93103 28 */ +/* 125 : 100000 13 */ + + + +/* + * Forward declare + */ +typedef struct vinfo_hack vinfo_hack; + + +/* + * Temporary data used by "vinfo_init()" + * + * - Number of grids + * + * - Number of slopes + * + * - Slope values + * + * - Slope range per grid + */ +struct vinfo_hack +{ + + int num_slopes; + + long slopes[VINFO_MAX_SLOPES]; + + long slopes_min[MAX_SIGHT + 1][MAX_SIGHT + 1]; + long slopes_max[MAX_SIGHT + 1][MAX_SIGHT + 1]; +}; + + + +/* + * Save a slope + */ +static void vinfo_init_aux(vinfo_hack *hack, int y, int x, long m) +{ + int i; + + /* Handle "legal" slopes */ + if ((m > 0) && (m <= SCALE)) + { + /* Look for that slope */ + for (i = 0; i < hack->num_slopes; i++) + { + if (hack->slopes[i] == m) break; + } + + /* New slope */ + if (i == hack->num_slopes) + { + /* Paranoia */ + if (hack->num_slopes >= VINFO_MAX_SLOPES) + { + quit_fmt("Too many slopes (%d)!", + VINFO_MAX_SLOPES); + } + + /* Save the slope, and advance */ + hack->slopes[hack->num_slopes++] = m; + } + } + + /* Track slope range */ + if (hack->slopes_min[y][x] > m) hack->slopes_min[y][x] = m; + if (hack->slopes_max[y][x] < m) hack->slopes_max[y][x] = m; +} + + + +/* + * Initialize the "vinfo" array + * + * Full Octagon (radius 20), Grids=1149 + * + * Quadrant (south east), Grids=308, Slopes=251 + * + * Octant (east then south), Grids=161, Slopes=126 + * + * This function assumes that VINFO_MAX_GRIDS and VINFO_MAX_SLOPES + * have the correct values, which can be derived by setting them to + * a number which is too high, running this function, and using the + * error messages to obtain the correct values. + */ +errr vinfo_init(void) +{ + int i, y, x; + + long m; + + int num_grids = 0; + + int queue_head = 0; + int queue_tail = 0; + vinfo_type *queue[VINFO_MAX_GRIDS*2]; + + + /* Make hack */ + vinfo_hack hack; + WIPE(&hack, vinfo_hack); + + /* Analyze grids */ + for (y = 0; y <= MAX_SIGHT; ++y) + { + for (x = y; x <= MAX_SIGHT; ++x) + { + /* Skip grids which are out of sight range */ + if (distance(0, 0, y, x) > MAX_SIGHT) continue; + + /* Default slope range */ + hack.slopes_min[y][x] = 999999999; + hack.slopes_max[y][x] = 0; + + /* Paranoia */ + if (num_grids >= VINFO_MAX_GRIDS) + { + quit_fmt("Too many grids (%d >= %d)!", + num_grids, VINFO_MAX_GRIDS); + } + + /* Count grids */ + num_grids++; + + /* Slope to the top right corner */ + m = SCALE * (1000L * y - 500) / (1000L * x + 500); + + /* Handle "legal" slopes */ + vinfo_init_aux(&hack, y, x, m); + + /* Slope to top left corner */ + m = SCALE * (1000L * y - 500) / (1000L * x - 500); + + /* Handle "legal" slopes */ + vinfo_init_aux(&hack, y, x, m); + + /* Slope to bottom right corner */ + m = SCALE * (1000L * y + 500) / (1000L * x + 500); + + /* Handle "legal" slopes */ + vinfo_init_aux(&hack, y, x, m); + + /* Slope to bottom left corner */ + m = SCALE * (1000L * y + 500) / (1000L * x - 500); + + /* Handle "legal" slopes */ + vinfo_init_aux(&hack, y, x, m); + } + } + + + /* Enforce maximal efficiency */ + if (num_grids < VINFO_MAX_GRIDS) + { + quit_fmt("Too few grids (%d < %d)!", + num_grids, VINFO_MAX_GRIDS); + } + + /* Enforce maximal efficiency */ + if (hack.num_slopes < VINFO_MAX_SLOPES) + { + quit_fmt("Too few slopes (%d < %d)!", + hack.num_slopes, VINFO_MAX_SLOPES); + } + + + /* Sort slopes numerically */ + std::sort(std::begin(hack.slopes), std::end(hack.slopes)); + + + + /* Enqueue player grid */ + queue[queue_tail++] = &vinfo[0]; + + /* Process queue */ + while (queue_head < queue_tail) + { + int e; + + /* Index */ + e = queue_head; + + /* Dequeue next grid */ + queue_head++; + + /* Location of main grid */ + y = vinfo[e].grid_y[0]; + x = vinfo[e].grid_x[0]; + + + /* Compute grid offsets */ + vinfo[e].grid_y[0] = + y; + vinfo[e].grid_x[0] = + x; + vinfo[e].grid_y[1] = + x; + vinfo[e].grid_x[1] = + y; + vinfo[e].grid_y[2] = + x; + vinfo[e].grid_x[2] = -y; + vinfo[e].grid_y[3] = + y; + vinfo[e].grid_x[3] = -x; + vinfo[e].grid_y[4] = -y; + vinfo[e].grid_x[4] = -x; + vinfo[e].grid_y[5] = -x; + vinfo[e].grid_x[5] = -y; + vinfo[e].grid_y[6] = -x; + vinfo[e].grid_x[6] = + y; + vinfo[e].grid_y[7] = -y; + vinfo[e].grid_x[7] = + x; + + + /* Analyze slopes */ + for (i = 0; i < hack.num_slopes; ++i) + { + m = hack.slopes[i]; + + /* Memorize intersection slopes (for non-player-grids) */ + if ((e > 0) && + (hack.slopes_min[y][x] < m) && + (m < hack.slopes_max[y][x])) + { + switch (i / 32) + { + case 3: + vinfo[e].bits_3 |= (1L << (i % 32)); + break; + case 2: + vinfo[e].bits_2 |= (1L << (i % 32)); + break; + case 1: + vinfo[e].bits_1 |= (1L << (i % 32)); + break; + case 0: + vinfo[e].bits_0 |= (1L << (i % 32)); + break; + } + } + } + + + /* Default */ + vinfo[e].next_0 = &vinfo[0]; + + /* Grid next child */ + if (distance(0, 0, y, x + 1) <= MAX_SIGHT) + { + if ((queue[queue_tail - 1]->grid_y[0] != y) || + (queue[queue_tail - 1]->grid_x[0] != x + 1)) + { + vinfo[queue_tail].grid_y[0] = y; + vinfo[queue_tail].grid_x[0] = x + 1; + queue[queue_tail] = &vinfo[queue_tail]; + queue_tail++; + } + + vinfo[e].next_0 = &vinfo[queue_tail - 1]; + } + + + /* Default */ + vinfo[e].next_1 = &vinfo[0]; + + /* Grid diag child */ + if (distance(0, 0, y + 1, x + 1) <= MAX_SIGHT) + { + if ((queue[queue_tail - 1]->grid_y[0] != y + 1) || + (queue[queue_tail - 1]->grid_x[0] != x + 1)) + { + vinfo[queue_tail].grid_y[0] = y + 1; + vinfo[queue_tail].grid_x[0] = x + 1; + queue[queue_tail] = &vinfo[queue_tail]; + queue_tail++; + } + + vinfo[e].next_1 = &vinfo[queue_tail - 1]; + } + + + /* Hack -- main diagonal has special children */ + if (y == x) vinfo[e].next_0 = vinfo[e].next_1; + + + /* Extra values */ + vinfo[e].y = y; + vinfo[e].x = x; + vinfo[e].d = ((y > x) ? (y + x / 2) : (x + y / 2)); + vinfo[e].r = ((!y) ? x : (!x) ? y : (y == x) ? y : 0); + } + + + /* Verify maximal bits XXX XXX XXX */ + if (((vinfo[1].bits_3 | vinfo[2].bits_3) != VINFO_BITS_3) || + ((vinfo[1].bits_2 | vinfo[2].bits_2) != VINFO_BITS_2) || + ((vinfo[1].bits_1 | vinfo[2].bits_1) != VINFO_BITS_1) || + ((vinfo[1].bits_0 | vinfo[2].bits_0) != VINFO_BITS_0)) + { + quit("Incorrect bit masks!"); + } + + + /* Success */ + return (0); +} + + + +/* + * Forget the "CAVE_VIEW" grids, redrawing as needed + */ +void forget_view(void) +{ + int i; + + int fast_view_n = view_n; + + cave_type *c_ptr; + + + /* None to forget */ + if (!fast_view_n) return; + + /* Clear them all */ + for (i = 0; i < fast_view_n; i++) + { + int y = view_y[i]; + int x = view_x[i]; + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Clear "CAVE_VIEW", "CAVE_SEEN" and player torch flags */ + c_ptr->info &= ~(CAVE_VIEW | CAVE_SEEN | CAVE_PLIT); + + /* Redraw */ + lite_spot(y, x); + } + + /* None left */ + view_n = 0; +} + + + +/* + * Calculate the complete field of view using a new algorithm + * + * If "view_y/x" and "temp_y/x" were global pointers to arrays of grids, as + * opposed to actual arrays of grids, then we could be more efficient by + * using "pointer swapping". + * + * Normally, vision along the major axes is more likely than vision + * along the diagonal axes, so we check the bits corresponding to + * the lines of sight near the major axes first. + * + * We use the "temp_y/x" array (and the "CAVE_TEMP" flag) to keep track of + * which grids were previously marked "CAVE_SEEN", since only those grids + * whose "CAVE_SEEN" value changes during this routine must be redrawn. + * + * This function is now responsible for maintaining the "CAVE_SEEN" + * flags as well as the "CAVE_VIEW" flags, which is good, because + * the only grids which normally need to be memorized and/or redrawn + * are the ones whose "CAVE_SEEN" flag changes during this routine. + * + * Basically, this function divides the "octagon of view" into octants of + * grids (where grids on the main axes and diagonal axes are "shared" by + * two octants), and processes each octant one at a time, processing each + * octant one grid at a time, processing only those grids which "might" be + * viewable, and setting the "CAVE_VIEW" flag for each grid for which there + * is an (unobstructed) line of sight from the center of the player grid to + * any internal point in the grid (and collecting these "CAVE_VIEW" grids + * into the "view_y/x" array), and setting the "CAVE_SEEN" flag for the grid + * if, in addition, the grid is "illuminated" in some way. + * + * This function relies on a theorem (suggested and proven by Mat Hostetter) + * which states that in each octant of a field of view, a given grid will + * be "intersected" by one or more unobstructed "lines of sight" from the + * center of the player grid if and only if it is "intersected" by at least + * one such unobstructed "line of sight" which passes directly through some + * corner of some grid in the octant which is not shared by any other octant. + * The proof is based on the fact that there are at least three significant + * lines of sight involving any non-shared grid in any octant, one which + * intersects the grid and passes though the corner of the grid closest to + * the player, and two which "brush" the grid, passing through the "outer" + * corners of the grid, and that any line of sight which intersects a grid + * without passing through the corner of a grid in the octant can be "slid" + * slowly towards the corner of the grid closest to the player, until it + * either reaches it or until it brushes the corner of another grid which + * is closer to the player, and in either case, the existanc of a suitable + * line of sight is thus demonstrated. + * + * It turns out that in each octant of the radius 20 "octagon of view", + * there are 161 grids (with 128 not shared by any other octant), and there + * are exactly 126 distinct "lines of sight" passing from the center of the + * player grid through any corner of any non-shared grid in the octant. To + * determine if a grid is "viewable" by the player, therefore, you need to + * simply show that one of these 126 lines of sight intersects the grid but + * does not intersect any wall grid closer to the player. So we simply use + * a bit vector with 126 bits to represent the set of interesting lines of + * sight which have not yet been obstructed by wall grids, and then we scan + * all the grids in the octant, moving outwards from the player grid. For + * each grid, if any of the lines of sight which intersect that grid have not + * yet been obstructed, then the grid is viewable. Furthermore, if the grid + * is a wall grid, then all of the lines of sight which intersect the grid + * should be marked as obstructed for future reference. Also, we only need + * to check those grids for whom at least one of the "parents" was a viewable + * non-wall grid, where the parents include the two grids touching the grid + * but closer to the player grid (one adjacent, and one diagonal). For the + * bit vector, we simply use 4 32-bit integers. All of the static values + * which are needed by this function are stored in the large "vinfo" array + * (above), which is machine generated by another program. XXX XXX XXX + * + * Hack -- The queue must be able to hold more than VINFO_MAX_GRIDS grids + * because the grids at the edge of the field of view use "grid zero" as + * their children, and the queue must be able to hold several of these + * special grids. Because the actual number of required grids is bizarre, + * we simply allocate twice as many as we would normally need. XXX XXX XXX + */ +void update_view(void) +{ + int i, o; + int y, x; + + int radius; + + int fast_view_n = view_n; + + int fast_temp_n = 0; + + cave_type *c_ptr; + + u16b info; + + + /*** Step 0 -- Begin ***/ + + /* Save the old "view" grids for later */ + for (i = 0; i < fast_view_n; i++) + { + /* Location */ + y = view_y[i]; + x = view_x[i]; + + /* Grid */ + c_ptr = &cave[y][x]; + + /* Get grid info */ + info = c_ptr->info; + ; + + /* Save "CAVE_SEEN" grids */ + if (info & (CAVE_SEEN)) + { + /* Set "CAVE_TEMP" flag */ + info |= (CAVE_TEMP); + + /* Save grid for later */ + temp_y[fast_temp_n] = y; + temp_x[fast_temp_n++] = x; + } + + /* Clear "CAVE_VIEW", "CAVE_SEEN" and player torch flags */ + info &= ~(CAVE_VIEW | CAVE_SEEN | CAVE_PLIT); + + /* Save cave info */ + c_ptr->info = info; + } + + /* Reset the "view" array */ + fast_view_n = 0; + + /* Extract "radius" value */ + radius = p_ptr->cur_lite; + + /* Handle real light */ + if (radius > 0) ++radius; + + + /*** Step 1 -- player grid ***/ + + /* Player grid */ + c_ptr = &cave[p_ptr->py][p_ptr->px]; + + /* Get grid info */ + info = c_ptr->info; + + /* Assume viewable */ + info |= (CAVE_VIEW); + + /* Torch-lit grid */ + if (0 < radius) + { + /* Mark as "CAVE_SEEN" and torch-lit */ + info |= (CAVE_SEEN | CAVE_PLIT); + } + + + /* Perma-lit grid */ + else if (info & (CAVE_GLOW)) + { + /* Mark as "CAVE_SEEN" */ + info |= (CAVE_SEEN); + } + + /* Save cave info */ + c_ptr->info = info; + + /* Save in array */ + view_y[fast_view_n] = p_ptr->py; + view_x[fast_view_n++] = p_ptr->px; + + + /*** Step 2 -- octants ***/ + + /* Scan each octant */ + for (o = 0; o < 8; o++) + { + vinfo_type *p; + + /* Last added */ + vinfo_type *last = &vinfo[0]; + + /* Grid queue */ + int queue_head = 0; + int queue_tail = 0; + vinfo_type *queue[VINFO_MAX_GRIDS*2]; + + /* Slope bit vector */ + u32b bits0 = VINFO_BITS_0; + u32b bits1 = VINFO_BITS_1; + u32b bits2 = VINFO_BITS_2; + u32b bits3 = VINFO_BITS_3; + + /* Reset queue */ + queue_head = queue_tail = 0; + + /* Initial grids */ + queue[queue_tail++] = &vinfo[1]; + queue[queue_tail++] = &vinfo[2]; + + /* Process queue */ + while (queue_head < queue_tail) + { + /* Dequeue next grid */ + p = queue[queue_head++]; + + /* Check bits */ + if ((bits0 & (p->bits_0)) || + (bits1 & (p->bits_1)) || + (bits2 & (p->bits_2)) || + (bits3 & (p->bits_3))) + { + /* Extract coordinate value */ + y = p_ptr->py + p->grid_y[o]; + x = p_ptr->px + p->grid_x[o]; + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Get grid info */ + info = c_ptr->info; + + /* Handle wall */ + if (info & (CAVE_WALL)) + { + /* Clear bits */ + bits0 &= ~(p->bits_0); + bits1 &= ~(p->bits_1); + bits2 &= ~(p->bits_2); + bits3 &= ~(p->bits_3); + + /* Newly viewable wall */ + if (!(info & (CAVE_VIEW))) + { + /* Mark as viewable */ + info |= (CAVE_VIEW); + + /* Torch-lit grids */ + if (p->d < radius) + { + /* Mark as "CAVE_SEEN" and torch-lit */ + info |= (CAVE_SEEN | CAVE_PLIT); + } + + /* Monster-lit grids */ + else if (info & (CAVE_MLIT)) + { + /* Mark as "CAVE_SEEN" */ + info |= (CAVE_SEEN); + } + + /* Perma-lit grids */ + else if (info & (CAVE_GLOW)) + { + /* Hack -- move towards player */ + int yy = (y < p_ptr->py) ? (y + 1) : (y > p_ptr->py) ? (y - 1) : y; + int xx = (x < p_ptr->px) ? (x + 1) : (x > p_ptr->px) ? (x - 1) : x; + + /* Check for "simple" illumination */ + if (cave[yy][xx].info & (CAVE_GLOW)) + { + /* Mark as seen */ + info |= (CAVE_SEEN); + } + } + + /* Save cave info */ + c_ptr->info = info; + + /* Save in array */ + view_y[fast_view_n] = y; + view_x[fast_view_n++] = x; + } + } + + /* Handle non-wall */ + else + { + /* Enqueue child */ + if (last != p->next_0) + { + queue[queue_tail++] = last = p->next_0; + } + + /* Enqueue child */ + if (last != p->next_1) + { + queue[queue_tail++] = last = p->next_1; + } + + /* Newly viewable non-wall */ + if (!(info & (CAVE_VIEW))) + { + /* Mark as "viewable" */ + info |= (CAVE_VIEW); + + /* Torch-lit grids */ + if (p->d < radius) + { + /* Mark as "CAVE_SEEN" and torch-lit */ + info |= (CAVE_SEEN | CAVE_PLIT); + } + + /* Perma-lit or monster-lit grids */ + else if (info & (CAVE_GLOW | CAVE_MLIT)) + { + /* Mark as "CAVE_SEEN" */ + info |= (CAVE_SEEN); + } + + /* Save cave info */ + c_ptr->info = info; + + /* Save in array */ + view_y[fast_view_n] = y; + view_x[fast_view_n++] = x; + } + } + } + } + } + + + /*** Step 3 -- Complete the algorithm ***/ + + /* Handle blindness */ + if (p_ptr->blind) + { + /* Process "new" grids */ + for (i = 0; i < fast_view_n; i++) + { + /* Location */ + y = view_y[i]; + x = view_x[i]; + + /* Grid cannot be "CAVE_SEEN" */ + cave[y][x].info &= ~(CAVE_SEEN); + } + } + + /* Process "new" grids */ + for (i = 0; i < fast_view_n; i++) + { + /* Location */ + y = view_y[i]; + x = view_x[i]; + + /* Get grid info */ + info = cave[y][x].info; + + /* Was not "CAVE_SEEN", is now "CAVE_SEEN" */ + if ((info & (CAVE_SEEN)) && !(info & (CAVE_TEMP))) + { + /* Note */ + note_spot(y, x); + + /* Redraw */ + lite_spot(y, x); + } + } + + /* Process "old" grids */ + for (i = 0; i < fast_temp_n; i++) + { + /* Location */ + y = temp_y[i]; + x = temp_x[i]; + + /* Grid */ + c_ptr = &cave[y][x]; + + /* Get grid info */ + info = c_ptr->info; + + /* Clear "CAVE_TEMP" flag */ + info &= ~(CAVE_TEMP); + + /* Save cave info */ + c_ptr->info = info; + + /* Was "CAVE_SEEN", is now not "CAVE_SEEN" */ + if (!(info & (CAVE_SEEN))) + { + /* Redraw */ + lite_spot(y, x); + } + } + + + /* Save 'view_n' */ + view_n = fast_view_n; +} + + +/* + * Clear monster light + */ +void forget_mon_lite(void) +{ + int i, y, x; + + /* Process all the monster-lit grids */ + for (i = 0; i < lite_n; i++) + { + /* Access location */ + y = lite_y[i]; + x = lite_x[i]; + + /* Clear monster light flag */ + cave[y][x].info &= ~(CAVE_MLIT); + } + + /* Forget light array */ + lite_n = 0; +} + + +/* + * Update squares illuminated by monsters + * + * Code taken from Steven Fuerst's work for ZAngband, without support + * for multiple lite radii, and with necessary modifications for different + * internal representation of dungeon/wilderness. Other minor changes + * are mine... + * + * I'm not sure if I can handle wide radius well. Consider the following + * example, with p carrying a radius 3 light source: + * + * ##%# + * .x.. + * p##@ + * + * % should be illuminated, although the beam path is entirely out of + * player's los (because of grid-based nature of cave representation)... + * And I'm extremely reluctant to introduce symmetrical los. The current + * asymmetrical system has its own merit, and all the rules of games are + * asymmetrical, in some way or another... + * + * The code below exploits special characteristics of radius one light + * where one can fairly safely use light source's visibility (in terms of los) + * to determine if we can illuminate walls XXX + * + * This function works within the current player's field of view + * calculated by update_view(), so it should normally be called + * whenever FoV is updated (== PU_VIEW | PU_MON_LITE). The other + * case is when RF9_HAS_LITE monsters have moved or dead. Monster + * creation occurs out of LoS, so I chose not to take this into + * consideration. + * + * The CAVE_TEMP flag is used by the function to remember "old" monster-lit + * grids so that it can only redraw squares whose visibility has changed. + * + * Doing this in the update_view() order (update "new" grids, then "old") + * would result in bizarre lighting effects XXX XXX + * + * It has been made possible again to draw torch/monster-lit grids in + * different colours, even when they are in permanently lit locations + * by using (CAVE_PLIT|CAVE_MLIT) as if it were old CAVE_LITE, but I don't + * think it's appropriate for torch lights to be visible under the Sun :) + * or brighter light, and it doesn't work well with PernAngband's already + * colourful terrain features in aesthetically pleasing ways... -- pelpel + */ +void update_mon_lite(void) +{ + int i, y, x, d; + int fy, fx; + + cave_type *c_ptr; + u16b info; + + bool_ invis; + + s16b fast_lite_n = lite_n; + s16b fast_temp_n; + + + /* Mega-Hack -- It's unnecessary there */ + if (p_ptr->wild_mode) return; + + /* Handle special case -- Blindness */ + if (p_ptr->blind) + { + for (i = 0; i < fast_lite_n; i++) + { + /* Light location */ + y = lite_y[i]; + x = lite_x[i]; + + /* Forget monster light and view */ + cave[y][x].info &= ~(CAVE_MLIT | CAVE_SEEN); + + /* Redraw spot */ + /* lite_spot(y, x); */ + } + + /* Clear the light list */ + lite_n = 0; + + /* Done */ + return; + } + + + /* Remember and clear all monster-lit grids */ + for (i = 0; i < fast_lite_n; i++) + { + /* Lit location */ + y = lite_y[i]; + x = lite_x[i]; + + /* Access grid */ + c_ptr = &cave[y][x]; + + /* Access cave info of the grid */ + info = c_ptr->info; + + /* Remember it, by setting the CAVE_TEMP flag */ + info |= (CAVE_TEMP); + + /* Forget monster light */ + info &= ~(CAVE_MLIT); + + /* Unseen unless it's glowing or illuminated by player light source */ + if (!(info & (CAVE_GLOW | CAVE_PLIT))) + { + info &= ~(CAVE_SEEN); + } + + /* Save cave info flags */ + c_ptr->info = info; + } + + + /* Clear the temp list */ + fast_temp_n = 0; + + /* Loop through monsters, adding newly lit grids to changes list */ + for (i = 1; i < m_max; i++) + { + monster_type *m_ptr = &m_list[i]; + monster_race *r_ptr; + + /* Skip dead monsters */ + if (!m_ptr->r_idx) continue; + + /* Skip out-of-sight monsters (MAX_SIGHT + max radius) */ + if (m_ptr->cdis > MAX_SIGHT + 1) continue; + + /* Access monster race info (with possible ego mods) */ + r_ptr = race_info_idx(m_ptr->r_idx, m_ptr->ego); + + /* Skip monsters not carrying light source */ + if (!(r_ptr->flags9 & RF9_HAS_LITE)) continue; + + /* Access the location */ + fy = m_ptr->fy; + fx = m_ptr->fx; + + /* Extract monster grid visibility */ + invis = !player_has_los_bold(fy, fx); + + /* Nested loops may be a bad idea here XXX */ + for (d = 0; d < 9; d++) + { + y = fy + ddy_ddd[d]; + x = fx + ddx_ddd[d]; + + /* Paranoia */ + /* if (!in_bounds(y, x)) continue; */ + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Access cave info flags */ + info = c_ptr->info; + + /* Don't care grids out of player's los */ + if (!(info & (CAVE_VIEW))) continue; + + /* + * Avoid processing already monster-lit grids, + * for efficiency and to avoid temp array overflow + */ + if (info & (CAVE_MLIT)) continue; + + /* + * Hack XXX XXX -- light shouldn't penetrate walls + * + * OK NG + * .#. p#. | p. .p. p.. + * p.@ ..@ | .# .#. .#. + * | .@ .@. ..@ + * + * So if a monster carrying light source is out of player LoS, + * walls aren't illuminated. + * + * CAVEAT: % will be illuminated in cases like this: + * + * #%..@ + * p.... + * + * We don't have four sides for a wall grid, so... + */ + if (invis && (f_info[c_ptr->feat].flags1 & FF1_NO_VISION)) continue; + + /* Give monster light to the location */ + c_ptr->info |= (CAVE_MLIT | CAVE_SEEN); + + /* Save the location */ + temp_y[fast_temp_n] = y; + temp_x[fast_temp_n] = x; + fast_temp_n++; + } + } + + /* Process old grids */ + for (i = 0; i < fast_lite_n; i++) + { + /* Access location */ + y = lite_y[i]; + x = lite_x[i]; + + /* Access grid */ + c_ptr = &cave[y][x]; + + /* Was lit, is no longer lit */ + if (!(c_ptr->info & (CAVE_MLIT))) + { + /* Clear the temp flag */ + c_ptr->info &= ~(CAVE_TEMP); + + /* See if there was a visible monster */ + if (player_has_los_bold(y, x) && c_ptr->m_idx) + { + /* Hide the monster */ + update_mon(c_ptr->m_idx, FALSE); + } + else + { + /* Redraw */ + lite_spot(y, x); + } + } + } + + /* Copy the temp array into the light array */ + for (i = 0; i < fast_temp_n; i++) + { + /* Access location */ + y = temp_y[i]; + x = temp_x[i]; + + /* Access grid */ + c_ptr = &cave[y][x]; + + + /* No changes in illumination */ + if (c_ptr->info & (CAVE_TEMP)) + { + /* Clear the temp flag */ + c_ptr->info &= ~(CAVE_TEMP); + } + + /* Was not lit, is now lit */ + else + { + /* Remember the location, if appropriate */ + note_spot(y, x); + + /* See if there is a monster */ + if (c_ptr->m_idx) + { + /* Show it */ + update_mon(c_ptr->m_idx, FALSE); + } + else + { + /* Redraw */ + lite_spot(y, x); + } + } + + + /* Save the location */ + lite_y[i] = y; + lite_x[i] = x; + } + + /* Save lite_n */ + lite_n = fast_temp_n; + + /* Forget temp */ + temp_n = 0; +} + + + + + + +/* + * Hack -- provide some "speed" for the "flow" code + * This entry is the "current index" for the "when" field + * Note that a "when" value of "zero" means "not used". + * + * Note that the "cost" indexes from 1 to 127 are for + * "old" data, and from 128 to 255 are for "new" data. + * + * This means that as long as the player does not "teleport", + * then any monster up to 128 + MONSTER_FLOW_DEPTH will be + * able to track down the player, and in general, will be + * able to track down either the player or a position recently + * occupied by the player. + */ +static int flow_n = 0; + + +/* + * Hack -- forget the "flow" information + */ +void forget_flow(void) +{ + int x, y; + + /* Nothing to forget */ + if (!flow_n) return; + + /* Check the entire dungeon */ + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + /* Forget the old data */ + cave[y][x].cost = 0; + cave[y][x].when = 0; + } + } + + /* Start over */ + flow_n = 0; +} + + +/* + * Hack -- Allow us to treat the "seen" array as a queue + */ +static int flow_head = 0; +static int flow_tail = 0; + + +/* + * Take note of a reachable grid. Assume grid is legal. + */ +static void update_flow_aux(int y, int x, int n) +{ + cave_type *c_ptr; + + int old_head = flow_head; + + + /* Get the grid */ + c_ptr = &cave[y][x]; + + /* Ignore "pre-stamped" entries */ + if (c_ptr->when == flow_n) return; + + /* Ignore "walls" and "rubble" */ + if (c_ptr->feat >= FEAT_RUBBLE) return; + + /* Save the time-stamp */ + c_ptr->when = flow_n; + + /* Save the flow cost */ + c_ptr->cost = n; + + /* Hack -- limit flow depth */ + if (n == MONSTER_FLOW_DEPTH) return; + + /* Enqueue that entry */ + temp_y[flow_head] = y; + temp_x[flow_head] = x; + + /* Advance the queue */ + if (++flow_head == TEMP_MAX) flow_head = 0; + + /* Hack -- notice overflow by forgetting new entry */ + if (flow_head == flow_tail) flow_head = old_head; +} + + +/* + * Hack -- fill in the "cost" field of every grid that the player + * can "reach" with the number of steps needed to reach that grid. + * This also yields the "distance" of the player from every grid. + * + * In addition, mark the "when" of the grids that can reach + * the player with the incremented value of "flow_n". + * + * Hack -- use the "seen" array as a "circular queue". + * + * We do not need a priority queue because the cost from grid + * to grid is always "one" and we process them in order. + */ +void update_flow(void) +{ + int x, y, d; + + /* Hack -- disabled */ + if (!flow_by_sound) return; + + /* Paranoia -- make sure the array is empty */ + if (temp_n) return; + + /* Cycle the old entries (once per 128 updates) */ + if (flow_n == 255) + { + /* Rotate the time-stamps */ + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + int w = cave[y][x].when; + cave[y][x].when = (w > 128) ? (w - 128) : 0; + } + } + + /* Restart */ + flow_n = 127; + } + + /* Start a new flow (never use "zero") */ + flow_n++; + + + /* Reset the "queue" */ + flow_head = flow_tail = 0; + + /* Add the player's grid to the queue */ + update_flow_aux(p_ptr->py, p_ptr->px, 0); + + /* Now process the queue */ + while (flow_head != flow_tail) + { + /* Extract the next entry */ + y = temp_y[flow_tail]; + x = temp_x[flow_tail]; + + /* Forget that entry */ + if (++flow_tail == TEMP_MAX) flow_tail = 0; + + /* Add the "children" */ + for (d = 0; d < 8; d++) + { + /* Add that child if "legal" */ + update_flow_aux(y + ddy_ddd[d], x + ddx_ddd[d], cave[y][x].cost + 1); + } + } + + /* Forget the flow info */ + flow_head = flow_tail = 0; +} + + + + + + + +/* + * Hack -- map the current panel (plus some) ala "magic mapping" + */ +void map_area(void) +{ + int i, x, y, y1, y2, x1, x2; + + cave_type *c_ptr; + + + /* Pick an area to map */ + y1 = panel_row_min - randint(10); + y2 = panel_row_max + randint(10); + x1 = panel_col_min - randint(20); + x2 = panel_col_max + randint(20); + + /* Speed -- shrink to fit legal bounds */ + if (y1 < 1) y1 = 1; + if (y2 > cur_hgt - 2) y2 = cur_hgt - 2; + if (x1 < 1) x1 = 1; + if (x2 > cur_wid - 2) x2 = cur_wid - 2; + + /* Scan that area */ + for (y = y1; y <= y2; y++) + { + for (x = x1; x <= x2; x++) + { + c_ptr = &cave[y][x]; + + /* All non-walls are "checked" */ + if (!is_wall(c_ptr)) + { + /* Memorize normal features */ + if (!cave_plain_floor_grid(c_ptr)) + { + /* Memorize the object */ + c_ptr->info |= (CAVE_MARK); + } + + /* Memorize known walls */ + for (i = 0; i < 8; i++) + { + c_ptr = &cave[y + ddy_ddd[i]][x + ddx_ddd[i]]; + + /* Memorize walls (etc) */ + if (is_wall(c_ptr)) + { + /* Memorize the walls */ + c_ptr->info |= (CAVE_MARK); + } + } + } + } + } + + /* Redraw map */ + p_ptr->redraw |= (PR_MAP); + + /* Window stuff */ + p_ptr->window |= (PW_OVERHEAD); +} + + + +/* + * Light up the dungeon using "clairvoyance" + * + * This function "illuminates" every grid in the dungeon, memorizes all + * "objects", memorizes all grids as with magic mapping, and, under the + * standard option settings (view_perma_grids but not view_torch_grids) + * memorizes all floor grids too. + * + * Note that if "view_perma_grids" is not set, we do not memorize floor + * grids, since this would defeat the purpose of "view_perma_grids", not + * that anyone seems to play without this option. + * + * Note that if "view_torch_grids" is set, we do not memorize floor grids, + * since this would prevent the use of "view_torch_grids" as a method to + * keep track of what grids have been observed directly. + */ +void wiz_lite(void) +{ + int i, y, x; + + + /* Memorize objects */ + for (i = 1; i < o_max; i++) + { + object_type *o_ptr = &o_list[i]; + + /* Skip dead objects */ + if (!o_ptr->k_idx) continue; + + /* Skip held objects */ + if (o_ptr->held_m_idx) continue; + + /* Memorize */ + o_ptr->marked = TRUE; + } + + /* Scan all normal grids */ + for (y = 1; y < cur_hgt - 1; y++) + { + /* Scan all normal grids */ + for (x = 1; x < cur_wid - 1; x++) + { + cave_type *c_ptr = &cave[y][x]; + + if (c_ptr->m_idx) + { + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + monster_race *r_ptr = race_inf(m_ptr); + + if (r_ptr->flags9 & RF9_MIMIC) + { + object_type *o_ptr = &o_list[m_ptr->hold_o_idx]; + + o_ptr->marked = TRUE; + } + } + + /* Process all non-walls */ + /* if (c_ptr->feat < FEAT_SECRET) */ + { + /* Scan all neighbors */ + for (i = 0; i < 9; i++) + { + int yy = y + ddy_ddd[i]; + int xx = x + ddx_ddd[i]; + + /* Get the grid */ + c_ptr = &cave[yy][xx]; + + /* Perma-lite the grid */ + c_ptr->info |= (CAVE_GLOW); + + /* Memorize normal features */ + if (!cave_plain_floor_grid(c_ptr)) + { + /* Memorize the grid */ + c_ptr->info |= (CAVE_MARK); + } + + /* Normally, memorize floors (see above) */ + if (view_perma_grids && !view_torch_grids) + { + /* Memorize the grid */ + c_ptr->info |= (CAVE_MARK); + } + } + } + } + } + + /* Fully update the visuals */ + p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE); + + /* Redraw map */ + p_ptr->redraw |= (PR_MAP); + + /* Window stuff */ + p_ptr->window |= (PW_OVERHEAD); +} + +void wiz_lite_extra(void) +{ + int y, x; + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + cave[y][x].info |= (CAVE_GLOW | CAVE_MARK); + } + } + wiz_lite(); +} + +/* + * Forget the dungeon map (ala "Thinking of Maud..."). + */ +void wiz_dark(void) +{ + int i, y, x; + + + /* Forget every grid */ + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + cave_type *c_ptr = &cave[y][x]; + + /* Process the grid */ + c_ptr->info &= ~(CAVE_MARK); + } + } + + /* Forget all objects */ + for (i = 1; i < o_max; i++) + { + object_type *o_ptr = &o_list[i]; + + /* Skip dead objects */ + if (!o_ptr->k_idx) continue; + + /* Skip held objects */ + if (o_ptr->held_m_idx) continue; + + /* Forget the object */ + o_ptr->marked = FALSE; + } + + /* Fully update the visuals */ + p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE); + + /* Redraw map */ + p_ptr->redraw |= (PR_MAP); + + /* Window stuff */ + p_ptr->window |= (PW_OVERHEAD); +} + + + + + +/* + * Change the "feat" flag for a grid, and notice/redraw the grid + */ +void cave_set_feat(int y, int x, int feat) +{ + cave_type *c_ptr = &cave[y][x]; + + /* Change the feature */ + c_ptr->feat = feat; + + /* + * Handle "wall/door" grids + * + * XXX XXX XXX This assumes c_ptr->mimic doesn't mimic terrain + * features whose LoS behaviour is different from its own, in + * most cases. Level boundaries are the most notable exception, + * where "real" terrain is always FEAT_PERM_SOLID, and the fact + * is (ab)used to prevent out-of-range access to the cave array. + * If we were going to implement an evil dungeon type in which + * everything is mimicked, then this function, los(), projectable(), + * project_path() and maybe some functions in melee2.c might + * better use c_ptr->mimic when it's set -- pelpel + */ + if (!cave_sight_grid(c_ptr)) + { + c_ptr->info |= (CAVE_WALL); + } + + /* Handle "floor"/etc grids */ + else + { + c_ptr->info &= ~(CAVE_WALL); + } + + /* Notice & Redraw */ + if (character_dungeon) + { + /* Notice */ + note_spot(y, x); + + /* Redraw */ + lite_spot(y, x); + } +} + + +/* + * Place floor terrain at (y, x) according to dungeon info + */ +void place_floor(int y, int x) +{ + cave_set_feat(y, x, floor_type[rand_int(100)]); +} + +/* + * This routine is used when the current feature gets convert to a floor and + * the possible floor types include glass which is permanent. An unpassable + * feature is undesirable, so the glass gets convert to molten glass which + * is passable. + */ +void place_floor_convert_glass(int y, int x) +{ + place_floor(y, x); + + if (cave[y][x].feat == 188) cave[y][x].feat = 103; +} + +/* + * Place a cave filler at (y, x) + */ +void place_filler(int y, int x) +{ + cave_set_feat(y, x, fill_type[rand_int(100)]); +} + + +/* + * Calculate "incremental motion". Used by project() and shoot(). + * Assumes that (*y,*x) lies on the path from (y1,x1) to (y2,x2). + */ +void mmove2(int *y, int *x, int y1, int x1, int y2, int x2) +{ + int dy, dx, dist, shift; + + /* Extract the distance travelled */ + dy = (*y < y1) ? y1 - *y : *y - y1; + dx = (*x < x1) ? x1 - *x : *x - x1; + + /* Number of steps */ + dist = (dy > dx) ? dy : dx; + + /* We are calculating the next location */ + dist++; + + + /* Calculate the total distance along each axis */ + dy = (y2 < y1) ? (y1 - y2) : (y2 - y1); + dx = (x2 < x1) ? (x1 - x2) : (x2 - x1); + + /* Paranoia -- Hack -- no motion */ + if (!dy && !dx) return; + + + /* Move mostly vertically */ + if (dy > dx) + { + /* Extract a shift factor */ + shift = (dist * dx + (dy - 1) / 2) / dy; + + /* Sometimes move along the minor axis */ + (*x) = (x2 < x1) ? (x1 - shift) : (x1 + shift); + + /* Always move along major axis */ + (*y) = (y2 < y1) ? (y1 - dist) : (y1 + dist); + } + + /* Move mostly horizontally */ + else + { + /* Extract a shift factor */ + shift = (dist * dy + (dx - 1) / 2) / dx; + + /* Sometimes move along the minor axis */ + (*y) = (y2 < y1) ? (y1 - shift) : (y1 + shift); + + /* Always move along major axis */ + (*x) = (x2 < x1) ? (x1 - dist) : (x1 + dist); + } +} + + + +/* + * Determine if a bolt spell cast from (y1,x1) to (y2,x2) will arrive + * at the final destination, assuming no monster gets in the way. + * + * This is slightly (but significantly) different from "los(y1,x1,y2,x2)". + */ +bool_ projectable(int y1, int x1, int y2, int x2) +{ + int dist, y, x; + + /* Start at the initial location */ + y = y1, x = x1; + + /* See "project()" */ + for (dist = 0; dist <= MAX_RANGE; dist++) + { + /* Check for arrival at "final target" */ + /* + * NB: this check was AFTER the 'never pass + * thru walls' clause, below. Switching them + * lets monsters shoot a the player if s/he is + * visible but in a wall + */ + if ((x == x2) && (y == y2)) return (TRUE); + + /* Never pass through walls */ + if (dist && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) break; + + /* Calculate the new location */ + mmove2(&y, &x, y1, x1, y2, x2); + } + + + /* Assume obstruction */ + return (FALSE); +} + + + + +/* + * Standard "find me a location" function + * + * Obtains a legal location within the given distance of the initial + * location, and with "los()" from the source to destination location. + * + * This function is often called from inside a loop which searches for + * locations while increasing the "d" distance. + * + * Currently the "m" parameter is unused. + */ +void scatter(int *yp, int *xp, int y, int x, int d) +{ + int nx, ny; + int attempts_left = 5000; + + /* Pick a location */ + while (--attempts_left) + { + /* Pick a new location */ + ny = rand_spread(y, d); + nx = rand_spread(x, d); + + /* Ignore illegal locations and outer walls */ + if (!in_bounds(ny, nx)) continue; + + /* Ignore "excessively distant" locations */ + if ((d > 1) && (distance(y, x, ny, nx) > d)) continue; + + /* Require "line of sight" */ + if (los(y, x, ny, nx)) break; + } + + if (attempts_left > 0) + { + /* Save the location */ + (*yp) = ny; + (*xp) = nx; + } +} + + + + +/* + * Track a new monster + */ +void health_track(int m_idx) +{ + /* Track a new guy */ + health_who = m_idx; + + /* Redraw (later) */ + p_ptr->redraw |= (PR_HEALTH); +} + + + +/* + * Hack -- track the given monster race + */ +void monster_race_track(int r_idx, int ego) +{ + /* Save this monster ID */ + monster_race_idx = r_idx; + monster_ego_idx = ego; + + /* Window stuff */ + p_ptr->window |= (PW_MONSTER); +} + + + +/* + * Hack -- track the given object kind + */ +void object_track(object_type *o_ptr) +{ + /* Save this monster ID */ + tracked_object = o_ptr; + + /* Window stuff */ + p_ptr->window |= (PW_OBJECT); +} + + + +/* + * Something has happened to disturb the player. + * + * The first arg indicates a major disturbance, which affects search. + * + * All disturbance cancels repeated commands, resting, and running. + */ +void disturb(int stop_search) +{ + /* Cancel auto-commands */ + /* command_new = 0; */ + + /* Cancel repeated commands */ + if (command_rep) + { + /* Cancel */ + command_rep = 0; + + /* Redraw the state (later) */ + p_ptr->redraw |= (PR_STATE); + } + + /* Cancel Resting */ + if (resting) + { + /* Cancel */ + resting = 0; + + /* Redraw the state (later) */ + p_ptr->redraw |= (PR_STATE); + } + + /* Cancel running */ + if (running) + { + /* Cancel */ + running = 0; + + /* Calculate torch radius */ + p_ptr->update |= (PU_TORCH); + } + + /* Cancel searching if requested */ + if (stop_search && p_ptr->searching) + { + /* Cancel */ + p_ptr->searching = FALSE; + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + } + + /* Flush the input if requested */ + if (flush_disturb) flush(); +} + + +/* + * Hack -- Check if a level is a "quest" level + */ +int is_quest(int level) +{ + int i = random_quest_number(); + + /* Check quests */ + if (p_ptr->inside_quest) + return (p_ptr->inside_quest); + + if (i) return (QUEST_RANDOM); + + /* Nope */ + return (0); +} + + +/* + * Return the index of the random quest on this level + * (or zero) + */ +int random_quest_number() +{ + if ((dun_level >= 1) && (dun_level < MAX_RANDOM_QUEST) && + (dungeon_flags1 & DF1_PRINCIPAL) && + (random_quests[dun_level].type) && + (!random_quests[dun_level].done) && + (!is_randhero(dun_level))) + { + return dun_level; + } + + /* Nope */ + return 0; +} + + +/* + * handle spell effects + */ +int effect_pop() +{ + int i; + + for (i = 1; i < MAX_EFFECTS; i++) + if (!effects[i].time) + return i; + return -1; +} + +int new_effect(int type, int dam, int time, int cy, int cx, int rad, s32b flags) +{ + int i; + + if ((i = effect_pop()) == -1) return -1; + + effects[i].type = type; + effects[i].dam = dam; + effects[i].time = time; + effects[i].flags = flags; + effects[i].cx = cx; + effects[i].cy = cy; + effects[i].rad = rad; + return i; +} diff --git a/src/cmd1.c b/src/cmd1.c deleted file mode 100644 index ff493174..00000000 --- a/src/cmd1.c +++ /dev/null @@ -1,5147 +0,0 @@ -/* File: cmd1.c */ - -/* Purpose: Movement commands (part 1) */ - -/* - * 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 "angband.h" - -#include "quark.h" -#include "hooks.h" - -#define MAX_VAMPIRIC_DRAIN 100 - - -/* - * Determine if the player "hits" a monster (normal combat). - * Note -- Always miss 5%, always hit 5%, otherwise random. - */ -bool_ test_hit_fire(int chance, int ac, int vis) -{ - int k; - - - /* Percentile dice */ - k = rand_int(100); - - /* Hack -- Instant miss or hit */ - if (k < 10) return (k < 5); - - /* Never hit */ - if (chance <= 0) return (FALSE); - - /* Invisible monsters are harder to hit */ - if (!vis) chance = (chance + 1) / 2; - - /* Power competes against armor */ - if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE); - - /* Assume hit */ - return (TRUE); -} - - - -/* - * Determine if the player "hits" a monster (normal combat). - * - * Note -- Always miss 5%, always hit 5%, otherwise random. - */ -bool_ test_hit_norm(int chance, int ac, int vis) -{ - int k; - - - /* Percentile dice */ - k = rand_int(100); - - /* Hack -- Instant miss or hit */ - if (k < 10) return (k < 5); - - /* Wimpy attack never hits */ - if (chance <= 0) return (FALSE); - - /* Penalize invisible targets */ - if (!vis) chance = (chance + 1) / 2; - - /* Power must defeat armor */ - if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE); - - /* Assume hit */ - return (TRUE); -} - - - -/* - * Critical hits (from objects thrown by player) - * Factor in item weight, total plusses, and player level. - */ -s16b critical_shot(int weight, int plus, int dam, int skill) -{ - int i, k; - - - /* Extract "shot" power */ - i = (weight + ((p_ptr->to_h + plus) * 4) + - get_skill_scale(skill, 100)); - i += 50 * p_ptr->xtra_crit; - i += luck( -100, 100); - - /* Critical hit */ - if (randint(5000) <= i) - { - k = weight + randint(500); - - if (k < 500) - { - msg_print("It was a good hit!"); - dam = 2 * dam + 5; - } - else if (k < 1000) - { - msg_print("It was a great hit!"); - dam = 2 * dam + 10; - } - else - { - msg_print("It was a superb hit!"); - dam = 3 * dam + 15; - } - } - - return (dam); -} - -/* - * Critical hits (by player) - * - * Factor in weapon weight, total plusses, player level. - */ -s16b critical_norm(int weight, int plus, int dam, int weapon_tval, bool_ *done_crit) -{ - int i, k, num = randint(5000); - - *done_crit = FALSE; - - /* Extract "blow" power */ - i = (weight + ((p_ptr->to_h + plus) * 5) + - get_skill_scale(p_ptr->melee_style, 150)); - i += 50 * p_ptr->xtra_crit; - if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS)) - { - i += get_skill_scale(SKILL_CRITS, 40 * 50); - } - i += luck( -100, 100); - - /* Force good strikes */ - if (p_ptr->tim_deadly) - { - set_tim_deadly(p_ptr->tim_deadly - 1); - msg_print("It was a *GREAT* hit!"); - dam = 3 * dam + 20; - *done_crit = TRUE; - } - - /* Chance */ - else if (num <= i) - { - k = weight + randint(650); - if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS)) - { - k += get_skill_scale(SKILL_CRITS, 400); - } - - if (k < 400) - { - msg_print("It was a good hit!"); - dam = 2 * dam + 5; - } - else if (k < 700) - { - msg_print("It was a great hit!"); - dam = 2 * dam + 10; - } - else if (k < 900) - { - msg_print("It was a superb hit!"); - dam = 3 * dam + 15; - } - else if (k < 1300) - { - msg_print("It was a *GREAT* hit!"); - dam = 3 * dam + 20; - } - else - { - msg_print("It was a *SUPERB* hit!"); - dam = ((7 * dam) / 2) + 25; - } - *done_crit = TRUE; - } - - return (dam); -} - - - -/* - * Extract the "total damage" from a given object hitting a given monster. - * - * Note that "flasks of oil" do NOT do fire damage, although they - * certainly could be made to do so. XXX XXX - * - * Note that most brands and slays are x3, except Slay Animal (x2), - * Slay Evil (x2), and Kill dragon (x5). - */ -s16b tot_dam_aux(object_type *o_ptr, int tdam, monster_type *m_ptr, - s32b *special) -{ - int mult = 1; - - monster_race *r_ptr = race_inf(m_ptr); - - u32b f1, f2, f3, f4, f5, esp; - - - /* Extract the flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Some "weapons" and "ammo" do extra damage */ - switch (o_ptr->tval) - { - case TV_SHOT: - case TV_ARROW: - case TV_BOLT: - case TV_BOOMERANG: - case TV_HAFTED: - case TV_POLEARM: - case TV_SWORD: - case TV_AXE: - case TV_DIGGING: - { - /* Slay Animal */ - if ((f1 & (TR1_SLAY_ANIMAL)) && (r_ptr->flags3 & (RF3_ANIMAL))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_ANIMAL); - } - - if (mult < 2) mult = 2; - } - - /* Slay Evil */ - if ((f1 & (TR1_SLAY_EVIL)) && (r_ptr->flags3 & (RF3_EVIL))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_EVIL); - } - - if (mult < 2) mult = 2; - } - - /* Slay Undead */ - if ((f1 & (TR1_SLAY_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_UNDEAD); - } - - if (mult < 3) mult = 3; - } - - /* Slay Demon */ - if ((f1 & (TR1_SLAY_DEMON)) && (r_ptr->flags3 & (RF3_DEMON))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_DEMON); - } - - if (mult < 3) mult = 3; - } - - /* Slay Orc */ - if ((f1 & (TR1_SLAY_ORC)) && (r_ptr->flags3 & (RF3_ORC))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_ORC); - } - - if (mult < 3) mult = 3; - } - - /* Slay Troll */ - if ((f1 & (TR1_SLAY_TROLL)) && (r_ptr->flags3 & (RF3_TROLL))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_TROLL); - } - - if (mult < 3) mult = 3; - } - - /* Slay Giant */ - if ((f1 & (TR1_SLAY_GIANT)) && (r_ptr->flags3 & (RF3_GIANT))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_GIANT); - } - - if (mult < 3) mult = 3; - } - - /* Slay Dragon */ - if ((f1 & (TR1_SLAY_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_DRAGON); - } - - if (mult < 3) mult = 3; - } - - /* Execute Dragon */ - if ((f1 & (TR1_KILL_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_DRAGON); - } - - if (mult < 5) mult = 5; - } - - /* Execute Undead */ - if ((f5 & (TR5_KILL_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_UNDEAD); - } - - if (mult < 5) mult = 5; - } - - /* Execute Demon */ - if ((f5 & (TR5_KILL_DEMON)) && (r_ptr->flags3 & (RF3_DEMON))) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_DEMON); - } - - if (mult < 5) mult = 5; - } - - - /* Brand (Acid) */ - if (f1 & (TR1_BRAND_ACID)) - { - /* Notice immunity */ - if (r_ptr->flags3 & (RF3_IM_ACID)) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_IM_ACID); - } - } - - /* Notice susceptibility */ - else if (r_ptr->flags9 & (RF9_SUSCEP_ACID)) - { - if (m_ptr->ml) - { - r_ptr->r_flags9 |= (RF9_SUSCEP_ACID); - } - if (mult < 6) mult = 6; - } - - /* Otherwise, take the damage */ - else - { - if (mult < 3) mult = 3; - } - } - - /* Brand (Elec) */ - if (f1 & (TR1_BRAND_ELEC)) - { - /* Notice immunity */ - if (r_ptr->flags3 & (RF3_IM_ELEC)) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_IM_ELEC); - } - } - - /* Notice susceptibility */ - else if (r_ptr->flags9 & (RF9_SUSCEP_ELEC)) - { - if (m_ptr->ml) - { - r_ptr->r_flags9 |= (RF9_SUSCEP_ELEC); - } - if (mult < 6) mult = 6; - } - - /* Otherwise, take the damage */ - else - { - if (mult < 3) mult = 3; - } - } - - /* Brand (Fire) */ - if (f1 & (TR1_BRAND_FIRE)) - { - /* Notice immunity */ - if (r_ptr->flags3 & (RF3_IM_FIRE)) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_IM_FIRE); - } - } - - /* Notice susceptibility */ - else if (r_ptr->flags3 & (RF3_SUSCEP_FIRE)) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_SUSCEP_FIRE); - } - if (mult < 6) mult = 6; - } - - /* Otherwise, take the damage */ - else - { - if (mult < 3) mult = 3; - } - } - - /* Brand (Cold) */ - if (f1 & (TR1_BRAND_COLD)) - { - /* Notice immunity */ - if (r_ptr->flags3 & (RF3_IM_COLD)) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_IM_COLD); - } - } - - /* Notice susceptibility */ - else if (r_ptr->flags3 & (RF3_SUSCEP_COLD)) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_SUSCEP_COLD); - } - if (mult < 6) mult = 6; - } - - /* Otherwise, take the damage */ - else - { - if (mult < 3) mult = 3; - } - } - - /* Brand (Poison) */ - if (f1 & (TR1_BRAND_POIS) || (p_ptr->tim_poison)) - { - /* Notice immunity */ - if (r_ptr->flags3 & (RF3_IM_POIS)) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_IM_POIS); - } - } - - /* Notice susceptibility */ - else if (r_ptr->flags9 & (RF9_SUSCEP_POIS)) - { - if (m_ptr->ml) - { - r_ptr->r_flags9 |= (RF9_SUSCEP_POIS); - } - if (mult < 6) mult = 6; - if (magik(95)) *special |= SPEC_POIS; - } - - /* Otherwise, take the damage */ - else - { - if (mult < 3) mult = 3; - if (magik(50)) *special |= SPEC_POIS; - } - } - - /* Wounding */ - if (f5 & (TR5_WOUNDING)) - { - /* Notice immunity */ - if (r_ptr->flags8 & (RF8_NO_CUT)) - { - if (m_ptr->ml) - { - r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT); - } - } - - /* Otherwise, take the damage */ - else - { - if (magik(50)) *special |= SPEC_CUT; - } - } - break; - } - } - - - /* Return the total damage */ - return (tdam * mult); -} - - -/* - * Search for hidden things - */ -void search(void) -{ - int y, x, chance; - - s16b this_o_idx, next_o_idx = 0; - - cave_type *c_ptr; - - - /* Start with base search ability */ - chance = p_ptr->skill_srh; - - /* Penalize various conditions */ - if (p_ptr->blind || no_lite()) chance = chance / 10; - if (p_ptr->confused || p_ptr->image) chance = chance / 10; - - /* Search the nearby grids, which are always in bounds */ - for (y = (p_ptr->py - 1); y <= (p_ptr->py + 1); y++) - { - for (x = (p_ptr->px - 1); x <= (p_ptr->px + 1); x++) - { - /* Sometimes, notice things */ - if (rand_int(100) < chance) - { - /* Access the grid */ - c_ptr = &cave[y][x]; - - /* Invisible trap */ - if ((c_ptr->t_idx != 0) && !(c_ptr->info & CAVE_TRDT)) - { - /* Pick a trap */ - pick_trap(y, x); - - /* Message */ - msg_print("You have found a trap."); - - /* Disturb */ - disturb(0); - } - - /* Secret door */ - if (c_ptr->feat == FEAT_SECRET) - { - /* Message */ - msg_print("You have found a secret door."); - - /* Pick a door XXX XXX XXX */ - cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00); - cave[y][x].mimic = 0; - lite_spot(y, x); - - /* Disturb */ - disturb(0); - } - - /* Scan all objects in the grid */ - for (this_o_idx = c_ptr->o_idx; this_o_idx; - this_o_idx = next_o_idx) - { - object_type * o_ptr; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Skip non-chests */ - if (o_ptr->tval != TV_CHEST) continue; - - /* Skip non-trapped chests */ - if (!o_ptr->pval) continue; - - /* Identify once */ - if (!object_known_p(o_ptr)) - { - /* Message */ - msg_print("You have discovered a trap on the chest!"); - - /* Know the trap */ - object_known(o_ptr); - - /* Notice it */ - disturb(0); - } - } - } - } - } -} - - - - -/* - * Player "wants" to pick up an object or gold. - * Note that we ONLY handle things that can be picked up. - * See "move_player()" for handling of other things. - */ -void carry(int pickup) -{ - if (!p_ptr->disembodied) - { - py_pickup_floor(pickup); - } -} - - -/* - * Handle player hitting a real trap - */ -static void hit_trap(void) -{ - bool_ ident = FALSE; - - cave_type *c_ptr; - - - /* Disturb the player */ - disturb(0); - - /* Get the cave grid */ - c_ptr = &cave[p_ptr->py][p_ptr->px]; - if (c_ptr->t_idx != 0) - { - ident = player_activate_trap_type(p_ptr->py, p_ptr->px, NULL, -1); - if (ident) - { - t_info[c_ptr->t_idx].ident = TRUE; - msg_format("You identified the trap as %s.", - t_name + t_info[c_ptr->t_idx].name); - } - } -} - - -void touch_zap_player(monster_type *m_ptr) -{ - int aura_damage = 0; - - monster_race *r_ptr = race_inf(m_ptr); - - - if (r_ptr->flags2 & (RF2_AURA_FIRE)) - { - if (!(p_ptr->immune_fire)) - { - char aura_dam[80]; - - aura_damage = - damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17)); - - /* Hack -- Get the "died from" name */ - monster_desc(aura_dam, m_ptr, 0x88); - - msg_print("You are suddenly very hot!"); - - if (p_ptr->oppose_fire) aura_damage = (aura_damage + 2) / 3; - if (p_ptr->resist_fire) aura_damage = (aura_damage + 2) / 3; - if (p_ptr->sensible_fire) aura_damage = (aura_damage + 2) * 2; - - take_hit(aura_damage, aura_dam); - r_ptr->r_flags2 |= RF2_AURA_FIRE; - handle_stuff(); - } - } - - - if (r_ptr->flags2 & (RF2_AURA_ELEC)) - { - if (!(p_ptr->immune_elec)) - { - char aura_dam[80]; - - aura_damage = - damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17)); - - /* Hack -- Get the "died from" name */ - monster_desc(aura_dam, m_ptr, 0x88); - - if (p_ptr->oppose_elec) aura_damage = (aura_damage + 2) / 3; - if (p_ptr->resist_elec) aura_damage = (aura_damage + 2) / 3; - - msg_print("You get zapped!"); - take_hit(aura_damage, aura_dam); - r_ptr->r_flags2 |= RF2_AURA_ELEC; - handle_stuff(); - } - } -} - - -/* - * Carried monster can attack too. - * Based on monst_attack_monst. - */ -static void carried_monster_attack(s16b m_idx, bool_ *fear, bool_ *mdeath, - int x, int y) -{ - monster_type *t_ptr = &m_list[m_idx]; - - monster_race *r_ptr; - - monster_race *tr_ptr = race_inf(t_ptr); - - int ap_cnt; - - int ac, rlev, pt; - - char t_name[80]; - - cptr sym_name = symbiote_name(TRUE); - - char temp[80]; - - bool_ blinked = FALSE, touched = FALSE; - - byte y_saver = t_ptr->fy; - - byte x_saver = t_ptr->fx; - - object_type *o_ptr; - - - /* Get the carried monster */ - o_ptr = &p_ptr->inventory[INVEN_CARRY]; - if (!o_ptr->k_idx) return; - - r_ptr = &r_info[o_ptr->pval]; - - /* Not allowed to attack */ - if (r_ptr->flags1 & RF1_NEVER_BLOW) return; - - /* Total armor */ - ac = t_ptr->ac; - - /* Extract the effective monster level */ - rlev = ((o_ptr->elevel >= 1) ? o_ptr->elevel : 1); - - /* Get the monster name (or "it") */ - monster_desc(t_name, t_ptr, 0); - - /* Assume no blink */ - blinked = FALSE; - - if (!t_ptr->ml) - { - msg_print("You hear noise."); - } - - /* Scan through all four blows */ - for (ap_cnt = 0; ap_cnt < 4; ap_cnt++) - { - bool_ visible = FALSE; - bool_ obvious = FALSE; - - int power = 0; - int damage = 0; - - cptr act = NULL; - - /* Extract the attack infomation */ - int effect = r_ptr->blow[ap_cnt].effect; - int method = r_ptr->blow[ap_cnt].method; - int d_dice = r_ptr->blow[ap_cnt].d_dice; - int d_side = r_ptr->blow[ap_cnt].d_side; - - /* Stop attacking if the target dies! */ - if (t_ptr->fx != x_saver || t_ptr->fy != y_saver) - break; - - /* Hack -- no more attacks */ - if (!method) break; - - if (blinked) /* Stop! */ - { - /* break; */ - } - - /* Extract visibility (before blink) */ - visible = TRUE; - - /* Extract the attack "power" */ - power = get_attack_power(effect); - - /* Monster hits */ - if (!effect || check_hit2(power, rlev, ac)) - { - /* Always disturbing */ - disturb(1); - - /* Describe the attack method */ - switch (method) - { - case RBM_HIT: - { - act = "hits %s."; - touched = TRUE; - break; - } - - case RBM_TOUCH: - { - act = "touches %s."; - touched = TRUE; - break; - } - - case RBM_PUNCH: - { - act = "punches %s."; - touched = TRUE; - break; - } - - case RBM_KICK: - { - act = "kicks %s."; - touched = TRUE; - break; - } - - case RBM_CLAW: - { - act = "claws %s."; - touched = TRUE; - break; - } - - case RBM_BITE: - { - act = "bites %s."; - touched = TRUE; - break; - } - - case RBM_STING: - { - act = "stings %s."; - touched = TRUE; - break; - } - - case RBM_XXX1: - { - act = "XXX1's %s."; - break; - } - - case RBM_BUTT: - { - act = "butts %s."; - touched = TRUE; - break; - } - - case RBM_CRUSH: - { - act = "crushes %s."; - touched = TRUE; - break; - } - - case RBM_ENGULF: - { - act = "engulfs %s."; - touched = TRUE; - break; - } - - case RBM_CHARGE: - { - act = "charges %s."; - touched = TRUE; - break; - } - - case RBM_CRAWL: - { - act = "crawls on %s."; - touched = TRUE; - break; - } - - case RBM_DROOL: - { - act = "drools on %s."; - touched = FALSE; - break; - } - - case RBM_SPIT: - { - act = "spits on %s."; - touched = FALSE; - break; - } - - case RBM_GAZE: - { - act = "gazes at %s."; - touched = FALSE; - break; - } - - case RBM_WAIL: - { - act = "wails at %s."; - touched = FALSE; - break; - } - - case RBM_SPORE: - { - act = "releases spores at %s."; - touched = FALSE; - break; - } - - case RBM_XXX4: - { - act = "projects XXX4's at %s."; - touched = FALSE; - break; - } - - case RBM_BEG: - { - act = "begs %s for money."; - touched = FALSE; - t_ptr->csleep = 0; - break; - } - - case RBM_INSULT: - { - act = "insults %s."; - touched = FALSE; - t_ptr->csleep = 0; - break; - } - - case RBM_MOAN: - { - act = "moans at %s."; - touched = FALSE; - t_ptr->csleep = 0; - break; - } - - case RBM_SHOW: - { - act = "sings to %s."; - touched = FALSE; - t_ptr->csleep = 0; - break; - } - } - - /* Message */ - if (act) - { - strfmt(temp, act, t_name); - if (t_ptr->ml) - msg_format("%s %s", sym_name, temp); - - } - - /* Hack -- assume all attacks are obvious */ - obvious = TRUE; - - /* Roll out the damage */ - damage = damroll(d_dice, d_side); - - pt = GF_MISSILE; - - /* Apply appropriate damage */ - switch (effect) - { - case 0: - { - damage = 0; - pt = 0; - break; - } - - case RBE_HURT: - case RBE_SANITY: - { - damage -= (damage * ((ac < 150) ? ac : 150) / 250); - break; - } - - case RBE_POISON: - case RBE_DISEASE: - { - pt = GF_POIS; - break; - } - - case RBE_UN_BONUS: - case RBE_UN_POWER: - case RBE_ABOMINATION: - { - pt = GF_DISENCHANT; - break; - } - - case RBE_EAT_FOOD: - case RBE_EAT_LITE: - { - pt = damage = 0; - break; - } - - case RBE_EAT_ITEM: - case RBE_EAT_GOLD: - { - pt = damage = 0; - if (randint(2) == 1) blinked = TRUE; - break; - } - - case RBE_ACID: - { - pt = GF_ACID; - break; - } - - case RBE_ELEC: - { - pt = GF_ELEC; - break; - } - - case RBE_FIRE: - { - pt = GF_FIRE; - break; - } - - case RBE_COLD: - { - pt = GF_COLD; - break; - } - - case RBE_BLIND: - { - break; - } - - case RBE_CONFUSE: - case RBE_HALLU: - { - pt = GF_CONFUSION; - break; - } - - case RBE_TERRIFY: - { - pt = GF_TURN_ALL; - break; - } - - case RBE_PARALYZE: - { - pt = GF_OLD_SLEEP; /* sort of close... */ - break; - } - - case RBE_LOSE_STR: - case RBE_LOSE_INT: - case RBE_LOSE_WIS: - case RBE_LOSE_DEX: - case RBE_LOSE_CON: - case RBE_LOSE_CHR: - case RBE_LOSE_ALL: - case RBE_PARASITE: - { - break; - } - - case RBE_SHATTER: - { - if (damage > 23) - { - /* Prevent destruction of quest levels and town */ - if (!is_quest(dun_level) && dun_level) - earthquake(p_ptr->py, p_ptr->px, 8); - } - break; - } - - case RBE_EXP_10: - case RBE_EXP_20: - case RBE_EXP_40: - case RBE_EXP_80: - { - pt = GF_NETHER; - break; - } - - case RBE_TIME: - { - pt = GF_TIME; - break; - } - - default: - { - pt = 0; - break; - } - } - - if (pt) - { - /* Do damage if not exploding */ - project(0, 0, t_ptr->fy, t_ptr->fx, - (pt == GF_OLD_SLEEP ? r_ptr->level : damage), pt, - PROJECT_KILL | PROJECT_STOP); - - if (touched) - { - /* Aura fire */ - if ((tr_ptr->flags2 & RF2_AURA_FIRE) && - !(r_ptr->flags3 & RF3_IM_FIRE)) - { - if (t_ptr->ml) - { - blinked = FALSE; - msg_format("You are suddenly very hot!"); - if (t_ptr->ml) - tr_ptr->r_flags2 |= RF2_AURA_FIRE; - } - project(m_idx, 0, p_ptr->py, p_ptr->px, - damroll(1 + ((t_ptr->level) / 26), - 1 + ((t_ptr->level) / 17)), - GF_FIRE, PROJECT_KILL | PROJECT_STOP); - } - - /* Aura elec */ - if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) && - !(r_ptr->flags3 & (RF3_IM_ELEC))) - { - if (t_ptr->ml) - { - blinked = FALSE; - msg_format("You get zapped!"); - if (t_ptr->ml) - tr_ptr->r_flags2 |= RF2_AURA_ELEC; - } - project(m_idx, 0, p_ptr->py, p_ptr->px, - damroll(1 + ((t_ptr->level) / 26), - 1 + ((t_ptr->level) / 17)), - GF_ELEC, PROJECT_KILL | PROJECT_STOP); - } - } - } - } - - /* Monster missed player */ - else - { - /* Analyze failed attacks */ - switch (method) - { - case RBM_HIT: - case RBM_TOUCH: - case RBM_PUNCH: - case RBM_KICK: - case RBM_CLAW: - case RBM_BITE: - case RBM_STING: - case RBM_XXX1: - case RBM_BUTT: - case RBM_CRUSH: - case RBM_ENGULF: - case RBM_CHARGE: - { - /* Disturb */ - disturb(1); - - /* Message */ - msg_format("%s misses %s.", sym_name, t_name); - break; - } - } - } - - - /* Analyze "visible" monsters only */ - if (visible) - { - /* Count "obvious" attacks (and ones that cause damage) */ - if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10)) - { - /* Count attacks of this type */ - if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) - { - r_ptr->r_blows[ap_cnt]++; - } - } - } - } - - /* Blink away */ - if (blinked) - { - msg_format("You and %s flee laughing!", symbiote_name(FALSE)); - - teleport_player(MAX_SIGHT * 2 + 5); - } -} - -/* - * Carried monster can attack too. - * Based on monst_attack_monst. - */ -static void incarnate_monster_attack(s16b m_idx, bool_ *fear, bool_ *mdeath, - int x, int y) -{ - monster_type *t_ptr = &m_list[m_idx]; - - monster_race *r_ptr; - - monster_race *tr_ptr = race_inf(t_ptr); - - int ap_cnt; - - int ac, rlev, pt; - - char t_name[80]; - - char temp[80]; - - bool_ blinked = FALSE, touched = FALSE; - - byte y_saver = t_ptr->fy; - - byte x_saver = t_ptr->fx; - - - if (!p_ptr->body_monster) return; - - r_ptr = race_info_idx(p_ptr->body_monster, 0); - - /* Not allowed to attack */ - if (r_ptr->flags1 & RF1_NEVER_BLOW) return; - - /* Total armor */ - ac = t_ptr->ac; - - /* Extract the effective monster level */ - rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); - - /* Get the monster name (or "it") */ - monster_desc(t_name, t_ptr, 0); - - /* Assume no blink */ - blinked = FALSE; - - if (!t_ptr->ml) - { - msg_print("You hear noise."); - } - - /* Scan through all four blows */ - for (ap_cnt = 0; ap_cnt < (p_ptr->num_blow > 4) ? 4 : p_ptr->num_blow; - ap_cnt++) - { - bool_ visible = FALSE; - bool_ obvious = FALSE; - - int power = 0; - int damage = 0; - - cptr act = NULL; - - /* Extract the attack infomation */ - int effect = r_ptr->blow[ap_cnt].effect; - int method = r_ptr->blow[ap_cnt].method; - int d_dice = r_ptr->blow[ap_cnt].d_dice; - int d_side = r_ptr->blow[ap_cnt].d_side; - - /* Stop attacking if the target dies! */ - if (t_ptr->fx != x_saver || t_ptr->fy != y_saver) - break; - - /* Hack -- no more attacks */ - if (!method) break; - - if (blinked) /* Stop! */ - { - /* break; */ - } - - /* Extract visibility (before blink) */ - visible = TRUE; - - /* Extract the attack "power" */ - power = get_attack_power(effect); - - /* Monster hits */ - if (!effect || check_hit2(power, rlev, ac)) - { - /* Always disturbing */ - disturb(1); - - /* Describe the attack method */ - switch (method) - { - case RBM_HIT: - { - act = "hit %s."; - touched = TRUE; - break; - } - - case RBM_TOUCH: - { - act = "touch %s."; - touched = TRUE; - break; - } - - case RBM_PUNCH: - { - act = "punch %s."; - touched = TRUE; - break; - } - - case RBM_KICK: - { - act = "kick %s."; - touched = TRUE; - break; - } - - case RBM_CLAW: - { - act = "claw %s."; - touched = TRUE; - break; - } - - case RBM_BITE: - { - act = "bite %s."; - touched = TRUE; - break; - } - - case RBM_STING: - { - act = "sting %s."; - touched = TRUE; - break; - } - - case RBM_XXX1: - { - act = "XXX1's %s."; - break; - } - - case RBM_BUTT: - { - act = "butt %s."; - touched = TRUE; - break; - } - - case RBM_CRUSH: - { - act = "crush %s."; - touched = TRUE; - break; - } - - case RBM_ENGULF: - { - act = "engulf %s."; - touched = TRUE; - break; - } - - case RBM_CHARGE: - { - act = "charge %s."; - touched = TRUE; - break; - } - - case RBM_CRAWL: - { - act = "crawl on %s."; - touched = TRUE; - break; - } - - case RBM_DROOL: - { - act = "drool on %s."; - touched = FALSE; - break; - } - - case RBM_SPIT: - { - act = "spit on %s."; - touched = FALSE; - break; - } - - case RBM_GAZE: - { - act = "gaze at %s."; - touched = FALSE; - break; - } - - case RBM_WAIL: - { - act = "wail at %s."; - touched = FALSE; - break; - } - - case RBM_SPORE: - { - act = "release spores at %s."; - touched = FALSE; - break; - } - - case RBM_XXX4: - { - act = "project XXX4's at %s."; - touched = FALSE; - break; - } - - case RBM_BEG: - { - act = "beg %s for money."; - touched = FALSE; - t_ptr->csleep = 0; - break; - } - - case RBM_INSULT: - { - act = "insult %s."; - touched = FALSE; - t_ptr->csleep = 0; - break; - } - - case RBM_MOAN: - { - act = "moan at %s."; - touched = FALSE; - t_ptr->csleep = 0; - break; - } - - case RBM_SHOW: - { - act = "sing to %s."; - touched = FALSE; - t_ptr->csleep = 0; - break; - } - } - - /* Message */ - if (act) - { - strfmt(temp, act, t_name); - if (t_ptr->ml) - msg_format("You %s", temp); - - } - - /* Hack -- assume all attacks are obvious */ - obvious = TRUE; - - /* Roll out the damage */ - damage = damroll(d_dice, d_side) + p_ptr->to_d; - - pt = GF_MISSILE; - - /* Apply appropriate damage */ - switch (effect) - { - case 0: - { - damage = 0; - pt = 0; - break; - } - - case RBE_HURT: - case RBE_SANITY: - { - damage -= (damage * ((ac < 150) ? ac : 150) / 250); - break; - } - - case RBE_POISON: - case RBE_DISEASE: - { - pt = GF_POIS; - break; - } - - case RBE_UN_BONUS: - case RBE_UN_POWER: - { - pt = GF_DISENCHANT; - break; - } - - case RBE_EAT_FOOD: - case RBE_EAT_LITE: - { - pt = damage = 0; - break; - } - - case RBE_EAT_ITEM: - case RBE_EAT_GOLD: - { - pt = damage = 0; - if (randint(2) == 1) blinked = TRUE; - break; - } - - case RBE_ACID: - { - pt = GF_ACID; - break; - } - - case RBE_ELEC: - { - pt = GF_ELEC; - break; - } - - case RBE_FIRE: - { - pt = GF_FIRE; - break; - } - - case RBE_COLD: - { - pt = GF_COLD; - break; - } - - case RBE_BLIND: - { - break; - } - - case RBE_HALLU: - case RBE_CONFUSE: - { - pt = GF_CONFUSION; - break; - } - - case RBE_TERRIFY: - { - pt = GF_TURN_ALL; - break; - } - - case RBE_PARALYZE: - { - pt = GF_OLD_SLEEP; /* sort of close... */ - break; - } - - case RBE_LOSE_STR: - case RBE_LOSE_INT: - case RBE_LOSE_WIS: - case RBE_LOSE_DEX: - case RBE_LOSE_CON: - case RBE_LOSE_CHR: - case RBE_LOSE_ALL: - case RBE_PARASITE: - { - break; - } - - case RBE_SHATTER: - { - if (damage > 23) - { - /* Prevent destruction of quest levels and town */ - if (!is_quest(dun_level) && dun_level) - earthquake(p_ptr->py, p_ptr->px, 8); - } - break; - } - - case RBE_EXP_10: - case RBE_EXP_20: - case RBE_EXP_40: - case RBE_EXP_80: - { - pt = GF_NETHER; - break; - } - - case RBE_TIME: - { - pt = GF_TIME; - break; - } - - default: - { - pt = 0; - break; - } - } - - if (pt) - { - /* Do damage if not exploding */ - project(0, 0, t_ptr->fy, t_ptr->fx, - (pt == GF_OLD_SLEEP ? p_ptr->lev * 2 : damage), pt, - PROJECT_KILL | PROJECT_STOP); - - if (touched) - { - /* Aura fire */ - if ((tr_ptr->flags2 & RF2_AURA_FIRE) && - !(r_ptr->flags3 & RF3_IM_FIRE)) - { - if (t_ptr->ml) - { - blinked = FALSE; - msg_format("You are suddenly very hot!"); - if (t_ptr->ml) - tr_ptr->r_flags2 |= RF2_AURA_FIRE; - } - project(m_idx, 0, p_ptr->py, p_ptr->px, - damroll(1 + ((t_ptr->level) / 26), - 1 + ((t_ptr->level) / 17)), - GF_FIRE, PROJECT_KILL | PROJECT_STOP); - } - - /* Aura elec */ - if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) && - !(r_ptr->flags3 & (RF3_IM_ELEC))) - { - if (t_ptr->ml) - { - blinked = FALSE; - msg_format("You get zapped!"); - if (t_ptr->ml) - tr_ptr->r_flags2 |= RF2_AURA_ELEC; - } - project(m_idx, 0, p_ptr->py, p_ptr->px, - damroll(1 + ((t_ptr->level) / 26), - 1 + ((t_ptr->level) / 17)), - GF_ELEC, PROJECT_KILL | PROJECT_STOP); - } - - } - } - } - - /* Monster missed player */ - else - { - /* Analyze failed attacks */ - switch (method) - { - case RBM_HIT: - case RBM_TOUCH: - case RBM_PUNCH: - case RBM_KICK: - case RBM_CLAW: - case RBM_BITE: - case RBM_STING: - case RBM_XXX1: - case RBM_BUTT: - case RBM_CRUSH: - case RBM_ENGULF: - case RBM_CHARGE: - { - /* Disturb */ - disturb(1); - - /* Message */ - msg_format("You miss %s.", t_name); - - break; - } - } - } - - - /* Analyze "visible" monsters only */ - if (visible) - { - /* Count "obvious" attacks (and ones that cause damage) */ - if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10)) - { - /* Count attacks of this type */ - if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) - { - r_ptr->r_blows[ap_cnt]++; - } - } - } - } - - /* Blink away */ - if (blinked) - { - msg_print("You flee laughing!"); - - teleport_player(MAX_SIGHT * 2 + 5); - } -} - - -/* - * Fetch an attack description from dam_*.txt files. - */ - -static void flavored_attack(int percent, char *output) -{ - int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane; - bool_ insane = (rand_int(100) < insanity); - - if (percent < 5) - { - if (!insane) - strcpy(output, "You scratch %s."); - else - get_rnd_line("dam_none.txt", output); - - } - else if (percent < 30) - { - if (!insane) - strcpy(output, "You hit %s."); - else - get_rnd_line("dam_med.txt", output); - } - else if (percent < 60) - { - if (!insane) - strcpy(output, "You wound %s."); - else - get_rnd_line("dam_lots.txt", output); - } - else if (percent < 95) - { - if (!insane) - strcpy(output, "You cripple %s."); - else - get_rnd_line("dam_huge.txt", output); - - } - else - { - if (!insane) - strcpy(output, "You demolish %s."); - else - get_rnd_line("dam_xxx.txt", output); - } -} - - -/* - * Apply the special effects of an attack - */ -void attack_special(monster_type *m_ptr, s32b special, int dam) -{ - char m_name[80]; - - monster_race *r_ptr = race_inf(m_ptr); - - - /* Extract monster name (or "it") */ - monster_desc(m_name, m_ptr, 0); - - /* Special - Cut monster */ - if (special & SPEC_CUT) - { - /* Cut the monster */ - if (r_ptr->flags8 & (RF8_NO_CUT)) - { - if (m_ptr->ml) - { - r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT); - } - } - else if (rand_int(100) >= r_ptr->level) - { - /* Already partially poisoned */ - if (m_ptr->bleeding) msg_format("%^s is bleeding more strongly.", - m_name); - /* Was not poisoned */ - else - msg_format("%^s is bleeding.", m_name); - - m_ptr->bleeding += dam * 2; - } - } - - /* Special - Poison monster */ - if (special & SPEC_POIS) - { - /* Poison the monster */ - if (r_ptr->flags3 & (RF3_IM_POIS)) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_IM_POIS); - } - } - /* Notice susceptibility */ - else if (r_ptr->flags9 & (RF9_SUSCEP_POIS)) - { - if (m_ptr->ml) - { - r_ptr->r_flags9 |= (RF9_SUSCEP_POIS); - } - /* Already partially poisoned */ - if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name); - /* Was not poisoned */ - else - msg_format("%^s is poisoned.", m_name); - - m_ptr->poisoned += dam * 2; - } - else if (rand_int(100) >= r_ptr->level) - { - /* Already partially poisoned */ - if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name); - /* Was not poisoned */ - else - msg_format("%^s is poisoned.", m_name); - - m_ptr->poisoned += dam; - } - } -} - - -/* - * Bare handed attacks - */ -static void py_attack_hand(int *k, monster_type *m_ptr, s32b *special) -{ - s16b special_effect = 0, stun_effect = 0, times = 0; - martial_arts *ma_ptr, *old_ptr, *blow_table = ma_blows; - int resist_stun = 0, max = MAX_MA; - monster_race *r_ptr = race_inf(m_ptr); - char m_name[80]; - bool_ desc = FALSE; - bool_ done_crit; - int plev = p_ptr->lev; - - if ((!p_ptr->body_monster) && (p_ptr->mimic_form == resolve_mimic_name("Bear")) && - (p_ptr->melee_style == SKILL_BEAR)) - { - blow_table = bear_blows; - max = MAX_BEAR; - plev = get_skill(SKILL_BEAR); - } - if (p_ptr->melee_style == SKILL_HAND) - { - blow_table = ma_blows; - max = MAX_MA; - plev = get_skill(SKILL_HAND); - } - ma_ptr = &blow_table[0]; - old_ptr = &blow_table[0]; - - /* Extract monster name (or "it") */ - monster_desc(m_name, m_ptr, 0); - - if (r_ptr->flags1 & RF1_UNIQUE) resist_stun += 88; - if (r_ptr->flags3 & RF3_NO_CONF) resist_stun += 44; - if (r_ptr->flags3 & RF3_NO_SLEEP) resist_stun += 44; - if ((r_ptr->flags3 & RF3_UNDEAD) || - (r_ptr->flags3 & RF3_NONLIVING)) resist_stun += 88; - - if (plev) - { - for (times = 0; times < (plev < 7 ? 1 : plev / 7); times++) - { - do - { - ma_ptr = &blow_table[(randint(max)) - 1]; - } - while ((ma_ptr->min_level > plev) || (randint(plev) < ma_ptr->chance)); - - /* keep the highest level attack available we found */ - if ((ma_ptr->min_level > old_ptr->min_level) && - !(p_ptr->stun || p_ptr->confused)) - { - old_ptr = ma_ptr; - - if (wizard && cheat_xtra) - { - msg_print("Attack re-selected."); - } - } - else - { - ma_ptr = old_ptr; - } - } - } - - *k = damroll(ma_ptr->dd, ma_ptr->ds); - - if (ma_ptr->effect & MA_KNEE) - { - if (r_ptr->flags1 & RF1_MALE) - { - if (!desc) msg_format("You hit %s in the groin with your knee!", - m_name); - sound(SOUND_PAIN); - special_effect = MA_KNEE; - } - else if (!desc) msg_format(ma_ptr->desc, m_name); - - desc = TRUE; - } - if (ma_ptr->effect & MA_FULL_SLOW) - { - special_effect = MA_SLOW; - if (!desc) msg_format(ma_ptr->desc, m_name); - - desc = TRUE; - } - if (ma_ptr->effect & MA_SLOW) - { - if (! - ((r_ptr->flags1 & RF1_NEVER_MOVE) || - strchr("UjmeEv$,DdsbBFIJQSXclnw!=?", r_ptr->d_char))) - { - if (!desc) msg_format("You kick %s in the ankle.", m_name); - special_effect = MA_SLOW; - } - else if (!desc) msg_format(ma_ptr->desc, m_name); - - desc = TRUE; - } - if (ma_ptr->effect & MA_STUN) - { - if (ma_ptr->power) - { - stun_effect = (ma_ptr->power / 2) + randint(ma_ptr->power / 2); - } - - if (!desc) msg_format(ma_ptr->desc, m_name); - desc = TRUE; - } - if (ma_ptr->effect & MA_WOUND) - { - if (magik(ma_ptr->power)) - { - *special |= SPEC_CUT; - } - if (!desc) msg_format(ma_ptr->desc, m_name); - desc = TRUE; - } - - *k = critical_norm(plev * (randint(10)), ma_ptr->min_level, *k, -1, &done_crit); - - if ((special_effect & MA_KNEE) && ((*k + p_ptr->to_d) < m_ptr->hp)) - { - msg_format("%^s moans in agony!", m_name); - stun_effect = 7 + randint(13); - resist_stun /= 3; - } - if (((special_effect & MA_FULL_SLOW) || (special_effect & MA_SLOW)) && - ((*k + p_ptr->to_d) < m_ptr->hp)) - { - if (!(r_ptr->flags1 & RF1_UNIQUE) && - (randint(plev) > m_ptr->level) && m_ptr->mspeed > 60) - { - msg_format("%^s starts limping slower.", m_name); - m_ptr->mspeed -= 10; - } - } - - if (stun_effect && ((*k + p_ptr->to_d) < m_ptr->hp)) - { - if (plev > randint(m_ptr->level + resist_stun + 10)) - { - if (m_ptr->stunned) - msg_format("%^s is still stunned.", m_name); - else - msg_format("%^s is stunned.", m_name); - - m_ptr->stunned += (stun_effect); - } - } -} - - -/* - * Apply nazgul effects - */ -void do_nazgul(int *k, int *num, int num_blow, int weap, monster_race *r_ptr, - object_type *o_ptr) -{ - u32b f1, f2, f3, f4, f5, esp; - - bool_ mundane; - bool_ allow_shatter = TRUE; - - /* Extract mundane-ness of the current weapon */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* It should be Slay Evil, Slay Undead, or *Slay Undead* */ - mundane = !(f1 & TR1_SLAY_EVIL) && !(f1 & TR1_SLAY_UNDEAD) && - !(f5 & TR5_KILL_UNDEAD); - - /* Some blades can resist shattering */ - if (f5 & TR5_RES_MORGUL) - allow_shatter = FALSE; - - /* Mega Hack -- Hitting Nazgul is REALY dangerous (ideas from Akhronath) */ - if (r_ptr->flags7 & RF7_NAZGUL) - { - if ((!o_ptr->name2) && (!artifact_p(o_ptr)) && allow_shatter) - { - msg_print("Your weapon *DISINTEGRATES*!"); - *k = 0; - - inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE); - - /* To stop attacking */ - *num = num_blow; - } - else if (o_ptr->name2) - { - if (mundane) - { - msg_print - ("The Ringwraith is IMPERVIOUS to the mundane weapon."); - *k = 0; - } - - /* 25% chance of getting destroyed */ - if (magik(25) && allow_shatter) - { - msg_print("Your weapon is destroyed!"); - - inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE); - - /* To stop attacking */ - *num = num_blow; - } - } - else if (artifact_p(o_ptr)) - { - if (mundane) - { - msg_print - ("The Ringwraith is IMPERVIOUS to the mundane weapon."); - *k = 0; - } - - apply_disenchant(INVEN_WIELD + weap); - - /* 1/1000 chance of getting destroyed */ - if (!rand_int(1000) && allow_shatter) - { - msg_print("Your weapon is destroyed!"); - - inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE); - - /* To stop attacking */ - *num = num_blow; - } - } - - /* If any damage is done, then 25% chance of getting the Black Breath */ - if (*k) - { - if (magik(25)) - { - msg_print("Your foe calls upon your soul!"); - msg_print - ("You feel the Black Breath slowly draining you of life..."); - p_ptr->black_breath = TRUE; - } - } - } -} - - -/* - * Player attacks a (poor, defenseless) creature -RAK- - * - * If no "weapon" is available, then "punch" the monster one time. - */ -void py_attack(int y, int x, int max_blow) -{ - int num = 0, k, bonus, chance; - - s32b special = 0; - - cave_type *c_ptr = &cave[y][x]; - - monster_type *m_ptr = &m_list[c_ptr->m_idx]; - - monster_race *r_ptr = race_inf(m_ptr); - - object_type *o_ptr; - - char m_name[80]; - - bool_ fear = FALSE; - - bool_ mdeath = FALSE; - - bool_ backstab = FALSE; - - bool_ vorpal_cut = FALSE; - - int chaos_effect = 0; - - bool_ stab_fleeing = FALSE; - - bool_ do_quake = FALSE; - - bool_ done_crit = FALSE; - - bool_ drain_msg = TRUE; - - int drain_result = 0, drain_heal = 0; - - int drain_left = MAX_VAMPIRIC_DRAIN; - - /* A massive hack -- life-draining weapons */ - u32b f1, f2, f3, f4, f5, esp; - - int weap; - - /* Disturb the player */ - disturb(0); - - if (r_info[p_ptr->body_monster].flags1 & RF1_NEVER_BLOW) - { - msg_print("You cannot attack in this form!"); - return; - } - - if (get_skill(SKILL_BACKSTAB)) - { - if ((m_ptr->csleep) && (m_ptr->ml)) - { - /* Can't backstab creatures that we can't see, right? */ - backstab = TRUE; - } - else if ((m_ptr->monfear) && (m_ptr->ml)) - { - stab_fleeing = TRUE; - } - } - - /* Disturb the monster */ - m_ptr->csleep = 0; - - - /* Extract monster name (or "it") */ - monster_desc(m_name, m_ptr, 0); - - /* Dont even bother */ - if (r_ptr->flags7 & RF7_IM_MELEE) - { - msg_format("%^s is immune to melee attacks."); - return; - } - - /* Auto-Recall if possible and visible */ - if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); - - /* Track a new monster */ - if (m_ptr->ml) health_track(c_ptr->m_idx); - - /* Stop if friendly */ - if ((is_friend(m_ptr) >= 0) && - !(p_ptr->stun || p_ptr->confused || p_ptr->image || - !(m_ptr->ml))) - { - if (!(p_ptr->inventory[INVEN_WIELD].art_name)) - { - msg_format("You stop to avoid hitting %s.", m_name); - return; - } - - if (! - (streq - (quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'"))) - { - msg_format("You stop to avoid hitting %s.", m_name); - return; - } - - msg_format("Your black blade greedily attacks %s!", m_name); - } - - /* Break goi/manashield */ - if (p_ptr->invuln) - { - set_invuln(0); - } - if (p_ptr->disrupt_shield) - { - set_disrupt_shield(0); - } - - /* Handle player fear */ - if (p_ptr->afraid) - { - /* Message */ - if (m_ptr->ml) - msg_format("You are too afraid to attack %s!", m_name); - else - msg_format("There is something scary in your way!"); - - /* Done */ - return; - } - - /* Monsters can use barehanded combat, but not weapon combat */ - if ((p_ptr->body_monster) && - (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON]) && - !(p_ptr->melee_style == SKILL_HAND)) - { - incarnate_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x); - } - /* Otherwise use your weapon(s) */ - else - { - int weapons; - if (p_ptr->melee_style == SKILL_MASTERY) - weapons = r_info[p_ptr->body_monster].body_parts[BODY_WEAPON]; - else /* SKILL_HAND */ - weapons = 1; - - /* Attack with ALL the weapons !!!!! -- ooh that's gonna hurt YOU */ - for (weap = 0; weap < weapons; ++weap) - { - /* Monster is already dead ? oh :( */ - if (mdeath) break; - - /* Reset the blows counter */ - num = 0; - - /* Access the weapon */ - o_ptr = &p_ptr->inventory[INVEN_WIELD + weap]; - - /* Calculate the "attack quality" */ - bonus = p_ptr->to_h + p_ptr->to_h_melee + o_ptr->to_h; - chance = p_ptr->skill_thn + (bonus * BTH_PLUS_ADJ); - - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if (!(f4 & TR4_NEVER_BLOW)) - { - int num_blow = p_ptr->num_blow; - - /* Restrict to max_blow(if max_blow >= 0) */ - if ((max_blow >= 0) && - (num_blow > max_blow)) num_blow = max_blow; - - /* Attack once for each legal blow */ - while (num++ < num_blow) - { - /* Test for hit */ - if (test_hit_norm(chance, m_ptr->ac, m_ptr->ml)) - { - /* Sound */ - sound(SOUND_HIT); - - /* Hack -- bare hands do one damage */ - k = 1; - - /* Select a chaotic effect (50% chance) */ - if ((f1 & TR1_CHAOTIC) && (rand_int(2) == 0)) - { - if (randint(5) < 3) - { - /* Vampiric (20%) */ - chaos_effect = 1; - } - else if (rand_int(250) == 0) - { - /* Quake (0.12%) */ - chaos_effect = 2; - } - else if (rand_int(10)) - { - /* Confusion (26.892%) */ - chaos_effect = 3; - } - else if (rand_int(2) == 0) - { - /* Teleport away (1.494%) */ - chaos_effect = 4; - } - else - { - /* Polymorph (1.494%) */ - chaos_effect = 5; - } - } - - /* Vampiric drain */ - if ((f1 & TR1_VAMPIRIC) || (chaos_effect == 1)) - { - if (! - ((r_ptr->flags3 & RF3_UNDEAD) || - (r_ptr->flags3 & RF3_NONLIVING))) - drain_result = m_ptr->hp; - else - drain_result = 0; - } - - if (f1 & TR1_VORPAL && (randint(6) == 1)) - vorpal_cut = TRUE; - else - vorpal_cut = FALSE; - - /* Should we attack with hands or not ? */ - if (p_ptr->melee_style != SKILL_MASTERY) - { - py_attack_hand(&k, m_ptr, &special); - } - /* Handle normal weapon */ - else if (o_ptr->k_idx) - { - k = damroll(o_ptr->dd, o_ptr->ds); - k = tot_dam_aux(o_ptr, k, m_ptr, &special); - - if (backstab) - { - k += (k * - get_skill_scale(SKILL_BACKSTAB, - 100)) / 100; - } - else if (stab_fleeing) - { - k += (k * get_skill_scale(SKILL_BACKSTAB, 70)) / - 100; - } - - if ((p_ptr->impact && ((k > 50) || randint(7) == 1)) - || (chaos_effect == 2)) - { - do_quake = TRUE; - } - - k = critical_norm(o_ptr->weight, o_ptr->to_h, k, o_ptr->tval, &done_crit); - - /* Stunning blow */ - if (magik(get_skill(SKILL_STUN)) && (o_ptr->tval == TV_HAFTED) && (o_ptr->weight > 50) && done_crit) - { - if (!(r_ptr->flags4 & (RF4_BR_SOUN)) && !(r_ptr->flags4 & (RF4_BR_WALL)) && k) - { - int tmp; - - /* Get stunned */ - if (m_ptr->stunned) - { - msg_format("%^s is more dazed.", m_name); - tmp = m_ptr->stunned + get_skill_scale(SKILL_STUN, 30) + 10; - } - else - { - msg_format("%^s is dazed.", m_name); - tmp = get_skill_scale(SKILL_STUN, 60) + 20; - } - - /* Apply stun */ - m_ptr->stunned = (tmp < 200) ? tmp : 200; - } - } - - if (vorpal_cut) - { - int step_k = k; - - msg_format("Your weapon cuts deep into %s!", - m_name); - do - { - k += step_k; - } - while (randint(4) == 1); - } - - PRAY_GOD(GOD_TULKAS) - { - if (magik(wisdom_scale(130) - m_ptr->level) && (p_ptr->grace > 1000)) - { - msg_print("You feel the hand of Tulkas helping your blow."); - k += (o_ptr->to_d + p_ptr->to_d_melee) * 2; - } - else k += o_ptr->to_d + p_ptr->to_d_melee; - } - else k += o_ptr->to_d; - - /* Project some more nasty stuff? */ - if (p_ptr->tim_project) - { - project(0, p_ptr->tim_project_rad, y, x, p_ptr->tim_project_dam, p_ptr->tim_project_gf, p_ptr->tim_project_flag | PROJECT_JUMP); - if (!c_ptr->m_idx) - { - mdeath = TRUE; - break; - } - } - - do_nazgul(&k, &num, num_blow, weap, r_ptr, o_ptr); - - } - - /* Melkor can cast curse for you*/ - PRAY_GOD(GOD_MELKOR) - { - int lv = get_level_s(MELKOR_CURSE, 100); - - if (lv >= 10) - { - int chance = (wisdom_scale(30) * lv) / ((m_ptr->level < 1) ? 1 : m_ptr->level); - - if (chance < 1) chance = 1; - if ((p_ptr->grace > 5000) && magik(chance)) - { - do_melkor_curse(c_ptr->m_idx); - } - } - } - - /* May it clone the monster ? */ - if ((f4 & TR4_CLONE) && magik(30)) - { - msg_format("Oh no! Your weapon clones %^s!", - m_name); - multiply_monster(c_ptr->m_idx, FALSE, TRUE); - } - - /* Apply the player damage bonuses */ - k += p_ptr->to_d + p_ptr->to_d_melee; - - /* No negative damage */ - if (k < 0) k = 0; - - /* Message */ - if (!(backstab || stab_fleeing)) - { - /* These monsters never have flavoured combat msgs */ - if (strchr("vwjmelX,.*", r_ptr->d_char)) - { - msg_format("You hit %s.", m_name); - } - - /* Print flavoured messages if requested */ - else - { - char buff[255]; - - flavored_attack((100 * k) / m_ptr->maxhp, buff); - msg_format(buff, m_name); - } - } - else if (backstab) - { - char buf[80]; - - monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego); - - backstab = FALSE; - - msg_format - ("You cruelly stab the helpless, sleeping %s!", - buf); - } - else - { - char buf[80]; - - monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego); - - msg_format("You backstab the fleeing %s!", buf); - } - - /* Complex message */ - if (wizard) - { - msg_format("You do %d (out of %d) damage.", k, - m_ptr->hp); - } - - if (special) attack_special(m_ptr, special, k); - - /* Damage, check for fear and death */ - if (mon_take_hit(c_ptr->m_idx, k, &fear, NULL)) - { - /* Hack -- High-level warriors can spread their attacks out - * among weaker foes. - */ - if ((has_ability(AB_SPREAD_BLOWS)) && (num < num_blow) && - (energy_use)) - { - energy_use = energy_use * num / num_blow; - } - mdeath = TRUE; - break; - } - - switch (is_friend(m_ptr)) - { - case 1: - msg_format("%^s gets angry!", m_name); - change_side(m_ptr); - break; - case 0: - msg_format("%^s gets angry!", m_name); - m_ptr->status = MSTATUS_NEUTRAL_M; - break; - } - - touch_zap_player(m_ptr); - - /* Are we draining it? A little note: If the monster is - dead, the drain does not work... */ - - if (drain_result) - { - drain_result -= m_ptr->hp; /* Calculate the difference */ - - if (drain_result > 0) /* Did we really hurt it? */ - { - drain_heal = damroll(4, (drain_result / 6)); - - if (cheat_xtra) - { - msg_format("Draining left: %d", drain_left); - } - - if (drain_left) - { - if (drain_heal < drain_left) - { - drain_left -= drain_heal; - } - else - { - drain_heal = drain_left; - drain_left = 0; - } - - if (drain_msg) - { - msg_format - ("Your weapon drains life from %s!", - m_name); - drain_msg = FALSE; - } - - hp_player(drain_heal); - /* We get to keep some of it! */ - } - } - } - - /* Confusion attack */ - if ((p_ptr->confusing) || (chaos_effect == 3)) - { - /* Cancel glowing hands */ - if (p_ptr->confusing) - { - p_ptr->confusing = FALSE; - msg_print("Your hands stop glowing."); - } - - /* Confuse the monster */ - if (r_ptr->flags3 & (RF3_NO_CONF)) - { - if (m_ptr->ml) - { - r_ptr->r_flags3 |= (RF3_NO_CONF); - } - - msg_format("%^s is unaffected.", m_name); - } - else if (rand_int(100) < m_ptr->level) - { - msg_format("%^s is unaffected.", m_name); - } - else - { - msg_format("%^s appears confused.", m_name); - m_ptr->confused += - 10 + rand_int(get_skill(SKILL_COMBAT)) / 5; - } - } - - else if (chaos_effect == 4) - { - msg_format("%^s disappears!", m_name); - teleport_away(c_ptr->m_idx, 50); - num = num_blow + 1; /* Can't hit it anymore! */ - } - - else if ((chaos_effect == 5) && cave_floor_bold(y, x) && - (randint(90) > m_ptr->level)) - { - if (!((r_ptr->flags1 & RF1_UNIQUE) || - (r_ptr->flags4 & RF4_BR_CHAO) || - (m_ptr->mflag & MFLAG_QUEST))) - { - /* Handle polymorph */ - if (do_poly_monster(y, x)) - { - /* Polymorph succeeded */ - msg_format("%^s changes!", m_name); - - /* Hack -- Get new monster */ - m_ptr = &m_list[c_ptr->m_idx]; - - /* Oops, we need a different name... */ - monster_desc(m_name, m_ptr, 0); - - /* Hack -- Get new race */ - r_ptr = race_inf(m_ptr); - - fear = FALSE; - } - else - { - msg_format("%^s resists.", m_name); - } - } - else - { - msg_format("%^s is unaffected.", m_name); - } - } - } - - /* Player misses */ - else - { - /* Sound */ - sound(SOUND_MISS); - - backstab = FALSE; /* Clumsy! */ - - /* Message */ - msg_format("You miss %s.", m_name); - } - } - } - else - { - msg_print("You can't attack with that weapon."); - } - } - } - - /* Carried monster can attack too */ - if ((!mdeath) && m_list[c_ptr->m_idx].hp) - carried_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x); - - /* Hack -- delay fear messages */ - if (fear && m_ptr->ml) - { - /* Sound */ - sound(SOUND_FLEE); - - /* Message */ - msg_format("%^s flees in terror!", m_name); - } - - /* Mega-Hack -- apply earthquake brand */ - if (do_quake) - { - /* Prevent destruction of quest levels and town */ - if (!is_quest(dun_level) && dun_level) - earthquake(p_ptr->py, p_ptr->px, 10); - } -} - - - -static bool_ pattern_tile(int y, int x) -{ - return ((cave[y][x].feat <= FEAT_PATTERN_XTRA2) && - (cave[y][x].feat >= FEAT_PATTERN_START)); -} - - -static bool_ pattern_seq(int c_y, int c_x, int n_y, int n_x) -{ - if (!(pattern_tile(c_y, c_x)) && !(pattern_tile(n_y, n_x))) - return TRUE; - - if (cave[n_y][n_x].feat == FEAT_PATTERN_START) - { - if ((!(pattern_tile(c_y, c_x))) && - !(p_ptr->confused || p_ptr->stun || p_ptr->image)) - { - if (get_check - ("If you start walking the Straight Road, you must walk the whole way. Ok? ")) - return TRUE; - else - return FALSE; - } - else - return TRUE; - } - else if ((cave[n_y][n_x].feat == FEAT_PATTERN_OLD) || - (cave[n_y][n_x].feat == FEAT_PATTERN_END) || - (cave[n_y][n_x].feat == FEAT_PATTERN_XTRA2)) - { - if (pattern_tile(c_y, c_x)) - { - return TRUE; - } - else - { - msg_print - ("You must start walking the Straight Road from the startpoint."); - return FALSE; - } - } - else if ((cave[n_y][n_x].feat == FEAT_PATTERN_XTRA1) || - (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA1)) - { - return TRUE; - } - else if (cave[c_y][c_x].feat == FEAT_PATTERN_START) - { - if (pattern_tile(n_y, n_x)) - return TRUE; - else - { - msg_print("You must walk the Straight Road in correct order."); - return FALSE; - } - } - else if ((cave[c_y][c_x].feat == FEAT_PATTERN_OLD) || - (cave[c_y][c_x].feat == FEAT_PATTERN_END) || - (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA2)) - { - if (!pattern_tile(n_y, n_x)) - { - msg_print("You may not step off from the Straight Road."); - return FALSE; - } - else - { - return TRUE; - } - } - else - { - if (!pattern_tile(c_y, c_x)) - { - msg_print - ("You must start walking the Straight Road from the startpoint."); - return FALSE; - } - else - { - byte ok_move = FEAT_PATTERN_START; - switch (cave[c_y][c_x].feat) - { - case FEAT_PATTERN_1: - ok_move = FEAT_PATTERN_2; - break; - case FEAT_PATTERN_2: - ok_move = FEAT_PATTERN_3; - break; - case FEAT_PATTERN_3: - ok_move = FEAT_PATTERN_4; - break; - case FEAT_PATTERN_4: - ok_move = FEAT_PATTERN_1; - break; - default: - if (wizard) - msg_format("Funny Straight Road walking, %d.", - cave[c_y][c_x]); - return TRUE; /* Goof-up */ - } - - if ((cave[n_y][n_x].feat == ok_move) || - (cave[n_y][n_x].feat == cave[c_y][c_x].feat)) - return TRUE; - else - { - if (!pattern_tile(n_y, n_x)) - msg_print("You may not step off from the Straight Road."); - else - msg_print - ("You must walk the Straight Road in correct order."); - - return FALSE; - } - } - } -} - - - -bool_ player_can_enter(byte feature) -{ - bool_ pass_wall; - - bool_ only_wall = FALSE; - - - /* Player can not walk through "walls" unless in Shadow Form */ - if (p_ptr->wraith_form || (PRACE_FLAG(PR1_SEMI_WRAITH))) - pass_wall = TRUE; - else - pass_wall = FALSE; - - /* Wall mimicry force the player to stay in walls */ - if (p_ptr->mimic_extra & CLASS_WALL) - { - only_wall = TRUE; - } - - /* Don't let the player kill himself with one keystroke */ - if (p_ptr->wild_mode) - { - if (feature == FEAT_DEEP_WATER) - { - int wt = weight_limit() / 2; - - if ((calc_total_weight() >= wt) && !(p_ptr->ffall)) - return (FALSE); - } - else if (feature == FEAT_SHAL_LAVA || - feature == FEAT_DEEP_LAVA) - { - if (!(p_ptr->resist_fire || - p_ptr->immune_fire || - p_ptr->oppose_fire || - p_ptr->ffall)) - return (FALSE); - } - } - - if (feature == FEAT_TREES) - { - if (p_ptr->fly || - pass_wall || - (has_ability(AB_TREE_WALK)) || - (p_ptr->mimic_form == resolve_mimic_name("Ent")) || - ((p_ptr->grace >= 9000) && (p_ptr->praying) && (p_ptr->pgod == GOD_YAVANNA))) - return (TRUE); - } - - if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB)) - return (TRUE); - if ((p_ptr->fly) && - ((f_info[feature].flags1 & FF1_CAN_FLY) || - (f_info[feature].flags1 & FF1_CAN_LEVITATE))) - return (TRUE); - else if (only_wall && (f_info[feature].flags1 & FF1_FLOOR)) - return (FALSE); - else if ((p_ptr->ffall) && - (f_info[feature].flags1 & FF1_CAN_LEVITATE)) - return (TRUE); - else if ((pass_wall || only_wall) && - (f_info[feature].flags1 & FF1_CAN_PASS)) - return (TRUE); - else if (f_info[feature].flags1 & FF1_NO_WALK) - return (FALSE); - else if ((f_info[feature].flags1 & FF1_WEB) && - ((!(r_info[p_ptr->body_monster].flags7 & RF7_SPIDER)) && (p_ptr->mimic_form != resolve_mimic_name("Spider")))) - return (FALSE); - - return (TRUE); -} - -/* - * Move player in the given direction, with the given "pickup" flag. - * - * This routine should (probably) always induce energy expenditure. - * - * Note that moving will *always* take a turn, and will *always* hit - * any monster which might be in the destination grid. Previously, - * moving into walls was "free" and did NOT hit invisible monsters. - */ -void move_player_aux(int dir, int do_pickup, int run, bool_ disarm) -{ - int y, x, tmp; - - cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; - - monster_type *m_ptr; - - monster_race *r_ptr = &r_info[p_ptr->body_monster], *mr_ptr; - - char m_name[80]; - - bool_ stormbringer = FALSE; - - bool_ old_dtrap, new_dtrap; - - bool_ oktomove = TRUE; - - - /* Hack - random movement */ - if (p_ptr->disembodied) - tmp = dir; - else if ((r_ptr->flags1 & RF1_RAND_25) && (r_ptr->flags1 & RF1_RAND_50)) - { - if (randint(100) < 75) - tmp = randint(9); - else - tmp = dir; - } - else if (r_ptr->flags1 & RF1_RAND_50) - { - if (randint(100) < 50) - tmp = randint(9); - else - tmp = dir; - } - else if (r_ptr->flags1 & RF1_RAND_25) - { - if (randint(100) < 25) - tmp = randint(9); - else - tmp = dir; - } - else - { - tmp = dir; - } - - if ((c_ptr->feat == FEAT_ICE) && (!p_ptr->ffall && !p_ptr->fly)) - { - if (magik(70 - p_ptr->lev)) - { - tmp = randint(9); - msg_print("You slip on the icy floor."); - } - else - tmp = dir; - } - - /* Find the result of moving */ - y = p_ptr->py + ddy[tmp]; - x = p_ptr->px + ddx[tmp]; - - /* Examine the destination */ - c_ptr = &cave[y][x]; - - /* Change oldpx and oldpy to place the player well when going back to big mode */ - if (p_ptr->wild_mode) - { - if (ddy[tmp] > 0) p_ptr->oldpy = 1; - if (ddy[tmp] < 0) p_ptr->oldpy = MAX_HGT - 2; - if (ddy[tmp] == 0) p_ptr->oldpy = MAX_HGT / 2; - if (ddx[tmp] > 0) p_ptr->oldpx = 1; - if (ddx[tmp] < 0) p_ptr->oldpx = MAX_WID - 2; - if (ddx[tmp] == 0) p_ptr->oldpx = MAX_WID / 2; - } - - /* Exit the area */ - if (!dun_level && !p_ptr->wild_mode && !is_quest(dun_level) && - ((x == 0) || (x == cur_wid - 1) || (y == 0) || (y == cur_hgt - 1))) - { - /* Can the player enter the grid? */ - if (player_can_enter(c_ptr->mimic)) - { - /* Hack: move to new area */ - if ((y == 0) && (x == 0)) - { - p_ptr->wilderness_y--; - p_ptr->wilderness_x--; - p_ptr->oldpy = cur_hgt - 2; - p_ptr->oldpx = cur_wid - 2; - ambush_flag = FALSE; - } - - else if ((y == 0) && (x == MAX_WID - 1)) - { - p_ptr->wilderness_y--; - p_ptr->wilderness_x++; - p_ptr->oldpy = cur_hgt - 2; - p_ptr->oldpx = 1; - ambush_flag = FALSE; - } - - else if ((y == MAX_HGT - 1) && (x == 0)) - { - p_ptr->wilderness_y++; - p_ptr->wilderness_x--; - p_ptr->oldpy = 1; - p_ptr->oldpx = cur_wid - 2; - ambush_flag = FALSE; - } - - else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1)) - { - p_ptr->wilderness_y++; - p_ptr->wilderness_x++; - p_ptr->oldpy = 1; - p_ptr->oldpx = 1; - ambush_flag = FALSE; - } - - else if (y == 0) - { - p_ptr->wilderness_y--; - p_ptr->oldpy = cur_hgt - 2; - p_ptr->oldpx = x; - ambush_flag = FALSE; - } - - else if (y == cur_hgt - 1) - { - p_ptr->wilderness_y++; - p_ptr->oldpy = 1; - p_ptr->oldpx = x; - ambush_flag = FALSE; - } - - else if (x == 0) - { - p_ptr->wilderness_x--; - p_ptr->oldpx = cur_wid - 2; - p_ptr->oldpy = y; - ambush_flag = FALSE; - } - - else if (x == cur_wid - 1) - { - p_ptr->wilderness_x++; - p_ptr->oldpx = 1; - p_ptr->oldpy = y; - ambush_flag = FALSE; - } - - p_ptr->leaving = TRUE; - - return; - } - } - - /* Some hooks */ - if (process_hooks(HOOK_MOVE, "(d,d)", y, x)) return; - - { - hook_move_in in = { y, x }; - if (process_hooks_new(HOOK_MOVE, &in, NULL)) { - return; /* Prevent movement */ - } - } - - if (p_ptr->dripping_tread > 0) - { - geomancy_random_floor(y, x, FALSE); - p_ptr->dripping_tread -= 1; - if (p_ptr->dripping_tread == 0) - { - msg_print("You stop dripping raw elemental energies."); - } - } - - - /* Get the monster */ - m_ptr = &m_list[c_ptr->m_idx]; - mr_ptr = race_inf(m_ptr); - - if (p_ptr->inventory[INVEN_WIELD].art_name) - { - if (streq(quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'")) - stormbringer = TRUE; - } - - /* Hack -- attack monsters */ - if (c_ptr->m_idx && (m_ptr->ml || player_can_enter(c_ptr->feat))) - { - - /* Attack -- only if we can see it OR it is not in a wall */ - if ((is_friend(m_ptr) > 0) && - !(p_ptr->confused || p_ptr->image || !(m_ptr->ml) || p_ptr->stun) && - (pattern_seq(p_ptr->py, p_ptr->px, y, x)) && - ((player_can_enter(cave[y][x].feat)))) - { - m_ptr->csleep = 0; - - /* Extract monster name (or "it") */ - monster_desc(m_name, m_ptr, 0); - - /* Auto-Recall if possible and visible */ - if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); - - /* Track a new monster */ - if (m_ptr->ml) health_track(c_ptr->m_idx); - - /* displace? */ - if (stormbringer && (randint(1000) > 666)) - { - py_attack(y, x, -1); - } - else if (cave_floor_bold(p_ptr->py, p_ptr->px) || - (mr_ptr->flags2 & RF2_PASS_WALL)) - { - msg_format("You push past %s.", m_name); - m_ptr->fy = p_ptr->py; - m_ptr->fx = p_ptr->px; - cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx; - c_ptr->m_idx = 0; - update_mon(cave[p_ptr->py][p_ptr->px].m_idx, TRUE); - } - else - { - msg_format("%^s is in your way!", m_name); - energy_use = 0; - oktomove = FALSE; - } - - /* now continue on to 'movement' */ - } - else - { - py_attack(y, x, -1); - oktomove = FALSE; - } - } - - else if ((c_ptr->feat == FEAT_DARK_PIT) && !p_ptr->ffall) - { - msg_print("You can't cross the chasm."); - running = 0; - oktomove = FALSE; - } - - /* Disarm a visible trap */ - else if (easy_disarm && disarm && (c_ptr->info & (CAVE_TRDT))) - { - (void)do_cmd_disarm_aux(y, x, tmp, do_pickup); - return; - } - - /* Don't step on known traps. */ - else if (disarm && (c_ptr->info & (CAVE_TRDT)) && !(p_ptr->confused || p_ptr->stun || p_ptr->image)) - { - msg_print("You stop to avoid triggering the trap."); - energy_use = 0; - oktomove = FALSE; - } - - /* Player can't enter ? soo bad for him/her ... */ - else if (!player_can_enter(c_ptr->feat)) - { - oktomove = FALSE; - - /* Disturb the player */ - disturb(0); - - if (p_ptr->prob_travel) - { - if (passwall(tmp, TRUE)) return; - } - - /* Notice things in the dark */ - if (!(c_ptr->info & (CAVE_MARK)) && !(c_ptr->info & (CAVE_SEEN))) - { - /* Rubble */ - if (c_ptr->feat == FEAT_RUBBLE) - { - msg_print("You feel some rubble blocking your way."); - c_ptr->info |= (CAVE_MARK); - lite_spot(y, x); - } - - /* Closed door */ - else if (c_ptr->feat < FEAT_SECRET) - { - msg_print("You feel a closed door blocking your way."); - c_ptr->info |= (CAVE_MARK); - lite_spot(y, x); - } - - /* Wall (or secret door) */ - else - { - int feat; - - if (c_ptr->mimic) feat = c_ptr->mimic; - else - feat = f_info[c_ptr->feat].mimic; - - msg_format("You feel %s.", f_text + f_info[feat].block); - c_ptr->info |= (CAVE_MARK); - lite_spot(y, x); - } - } - - /* Notice things */ - else - { - /* Rubble */ - if (c_ptr->feat == FEAT_RUBBLE) - { - if (!easy_tunnel) - { - msg_print("There is rubble blocking your way."); - - if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) - energy_use = 0; - /* - * Well, it makes sense that you lose time bumping into - * a wall _if_ you are confused, stunned or blind; but - * typing mistakes should not cost you a turn... - */ - } - else - { - do_cmd_tunnel_aux(y, x, dir); - return; - } - } - /* Closed doors */ - else if ((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL)) - { - if (easy_open) - { - if (easy_open_door(y, x)) return; - } - else - { - msg_print("There is a closed door blocking your way."); - - if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) - energy_use = 0; - } - } - - /* Wall (or secret door) */ - else - { - if (!easy_tunnel) - { - int feat; - - if (c_ptr->mimic) feat = c_ptr->mimic; - else - feat = f_info[c_ptr->feat].mimic; - - msg_format("There is %s.", f_text + f_info[feat].block); - - if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) - energy_use = 0; - } - else - { - do_cmd_tunnel_aux(y, x, dir); - return; - } - } - } - - /* Sound */ - sound(SOUND_HITWALL); - } - - /* Normal movement */ - if (!pattern_seq(p_ptr->py, p_ptr->px, y, x)) - { - if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) - { - energy_use = 0; - } - - disturb(0); /* To avoid a loop with running */ - - oktomove = FALSE; - } - - - /* - * Check trap detection status -- retrieve them here - * because they are used by the movement code as well - */ - old_dtrap = ((cave[p_ptr->py][p_ptr->px].info & CAVE_DETECT) != 0); - new_dtrap = ((cave[y][x].info & CAVE_DETECT) != 0); - - /* Normal movement */ - if (oktomove && running && disturb_detect) - { - /* - * Disturb the player when about to leave the trap detected - * area - */ - if (old_dtrap && !new_dtrap) - { - /* Disturb player */ - disturb(0); - - /* but don't take a turn */ - energy_use = 0; - - /* Tell player why */ - cmsg_print(TERM_VIOLET, "You are about to leave a trap detected zone."); - /* Flush */ - /* msg_print(NULL); */ - - oktomove = FALSE; - } - } - - /* Normal movement */ - if (oktomove) - { - int oy, ox; - int feat; - - /* Rooted means no move */ - if (p_ptr->tim_roots) return; - - /* Save old location */ - oy = p_ptr->py; - ox = p_ptr->px; - - /* Move the player */ - p_ptr->py = y; - p_ptr->px = x; - - if (cave[p_ptr->py][p_ptr->px].mimic) feat = cave[p_ptr->py][p_ptr->px].mimic; - else - feat = cave[p_ptr->py][p_ptr->px].feat; - - /* Some hooks */ - if (process_hooks(HOOK_MOVED, "(d,d)", oy, ox)) return; - - /* Redraw new spot */ - lite_spot(p_ptr->py, p_ptr->px); - - /* Redraw old spot */ - lite_spot(oy, ox); - - /* Sound */ - /* sound(SOUND_WALK); */ - - /* Check for new panel (redraw map) */ - verify_panel(); - - /* Check detection status */ - if (old_dtrap && !new_dtrap) - { - cmsg_print(TERM_VIOLET, "You leave a trap detected zone."); - if (running) msg_print(NULL); - p_ptr->redraw |= (PR_DTRAP); - } - else if (!old_dtrap && new_dtrap) - { - cmsg_print(TERM_L_BLUE, "You enter a trap detected zone."); - if (running) msg_print(NULL); - p_ptr->redraw |= (PR_DTRAP); - } - - /* Update stuff */ - p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); - - /* Update the monsters */ - p_ptr->update |= (PU_DISTANCE); - - /* Window stuff */ - if (!run) p_ptr->window |= (PW_OVERHEAD); - - /* Some feature descs */ - if (f_info[cave[p_ptr->py][p_ptr->px].feat].text > 1) - { - /* Mega-hack for dungeon branches */ - if ((feat == FEAT_MORE) && c_ptr->special) - { - msg_format("There is %s", d_text + d_info[c_ptr->special].text); - } - else - { - msg_print(f_text + f_info[feat].text); - } - - /* Flush message while running */ - if (running) msg_print(NULL); - } - - /* Spontaneous Searching */ - if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos))) - { - search(); - } - - /* Continuous Searching */ - if (p_ptr->searching) - { - search(); - } - - /* Handle "objects" */ - carry(do_pickup); - - /* Handle "store doors" */ - if (c_ptr->feat == FEAT_SHOP) - { - /* Disturb */ - disturb(0); - - /* Hack -- Enter store */ - command_new = '_'; - } - - else if (cave[y][x].feat >= FEAT_ALTAR_HEAD && - cave[y][x].feat <= FEAT_ALTAR_TAIL) - { - cptr name = f_name + f_info[cave[y][x].feat].name; - cptr pref = (is_a_vowel(name[0])) ? "an" : "a"; - - msg_format("You see %s %s.", pref, name); - - /* Flush message while running */ - if (running) msg_print(NULL); - } - - /* Discover invisible traps */ - else if ((c_ptr->t_idx != 0) && - !(f_info[cave[y][x].feat].flags1 & FF1_DOOR)) - { - /* Disturb */ - disturb(0); - - if (!(c_ptr->info & (CAVE_TRDT))) - { - /* Message */ - msg_print("You found a trap!"); - - /* Pick a trap */ - pick_trap(p_ptr->py, p_ptr->px); - } - - /* Hit the trap */ - hit_trap(); - } - - /* Execute the inscription */ - else if (c_ptr->inscription) - { - /* Disturb */ - disturb(0); - - msg_format("There is an inscription here: %s", - inscription_info[c_ptr->inscription].text); - if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_WALK) - { - execute_inscription(c_ptr->inscription, p_ptr->py, p_ptr->px); - } - } - } - - /* Update wilderness knowledge */ - if (p_ptr->wild_mode) - { - if (wizard) msg_format("y:%d, x:%d", p_ptr->py, p_ptr->px); - - /* Update the known wilderness */ - reveal_wilderness_around_player(p_ptr->py, p_ptr->px, 0, WILDERNESS_SEE_RADIUS); - - /* Walking the wild isnt meaningfull */ - p_ptr->did_nothing = TRUE; - } -} - -void move_player(int dir, int do_pickup, bool_ disarm) -{ - move_player_aux(dir, do_pickup, 0, disarm); -} - - -/* - * Hack -- Grid-based version of see_obstacle - */ -static int see_obstacle_grid(cave_type *c_ptr) -{ - /* - * Hack -- Avoid hitting detected traps, because we cannot rely on - * the CAVE_MARK check below, and traps can be set to nearly - * everything the player can move on to XXX XXX XXX - */ - if (c_ptr->info & (CAVE_TRDT)) return (TRUE); - - - /* Hack -- Handle special cases XXX XXX */ - switch (c_ptr->feat) - { - /* Require levitation */ - case FEAT_DARK_PIT: - case FEAT_DEEP_WATER: - case FEAT_ICE: - { - if (p_ptr->ffall || p_ptr->fly) return (FALSE); - } - - /* Require immunity */ - case FEAT_DEEP_LAVA: - case FEAT_SHAL_LAVA: - { - if (p_ptr->invuln || p_ptr->immune_fire) return (FALSE); - } - } - - - /* "Safe" floor grids aren't obstacles */ - if (f_info[c_ptr->feat].flags1 & FF1_CAN_RUN) return (FALSE); - - /* Must be known to the player */ - if (!(c_ptr->info & (CAVE_MARK))) return (FALSE); - - /* Default */ - return (TRUE); -} - - -/* - * Hack -- Check for a "known wall" or "dangerous" feature (see below) - */ -static int see_obstacle(int dir, int y, int x) -{ - /* Get the new location */ - y += ddy[dir]; - x += ddx[dir]; - - /* Illegal grids are not known walls */ - if (!in_bounds2(y, x)) return (FALSE); - - /* Analyse the grid */ - return (see_obstacle_grid(&cave[y][x])); -} - - -/* - * Hack -- Check for an "unknown corner" (see below) - */ -static int see_nothing(int dir, int y, int x) -{ - /* Get the new location */ - y += ddy[dir]; - x += ddx[dir]; - - /* Illegal grids are unknown */ - if (!in_bounds2(y, x)) return (TRUE); - - /* Memorized grids are always known */ - if (cave[y][x].info & (CAVE_MARK)) return (FALSE); - - /* Non-floor grids are unknown */ - if (!cave_floor_bold(y, x)) return (TRUE); - - /* Viewable door/wall grids are known */ - if (player_can_see_bold(y, x)) return (FALSE); - - /* Default */ - return (TRUE); -} - - - - - -/* - * The running algorithm: -CJS- - * - * In the diagrams below, the player has just arrived in the - * grid marked as '@', and he has just come from a grid marked - * as 'o', and he is about to enter the grid marked as 'x'. - * - * Of course, if the "requested" move was impossible, then you - * will of course be blocked, and will stop. - * - * Overview: You keep moving until something interesting happens. - * If you are in an enclosed space, you follow corners. This is - * the usual corridor scheme. If you are in an open space, you go - * straight, but stop before entering enclosed space. This is - * analogous to reaching doorways. If you have enclosed space on - * one side only (that is, running along side a wall) stop if - * your wall opens out, or your open space closes in. Either case - * corresponds to a doorway. - * - * What happens depends on what you can really SEE. (i.e. if you - * have no light, then running along a dark corridor is JUST like - * running in a dark room.) The algorithm works equally well in - * corridors, rooms, mine tailings, earthquake rubble, etc, etc. - * - * These conditions are kept in static memory: - * find_openarea You are in the open on at least one - * side. - * find_breakleft You have a wall on the left, and will - * stop if it opens - * find_breakright You have a wall on the right, and will - * stop if it opens - * - * To initialize these conditions, we examine the grids adjacent - * to the grid marked 'x', two on each side (marked 'L' and 'R'). - * If either one of the two grids on a given side is seen to be - * closed, then that side is considered to be closed. If both - * sides are closed, then it is an enclosed (corridor) run. - * - * LL L - * @x LxR - * RR @R - * - * Looking at more than just the immediate squares is - * significant. Consider the following case. A run along the - * corridor will stop just before entering the center point, - * because a choice is clearly established. Running in any of - * three available directions will be defined as a corridor run. - * Note that a minor hack is inserted to make the angled corridor - * entry (with one side blocked near and the other side blocked - * further away from the runner) work correctly. The runner moves - * diagonally, but then saves the previous direction as being - * straight into the gap. Otherwise, the tail end of the other - * entry would be perceived as an alternative on the next move. - * - * #.# - * ##.## - * .@x.. - * ##.## - * #.# - * - * Likewise, a run along a wall, and then into a doorway (two - * runs) will work correctly. A single run rightwards from @ will - * stop at 1. Another run right and down will enter the corridor - * and make the corner, stopping at the 2. - * - * #@x 1 - * ########### ###### - * 2 # - * ############# - * # - * - * After any move, the function area_affect is called to - * determine the new surroundings, and the direction of - * subsequent moves. It examines the current player location - * (at which the runner has just arrived) and the previous - * direction (from which the runner is considered to have come). - * - * Moving one square in some direction places you adjacent to - * three or five new squares (for straight and diagonal moves - * respectively) to which you were not previously adjacent, - * marked as '!' in the diagrams below. - * - * ...! ... - * .o@! .o.! - * ...! ..@! - * !!! - * - * You STOP if any of the new squares are interesting in any way: - * for example, if they contain visible monsters or treasure. - * - * You STOP if any of the newly adjacent squares seem to be open, - * and you are also looking for a break on that side. (that is, - * find_openarea AND find_break). - * - * You STOP if any of the newly adjacent squares do NOT seem to be - * open and you are in an open area, and that side was previously - * entirely open. - * - * Corners: If you are not in the open (i.e. you are in a corridor) - * and there is only one way to go in the new squares, then turn in - * that direction. If there are more than two new ways to go, STOP. - * If there are two ways to go, and those ways are separated by a - * square which does not seem to be open, then STOP. - * - * Otherwise, we have a potential corner. There are two new open - * squares, which are also adjacent. One of the new squares is - * diagonally located, the other is straight on (as in the diagram). - * We consider two more squares further out (marked below as ?). - * - * We assign "option" to the straight-on grid, and "option2" to the - * diagonal grid, and "check_dir" to the grid marked 's'. - * - * .s - * @x? - * #? - * - * If they are both seen to be closed, then it is seen that no - * benefit is gained from moving straight. It is a known corner. - * To cut the corner, go diagonally, otherwise go straight, but - * pretend you stepped diagonally into that next location for a - * full view next time. Conversely, if one of the ? squares is - * not seen to be closed, then there is a potential choice. We check - * to see whether it is a potential corner or an intersection/room entrance. - * If the square two spaces straight ahead, and the space marked with 's' - * are both blank, then it is a potential corner and enter if find_examine - * is set, otherwise must stop because it is not a corner. - */ - - - - -/* - * Hack -- allow quick "cycling" through the legal directions - */ -static byte cycle[] = { 1, 2, 3, 6, 9, 8, 7, 4, 1, 2, 3, 6, 9, 8, 7, 4, 1 }; - -/* - * Hack -- map each direction into the "middle" of the "cycle[]" array - */ -static byte chome[] = { 0, 8, 9, 10, 7, 0, 11, 6, 5, 4 }; - -/* - * The direction we are running - */ -static byte find_current; - -/* - * The direction we came from - */ -static byte find_prevdir; - -/* - * We are looking for open area - */ -static bool_ find_openarea; - -/* - * We are looking for a break - */ -static bool_ find_breakright; -static bool_ find_breakleft; - - - -/* - * Initialize the running algorithm for a new direction. - * - * Diagonal Corridor -- allow diaginal entry into corridors. - * - * Blunt Corridor -- If there is a wall two spaces ahead and - * we seem to be in a corridor, then force a turn into the side - * corridor, must be moving straight into a corridor here. ??? - * - * Diagonal Corridor Blunt Corridor (?) - * # # # - * #x# @x# - * @p. p - */ -static void run_init(int dir) -{ - int row, col, deepleft, deepright; - - int i, shortleft, shortright; - - - /* Save the direction */ - find_current = dir; - - /* Assume running straight */ - find_prevdir = dir; - - /* Assume looking for open area */ - find_openarea = TRUE; - - /* Assume not looking for breaks */ - find_breakright = find_breakleft = FALSE; - - /* Assume no nearby walls */ - deepleft = deepright = FALSE; - shortright = shortleft = FALSE; - - /* Find the destination grid */ - row = p_ptr->py + ddy[dir]; - col = p_ptr->px + ddx[dir]; - - /* Extract cycle index */ - i = chome[dir]; - - /* Check for walls */ - if (see_obstacle(cycle[i + 1], p_ptr->py, p_ptr->px)) - { - find_breakleft = TRUE; - shortleft = TRUE; - } - else if (see_obstacle(cycle[i + 1], row, col)) - { - find_breakleft = TRUE; - deepleft = TRUE; - } - - /* Check for walls */ - if (see_obstacle(cycle[i - 1], p_ptr->py, p_ptr->px)) - { - find_breakright = TRUE; - shortright = TRUE; - } - else if (see_obstacle(cycle[i - 1], row, col)) - { - find_breakright = TRUE; - deepright = TRUE; - } - - /* Looking for a break */ - if (find_breakleft && find_breakright) - { - /* Not looking for open area */ - find_openarea = FALSE; - - /* Hack -- allow angled corridor entry */ - if (dir & 0x01) - { - if (deepleft && !deepright) - { - find_prevdir = cycle[i - 1]; - } - else if (deepright && !deepleft) - { - find_prevdir = cycle[i + 1]; - } - } - - /* Hack -- allow blunt corridor entry */ - else if (see_obstacle(cycle[i], row, col)) - { - if (shortleft && !shortright) - { - find_prevdir = cycle[i - 2]; - } - else if (shortright && !shortleft) - { - find_prevdir = cycle[i + 2]; - } - } - } -} - - -/* - * Update the current "run" path - * - * Return TRUE if the running should be stopped - */ -static bool_ run_test(void) -{ - int prev_dir, new_dir, check_dir = 0; - - int row, col; - - int i, max, inv; - - int option = 0, option2 = 0; - - cave_type *c_ptr; - - - /* Where we came from */ - prev_dir = find_prevdir; - - - /* Range of newly adjacent grids */ - max = (prev_dir & 0x01) + 1; - - - /* Look at every newly adjacent square. */ - for (i = -max; i <= max; i++) - { - s16b this_o_idx, next_o_idx = 0; - - - /* New direction */ - new_dir = cycle[chome[prev_dir] + i]; - - /* New location */ - row = p_ptr->py + ddy[new_dir]; - col = p_ptr->px + ddx[new_dir]; - - /* Access grid */ - c_ptr = &cave[row][col]; - - - /* Visible monsters abort running */ - if (c_ptr->m_idx) - { - monster_type *m_ptr = &m_list[c_ptr->m_idx]; - - /* Visible monster */ - if (m_ptr->ml) return (TRUE); - } - - /* Visible objects abort running */ - for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) - { - object_type * o_ptr; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Visible object */ - if (o_ptr->marked) return (TRUE); - } - - - /* Assume unknown */ - inv = TRUE; - - /* Check memorized grids */ - if (c_ptr->info & (CAVE_MARK)) - { - bool_ notice = TRUE; - - /* - * Examine the terrain -- conditional disturbance - * If we had more flags, we could make these customisable too - */ - switch (c_ptr->feat) - { - case FEAT_DEEP_LAVA: - case FEAT_SHAL_LAVA: - { - /* Ignore */ - if (p_ptr->invuln || p_ptr->immune_fire) notice = FALSE; - - /* Done */ - break; - } - - case FEAT_DEEP_WATER: - case FEAT_ICE: - { - /* Ignore */ - if (p_ptr->ffall || p_ptr->fly) notice = FALSE; - - /* Done */ - break; - } - - /* Open doors */ - case FEAT_OPEN: - case FEAT_BROKEN: - { - /* Option -- ignore */ - if (find_ignore_doors) notice = FALSE; - - /* Done */ - break; - } - - /* - * Stairs - too many of them, should find better ways to - * handle them (not scripting!, because it can be called - * from within the running algo) XXX XXX XXX - */ - case FEAT_LESS: - case FEAT_MORE: - case FEAT_QUEST_ENTER: - case FEAT_QUEST_EXIT: - case FEAT_QUEST_DOWN: - case FEAT_QUEST_UP: - case FEAT_SHAFT_UP: - case FEAT_SHAFT_DOWN: - case FEAT_WAY_LESS: - case FEAT_WAY_MORE: - /* XXX */ - case FEAT_BETWEEN: - case FEAT_BETWEEN2: - { - /* Option -- ignore */ - if (find_ignore_stairs) notice = FALSE; - - /* Done */ - break; - } - } - - /* Check the "don't notice running" flag */ - if (f_info[c_ptr->feat].flags1 & FF1_DONT_NOTICE_RUNNING) - { - notice = FALSE; - } - - /* A detected trap is interesting */ - if (c_ptr->info & (CAVE_TRDT)) notice = TRUE; - - /* Interesting feature */ - if (notice) return (TRUE); - - /* The grid is "visible" */ - inv = FALSE; - } - - /* Mega-Hack -- Maze code removes CAVE_MARK XXX XXX XXX */ - if (c_ptr->info & (CAVE_TRDT)) return (TRUE); - - /* Analyze unknown grids and floors */ - if (inv || cave_floor_bold(row, col)) - { - /* Looking for open area */ - if (find_openarea) - { - /* Nothing */ - } - - /* The first new direction. */ - else if (!option) - { - option = new_dir; - } - - /* Three new directions. Stop running. */ - else if (option2) - { - return (TRUE); - } - - /* Two non-adjacent new directions. Stop running. */ - else if (option != cycle[chome[prev_dir] + i - 1]) - { - return (TRUE); - } - - /* Two new (adjacent) directions (case 1) */ - else if (new_dir & 0x01) - { - check_dir = cycle[chome[prev_dir] + i - 2]; - option2 = new_dir; - } - - /* Two new (adjacent) directions (case 2) */ - else - { - check_dir = cycle[chome[prev_dir] + i + 1]; - option2 = option; - option = new_dir; - } - } - - /* Obstacle, while looking for open area */ - else - { - if (find_openarea) - { - if (i < 0) - { - /* Break to the right */ - find_breakright = TRUE; - } - - else if (i > 0) - { - /* Break to the left */ - find_breakleft = TRUE; - } - } - } - } - - - /* Looking for open area */ - if (find_openarea) - { - /* Hack -- look again */ - for (i = -max; i < 0; i++) - { - new_dir = cycle[chome[prev_dir] + i]; - - row = p_ptr->py + ddy[new_dir]; - col = p_ptr->px + ddx[new_dir]; - - /* Access grid */ - c_ptr = &cave[row][col]; - - /* Unknown grids or non-obstacle */ - if (!see_obstacle_grid(c_ptr)) - { - /* Looking to break right */ - if (find_breakright) - { - return (TRUE); - } - } - - /* Obstacle */ - else - { - /* Looking to break left */ - if (find_breakleft) - { - return (TRUE); - } - } - } - - /* Hack -- look again */ - for (i = max; i > 0; i--) - { - new_dir = cycle[chome[prev_dir] + i]; - - row = p_ptr->py + ddy[new_dir]; - col = p_ptr->px + ddx[new_dir]; - - /* Access grid */ - c_ptr = &cave[row][col]; - - /* Unknown grid or non-obstacle */ - if (!see_obstacle_grid(c_ptr)) - { - /* Looking to break left */ - if (find_breakleft) - { - return (TRUE); - } - } - - /* Obstacle */ - else - { - /* Looking to break right */ - if (find_breakright) - { - return (TRUE); - } - } - } - } - - - /* Not looking for open area */ - else - { - /* No options */ - if (!option) - { - return (TRUE); - } - - /* One option */ - else if (!option2) - { - /* Primary option */ - find_current = option; - - /* No other options */ - find_prevdir = option; - } - - /* Two options, examining corners */ - else if (find_examine && !find_cut) - { - /* Primary option */ - find_current = option; - - /* Hack -- allow curving */ - find_prevdir = option2; - } - - /* Two options, pick one */ - else - { - /* Get next location */ - row = p_ptr->py + ddy[option]; - col = p_ptr->px + ddx[option]; - - /* Don't see that it is closed off. */ - /* This could be a potential corner or an intersection. */ - if (!see_obstacle(option, row, col) || !see_obstacle(check_dir, row, col)) - { - /* Can not see anything ahead and in the direction we */ - /* are turning, assume that it is a potential corner. */ - if (find_examine && - see_nothing(option, row, col) && - see_nothing(option2, row, col)) - { - find_current = option; - find_prevdir = option2; - } - - /* STOP: we are next to an intersection or a room */ - else - { - return (TRUE); - } - } - - /* This corner is seen to be enclosed; we cut the corner. */ - else if (find_cut) - { - find_current = option2; - find_prevdir = option2; - } - - /* This corner is seen to be enclosed, and we */ - /* deliberately go the long way. */ - else - { - find_current = option; - find_prevdir = option2; - } - } - } - - - /* About to hit a known wall, stop */ - if (see_obstacle(find_current, p_ptr->py, p_ptr->px)) - { - return (TRUE); - } - - - /* Failure */ - return (FALSE); -} - - - -/* - * Take one step along the current "run" path - */ -void run_step(int dir) -{ - /* Start running */ - if (dir) - { - /* Hack -- do not start silly run */ - if (see_obstacle(dir, p_ptr->py, p_ptr->px) && - (cave[p_ptr->py + ddy[dir]][p_ptr->px + ddx[dir]].feat != FEAT_TREES)) - { - /* Message */ - msg_print("You cannot run in that direction."); - - /* Disturb */ - disturb(0); - - /* Done */ - return; - } - - /* Calculate torch radius */ - p_ptr->update |= (PU_TORCH); - - /* Initialize */ - run_init(dir); - } - - /* Keep running */ - else - { - /* Update run */ - if (run_test()) - { - /* Disturb */ - disturb(0); - - /* Done */ - return; - } - } - - /* Decrease the run counter */ - if (--running <= 0) return; - - /* Take time */ - energy_use = 100; - - - /* Move the player, using the "pickup" flag */ - move_player_aux(find_current, always_pickup, 1, TRUE); -} - - -/* - * Take care of the various things that can happen when you step - * into a space. (Objects, traps, and stores.) - */ -void step_effects(int y, int x, int do_pickup) -{ - /* Handle "objects" */ - py_pickup_floor(do_pickup); - - /* Handle "store doors" */ - if (cave[y][x].feat == FEAT_SHOP) - { - /* Disturb */ - disturb(0); - - /* Hack -- Enter store */ - command_new = KTRL('V'); - } - - /* Discover/set off traps */ - else if (cave[y][x].t_idx != 0) - { - /* Disturb */ - disturb(0); - - if (!(cave[y][x].info & CAVE_TRDT)) - { - /* Message */ - msg_print("You found a trap!"); - - /* Pick a trap */ - pick_trap(y, x); - } - - /* Hit the trap */ - hit_trap(); - } -} - -/* - * Issue a pet command - */ -void do_cmd_pet(void) -{ - int i = 0; - - int num = 0; - - int powers[36]; - - char power_desc[36][80]; - - bool_ flag, redraw; - - int ask; - - char choice; - - char out_val[160]; - - int pets = 0, pet_ctr = 0; - - bool_ all_pets = FALSE; - - monster_type *m_ptr; - - - for (num = 0; num < 36; num++) - { - powers[num] = 0; - strcpy(power_desc[num], ""); - } - - num = 0; - - if (p_ptr->confused) - { - msg_print("You are too confused to command your pets."); - energy_use = 0; - return; - } - - /* Calculate pets */ - /* Process the monsters (backwards) */ - for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) - { - /* Access the monster */ - m_ptr = &m_list[pet_ctr]; - - if (m_ptr->status >= MSTATUS_FRIEND) pets++; - } - - if (pets == 0) - { - msg_print("You have no pets/companions."); - energy_use = 0; - return; - } - else - { - strcpy(power_desc[num], "dismiss pets"); - powers[num++] = 1; - strcpy(power_desc[num], "dismiss companions"); - powers[num++] = 10; - strcpy(power_desc[num], "call pets"); - powers[num++] = 2; - strcpy(power_desc[num], "follow me"); - powers[num++] = 6; - strcpy(power_desc[num], "seek and destroy"); - powers[num++] = 3; - if (p_ptr->pet_open_doors) - strcpy(power_desc[num], "disallow open doors"); - else - strcpy(power_desc[num], "allow open doors"); - powers[num++] = 4; - if (p_ptr->pet_pickup_items) - strcpy(power_desc[num], "disallow pickup items"); - else - strcpy(power_desc[num], "allow pickup items"); - powers[num++] = 5; - strcpy(power_desc[num], "give target to a friend"); - powers[num++] = 7; - strcpy(power_desc[num], "give target to all friends"); - powers[num++] = 8; - strcpy(power_desc[num], "friend forget target"); - powers[num++] = 9; - } - - /* Nothing chosen yet */ - flag = FALSE; - - /* No redraw yet */ - redraw = FALSE; - - /* Build a prompt (accept all spells) */ - if (num <= 26) - { - /* Build a prompt (accept all spells) */ - strnfmt(out_val, 78, - "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0), - I2A(num - 1)); - } - else - { - strnfmt(out_val, 78, - "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0), - '0' + num - 27); - } - - /* Get a command from the user */ - while (!flag && get_com(out_val, &choice)) - { - /* Request redraw */ - if ((choice == ' ') || (choice == '*') || (choice == '?')) - { - /* Show the list */ - if (!redraw) - { - byte y = 1, x = 0; - int ctr = 0; - char dummy[80]; - - strcpy(dummy, ""); - - /* Show list */ - redraw = TRUE; - - /* Save the screen */ - character_icky = TRUE; - Term_save(); - - prt("", y++, x); - - while (ctr < num) - { - strnfmt(dummy, 80, "%c) %s", I2A(ctr), power_desc[ctr]); - prt(dummy, y + ctr, x); - ctr++; - } - - if (ctr < 17) - { - prt("", y + ctr, x); - } - else - { - prt("", y + 17, x); - } - } - - /* Hide the list */ - else - { - /* Hide list */ - redraw = FALSE; - - /* Restore the screen */ - Term_load(); - character_icky = FALSE; - } - - /* Redo asking */ - continue; - } - - if (choice == '\r' && num == 1) - { - choice = 'a'; - } - - if (isalpha(choice)) - { - /* Note verify */ - ask = (isupper(choice)); - - /* Lowercase */ - if (ask) choice = tolower(choice); - - /* Extract request */ - i = (islower(choice) ? A2I(choice) : -1); - } - else - { - ask = FALSE; /* Can't uppercase digits */ - - i = choice - '0' + 26; - } - - /* Totally Illegal */ - if ((i < 0) || (i >= num)) - { - bell(); - continue; - } - - /* Verify it */ - if (ask) - { - char tmp_val[160]; - - /* Prompt */ - strnfmt(tmp_val, 78, "Use %s? ", power_desc[i]); - - /* Belay that order */ - if (!get_check(tmp_val)) continue; - } - - /* Stop the loop */ - flag = TRUE; - } - - /* Restore the screen */ - if (redraw) - { - Term_load(); - character_icky = FALSE; - } - - /* Abort if needed */ - if (!flag) - { - energy_use = 0; - return; - } - - switch (powers[i]) - { - /* forget target */ - case 9: - { - monster_type *m_ptr; - int ii, jj; - - msg_print("Select the friendly monster:"); - if (!tgt_pt(&ii, &jj)) return; - - if (cave[jj][ii].m_idx) - { - m_ptr = &m_list[cave[jj][ii].m_idx]; - - if (m_ptr->status < MSTATUS_PET) - { - msg_print("You cannot give orders to this monster."); - return; - } - - m_ptr->target = -1; - } - break; - } - /* Give target to all */ - case 8: - { - monster_type *m_ptr; - int ii, jj, i; - - - msg_print("Select the target monster:"); - if (!tgt_pt(&ii, &jj)) return; - - if (cave[jj][ii].m_idx) - { - msg_print("Target selected."); - - for (i = m_max - 1; i >= 1; i--) - { - /* Access the monster */ - m_ptr = &m_list[i]; - - if (!m_ptr->r_idx) continue; - - if (m_ptr->status < MSTATUS_PET) continue; - - m_ptr->target = cave[jj][ii].m_idx; - } - } - else - { - msg_print("This is not a correct target."); - return; - } - break; - } - case 1: /* Dismiss pets */ - { - int Dismissed = 0; - - if (get_check("Dismiss all pets? ")) all_pets = TRUE; - - /* Process the monsters (backwards) */ - for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) - { - monster_race *r_ptr; - - /* Access the monster */ - m_ptr = &m_list[pet_ctr]; - r_ptr = &r_info[m_ptr->r_idx]; - - if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_PET) || (m_ptr->status == MSTATUS_FRIEND))) /* Get rid of it! */ - { - bool_ checked = FALSE; - char command; - bool_ delete_this = FALSE; - - if (all_pets) - { - delete_this = TRUE; - } - else - { - char friend_name[80], check_friend[80]; - monster_desc(friend_name, m_ptr, 0x80); - strnfmt(check_friend, 80, "Dismiss %s? (Escape to cancel)", friend_name); - - while (!checked) - { - if (!get_com(check_friend, &command)) - { - /* get out of loop */ - checked = TRUE; - pet_ctr = 0; - } - else switch (command) - { - case 'Y': - case 'y': - delete_this = TRUE; - checked = TRUE; - break; - case 'n': - case 'N': - checked = TRUE; - break; - default: - bell(); - break; - } - } - } - - if (delete_this) - { - delete_monster_idx(pet_ctr); - Dismissed++; - } - } - } - - msg_format("You have dismissed %d pet%s.", Dismissed, - (Dismissed == 1 ? "" : "s")); - break; - } - case 10: /* Dismiss companions */ - { - int Dismissed = 0; - - if (get_check("Dismiss all companions? ")) all_pets = TRUE; - - /* Process the monsters (backwards) */ - for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) - { - monster_race *r_ptr; - - /* Access the monster */ - m_ptr = &m_list[pet_ctr]; - r_ptr = &r_info[m_ptr->r_idx]; - - if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_COMPANION))) /* Get rid of it! */ - { - bool_ delete_this = FALSE; - - if (all_pets) - delete_this = TRUE; - else - { - char friend_name[80], check_friend[80]; - monster_desc(friend_name, m_ptr, 0x80); - strnfmt(check_friend, 80, "Dismiss %s? ", friend_name); - - if (get_check(check_friend)) - delete_this = TRUE; - } - - if (delete_this) - { - delete_monster_idx(pet_ctr); - Dismissed++; - } - } - } - - msg_format("You have dismissed %d companion%s.", Dismissed, - (Dismissed == 1 ? "" : "s")); - break; - } - /* Call pets */ - case 2: - { - p_ptr->pet_follow_distance = 1; - break; - } - /* "Seek and destroy" */ - case 3: - { - p_ptr->pet_follow_distance = 255; - break; - } - /* flag - allow pets to open doors */ - case 4: - { - p_ptr->pet_open_doors = !p_ptr->pet_open_doors; - break; - } - /* flag - allow pets to pickup items */ - case 5: - { - p_ptr->pet_pickup_items = !p_ptr->pet_pickup_items; - - /* Drop objects being carried by pets */ - if (!p_ptr->pet_pickup_items) - { - for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) - { - /* Access the monster */ - m_ptr = &m_list[pet_ctr]; - - if (m_ptr->status >= MSTATUS_PET) - { - monster_drop_carried_objects(m_ptr); - } - } - } - - break; - } - /* "Follow Me" */ - case 6: - { - p_ptr->pet_follow_distance = 6; - break; - } - } -} - -/* - * Incarnate into a body - */ -bool_ do_cmd_integrate_body() -{ - cptr q, s; - - int item; - - object_type *o_ptr; - - - if (!p_ptr->disembodied) - { - msg_print("You are already in a body."); - return FALSE; - } - - /* Restrict choices to monsters */ - item_tester_tval = TV_CORPSE; - - /* Get an item */ - q = "Incarnate in which body? "; - s = "You have no corpse to incarnate in."; - if (!get_item(&item, q, s, (USE_FLOOR))) return FALSE; - - o_ptr = &o_list[0 - item]; - - if (o_ptr->sval != SV_CORPSE_CORPSE) - { - msg_print("You must select a corpse."); - return FALSE; - } - - p_ptr->body_monster = o_ptr->pval2; - p_ptr->chp = o_ptr->pval3; - - floor_item_increase(0 - item, -1); - floor_item_describe(0 - item); - floor_item_optimize(0 - item); - - msg_print("Your spirit is incarnated in your new body."); - p_ptr->wraith_form = FALSE; - p_ptr->disembodied = FALSE; - do_cmd_redraw(); - - return TRUE; -} - -/* - * Leave a body - */ -bool_ do_cmd_leave_body(bool_ drop_body) -{ - object_type *o_ptr, forge; - - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - int i; - - - if (p_ptr->disembodied) - { - msg_print("You are already disembodied."); - return FALSE; - } - - for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) - { - if (p_ptr->body_parts[i - INVEN_WIELD] && p_ptr->inventory[i].k_idx && - cursed_p(&p_ptr->inventory[i])) - { - msg_print("A cursed object is preventing you from leaving your body."); - return FALSE; - } - } - - if (drop_body) - { - if (magik(25 + get_skill_scale(SKILL_POSSESSION, 25) + get_skill(SKILL_PRESERVATION))) - { - o_ptr = &forge; - object_prep(o_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE)); - o_ptr->number = 1; - o_ptr->pval = 0; - o_ptr->pval2 = p_ptr->body_monster; - o_ptr->pval3 = p_ptr->chp; - o_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1; - object_aware(o_ptr); - object_known(o_ptr); - o_ptr->ident |= IDENT_STOREB; - - /* Unique corpses are unique */ - if (r_ptr->flags1 & RF1_UNIQUE) - { - o_ptr->name1 = 201; - } - - drop_near(o_ptr, -1, p_ptr->py, p_ptr->px); - } - else - msg_print - ("You do not manage to keep the corpse from rotting away."); - } - - msg_print("Your spirit leaves your body."); - p_ptr->disembodied = TRUE; - - /* Turn into a lost soul(just for the picture) */ - p_ptr->body_monster = test_monster_name("Lost soul"); - do_cmd_redraw(); - - return (TRUE); -} - - -bool_ execute_inscription(byte i, byte y, byte x) -{ - cave_type *c_ptr = &cave[y][x]; - - - /* Not enough mana in the current grid */ - if (c_ptr->mana < inscription_info[i].mana) return (TRUE); - - - /* Reduce the grid mana -- note: it can't be restored */ - c_ptr->mana -= inscription_info[i].mana; - - /* Analyse inscription type */ - switch (i) - { - case INSCRIP_LIGHT: - { - msg_print("The inscription shines in a bright light!"); - lite_room(y, x); - - break; - } - - case INSCRIP_DARK: - { - msg_print("The inscription is enveloped in a dark aura!"); - unlite_room(y, x); - - break; - } - - case INSCRIP_STORM: - { - msg_print("The inscription releases a powerful storm!"); - project(0, 3, y, x, damroll(10, 10), - GF_ELEC, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | - PROJECT_KILL | PROJECT_JUMP); - - break; - } - - case INSCRIP_PROTECTION: - { - return (FALSE); - - break; - } - - case INSCRIP_DWARF_SUMMON: - { - int yy = y, xx = x; - - scatter(&yy, &xx, y, x, 3); - place_monster_one(yy, xx, test_monster_name("Dwarven Warrior"), - 0, FALSE, MSTATUS_FRIEND); - - break; - } - - case INSCRIP_CHASM: - { - monster_type *m_ptr; - monster_race *r_ptr; - cave_type *c_ptr; - int ii = x, ij = y; - - cave_set_feat(ij, ii, FEAT_DARK_PIT); - msg_print("A chasm appears in the floor!"); - - if (cave[ij][ii].m_idx) - { - m_ptr = &m_list[cave[ij][ii].m_idx]; - r_ptr = race_inf(m_ptr); - - if (r_ptr->flags7 & RF7_CAN_FLY) - { - msg_print("The monster simply flies over the chasm."); - } - else - { - if (!(r_ptr->flags1 & RF1_UNIQUE)) - { - msg_print("The monster falls in the chasm!"); - delete_monster_idx(cave[ij][ii].m_idx); - } - } - } - - if (cave[ij][ii].o_idx) - { - s16b this_o_idx, next_o_idx = 0; - - c_ptr = &cave[ij][ii]; - - /* Scan all objects in the grid */ - for (this_o_idx = c_ptr->o_idx; this_o_idx; - this_o_idx = next_o_idx) - { - object_type * o_ptr; - bool_ plural = FALSE; - - char o_name[80]; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - if (o_ptr->number > 1) plural = TRUE; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Effect "observed" */ - if (o_ptr->marked) - { - object_desc(o_name, o_ptr, FALSE, 0); - } - - /* Artifacts get to resist */ - if (o_ptr->name1) - { - /* Observe the resist */ - if (o_ptr->marked) - { - msg_format("The %s %s simply fly over the chasm!", - o_name, (plural ? "are" : "is")); - } - } - - /* Kill it */ - else - { - /* Delete the object */ - delete_object_idx(this_o_idx); - - /* Redraw */ - lite_spot(ij, ii); - } - } - } - - break; - } - - case INSCRIP_BLACK_FIRE: - { - msg_print("The inscription releases a blast of hellfire!"); - project(0, 3, y, x, 200, - GF_HELL_FIRE, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | - PROJECT_KILL | PROJECT_JUMP); - - break; - } - } - - return (TRUE); -} - - -/* - * Choose an inscription and engrave it - */ -void do_cmd_engrave() -{ - char buf[41] = ""; - - byte i; - - strnfmt(buf, 41, "%s", inscription_info[cave[p_ptr->py][p_ptr->px].inscription].text); - - get_string("Engrave what? ", buf, 40); - - /* Silently do nothing when player his escape or enters an empty string */ - if (!buf[0]) return; - - for (i = 0; i < MAX_INSCRIPTIONS; i++) - { - if (!strcmp(inscription_info[i].text, buf)) - { - if (inscription_info[i].know) - { - /* Save the inscription */ - cave[p_ptr->py][p_ptr->px].inscription = i; - } - else - msg_print("You can't use this inscription for now."); - } - } - - /* Execute the inscription */ - if (inscription_info[cave[p_ptr->py][p_ptr->px].inscription].when & INSCRIP_EXEC_ENGRAVE) - { - execute_inscription(cave[p_ptr->py][p_ptr->px].inscription, p_ptr->py, p_ptr->px); - } - - energy_use += 300; -} - - -/* - * Let's do a spinning around attack: -- DG -- - * aDb - * y@k - * ooT - * Ah ... all of those will get hit. - */ -void do_spin() -{ - int i, j; - - - msg_print("You start spinning around..."); - - for (j = p_ptr->py - 1; j <= p_ptr->py + 1; j++) - { - for (i = p_ptr->px - 1; i <= p_ptr->px + 1; i++) - { - /* Avoid stupid bugs */ - if (in_bounds(j, i) && cave[j][i].m_idx) - py_attack(j, i, 1); - } - } -} diff --git a/src/cmd1.cc b/src/cmd1.cc new file mode 100644 index 00000000..ff493174 --- /dev/null +++ b/src/cmd1.cc @@ -0,0 +1,5147 @@ +/* File: cmd1.c */ + +/* Purpose: Movement commands (part 1) */ + +/* + * 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 "angband.h" + +#include "quark.h" +#include "hooks.h" + +#define MAX_VAMPIRIC_DRAIN 100 + + +/* + * Determine if the player "hits" a monster (normal combat). + * Note -- Always miss 5%, always hit 5%, otherwise random. + */ +bool_ test_hit_fire(int chance, int ac, int vis) +{ + int k; + + + /* Percentile dice */ + k = rand_int(100); + + /* Hack -- Instant miss or hit */ + if (k < 10) return (k < 5); + + /* Never hit */ + if (chance <= 0) return (FALSE); + + /* Invisible monsters are harder to hit */ + if (!vis) chance = (chance + 1) / 2; + + /* Power competes against armor */ + if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE); + + /* Assume hit */ + return (TRUE); +} + + + +/* + * Determine if the player "hits" a monster (normal combat). + * + * Note -- Always miss 5%, always hit 5%, otherwise random. + */ +bool_ test_hit_norm(int chance, int ac, int vis) +{ + int k; + + + /* Percentile dice */ + k = rand_int(100); + + /* Hack -- Instant miss or hit */ + if (k < 10) return (k < 5); + + /* Wimpy attack never hits */ + if (chance <= 0) return (FALSE); + + /* Penalize invisible targets */ + if (!vis) chance = (chance + 1) / 2; + + /* Power must defeat armor */ + if (rand_int(chance + luck( -10, 10)) < (ac * 3 / 4)) return (FALSE); + + /* Assume hit */ + return (TRUE); +} + + + +/* + * Critical hits (from objects thrown by player) + * Factor in item weight, total plusses, and player level. + */ +s16b critical_shot(int weight, int plus, int dam, int skill) +{ + int i, k; + + + /* Extract "shot" power */ + i = (weight + ((p_ptr->to_h + plus) * 4) + + get_skill_scale(skill, 100)); + i += 50 * p_ptr->xtra_crit; + i += luck( -100, 100); + + /* Critical hit */ + if (randint(5000) <= i) + { + k = weight + randint(500); + + if (k < 500) + { + msg_print("It was a good hit!"); + dam = 2 * dam + 5; + } + else if (k < 1000) + { + msg_print("It was a great hit!"); + dam = 2 * dam + 10; + } + else + { + msg_print("It was a superb hit!"); + dam = 3 * dam + 15; + } + } + + return (dam); +} + +/* + * Critical hits (by player) + * + * Factor in weapon weight, total plusses, player level. + */ +s16b critical_norm(int weight, int plus, int dam, int weapon_tval, bool_ *done_crit) +{ + int i, k, num = randint(5000); + + *done_crit = FALSE; + + /* Extract "blow" power */ + i = (weight + ((p_ptr->to_h + plus) * 5) + + get_skill_scale(p_ptr->melee_style, 150)); + i += 50 * p_ptr->xtra_crit; + if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS)) + { + i += get_skill_scale(SKILL_CRITS, 40 * 50); + } + i += luck( -100, 100); + + /* Force good strikes */ + if (p_ptr->tim_deadly) + { + set_tim_deadly(p_ptr->tim_deadly - 1); + msg_print("It was a *GREAT* hit!"); + dam = 3 * dam + 20; + *done_crit = TRUE; + } + + /* Chance */ + else if (num <= i) + { + k = weight + randint(650); + if ((weapon_tval == TV_SWORD) && (weight < 50) && get_skill(SKILL_CRITS)) + { + k += get_skill_scale(SKILL_CRITS, 400); + } + + if (k < 400) + { + msg_print("It was a good hit!"); + dam = 2 * dam + 5; + } + else if (k < 700) + { + msg_print("It was a great hit!"); + dam = 2 * dam + 10; + } + else if (k < 900) + { + msg_print("It was a superb hit!"); + dam = 3 * dam + 15; + } + else if (k < 1300) + { + msg_print("It was a *GREAT* hit!"); + dam = 3 * dam + 20; + } + else + { + msg_print("It was a *SUPERB* hit!"); + dam = ((7 * dam) / 2) + 25; + } + *done_crit = TRUE; + } + + return (dam); +} + + + +/* + * Extract the "total damage" from a given object hitting a given monster. + * + * Note that "flasks of oil" do NOT do fire damage, although they + * certainly could be made to do so. XXX XXX + * + * Note that most brands and slays are x3, except Slay Animal (x2), + * Slay Evil (x2), and Kill dragon (x5). + */ +s16b tot_dam_aux(object_type *o_ptr, int tdam, monster_type *m_ptr, + s32b *special) +{ + int mult = 1; + + monster_race *r_ptr = race_inf(m_ptr); + + u32b f1, f2, f3, f4, f5, esp; + + + /* Extract the flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Some "weapons" and "ammo" do extra damage */ + switch (o_ptr->tval) + { + case TV_SHOT: + case TV_ARROW: + case TV_BOLT: + case TV_BOOMERANG: + case TV_HAFTED: + case TV_POLEARM: + case TV_SWORD: + case TV_AXE: + case TV_DIGGING: + { + /* Slay Animal */ + if ((f1 & (TR1_SLAY_ANIMAL)) && (r_ptr->flags3 & (RF3_ANIMAL))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_ANIMAL); + } + + if (mult < 2) mult = 2; + } + + /* Slay Evil */ + if ((f1 & (TR1_SLAY_EVIL)) && (r_ptr->flags3 & (RF3_EVIL))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_EVIL); + } + + if (mult < 2) mult = 2; + } + + /* Slay Undead */ + if ((f1 & (TR1_SLAY_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_UNDEAD); + } + + if (mult < 3) mult = 3; + } + + /* Slay Demon */ + if ((f1 & (TR1_SLAY_DEMON)) && (r_ptr->flags3 & (RF3_DEMON))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_DEMON); + } + + if (mult < 3) mult = 3; + } + + /* Slay Orc */ + if ((f1 & (TR1_SLAY_ORC)) && (r_ptr->flags3 & (RF3_ORC))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_ORC); + } + + if (mult < 3) mult = 3; + } + + /* Slay Troll */ + if ((f1 & (TR1_SLAY_TROLL)) && (r_ptr->flags3 & (RF3_TROLL))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_TROLL); + } + + if (mult < 3) mult = 3; + } + + /* Slay Giant */ + if ((f1 & (TR1_SLAY_GIANT)) && (r_ptr->flags3 & (RF3_GIANT))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_GIANT); + } + + if (mult < 3) mult = 3; + } + + /* Slay Dragon */ + if ((f1 & (TR1_SLAY_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_DRAGON); + } + + if (mult < 3) mult = 3; + } + + /* Execute Dragon */ + if ((f1 & (TR1_KILL_DRAGON)) && (r_ptr->flags3 & (RF3_DRAGON))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_DRAGON); + } + + if (mult < 5) mult = 5; + } + + /* Execute Undead */ + if ((f5 & (TR5_KILL_UNDEAD)) && (r_ptr->flags3 & (RF3_UNDEAD))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_UNDEAD); + } + + if (mult < 5) mult = 5; + } + + /* Execute Demon */ + if ((f5 & (TR5_KILL_DEMON)) && (r_ptr->flags3 & (RF3_DEMON))) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_DEMON); + } + + if (mult < 5) mult = 5; + } + + + /* Brand (Acid) */ + if (f1 & (TR1_BRAND_ACID)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_ACID)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_ACID); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags9 & (RF9_SUSCEP_ACID)) + { + if (m_ptr->ml) + { + r_ptr->r_flags9 |= (RF9_SUSCEP_ACID); + } + if (mult < 6) mult = 6; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + } + } + + /* Brand (Elec) */ + if (f1 & (TR1_BRAND_ELEC)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_ELEC)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_ELEC); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags9 & (RF9_SUSCEP_ELEC)) + { + if (m_ptr->ml) + { + r_ptr->r_flags9 |= (RF9_SUSCEP_ELEC); + } + if (mult < 6) mult = 6; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + } + } + + /* Brand (Fire) */ + if (f1 & (TR1_BRAND_FIRE)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_FIRE)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_FIRE); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags3 & (RF3_SUSCEP_FIRE)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_SUSCEP_FIRE); + } + if (mult < 6) mult = 6; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + } + } + + /* Brand (Cold) */ + if (f1 & (TR1_BRAND_COLD)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_COLD)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_COLD); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags3 & (RF3_SUSCEP_COLD)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_SUSCEP_COLD); + } + if (mult < 6) mult = 6; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + } + } + + /* Brand (Poison) */ + if (f1 & (TR1_BRAND_POIS) || (p_ptr->tim_poison)) + { + /* Notice immunity */ + if (r_ptr->flags3 & (RF3_IM_POIS)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_POIS); + } + } + + /* Notice susceptibility */ + else if (r_ptr->flags9 & (RF9_SUSCEP_POIS)) + { + if (m_ptr->ml) + { + r_ptr->r_flags9 |= (RF9_SUSCEP_POIS); + } + if (mult < 6) mult = 6; + if (magik(95)) *special |= SPEC_POIS; + } + + /* Otherwise, take the damage */ + else + { + if (mult < 3) mult = 3; + if (magik(50)) *special |= SPEC_POIS; + } + } + + /* Wounding */ + if (f5 & (TR5_WOUNDING)) + { + /* Notice immunity */ + if (r_ptr->flags8 & (RF8_NO_CUT)) + { + if (m_ptr->ml) + { + r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT); + } + } + + /* Otherwise, take the damage */ + else + { + if (magik(50)) *special |= SPEC_CUT; + } + } + break; + } + } + + + /* Return the total damage */ + return (tdam * mult); +} + + +/* + * Search for hidden things + */ +void search(void) +{ + int y, x, chance; + + s16b this_o_idx, next_o_idx = 0; + + cave_type *c_ptr; + + + /* Start with base search ability */ + chance = p_ptr->skill_srh; + + /* Penalize various conditions */ + if (p_ptr->blind || no_lite()) chance = chance / 10; + if (p_ptr->confused || p_ptr->image) chance = chance / 10; + + /* Search the nearby grids, which are always in bounds */ + for (y = (p_ptr->py - 1); y <= (p_ptr->py + 1); y++) + { + for (x = (p_ptr->px - 1); x <= (p_ptr->px + 1); x++) + { + /* Sometimes, notice things */ + if (rand_int(100) < chance) + { + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Invisible trap */ + if ((c_ptr->t_idx != 0) && !(c_ptr->info & CAVE_TRDT)) + { + /* Pick a trap */ + pick_trap(y, x); + + /* Message */ + msg_print("You have found a trap."); + + /* Disturb */ + disturb(0); + } + + /* Secret door */ + if (c_ptr->feat == FEAT_SECRET) + { + /* Message */ + msg_print("You have found a secret door."); + + /* Pick a door XXX XXX XXX */ + cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00); + cave[y][x].mimic = 0; + lite_spot(y, x); + + /* Disturb */ + disturb(0); + } + + /* Scan all objects in the grid */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; + this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Skip non-chests */ + if (o_ptr->tval != TV_CHEST) continue; + + /* Skip non-trapped chests */ + if (!o_ptr->pval) continue; + + /* Identify once */ + if (!object_known_p(o_ptr)) + { + /* Message */ + msg_print("You have discovered a trap on the chest!"); + + /* Know the trap */ + object_known(o_ptr); + + /* Notice it */ + disturb(0); + } + } + } + } + } +} + + + + +/* + * Player "wants" to pick up an object or gold. + * Note that we ONLY handle things that can be picked up. + * See "move_player()" for handling of other things. + */ +void carry(int pickup) +{ + if (!p_ptr->disembodied) + { + py_pickup_floor(pickup); + } +} + + +/* + * Handle player hitting a real trap + */ +static void hit_trap(void) +{ + bool_ ident = FALSE; + + cave_type *c_ptr; + + + /* Disturb the player */ + disturb(0); + + /* Get the cave grid */ + c_ptr = &cave[p_ptr->py][p_ptr->px]; + if (c_ptr->t_idx != 0) + { + ident = player_activate_trap_type(p_ptr->py, p_ptr->px, NULL, -1); + if (ident) + { + t_info[c_ptr->t_idx].ident = TRUE; + msg_format("You identified the trap as %s.", + t_name + t_info[c_ptr->t_idx].name); + } + } +} + + +void touch_zap_player(monster_type *m_ptr) +{ + int aura_damage = 0; + + monster_race *r_ptr = race_inf(m_ptr); + + + if (r_ptr->flags2 & (RF2_AURA_FIRE)) + { + if (!(p_ptr->immune_fire)) + { + char aura_dam[80]; + + aura_damage = + damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17)); + + /* Hack -- Get the "died from" name */ + monster_desc(aura_dam, m_ptr, 0x88); + + msg_print("You are suddenly very hot!"); + + if (p_ptr->oppose_fire) aura_damage = (aura_damage + 2) / 3; + if (p_ptr->resist_fire) aura_damage = (aura_damage + 2) / 3; + if (p_ptr->sensible_fire) aura_damage = (aura_damage + 2) * 2; + + take_hit(aura_damage, aura_dam); + r_ptr->r_flags2 |= RF2_AURA_FIRE; + handle_stuff(); + } + } + + + if (r_ptr->flags2 & (RF2_AURA_ELEC)) + { + if (!(p_ptr->immune_elec)) + { + char aura_dam[80]; + + aura_damage = + damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17)); + + /* Hack -- Get the "died from" name */ + monster_desc(aura_dam, m_ptr, 0x88); + + if (p_ptr->oppose_elec) aura_damage = (aura_damage + 2) / 3; + if (p_ptr->resist_elec) aura_damage = (aura_damage + 2) / 3; + + msg_print("You get zapped!"); + take_hit(aura_damage, aura_dam); + r_ptr->r_flags2 |= RF2_AURA_ELEC; + handle_stuff(); + } + } +} + + +/* + * Carried monster can attack too. + * Based on monst_attack_monst. + */ +static void carried_monster_attack(s16b m_idx, bool_ *fear, bool_ *mdeath, + int x, int y) +{ + monster_type *t_ptr = &m_list[m_idx]; + + monster_race *r_ptr; + + monster_race *tr_ptr = race_inf(t_ptr); + + int ap_cnt; + + int ac, rlev, pt; + + char t_name[80]; + + cptr sym_name = symbiote_name(TRUE); + + char temp[80]; + + bool_ blinked = FALSE, touched = FALSE; + + byte y_saver = t_ptr->fy; + + byte x_saver = t_ptr->fx; + + object_type *o_ptr; + + + /* Get the carried monster */ + o_ptr = &p_ptr->inventory[INVEN_CARRY]; + if (!o_ptr->k_idx) return; + + r_ptr = &r_info[o_ptr->pval]; + + /* Not allowed to attack */ + if (r_ptr->flags1 & RF1_NEVER_BLOW) return; + + /* Total armor */ + ac = t_ptr->ac; + + /* Extract the effective monster level */ + rlev = ((o_ptr->elevel >= 1) ? o_ptr->elevel : 1); + + /* Get the monster name (or "it") */ + monster_desc(t_name, t_ptr, 0); + + /* Assume no blink */ + blinked = FALSE; + + if (!t_ptr->ml) + { + msg_print("You hear noise."); + } + + /* Scan through all four blows */ + for (ap_cnt = 0; ap_cnt < 4; ap_cnt++) + { + bool_ visible = FALSE; + bool_ obvious = FALSE; + + int power = 0; + int damage = 0; + + cptr act = NULL; + + /* Extract the attack infomation */ + int effect = r_ptr->blow[ap_cnt].effect; + int method = r_ptr->blow[ap_cnt].method; + int d_dice = r_ptr->blow[ap_cnt].d_dice; + int d_side = r_ptr->blow[ap_cnt].d_side; + + /* Stop attacking if the target dies! */ + if (t_ptr->fx != x_saver || t_ptr->fy != y_saver) + break; + + /* Hack -- no more attacks */ + if (!method) break; + + if (blinked) /* Stop! */ + { + /* break; */ + } + + /* Extract visibility (before blink) */ + visible = TRUE; + + /* Extract the attack "power" */ + power = get_attack_power(effect); + + /* Monster hits */ + if (!effect || check_hit2(power, rlev, ac)) + { + /* Always disturbing */ + disturb(1); + + /* Describe the attack method */ + switch (method) + { + case RBM_HIT: + { + act = "hits %s."; + touched = TRUE; + break; + } + + case RBM_TOUCH: + { + act = "touches %s."; + touched = TRUE; + break; + } + + case RBM_PUNCH: + { + act = "punches %s."; + touched = TRUE; + break; + } + + case RBM_KICK: + { + act = "kicks %s."; + touched = TRUE; + break; + } + + case RBM_CLAW: + { + act = "claws %s."; + touched = TRUE; + break; + } + + case RBM_BITE: + { + act = "bites %s."; + touched = TRUE; + break; + } + + case RBM_STING: + { + act = "stings %s."; + touched = TRUE; + break; + } + + case RBM_XXX1: + { + act = "XXX1's %s."; + break; + } + + case RBM_BUTT: + { + act = "butts %s."; + touched = TRUE; + break; + } + + case RBM_CRUSH: + { + act = "crushes %s."; + touched = TRUE; + break; + } + + case RBM_ENGULF: + { + act = "engulfs %s."; + touched = TRUE; + break; + } + + case RBM_CHARGE: + { + act = "charges %s."; + touched = TRUE; + break; + } + + case RBM_CRAWL: + { + act = "crawls on %s."; + touched = TRUE; + break; + } + + case RBM_DROOL: + { + act = "drools on %s."; + touched = FALSE; + break; + } + + case RBM_SPIT: + { + act = "spits on %s."; + touched = FALSE; + break; + } + + case RBM_GAZE: + { + act = "gazes at %s."; + touched = FALSE; + break; + } + + case RBM_WAIL: + { + act = "wails at %s."; + touched = FALSE; + break; + } + + case RBM_SPORE: + { + act = "releases spores at %s."; + touched = FALSE; + break; + } + + case RBM_XXX4: + { + act = "projects XXX4's at %s."; + touched = FALSE; + break; + } + + case RBM_BEG: + { + act = "begs %s for money."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_INSULT: + { + act = "insults %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_MOAN: + { + act = "moans at %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_SHOW: + { + act = "sings to %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + } + + /* Message */ + if (act) + { + strfmt(temp, act, t_name); + if (t_ptr->ml) + msg_format("%s %s", sym_name, temp); + + } + + /* Hack -- assume all attacks are obvious */ + obvious = TRUE; + + /* Roll out the damage */ + damage = damroll(d_dice, d_side); + + pt = GF_MISSILE; + + /* Apply appropriate damage */ + switch (effect) + { + case 0: + { + damage = 0; + pt = 0; + break; + } + + case RBE_HURT: + case RBE_SANITY: + { + damage -= (damage * ((ac < 150) ? ac : 150) / 250); + break; + } + + case RBE_POISON: + case RBE_DISEASE: + { + pt = GF_POIS; + break; + } + + case RBE_UN_BONUS: + case RBE_UN_POWER: + case RBE_ABOMINATION: + { + pt = GF_DISENCHANT; + break; + } + + case RBE_EAT_FOOD: + case RBE_EAT_LITE: + { + pt = damage = 0; + break; + } + + case RBE_EAT_ITEM: + case RBE_EAT_GOLD: + { + pt = damage = 0; + if (randint(2) == 1) blinked = TRUE; + break; + } + + case RBE_ACID: + { + pt = GF_ACID; + break; + } + + case RBE_ELEC: + { + pt = GF_ELEC; + break; + } + + case RBE_FIRE: + { + pt = GF_FIRE; + break; + } + + case RBE_COLD: + { + pt = GF_COLD; + break; + } + + case RBE_BLIND: + { + break; + } + + case RBE_CONFUSE: + case RBE_HALLU: + { + pt = GF_CONFUSION; + break; + } + + case RBE_TERRIFY: + { + pt = GF_TURN_ALL; + break; + } + + case RBE_PARALYZE: + { + pt = GF_OLD_SLEEP; /* sort of close... */ + break; + } + + case RBE_LOSE_STR: + case RBE_LOSE_INT: + case RBE_LOSE_WIS: + case RBE_LOSE_DEX: + case RBE_LOSE_CON: + case RBE_LOSE_CHR: + case RBE_LOSE_ALL: + case RBE_PARASITE: + { + break; + } + + case RBE_SHATTER: + { + if (damage > 23) + { + /* Prevent destruction of quest levels and town */ + if (!is_quest(dun_level) && dun_level) + earthquake(p_ptr->py, p_ptr->px, 8); + } + break; + } + + case RBE_EXP_10: + case RBE_EXP_20: + case RBE_EXP_40: + case RBE_EXP_80: + { + pt = GF_NETHER; + break; + } + + case RBE_TIME: + { + pt = GF_TIME; + break; + } + + default: + { + pt = 0; + break; + } + } + + if (pt) + { + /* Do damage if not exploding */ + project(0, 0, t_ptr->fy, t_ptr->fx, + (pt == GF_OLD_SLEEP ? r_ptr->level : damage), pt, + PROJECT_KILL | PROJECT_STOP); + + if (touched) + { + /* Aura fire */ + if ((tr_ptr->flags2 & RF2_AURA_FIRE) && + !(r_ptr->flags3 & RF3_IM_FIRE)) + { + if (t_ptr->ml) + { + blinked = FALSE; + msg_format("You are suddenly very hot!"); + if (t_ptr->ml) + tr_ptr->r_flags2 |= RF2_AURA_FIRE; + } + project(m_idx, 0, p_ptr->py, p_ptr->px, + damroll(1 + ((t_ptr->level) / 26), + 1 + ((t_ptr->level) / 17)), + GF_FIRE, PROJECT_KILL | PROJECT_STOP); + } + + /* Aura elec */ + if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) && + !(r_ptr->flags3 & (RF3_IM_ELEC))) + { + if (t_ptr->ml) + { + blinked = FALSE; + msg_format("You get zapped!"); + if (t_ptr->ml) + tr_ptr->r_flags2 |= RF2_AURA_ELEC; + } + project(m_idx, 0, p_ptr->py, p_ptr->px, + damroll(1 + ((t_ptr->level) / 26), + 1 + ((t_ptr->level) / 17)), + GF_ELEC, PROJECT_KILL | PROJECT_STOP); + } + } + } + } + + /* Monster missed player */ + else + { + /* Analyze failed attacks */ + switch (method) + { + case RBM_HIT: + case RBM_TOUCH: + case RBM_PUNCH: + case RBM_KICK: + case RBM_CLAW: + case RBM_BITE: + case RBM_STING: + case RBM_XXX1: + case RBM_BUTT: + case RBM_CRUSH: + case RBM_ENGULF: + case RBM_CHARGE: + { + /* Disturb */ + disturb(1); + + /* Message */ + msg_format("%s misses %s.", sym_name, t_name); + break; + } + } + } + + + /* Analyze "visible" monsters only */ + if (visible) + { + /* Count "obvious" attacks (and ones that cause damage) */ + if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10)) + { + /* Count attacks of this type */ + if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) + { + r_ptr->r_blows[ap_cnt]++; + } + } + } + } + + /* Blink away */ + if (blinked) + { + msg_format("You and %s flee laughing!", symbiote_name(FALSE)); + + teleport_player(MAX_SIGHT * 2 + 5); + } +} + +/* + * Carried monster can attack too. + * Based on monst_attack_monst. + */ +static void incarnate_monster_attack(s16b m_idx, bool_ *fear, bool_ *mdeath, + int x, int y) +{ + monster_type *t_ptr = &m_list[m_idx]; + + monster_race *r_ptr; + + monster_race *tr_ptr = race_inf(t_ptr); + + int ap_cnt; + + int ac, rlev, pt; + + char t_name[80]; + + char temp[80]; + + bool_ blinked = FALSE, touched = FALSE; + + byte y_saver = t_ptr->fy; + + byte x_saver = t_ptr->fx; + + + if (!p_ptr->body_monster) return; + + r_ptr = race_info_idx(p_ptr->body_monster, 0); + + /* Not allowed to attack */ + if (r_ptr->flags1 & RF1_NEVER_BLOW) return; + + /* Total armor */ + ac = t_ptr->ac; + + /* Extract the effective monster level */ + rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); + + /* Get the monster name (or "it") */ + monster_desc(t_name, t_ptr, 0); + + /* Assume no blink */ + blinked = FALSE; + + if (!t_ptr->ml) + { + msg_print("You hear noise."); + } + + /* Scan through all four blows */ + for (ap_cnt = 0; ap_cnt < (p_ptr->num_blow > 4) ? 4 : p_ptr->num_blow; + ap_cnt++) + { + bool_ visible = FALSE; + bool_ obvious = FALSE; + + int power = 0; + int damage = 0; + + cptr act = NULL; + + /* Extract the attack infomation */ + int effect = r_ptr->blow[ap_cnt].effect; + int method = r_ptr->blow[ap_cnt].method; + int d_dice = r_ptr->blow[ap_cnt].d_dice; + int d_side = r_ptr->blow[ap_cnt].d_side; + + /* Stop attacking if the target dies! */ + if (t_ptr->fx != x_saver || t_ptr->fy != y_saver) + break; + + /* Hack -- no more attacks */ + if (!method) break; + + if (blinked) /* Stop! */ + { + /* break; */ + } + + /* Extract visibility (before blink) */ + visible = TRUE; + + /* Extract the attack "power" */ + power = get_attack_power(effect); + + /* Monster hits */ + if (!effect || check_hit2(power, rlev, ac)) + { + /* Always disturbing */ + disturb(1); + + /* Describe the attack method */ + switch (method) + { + case RBM_HIT: + { + act = "hit %s."; + touched = TRUE; + break; + } + + case RBM_TOUCH: + { + act = "touch %s."; + touched = TRUE; + break; + } + + case RBM_PUNCH: + { + act = "punch %s."; + touched = TRUE; + break; + } + + case RBM_KICK: + { + act = "kick %s."; + touched = TRUE; + break; + } + + case RBM_CLAW: + { + act = "claw %s."; + touched = TRUE; + break; + } + + case RBM_BITE: + { + act = "bite %s."; + touched = TRUE; + break; + } + + case RBM_STING: + { + act = "sting %s."; + touched = TRUE; + break; + } + + case RBM_XXX1: + { + act = "XXX1's %s."; + break; + } + + case RBM_BUTT: + { + act = "butt %s."; + touched = TRUE; + break; + } + + case RBM_CRUSH: + { + act = "crush %s."; + touched = TRUE; + break; + } + + case RBM_ENGULF: + { + act = "engulf %s."; + touched = TRUE; + break; + } + + case RBM_CHARGE: + { + act = "charge %s."; + touched = TRUE; + break; + } + + case RBM_CRAWL: + { + act = "crawl on %s."; + touched = TRUE; + break; + } + + case RBM_DROOL: + { + act = "drool on %s."; + touched = FALSE; + break; + } + + case RBM_SPIT: + { + act = "spit on %s."; + touched = FALSE; + break; + } + + case RBM_GAZE: + { + act = "gaze at %s."; + touched = FALSE; + break; + } + + case RBM_WAIL: + { + act = "wail at %s."; + touched = FALSE; + break; + } + + case RBM_SPORE: + { + act = "release spores at %s."; + touched = FALSE; + break; + } + + case RBM_XXX4: + { + act = "project XXX4's at %s."; + touched = FALSE; + break; + } + + case RBM_BEG: + { + act = "beg %s for money."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_INSULT: + { + act = "insult %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_MOAN: + { + act = "moan at %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + + case RBM_SHOW: + { + act = "sing to %s."; + touched = FALSE; + t_ptr->csleep = 0; + break; + } + } + + /* Message */ + if (act) + { + strfmt(temp, act, t_name); + if (t_ptr->ml) + msg_format("You %s", temp); + + } + + /* Hack -- assume all attacks are obvious */ + obvious = TRUE; + + /* Roll out the damage */ + damage = damroll(d_dice, d_side) + p_ptr->to_d; + + pt = GF_MISSILE; + + /* Apply appropriate damage */ + switch (effect) + { + case 0: + { + damage = 0; + pt = 0; + break; + } + + case RBE_HURT: + case RBE_SANITY: + { + damage -= (damage * ((ac < 150) ? ac : 150) / 250); + break; + } + + case RBE_POISON: + case RBE_DISEASE: + { + pt = GF_POIS; + break; + } + + case RBE_UN_BONUS: + case RBE_UN_POWER: + { + pt = GF_DISENCHANT; + break; + } + + case RBE_EAT_FOOD: + case RBE_EAT_LITE: + { + pt = damage = 0; + break; + } + + case RBE_EAT_ITEM: + case RBE_EAT_GOLD: + { + pt = damage = 0; + if (randint(2) == 1) blinked = TRUE; + break; + } + + case RBE_ACID: + { + pt = GF_ACID; + break; + } + + case RBE_ELEC: + { + pt = GF_ELEC; + break; + } + + case RBE_FIRE: + { + pt = GF_FIRE; + break; + } + + case RBE_COLD: + { + pt = GF_COLD; + break; + } + + case RBE_BLIND: + { + break; + } + + case RBE_HALLU: + case RBE_CONFUSE: + { + pt = GF_CONFUSION; + break; + } + + case RBE_TERRIFY: + { + pt = GF_TURN_ALL; + break; + } + + case RBE_PARALYZE: + { + pt = GF_OLD_SLEEP; /* sort of close... */ + break; + } + + case RBE_LOSE_STR: + case RBE_LOSE_INT: + case RBE_LOSE_WIS: + case RBE_LOSE_DEX: + case RBE_LOSE_CON: + case RBE_LOSE_CHR: + case RBE_LOSE_ALL: + case RBE_PARASITE: + { + break; + } + + case RBE_SHATTER: + { + if (damage > 23) + { + /* Prevent destruction of quest levels and town */ + if (!is_quest(dun_level) && dun_level) + earthquake(p_ptr->py, p_ptr->px, 8); + } + break; + } + + case RBE_EXP_10: + case RBE_EXP_20: + case RBE_EXP_40: + case RBE_EXP_80: + { + pt = GF_NETHER; + break; + } + + case RBE_TIME: + { + pt = GF_TIME; + break; + } + + default: + { + pt = 0; + break; + } + } + + if (pt) + { + /* Do damage if not exploding */ + project(0, 0, t_ptr->fy, t_ptr->fx, + (pt == GF_OLD_SLEEP ? p_ptr->lev * 2 : damage), pt, + PROJECT_KILL | PROJECT_STOP); + + if (touched) + { + /* Aura fire */ + if ((tr_ptr->flags2 & RF2_AURA_FIRE) && + !(r_ptr->flags3 & RF3_IM_FIRE)) + { + if (t_ptr->ml) + { + blinked = FALSE; + msg_format("You are suddenly very hot!"); + if (t_ptr->ml) + tr_ptr->r_flags2 |= RF2_AURA_FIRE; + } + project(m_idx, 0, p_ptr->py, p_ptr->px, + damroll(1 + ((t_ptr->level) / 26), + 1 + ((t_ptr->level) / 17)), + GF_FIRE, PROJECT_KILL | PROJECT_STOP); + } + + /* Aura elec */ + if ((tr_ptr->flags2 & (RF2_AURA_ELEC)) && + !(r_ptr->flags3 & (RF3_IM_ELEC))) + { + if (t_ptr->ml) + { + blinked = FALSE; + msg_format("You get zapped!"); + if (t_ptr->ml) + tr_ptr->r_flags2 |= RF2_AURA_ELEC; + } + project(m_idx, 0, p_ptr->py, p_ptr->px, + damroll(1 + ((t_ptr->level) / 26), + 1 + ((t_ptr->level) / 17)), + GF_ELEC, PROJECT_KILL | PROJECT_STOP); + } + + } + } + } + + /* Monster missed player */ + else + { + /* Analyze failed attacks */ + switch (method) + { + case RBM_HIT: + case RBM_TOUCH: + case RBM_PUNCH: + case RBM_KICK: + case RBM_CLAW: + case RBM_BITE: + case RBM_STING: + case RBM_XXX1: + case RBM_BUTT: + case RBM_CRUSH: + case RBM_ENGULF: + case RBM_CHARGE: + { + /* Disturb */ + disturb(1); + + /* Message */ + msg_format("You miss %s.", t_name); + + break; + } + } + } + + + /* Analyze "visible" monsters only */ + if (visible) + { + /* Count "obvious" attacks (and ones that cause damage) */ + if (obvious || damage || (r_ptr->r_blows[ap_cnt] > 10)) + { + /* Count attacks of this type */ + if (r_ptr->r_blows[ap_cnt] < MAX_UCHAR) + { + r_ptr->r_blows[ap_cnt]++; + } + } + } + } + + /* Blink away */ + if (blinked) + { + msg_print("You flee laughing!"); + + teleport_player(MAX_SIGHT * 2 + 5); + } +} + + +/* + * Fetch an attack description from dam_*.txt files. + */ + +static void flavored_attack(int percent, char *output) +{ + int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane; + bool_ insane = (rand_int(100) < insanity); + + if (percent < 5) + { + if (!insane) + strcpy(output, "You scratch %s."); + else + get_rnd_line("dam_none.txt", output); + + } + else if (percent < 30) + { + if (!insane) + strcpy(output, "You hit %s."); + else + get_rnd_line("dam_med.txt", output); + } + else if (percent < 60) + { + if (!insane) + strcpy(output, "You wound %s."); + else + get_rnd_line("dam_lots.txt", output); + } + else if (percent < 95) + { + if (!insane) + strcpy(output, "You cripple %s."); + else + get_rnd_line("dam_huge.txt", output); + + } + else + { + if (!insane) + strcpy(output, "You demolish %s."); + else + get_rnd_line("dam_xxx.txt", output); + } +} + + +/* + * Apply the special effects of an attack + */ +void attack_special(monster_type *m_ptr, s32b special, int dam) +{ + char m_name[80]; + + monster_race *r_ptr = race_inf(m_ptr); + + + /* Extract monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + /* Special - Cut monster */ + if (special & SPEC_CUT) + { + /* Cut the monster */ + if (r_ptr->flags8 & (RF8_NO_CUT)) + { + if (m_ptr->ml) + { + r_info[m_ptr->r_idx].r_flags8 |= (RF8_NO_CUT); + } + } + else if (rand_int(100) >= r_ptr->level) + { + /* Already partially poisoned */ + if (m_ptr->bleeding) msg_format("%^s is bleeding more strongly.", + m_name); + /* Was not poisoned */ + else + msg_format("%^s is bleeding.", m_name); + + m_ptr->bleeding += dam * 2; + } + } + + /* Special - Poison monster */ + if (special & SPEC_POIS) + { + /* Poison the monster */ + if (r_ptr->flags3 & (RF3_IM_POIS)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_IM_POIS); + } + } + /* Notice susceptibility */ + else if (r_ptr->flags9 & (RF9_SUSCEP_POIS)) + { + if (m_ptr->ml) + { + r_ptr->r_flags9 |= (RF9_SUSCEP_POIS); + } + /* Already partially poisoned */ + if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name); + /* Was not poisoned */ + else + msg_format("%^s is poisoned.", m_name); + + m_ptr->poisoned += dam * 2; + } + else if (rand_int(100) >= r_ptr->level) + { + /* Already partially poisoned */ + if (m_ptr->poisoned) msg_format("%^s is more poisoned.", m_name); + /* Was not poisoned */ + else + msg_format("%^s is poisoned.", m_name); + + m_ptr->poisoned += dam; + } + } +} + + +/* + * Bare handed attacks + */ +static void py_attack_hand(int *k, monster_type *m_ptr, s32b *special) +{ + s16b special_effect = 0, stun_effect = 0, times = 0; + martial_arts *ma_ptr, *old_ptr, *blow_table = ma_blows; + int resist_stun = 0, max = MAX_MA; + monster_race *r_ptr = race_inf(m_ptr); + char m_name[80]; + bool_ desc = FALSE; + bool_ done_crit; + int plev = p_ptr->lev; + + if ((!p_ptr->body_monster) && (p_ptr->mimic_form == resolve_mimic_name("Bear")) && + (p_ptr->melee_style == SKILL_BEAR)) + { + blow_table = bear_blows; + max = MAX_BEAR; + plev = get_skill(SKILL_BEAR); + } + if (p_ptr->melee_style == SKILL_HAND) + { + blow_table = ma_blows; + max = MAX_MA; + plev = get_skill(SKILL_HAND); + } + ma_ptr = &blow_table[0]; + old_ptr = &blow_table[0]; + + /* Extract monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + if (r_ptr->flags1 & RF1_UNIQUE) resist_stun += 88; + if (r_ptr->flags3 & RF3_NO_CONF) resist_stun += 44; + if (r_ptr->flags3 & RF3_NO_SLEEP) resist_stun += 44; + if ((r_ptr->flags3 & RF3_UNDEAD) || + (r_ptr->flags3 & RF3_NONLIVING)) resist_stun += 88; + + if (plev) + { + for (times = 0; times < (plev < 7 ? 1 : plev / 7); times++) + { + do + { + ma_ptr = &blow_table[(randint(max)) - 1]; + } + while ((ma_ptr->min_level > plev) || (randint(plev) < ma_ptr->chance)); + + /* keep the highest level attack available we found */ + if ((ma_ptr->min_level > old_ptr->min_level) && + !(p_ptr->stun || p_ptr->confused)) + { + old_ptr = ma_ptr; + + if (wizard && cheat_xtra) + { + msg_print("Attack re-selected."); + } + } + else + { + ma_ptr = old_ptr; + } + } + } + + *k = damroll(ma_ptr->dd, ma_ptr->ds); + + if (ma_ptr->effect & MA_KNEE) + { + if (r_ptr->flags1 & RF1_MALE) + { + if (!desc) msg_format("You hit %s in the groin with your knee!", + m_name); + sound(SOUND_PAIN); + special_effect = MA_KNEE; + } + else if (!desc) msg_format(ma_ptr->desc, m_name); + + desc = TRUE; + } + if (ma_ptr->effect & MA_FULL_SLOW) + { + special_effect = MA_SLOW; + if (!desc) msg_format(ma_ptr->desc, m_name); + + desc = TRUE; + } + if (ma_ptr->effect & MA_SLOW) + { + if (! + ((r_ptr->flags1 & RF1_NEVER_MOVE) || + strchr("UjmeEv$,DdsbBFIJQSXclnw!=?", r_ptr->d_char))) + { + if (!desc) msg_format("You kick %s in the ankle.", m_name); + special_effect = MA_SLOW; + } + else if (!desc) msg_format(ma_ptr->desc, m_name); + + desc = TRUE; + } + if (ma_ptr->effect & MA_STUN) + { + if (ma_ptr->power) + { + stun_effect = (ma_ptr->power / 2) + randint(ma_ptr->power / 2); + } + + if (!desc) msg_format(ma_ptr->desc, m_name); + desc = TRUE; + } + if (ma_ptr->effect & MA_WOUND) + { + if (magik(ma_ptr->power)) + { + *special |= SPEC_CUT; + } + if (!desc) msg_format(ma_ptr->desc, m_name); + desc = TRUE; + } + + *k = critical_norm(plev * (randint(10)), ma_ptr->min_level, *k, -1, &done_crit); + + if ((special_effect & MA_KNEE) && ((*k + p_ptr->to_d) < m_ptr->hp)) + { + msg_format("%^s moans in agony!", m_name); + stun_effect = 7 + randint(13); + resist_stun /= 3; + } + if (((special_effect & MA_FULL_SLOW) || (special_effect & MA_SLOW)) && + ((*k + p_ptr->to_d) < m_ptr->hp)) + { + if (!(r_ptr->flags1 & RF1_UNIQUE) && + (randint(plev) > m_ptr->level) && m_ptr->mspeed > 60) + { + msg_format("%^s starts limping slower.", m_name); + m_ptr->mspeed -= 10; + } + } + + if (stun_effect && ((*k + p_ptr->to_d) < m_ptr->hp)) + { + if (plev > randint(m_ptr->level + resist_stun + 10)) + { + if (m_ptr->stunned) + msg_format("%^s is still stunned.", m_name); + else + msg_format("%^s is stunned.", m_name); + + m_ptr->stunned += (stun_effect); + } + } +} + + +/* + * Apply nazgul effects + */ +void do_nazgul(int *k, int *num, int num_blow, int weap, monster_race *r_ptr, + object_type *o_ptr) +{ + u32b f1, f2, f3, f4, f5, esp; + + bool_ mundane; + bool_ allow_shatter = TRUE; + + /* Extract mundane-ness of the current weapon */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* It should be Slay Evil, Slay Undead, or *Slay Undead* */ + mundane = !(f1 & TR1_SLAY_EVIL) && !(f1 & TR1_SLAY_UNDEAD) && + !(f5 & TR5_KILL_UNDEAD); + + /* Some blades can resist shattering */ + if (f5 & TR5_RES_MORGUL) + allow_shatter = FALSE; + + /* Mega Hack -- Hitting Nazgul is REALY dangerous (ideas from Akhronath) */ + if (r_ptr->flags7 & RF7_NAZGUL) + { + if ((!o_ptr->name2) && (!artifact_p(o_ptr)) && allow_shatter) + { + msg_print("Your weapon *DISINTEGRATES*!"); + *k = 0; + + inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE); + + /* To stop attacking */ + *num = num_blow; + } + else if (o_ptr->name2) + { + if (mundane) + { + msg_print + ("The Ringwraith is IMPERVIOUS to the mundane weapon."); + *k = 0; + } + + /* 25% chance of getting destroyed */ + if (magik(25) && allow_shatter) + { + msg_print("Your weapon is destroyed!"); + + inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE); + + /* To stop attacking */ + *num = num_blow; + } + } + else if (artifact_p(o_ptr)) + { + if (mundane) + { + msg_print + ("The Ringwraith is IMPERVIOUS to the mundane weapon."); + *k = 0; + } + + apply_disenchant(INVEN_WIELD + weap); + + /* 1/1000 chance of getting destroyed */ + if (!rand_int(1000) && allow_shatter) + { + msg_print("Your weapon is destroyed!"); + + inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE); + + /* To stop attacking */ + *num = num_blow; + } + } + + /* If any damage is done, then 25% chance of getting the Black Breath */ + if (*k) + { + if (magik(25)) + { + msg_print("Your foe calls upon your soul!"); + msg_print + ("You feel the Black Breath slowly draining you of life..."); + p_ptr->black_breath = TRUE; + } + } + } +} + + +/* + * Player attacks a (poor, defenseless) creature -RAK- + * + * If no "weapon" is available, then "punch" the monster one time. + */ +void py_attack(int y, int x, int max_blow) +{ + int num = 0, k, bonus, chance; + + s32b special = 0; + + cave_type *c_ptr = &cave[y][x]; + + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + + monster_race *r_ptr = race_inf(m_ptr); + + object_type *o_ptr; + + char m_name[80]; + + bool_ fear = FALSE; + + bool_ mdeath = FALSE; + + bool_ backstab = FALSE; + + bool_ vorpal_cut = FALSE; + + int chaos_effect = 0; + + bool_ stab_fleeing = FALSE; + + bool_ do_quake = FALSE; + + bool_ done_crit = FALSE; + + bool_ drain_msg = TRUE; + + int drain_result = 0, drain_heal = 0; + + int drain_left = MAX_VAMPIRIC_DRAIN; + + /* A massive hack -- life-draining weapons */ + u32b f1, f2, f3, f4, f5, esp; + + int weap; + + /* Disturb the player */ + disturb(0); + + if (r_info[p_ptr->body_monster].flags1 & RF1_NEVER_BLOW) + { + msg_print("You cannot attack in this form!"); + return; + } + + if (get_skill(SKILL_BACKSTAB)) + { + if ((m_ptr->csleep) && (m_ptr->ml)) + { + /* Can't backstab creatures that we can't see, right? */ + backstab = TRUE; + } + else if ((m_ptr->monfear) && (m_ptr->ml)) + { + stab_fleeing = TRUE; + } + } + + /* Disturb the monster */ + m_ptr->csleep = 0; + + + /* Extract monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + /* Dont even bother */ + if (r_ptr->flags7 & RF7_IM_MELEE) + { + msg_format("%^s is immune to melee attacks."); + return; + } + + /* Auto-Recall if possible and visible */ + if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); + + /* Track a new monster */ + if (m_ptr->ml) health_track(c_ptr->m_idx); + + /* Stop if friendly */ + if ((is_friend(m_ptr) >= 0) && + !(p_ptr->stun || p_ptr->confused || p_ptr->image || + !(m_ptr->ml))) + { + if (!(p_ptr->inventory[INVEN_WIELD].art_name)) + { + msg_format("You stop to avoid hitting %s.", m_name); + return; + } + + if (! + (streq + (quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'"))) + { + msg_format("You stop to avoid hitting %s.", m_name); + return; + } + + msg_format("Your black blade greedily attacks %s!", m_name); + } + + /* Break goi/manashield */ + if (p_ptr->invuln) + { + set_invuln(0); + } + if (p_ptr->disrupt_shield) + { + set_disrupt_shield(0); + } + + /* Handle player fear */ + if (p_ptr->afraid) + { + /* Message */ + if (m_ptr->ml) + msg_format("You are too afraid to attack %s!", m_name); + else + msg_format("There is something scary in your way!"); + + /* Done */ + return; + } + + /* Monsters can use barehanded combat, but not weapon combat */ + if ((p_ptr->body_monster) && + (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON]) && + !(p_ptr->melee_style == SKILL_HAND)) + { + incarnate_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x); + } + /* Otherwise use your weapon(s) */ + else + { + int weapons; + if (p_ptr->melee_style == SKILL_MASTERY) + weapons = r_info[p_ptr->body_monster].body_parts[BODY_WEAPON]; + else /* SKILL_HAND */ + weapons = 1; + + /* Attack with ALL the weapons !!!!! -- ooh that's gonna hurt YOU */ + for (weap = 0; weap < weapons; ++weap) + { + /* Monster is already dead ? oh :( */ + if (mdeath) break; + + /* Reset the blows counter */ + num = 0; + + /* Access the weapon */ + o_ptr = &p_ptr->inventory[INVEN_WIELD + weap]; + + /* Calculate the "attack quality" */ + bonus = p_ptr->to_h + p_ptr->to_h_melee + o_ptr->to_h; + chance = p_ptr->skill_thn + (bonus * BTH_PLUS_ADJ); + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if (!(f4 & TR4_NEVER_BLOW)) + { + int num_blow = p_ptr->num_blow; + + /* Restrict to max_blow(if max_blow >= 0) */ + if ((max_blow >= 0) && + (num_blow > max_blow)) num_blow = max_blow; + + /* Attack once for each legal blow */ + while (num++ < num_blow) + { + /* Test for hit */ + if (test_hit_norm(chance, m_ptr->ac, m_ptr->ml)) + { + /* Sound */ + sound(SOUND_HIT); + + /* Hack -- bare hands do one damage */ + k = 1; + + /* Select a chaotic effect (50% chance) */ + if ((f1 & TR1_CHAOTIC) && (rand_int(2) == 0)) + { + if (randint(5) < 3) + { + /* Vampiric (20%) */ + chaos_effect = 1; + } + else if (rand_int(250) == 0) + { + /* Quake (0.12%) */ + chaos_effect = 2; + } + else if (rand_int(10)) + { + /* Confusion (26.892%) */ + chaos_effect = 3; + } + else if (rand_int(2) == 0) + { + /* Teleport away (1.494%) */ + chaos_effect = 4; + } + else + { + /* Polymorph (1.494%) */ + chaos_effect = 5; + } + } + + /* Vampiric drain */ + if ((f1 & TR1_VAMPIRIC) || (chaos_effect == 1)) + { + if (! + ((r_ptr->flags3 & RF3_UNDEAD) || + (r_ptr->flags3 & RF3_NONLIVING))) + drain_result = m_ptr->hp; + else + drain_result = 0; + } + + if (f1 & TR1_VORPAL && (randint(6) == 1)) + vorpal_cut = TRUE; + else + vorpal_cut = FALSE; + + /* Should we attack with hands or not ? */ + if (p_ptr->melee_style != SKILL_MASTERY) + { + py_attack_hand(&k, m_ptr, &special); + } + /* Handle normal weapon */ + else if (o_ptr->k_idx) + { + k = damroll(o_ptr->dd, o_ptr->ds); + k = tot_dam_aux(o_ptr, k, m_ptr, &special); + + if (backstab) + { + k += (k * + get_skill_scale(SKILL_BACKSTAB, + 100)) / 100; + } + else if (stab_fleeing) + { + k += (k * get_skill_scale(SKILL_BACKSTAB, 70)) / + 100; + } + + if ((p_ptr->impact && ((k > 50) || randint(7) == 1)) + || (chaos_effect == 2)) + { + do_quake = TRUE; + } + + k = critical_norm(o_ptr->weight, o_ptr->to_h, k, o_ptr->tval, &done_crit); + + /* Stunning blow */ + if (magik(get_skill(SKILL_STUN)) && (o_ptr->tval == TV_HAFTED) && (o_ptr->weight > 50) && done_crit) + { + if (!(r_ptr->flags4 & (RF4_BR_SOUN)) && !(r_ptr->flags4 & (RF4_BR_WALL)) && k) + { + int tmp; + + /* Get stunned */ + if (m_ptr->stunned) + { + msg_format("%^s is more dazed.", m_name); + tmp = m_ptr->stunned + get_skill_scale(SKILL_STUN, 30) + 10; + } + else + { + msg_format("%^s is dazed.", m_name); + tmp = get_skill_scale(SKILL_STUN, 60) + 20; + } + + /* Apply stun */ + m_ptr->stunned = (tmp < 200) ? tmp : 200; + } + } + + if (vorpal_cut) + { + int step_k = k; + + msg_format("Your weapon cuts deep into %s!", + m_name); + do + { + k += step_k; + } + while (randint(4) == 1); + } + + PRAY_GOD(GOD_TULKAS) + { + if (magik(wisdom_scale(130) - m_ptr->level) && (p_ptr->grace > 1000)) + { + msg_print("You feel the hand of Tulkas helping your blow."); + k += (o_ptr->to_d + p_ptr->to_d_melee) * 2; + } + else k += o_ptr->to_d + p_ptr->to_d_melee; + } + else k += o_ptr->to_d; + + /* Project some more nasty stuff? */ + if (p_ptr->tim_project) + { + project(0, p_ptr->tim_project_rad, y, x, p_ptr->tim_project_dam, p_ptr->tim_project_gf, p_ptr->tim_project_flag | PROJECT_JUMP); + if (!c_ptr->m_idx) + { + mdeath = TRUE; + break; + } + } + + do_nazgul(&k, &num, num_blow, weap, r_ptr, o_ptr); + + } + + /* Melkor can cast curse for you*/ + PRAY_GOD(GOD_MELKOR) + { + int lv = get_level_s(MELKOR_CURSE, 100); + + if (lv >= 10) + { + int chance = (wisdom_scale(30) * lv) / ((m_ptr->level < 1) ? 1 : m_ptr->level); + + if (chance < 1) chance = 1; + if ((p_ptr->grace > 5000) && magik(chance)) + { + do_melkor_curse(c_ptr->m_idx); + } + } + } + + /* May it clone the monster ? */ + if ((f4 & TR4_CLONE) && magik(30)) + { + msg_format("Oh no! Your weapon clones %^s!", + m_name); + multiply_monster(c_ptr->m_idx, FALSE, TRUE); + } + + /* Apply the player damage bonuses */ + k += p_ptr->to_d + p_ptr->to_d_melee; + + /* No negative damage */ + if (k < 0) k = 0; + + /* Message */ + if (!(backstab || stab_fleeing)) + { + /* These monsters never have flavoured combat msgs */ + if (strchr("vwjmelX,.*", r_ptr->d_char)) + { + msg_format("You hit %s.", m_name); + } + + /* Print flavoured messages if requested */ + else + { + char buff[255]; + + flavored_attack((100 * k) / m_ptr->maxhp, buff); + msg_format(buff, m_name); + } + } + else if (backstab) + { + char buf[80]; + + monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego); + + backstab = FALSE; + + msg_format + ("You cruelly stab the helpless, sleeping %s!", + buf); + } + else + { + char buf[80]; + + monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego); + + msg_format("You backstab the fleeing %s!", buf); + } + + /* Complex message */ + if (wizard) + { + msg_format("You do %d (out of %d) damage.", k, + m_ptr->hp); + } + + if (special) attack_special(m_ptr, special, k); + + /* Damage, check for fear and death */ + if (mon_take_hit(c_ptr->m_idx, k, &fear, NULL)) + { + /* Hack -- High-level warriors can spread their attacks out + * among weaker foes. + */ + if ((has_ability(AB_SPREAD_BLOWS)) && (num < num_blow) && + (energy_use)) + { + energy_use = energy_use * num / num_blow; + } + mdeath = TRUE; + break; + } + + switch (is_friend(m_ptr)) + { + case 1: + msg_format("%^s gets angry!", m_name); + change_side(m_ptr); + break; + case 0: + msg_format("%^s gets angry!", m_name); + m_ptr->status = MSTATUS_NEUTRAL_M; + break; + } + + touch_zap_player(m_ptr); + + /* Are we draining it? A little note: If the monster is + dead, the drain does not work... */ + + if (drain_result) + { + drain_result -= m_ptr->hp; /* Calculate the difference */ + + if (drain_result > 0) /* Did we really hurt it? */ + { + drain_heal = damroll(4, (drain_result / 6)); + + if (cheat_xtra) + { + msg_format("Draining left: %d", drain_left); + } + + if (drain_left) + { + if (drain_heal < drain_left) + { + drain_left -= drain_heal; + } + else + { + drain_heal = drain_left; + drain_left = 0; + } + + if (drain_msg) + { + msg_format + ("Your weapon drains life from %s!", + m_name); + drain_msg = FALSE; + } + + hp_player(drain_heal); + /* We get to keep some of it! */ + } + } + } + + /* Confusion attack */ + if ((p_ptr->confusing) || (chaos_effect == 3)) + { + /* Cancel glowing hands */ + if (p_ptr->confusing) + { + p_ptr->confusing = FALSE; + msg_print("Your hands stop glowing."); + } + + /* Confuse the monster */ + if (r_ptr->flags3 & (RF3_NO_CONF)) + { + if (m_ptr->ml) + { + r_ptr->r_flags3 |= (RF3_NO_CONF); + } + + msg_format("%^s is unaffected.", m_name); + } + else if (rand_int(100) < m_ptr->level) + { + msg_format("%^s is unaffected.", m_name); + } + else + { + msg_format("%^s appears confused.", m_name); + m_ptr->confused += + 10 + rand_int(get_skill(SKILL_COMBAT)) / 5; + } + } + + else if (chaos_effect == 4) + { + msg_format("%^s disappears!", m_name); + teleport_away(c_ptr->m_idx, 50); + num = num_blow + 1; /* Can't hit it anymore! */ + } + + else if ((chaos_effect == 5) && cave_floor_bold(y, x) && + (randint(90) > m_ptr->level)) + { + if (!((r_ptr->flags1 & RF1_UNIQUE) || + (r_ptr->flags4 & RF4_BR_CHAO) || + (m_ptr->mflag & MFLAG_QUEST))) + { + /* Handle polymorph */ + if (do_poly_monster(y, x)) + { + /* Polymorph succeeded */ + msg_format("%^s changes!", m_name); + + /* Hack -- Get new monster */ + m_ptr = &m_list[c_ptr->m_idx]; + + /* Oops, we need a different name... */ + monster_desc(m_name, m_ptr, 0); + + /* Hack -- Get new race */ + r_ptr = race_inf(m_ptr); + + fear = FALSE; + } + else + { + msg_format("%^s resists.", m_name); + } + } + else + { + msg_format("%^s is unaffected.", m_name); + } + } + } + + /* Player misses */ + else + { + /* Sound */ + sound(SOUND_MISS); + + backstab = FALSE; /* Clumsy! */ + + /* Message */ + msg_format("You miss %s.", m_name); + } + } + } + else + { + msg_print("You can't attack with that weapon."); + } + } + } + + /* Carried monster can attack too */ + if ((!mdeath) && m_list[c_ptr->m_idx].hp) + carried_monster_attack(c_ptr->m_idx, &fear, &mdeath, y, x); + + /* Hack -- delay fear messages */ + if (fear && m_ptr->ml) + { + /* Sound */ + sound(SOUND_FLEE); + + /* Message */ + msg_format("%^s flees in terror!", m_name); + } + + /* Mega-Hack -- apply earthquake brand */ + if (do_quake) + { + /* Prevent destruction of quest levels and town */ + if (!is_quest(dun_level) && dun_level) + earthquake(p_ptr->py, p_ptr->px, 10); + } +} + + + +static bool_ pattern_tile(int y, int x) +{ + return ((cave[y][x].feat <= FEAT_PATTERN_XTRA2) && + (cave[y][x].feat >= FEAT_PATTERN_START)); +} + + +static bool_ pattern_seq(int c_y, int c_x, int n_y, int n_x) +{ + if (!(pattern_tile(c_y, c_x)) && !(pattern_tile(n_y, n_x))) + return TRUE; + + if (cave[n_y][n_x].feat == FEAT_PATTERN_START) + { + if ((!(pattern_tile(c_y, c_x))) && + !(p_ptr->confused || p_ptr->stun || p_ptr->image)) + { + if (get_check + ("If you start walking the Straight Road, you must walk the whole way. Ok? ")) + return TRUE; + else + return FALSE; + } + else + return TRUE; + } + else if ((cave[n_y][n_x].feat == FEAT_PATTERN_OLD) || + (cave[n_y][n_x].feat == FEAT_PATTERN_END) || + (cave[n_y][n_x].feat == FEAT_PATTERN_XTRA2)) + { + if (pattern_tile(c_y, c_x)) + { + return TRUE; + } + else + { + msg_print + ("You must start walking the Straight Road from the startpoint."); + return FALSE; + } + } + else if ((cave[n_y][n_x].feat == FEAT_PATTERN_XTRA1) || + (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA1)) + { + return TRUE; + } + else if (cave[c_y][c_x].feat == FEAT_PATTERN_START) + { + if (pattern_tile(n_y, n_x)) + return TRUE; + else + { + msg_print("You must walk the Straight Road in correct order."); + return FALSE; + } + } + else if ((cave[c_y][c_x].feat == FEAT_PATTERN_OLD) || + (cave[c_y][c_x].feat == FEAT_PATTERN_END) || + (cave[c_y][c_x].feat == FEAT_PATTERN_XTRA2)) + { + if (!pattern_tile(n_y, n_x)) + { + msg_print("You may not step off from the Straight Road."); + return FALSE; + } + else + { + return TRUE; + } + } + else + { + if (!pattern_tile(c_y, c_x)) + { + msg_print + ("You must start walking the Straight Road from the startpoint."); + return FALSE; + } + else + { + byte ok_move = FEAT_PATTERN_START; + switch (cave[c_y][c_x].feat) + { + case FEAT_PATTERN_1: + ok_move = FEAT_PATTERN_2; + break; + case FEAT_PATTERN_2: + ok_move = FEAT_PATTERN_3; + break; + case FEAT_PATTERN_3: + ok_move = FEAT_PATTERN_4; + break; + case FEAT_PATTERN_4: + ok_move = FEAT_PATTERN_1; + break; + default: + if (wizard) + msg_format("Funny Straight Road walking, %d.", + cave[c_y][c_x]); + return TRUE; /* Goof-up */ + } + + if ((cave[n_y][n_x].feat == ok_move) || + (cave[n_y][n_x].feat == cave[c_y][c_x].feat)) + return TRUE; + else + { + if (!pattern_tile(n_y, n_x)) + msg_print("You may not step off from the Straight Road."); + else + msg_print + ("You must walk the Straight Road in correct order."); + + return FALSE; + } + } + } +} + + + +bool_ player_can_enter(byte feature) +{ + bool_ pass_wall; + + bool_ only_wall = FALSE; + + + /* Player can not walk through "walls" unless in Shadow Form */ + if (p_ptr->wraith_form || (PRACE_FLAG(PR1_SEMI_WRAITH))) + pass_wall = TRUE; + else + pass_wall = FALSE; + + /* Wall mimicry force the player to stay in walls */ + if (p_ptr->mimic_extra & CLASS_WALL) + { + only_wall = TRUE; + } + + /* Don't let the player kill himself with one keystroke */ + if (p_ptr->wild_mode) + { + if (feature == FEAT_DEEP_WATER) + { + int wt = weight_limit() / 2; + + if ((calc_total_weight() >= wt) && !(p_ptr->ffall)) + return (FALSE); + } + else if (feature == FEAT_SHAL_LAVA || + feature == FEAT_DEEP_LAVA) + { + if (!(p_ptr->resist_fire || + p_ptr->immune_fire || + p_ptr->oppose_fire || + p_ptr->ffall)) + return (FALSE); + } + } + + if (feature == FEAT_TREES) + { + if (p_ptr->fly || + pass_wall || + (has_ability(AB_TREE_WALK)) || + (p_ptr->mimic_form == resolve_mimic_name("Ent")) || + ((p_ptr->grace >= 9000) && (p_ptr->praying) && (p_ptr->pgod == GOD_YAVANNA))) + return (TRUE); + } + + if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB)) + return (TRUE); + if ((p_ptr->fly) && + ((f_info[feature].flags1 & FF1_CAN_FLY) || + (f_info[feature].flags1 & FF1_CAN_LEVITATE))) + return (TRUE); + else if (only_wall && (f_info[feature].flags1 & FF1_FLOOR)) + return (FALSE); + else if ((p_ptr->ffall) && + (f_info[feature].flags1 & FF1_CAN_LEVITATE)) + return (TRUE); + else if ((pass_wall || only_wall) && + (f_info[feature].flags1 & FF1_CAN_PASS)) + return (TRUE); + else if (f_info[feature].flags1 & FF1_NO_WALK) + return (FALSE); + else if ((f_info[feature].flags1 & FF1_WEB) && + ((!(r_info[p_ptr->body_monster].flags7 & RF7_SPIDER)) && (p_ptr->mimic_form != resolve_mimic_name("Spider")))) + return (FALSE); + + return (TRUE); +} + +/* + * Move player in the given direction, with the given "pickup" flag. + * + * This routine should (probably) always induce energy expenditure. + * + * Note that moving will *always* take a turn, and will *always* hit + * any monster which might be in the destination grid. Previously, + * moving into walls was "free" and did NOT hit invisible monsters. + */ +void move_player_aux(int dir, int do_pickup, int run, bool_ disarm) +{ + int y, x, tmp; + + cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; + + monster_type *m_ptr; + + monster_race *r_ptr = &r_info[p_ptr->body_monster], *mr_ptr; + + char m_name[80]; + + bool_ stormbringer = FALSE; + + bool_ old_dtrap, new_dtrap; + + bool_ oktomove = TRUE; + + + /* Hack - random movement */ + if (p_ptr->disembodied) + tmp = dir; + else if ((r_ptr->flags1 & RF1_RAND_25) && (r_ptr->flags1 & RF1_RAND_50)) + { + if (randint(100) < 75) + tmp = randint(9); + else + tmp = dir; + } + else if (r_ptr->flags1 & RF1_RAND_50) + { + if (randint(100) < 50) + tmp = randint(9); + else + tmp = dir; + } + else if (r_ptr->flags1 & RF1_RAND_25) + { + if (randint(100) < 25) + tmp = randint(9); + else + tmp = dir; + } + else + { + tmp = dir; + } + + if ((c_ptr->feat == FEAT_ICE) && (!p_ptr->ffall && !p_ptr->fly)) + { + if (magik(70 - p_ptr->lev)) + { + tmp = randint(9); + msg_print("You slip on the icy floor."); + } + else + tmp = dir; + } + + /* Find the result of moving */ + y = p_ptr->py + ddy[tmp]; + x = p_ptr->px + ddx[tmp]; + + /* Examine the destination */ + c_ptr = &cave[y][x]; + + /* Change oldpx and oldpy to place the player well when going back to big mode */ + if (p_ptr->wild_mode) + { + if (ddy[tmp] > 0) p_ptr->oldpy = 1; + if (ddy[tmp] < 0) p_ptr->oldpy = MAX_HGT - 2; + if (ddy[tmp] == 0) p_ptr->oldpy = MAX_HGT / 2; + if (ddx[tmp] > 0) p_ptr->oldpx = 1; + if (ddx[tmp] < 0) p_ptr->oldpx = MAX_WID - 2; + if (ddx[tmp] == 0) p_ptr->oldpx = MAX_WID / 2; + } + + /* Exit the area */ + if (!dun_level && !p_ptr->wild_mode && !is_quest(dun_level) && + ((x == 0) || (x == cur_wid - 1) || (y == 0) || (y == cur_hgt - 1))) + { + /* Can the player enter the grid? */ + if (player_can_enter(c_ptr->mimic)) + { + /* Hack: move to new area */ + if ((y == 0) && (x == 0)) + { + p_ptr->wilderness_y--; + p_ptr->wilderness_x--; + p_ptr->oldpy = cur_hgt - 2; + p_ptr->oldpx = cur_wid - 2; + ambush_flag = FALSE; + } + + else if ((y == 0) && (x == MAX_WID - 1)) + { + p_ptr->wilderness_y--; + p_ptr->wilderness_x++; + p_ptr->oldpy = cur_hgt - 2; + p_ptr->oldpx = 1; + ambush_flag = FALSE; + } + + else if ((y == MAX_HGT - 1) && (x == 0)) + { + p_ptr->wilderness_y++; + p_ptr->wilderness_x--; + p_ptr->oldpy = 1; + p_ptr->oldpx = cur_wid - 2; + ambush_flag = FALSE; + } + + else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1)) + { + p_ptr->wilderness_y++; + p_ptr->wilderness_x++; + p_ptr->oldpy = 1; + p_ptr->oldpx = 1; + ambush_flag = FALSE; + } + + else if (y == 0) + { + p_ptr->wilderness_y--; + p_ptr->oldpy = cur_hgt - 2; + p_ptr->oldpx = x; + ambush_flag = FALSE; + } + + else if (y == cur_hgt - 1) + { + p_ptr->wilderness_y++; + p_ptr->oldpy = 1; + p_ptr->oldpx = x; + ambush_flag = FALSE; + } + + else if (x == 0) + { + p_ptr->wilderness_x--; + p_ptr->oldpx = cur_wid - 2; + p_ptr->oldpy = y; + ambush_flag = FALSE; + } + + else if (x == cur_wid - 1) + { + p_ptr->wilderness_x++; + p_ptr->oldpx = 1; + p_ptr->oldpy = y; + ambush_flag = FALSE; + } + + p_ptr->leaving = TRUE; + + return; + } + } + + /* Some hooks */ + if (process_hooks(HOOK_MOVE, "(d,d)", y, x)) return; + + { + hook_move_in in = { y, x }; + if (process_hooks_new(HOOK_MOVE, &in, NULL)) { + return; /* Prevent movement */ + } + } + + if (p_ptr->dripping_tread > 0) + { + geomancy_random_floor(y, x, FALSE); + p_ptr->dripping_tread -= 1; + if (p_ptr->dripping_tread == 0) + { + msg_print("You stop dripping raw elemental energies."); + } + } + + + /* Get the monster */ + m_ptr = &m_list[c_ptr->m_idx]; + mr_ptr = race_inf(m_ptr); + + if (p_ptr->inventory[INVEN_WIELD].art_name) + { + if (streq(quark_str(p_ptr->inventory[INVEN_WIELD].art_name), "'Stormbringer'")) + stormbringer = TRUE; + } + + /* Hack -- attack monsters */ + if (c_ptr->m_idx && (m_ptr->ml || player_can_enter(c_ptr->feat))) + { + + /* Attack -- only if we can see it OR it is not in a wall */ + if ((is_friend(m_ptr) > 0) && + !(p_ptr->confused || p_ptr->image || !(m_ptr->ml) || p_ptr->stun) && + (pattern_seq(p_ptr->py, p_ptr->px, y, x)) && + ((player_can_enter(cave[y][x].feat)))) + { + m_ptr->csleep = 0; + + /* Extract monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + /* Auto-Recall if possible and visible */ + if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); + + /* Track a new monster */ + if (m_ptr->ml) health_track(c_ptr->m_idx); + + /* displace? */ + if (stormbringer && (randint(1000) > 666)) + { + py_attack(y, x, -1); + } + else if (cave_floor_bold(p_ptr->py, p_ptr->px) || + (mr_ptr->flags2 & RF2_PASS_WALL)) + { + msg_format("You push past %s.", m_name); + m_ptr->fy = p_ptr->py; + m_ptr->fx = p_ptr->px; + cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx; + c_ptr->m_idx = 0; + update_mon(cave[p_ptr->py][p_ptr->px].m_idx, TRUE); + } + else + { + msg_format("%^s is in your way!", m_name); + energy_use = 0; + oktomove = FALSE; + } + + /* now continue on to 'movement' */ + } + else + { + py_attack(y, x, -1); + oktomove = FALSE; + } + } + + else if ((c_ptr->feat == FEAT_DARK_PIT) && !p_ptr->ffall) + { + msg_print("You can't cross the chasm."); + running = 0; + oktomove = FALSE; + } + + /* Disarm a visible trap */ + else if (easy_disarm && disarm && (c_ptr->info & (CAVE_TRDT))) + { + (void)do_cmd_disarm_aux(y, x, tmp, do_pickup); + return; + } + + /* Don't step on known traps. */ + else if (disarm && (c_ptr->info & (CAVE_TRDT)) && !(p_ptr->confused || p_ptr->stun || p_ptr->image)) + { + msg_print("You stop to avoid triggering the trap."); + energy_use = 0; + oktomove = FALSE; + } + + /* Player can't enter ? soo bad for him/her ... */ + else if (!player_can_enter(c_ptr->feat)) + { + oktomove = FALSE; + + /* Disturb the player */ + disturb(0); + + if (p_ptr->prob_travel) + { + if (passwall(tmp, TRUE)) return; + } + + /* Notice things in the dark */ + if (!(c_ptr->info & (CAVE_MARK)) && !(c_ptr->info & (CAVE_SEEN))) + { + /* Rubble */ + if (c_ptr->feat == FEAT_RUBBLE) + { + msg_print("You feel some rubble blocking your way."); + c_ptr->info |= (CAVE_MARK); + lite_spot(y, x); + } + + /* Closed door */ + else if (c_ptr->feat < FEAT_SECRET) + { + msg_print("You feel a closed door blocking your way."); + c_ptr->info |= (CAVE_MARK); + lite_spot(y, x); + } + + /* Wall (or secret door) */ + else + { + int feat; + + if (c_ptr->mimic) feat = c_ptr->mimic; + else + feat = f_info[c_ptr->feat].mimic; + + msg_format("You feel %s.", f_text + f_info[feat].block); + c_ptr->info |= (CAVE_MARK); + lite_spot(y, x); + } + } + + /* Notice things */ + else + { + /* Rubble */ + if (c_ptr->feat == FEAT_RUBBLE) + { + if (!easy_tunnel) + { + msg_print("There is rubble blocking your way."); + + if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) + energy_use = 0; + /* + * Well, it makes sense that you lose time bumping into + * a wall _if_ you are confused, stunned or blind; but + * typing mistakes should not cost you a turn... + */ + } + else + { + do_cmd_tunnel_aux(y, x, dir); + return; + } + } + /* Closed doors */ + else if ((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL)) + { + if (easy_open) + { + if (easy_open_door(y, x)) return; + } + else + { + msg_print("There is a closed door blocking your way."); + + if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) + energy_use = 0; + } + } + + /* Wall (or secret door) */ + else + { + if (!easy_tunnel) + { + int feat; + + if (c_ptr->mimic) feat = c_ptr->mimic; + else + feat = f_info[c_ptr->feat].mimic; + + msg_format("There is %s.", f_text + f_info[feat].block); + + if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) + energy_use = 0; + } + else + { + do_cmd_tunnel_aux(y, x, dir); + return; + } + } + } + + /* Sound */ + sound(SOUND_HITWALL); + } + + /* Normal movement */ + if (!pattern_seq(p_ptr->py, p_ptr->px, y, x)) + { + if (!(p_ptr->confused || p_ptr->stun || p_ptr->image)) + { + energy_use = 0; + } + + disturb(0); /* To avoid a loop with running */ + + oktomove = FALSE; + } + + + /* + * Check trap detection status -- retrieve them here + * because they are used by the movement code as well + */ + old_dtrap = ((cave[p_ptr->py][p_ptr->px].info & CAVE_DETECT) != 0); + new_dtrap = ((cave[y][x].info & CAVE_DETECT) != 0); + + /* Normal movement */ + if (oktomove && running && disturb_detect) + { + /* + * Disturb the player when about to leave the trap detected + * area + */ + if (old_dtrap && !new_dtrap) + { + /* Disturb player */ + disturb(0); + + /* but don't take a turn */ + energy_use = 0; + + /* Tell player why */ + cmsg_print(TERM_VIOLET, "You are about to leave a trap detected zone."); + /* Flush */ + /* msg_print(NULL); */ + + oktomove = FALSE; + } + } + + /* Normal movement */ + if (oktomove) + { + int oy, ox; + int feat; + + /* Rooted means no move */ + if (p_ptr->tim_roots) return; + + /* Save old location */ + oy = p_ptr->py; + ox = p_ptr->px; + + /* Move the player */ + p_ptr->py = y; + p_ptr->px = x; + + if (cave[p_ptr->py][p_ptr->px].mimic) feat = cave[p_ptr->py][p_ptr->px].mimic; + else + feat = cave[p_ptr->py][p_ptr->px].feat; + + /* Some hooks */ + if (process_hooks(HOOK_MOVED, "(d,d)", oy, ox)) return; + + /* Redraw new spot */ + lite_spot(p_ptr->py, p_ptr->px); + + /* Redraw old spot */ + lite_spot(oy, ox); + + /* Sound */ + /* sound(SOUND_WALK); */ + + /* Check for new panel (redraw map) */ + verify_panel(); + + /* Check detection status */ + if (old_dtrap && !new_dtrap) + { + cmsg_print(TERM_VIOLET, "You leave a trap detected zone."); + if (running) msg_print(NULL); + p_ptr->redraw |= (PR_DTRAP); + } + else if (!old_dtrap && new_dtrap) + { + cmsg_print(TERM_L_BLUE, "You enter a trap detected zone."); + if (running) msg_print(NULL); + p_ptr->redraw |= (PR_DTRAP); + } + + /* Update stuff */ + p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); + + /* Update the monsters */ + p_ptr->update |= (PU_DISTANCE); + + /* Window stuff */ + if (!run) p_ptr->window |= (PW_OVERHEAD); + + /* Some feature descs */ + if (f_info[cave[p_ptr->py][p_ptr->px].feat].text > 1) + { + /* Mega-hack for dungeon branches */ + if ((feat == FEAT_MORE) && c_ptr->special) + { + msg_format("There is %s", d_text + d_info[c_ptr->special].text); + } + else + { + msg_print(f_text + f_info[feat].text); + } + + /* Flush message while running */ + if (running) msg_print(NULL); + } + + /* Spontaneous Searching */ + if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos))) + { + search(); + } + + /* Continuous Searching */ + if (p_ptr->searching) + { + search(); + } + + /* Handle "objects" */ + carry(do_pickup); + + /* Handle "store doors" */ + if (c_ptr->feat == FEAT_SHOP) + { + /* Disturb */ + disturb(0); + + /* Hack -- Enter store */ + command_new = '_'; + } + + else if (cave[y][x].feat >= FEAT_ALTAR_HEAD && + cave[y][x].feat <= FEAT_ALTAR_TAIL) + { + cptr name = f_name + f_info[cave[y][x].feat].name; + cptr pref = (is_a_vowel(name[0])) ? "an" : "a"; + + msg_format("You see %s %s.", pref, name); + + /* Flush message while running */ + if (running) msg_print(NULL); + } + + /* Discover invisible traps */ + else if ((c_ptr->t_idx != 0) && + !(f_info[cave[y][x].feat].flags1 & FF1_DOOR)) + { + /* Disturb */ + disturb(0); + + if (!(c_ptr->info & (CAVE_TRDT))) + { + /* Message */ + msg_print("You found a trap!"); + + /* Pick a trap */ + pick_trap(p_ptr->py, p_ptr->px); + } + + /* Hit the trap */ + hit_trap(); + } + + /* Execute the inscription */ + else if (c_ptr->inscription) + { + /* Disturb */ + disturb(0); + + msg_format("There is an inscription here: %s", + inscription_info[c_ptr->inscription].text); + if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_WALK) + { + execute_inscription(c_ptr->inscription, p_ptr->py, p_ptr->px); + } + } + } + + /* Update wilderness knowledge */ + if (p_ptr->wild_mode) + { + if (wizard) msg_format("y:%d, x:%d", p_ptr->py, p_ptr->px); + + /* Update the known wilderness */ + reveal_wilderness_around_player(p_ptr->py, p_ptr->px, 0, WILDERNESS_SEE_RADIUS); + + /* Walking the wild isnt meaningfull */ + p_ptr->did_nothing = TRUE; + } +} + +void move_player(int dir, int do_pickup, bool_ disarm) +{ + move_player_aux(dir, do_pickup, 0, disarm); +} + + +/* + * Hack -- Grid-based version of see_obstacle + */ +static int see_obstacle_grid(cave_type *c_ptr) +{ + /* + * Hack -- Avoid hitting detected traps, because we cannot rely on + * the CAVE_MARK check below, and traps can be set to nearly + * everything the player can move on to XXX XXX XXX + */ + if (c_ptr->info & (CAVE_TRDT)) return (TRUE); + + + /* Hack -- Handle special cases XXX XXX */ + switch (c_ptr->feat) + { + /* Require levitation */ + case FEAT_DARK_PIT: + case FEAT_DEEP_WATER: + case FEAT_ICE: + { + if (p_ptr->ffall || p_ptr->fly) return (FALSE); + } + + /* Require immunity */ + case FEAT_DEEP_LAVA: + case FEAT_SHAL_LAVA: + { + if (p_ptr->invuln || p_ptr->immune_fire) return (FALSE); + } + } + + + /* "Safe" floor grids aren't obstacles */ + if (f_info[c_ptr->feat].flags1 & FF1_CAN_RUN) return (FALSE); + + /* Must be known to the player */ + if (!(c_ptr->info & (CAVE_MARK))) return (FALSE); + + /* Default */ + return (TRUE); +} + + +/* + * Hack -- Check for a "known wall" or "dangerous" feature (see below) + */ +static int see_obstacle(int dir, int y, int x) +{ + /* Get the new location */ + y += ddy[dir]; + x += ddx[dir]; + + /* Illegal grids are not known walls */ + if (!in_bounds2(y, x)) return (FALSE); + + /* Analyse the grid */ + return (see_obstacle_grid(&cave[y][x])); +} + + +/* + * Hack -- Check for an "unknown corner" (see below) + */ +static int see_nothing(int dir, int y, int x) +{ + /* Get the new location */ + y += ddy[dir]; + x += ddx[dir]; + + /* Illegal grids are unknown */ + if (!in_bounds2(y, x)) return (TRUE); + + /* Memorized grids are always known */ + if (cave[y][x].info & (CAVE_MARK)) return (FALSE); + + /* Non-floor grids are unknown */ + if (!cave_floor_bold(y, x)) return (TRUE); + + /* Viewable door/wall grids are known */ + if (player_can_see_bold(y, x)) return (FALSE); + + /* Default */ + return (TRUE); +} + + + + + +/* + * The running algorithm: -CJS- + * + * In the diagrams below, the player has just arrived in the + * grid marked as '@', and he has just come from a grid marked + * as 'o', and he is about to enter the grid marked as 'x'. + * + * Of course, if the "requested" move was impossible, then you + * will of course be blocked, and will stop. + * + * Overview: You keep moving until something interesting happens. + * If you are in an enclosed space, you follow corners. This is + * the usual corridor scheme. If you are in an open space, you go + * straight, but stop before entering enclosed space. This is + * analogous to reaching doorways. If you have enclosed space on + * one side only (that is, running along side a wall) stop if + * your wall opens out, or your open space closes in. Either case + * corresponds to a doorway. + * + * What happens depends on what you can really SEE. (i.e. if you + * have no light, then running along a dark corridor is JUST like + * running in a dark room.) The algorithm works equally well in + * corridors, rooms, mine tailings, earthquake rubble, etc, etc. + * + * These conditions are kept in static memory: + * find_openarea You are in the open on at least one + * side. + * find_breakleft You have a wall on the left, and will + * stop if it opens + * find_breakright You have a wall on the right, and will + * stop if it opens + * + * To initialize these conditions, we examine the grids adjacent + * to the grid marked 'x', two on each side (marked 'L' and 'R'). + * If either one of the two grids on a given side is seen to be + * closed, then that side is considered to be closed. If both + * sides are closed, then it is an enclosed (corridor) run. + * + * LL L + * @x LxR + * RR @R + * + * Looking at more than just the immediate squares is + * significant. Consider the following case. A run along the + * corridor will stop just before entering the center point, + * because a choice is clearly established. Running in any of + * three available directions will be defined as a corridor run. + * Note that a minor hack is inserted to make the angled corridor + * entry (with one side blocked near and the other side blocked + * further away from the runner) work correctly. The runner moves + * diagonally, but then saves the previous direction as being + * straight into the gap. Otherwise, the tail end of the other + * entry would be perceived as an alternative on the next move. + * + * #.# + * ##.## + * .@x.. + * ##.## + * #.# + * + * Likewise, a run along a wall, and then into a doorway (two + * runs) will work correctly. A single run rightwards from @ will + * stop at 1. Another run right and down will enter the corridor + * and make the corner, stopping at the 2. + * + * #@x 1 + * ########### ###### + * 2 # + * ############# + * # + * + * After any move, the function area_affect is called to + * determine the new surroundings, and the direction of + * subsequent moves. It examines the current player location + * (at which the runner has just arrived) and the previous + * direction (from which the runner is considered to have come). + * + * Moving one square in some direction places you adjacent to + * three or five new squares (for straight and diagonal moves + * respectively) to which you were not previously adjacent, + * marked as '!' in the diagrams below. + * + * ...! ... + * .o@! .o.! + * ...! ..@! + * !!! + * + * You STOP if any of the new squares are interesting in any way: + * for example, if they contain visible monsters or treasure. + * + * You STOP if any of the newly adjacent squares seem to be open, + * and you are also looking for a break on that side. (that is, + * find_openarea AND find_break). + * + * You STOP if any of the newly adjacent squares do NOT seem to be + * open and you are in an open area, and that side was previously + * entirely open. + * + * Corners: If you are not in the open (i.e. you are in a corridor) + * and there is only one way to go in the new squares, then turn in + * that direction. If there are more than two new ways to go, STOP. + * If there are two ways to go, and those ways are separated by a + * square which does not seem to be open, then STOP. + * + * Otherwise, we have a potential corner. There are two new open + * squares, which are also adjacent. One of the new squares is + * diagonally located, the other is straight on (as in the diagram). + * We consider two more squares further out (marked below as ?). + * + * We assign "option" to the straight-on grid, and "option2" to the + * diagonal grid, and "check_dir" to the grid marked 's'. + * + * .s + * @x? + * #? + * + * If they are both seen to be closed, then it is seen that no + * benefit is gained from moving straight. It is a known corner. + * To cut the corner, go diagonally, otherwise go straight, but + * pretend you stepped diagonally into that next location for a + * full view next time. Conversely, if one of the ? squares is + * not seen to be closed, then there is a potential choice. We check + * to see whether it is a potential corner or an intersection/room entrance. + * If the square two spaces straight ahead, and the space marked with 's' + * are both blank, then it is a potential corner and enter if find_examine + * is set, otherwise must stop because it is not a corner. + */ + + + + +/* + * Hack -- allow quick "cycling" through the legal directions + */ +static byte cycle[] = { 1, 2, 3, 6, 9, 8, 7, 4, 1, 2, 3, 6, 9, 8, 7, 4, 1 }; + +/* + * Hack -- map each direction into the "middle" of the "cycle[]" array + */ +static byte chome[] = { 0, 8, 9, 10, 7, 0, 11, 6, 5, 4 }; + +/* + * The direction we are running + */ +static byte find_current; + +/* + * The direction we came from + */ +static byte find_prevdir; + +/* + * We are looking for open area + */ +static bool_ find_openarea; + +/* + * We are looking for a break + */ +static bool_ find_breakright; +static bool_ find_breakleft; + + + +/* + * Initialize the running algorithm for a new direction. + * + * Diagonal Corridor -- allow diaginal entry into corridors. + * + * Blunt Corridor -- If there is a wall two spaces ahead and + * we seem to be in a corridor, then force a turn into the side + * corridor, must be moving straight into a corridor here. ??? + * + * Diagonal Corridor Blunt Corridor (?) + * # # # + * #x# @x# + * @p. p + */ +static void run_init(int dir) +{ + int row, col, deepleft, deepright; + + int i, shortleft, shortright; + + + /* Save the direction */ + find_current = dir; + + /* Assume running straight */ + find_prevdir = dir; + + /* Assume looking for open area */ + find_openarea = TRUE; + + /* Assume not looking for breaks */ + find_breakright = find_breakleft = FALSE; + + /* Assume no nearby walls */ + deepleft = deepright = FALSE; + shortright = shortleft = FALSE; + + /* Find the destination grid */ + row = p_ptr->py + ddy[dir]; + col = p_ptr->px + ddx[dir]; + + /* Extract cycle index */ + i = chome[dir]; + + /* Check for walls */ + if (see_obstacle(cycle[i + 1], p_ptr->py, p_ptr->px)) + { + find_breakleft = TRUE; + shortleft = TRUE; + } + else if (see_obstacle(cycle[i + 1], row, col)) + { + find_breakleft = TRUE; + deepleft = TRUE; + } + + /* Check for walls */ + if (see_obstacle(cycle[i - 1], p_ptr->py, p_ptr->px)) + { + find_breakright = TRUE; + shortright = TRUE; + } + else if (see_obstacle(cycle[i - 1], row, col)) + { + find_breakright = TRUE; + deepright = TRUE; + } + + /* Looking for a break */ + if (find_breakleft && find_breakright) + { + /* Not looking for open area */ + find_openarea = FALSE; + + /* Hack -- allow angled corridor entry */ + if (dir & 0x01) + { + if (deepleft && !deepright) + { + find_prevdir = cycle[i - 1]; + } + else if (deepright && !deepleft) + { + find_prevdir = cycle[i + 1]; + } + } + + /* Hack -- allow blunt corridor entry */ + else if (see_obstacle(cycle[i], row, col)) + { + if (shortleft && !shortright) + { + find_prevdir = cycle[i - 2]; + } + else if (shortright && !shortleft) + { + find_prevdir = cycle[i + 2]; + } + } + } +} + + +/* + * Update the current "run" path + * + * Return TRUE if the running should be stopped + */ +static bool_ run_test(void) +{ + int prev_dir, new_dir, check_dir = 0; + + int row, col; + + int i, max, inv; + + int option = 0, option2 = 0; + + cave_type *c_ptr; + + + /* Where we came from */ + prev_dir = find_prevdir; + + + /* Range of newly adjacent grids */ + max = (prev_dir & 0x01) + 1; + + + /* Look at every newly adjacent square. */ + for (i = -max; i <= max; i++) + { + s16b this_o_idx, next_o_idx = 0; + + + /* New direction */ + new_dir = cycle[chome[prev_dir] + i]; + + /* New location */ + row = p_ptr->py + ddy[new_dir]; + col = p_ptr->px + ddx[new_dir]; + + /* Access grid */ + c_ptr = &cave[row][col]; + + + /* Visible monsters abort running */ + if (c_ptr->m_idx) + { + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + + /* Visible monster */ + if (m_ptr->ml) return (TRUE); + } + + /* Visible objects abort running */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Visible object */ + if (o_ptr->marked) return (TRUE); + } + + + /* Assume unknown */ + inv = TRUE; + + /* Check memorized grids */ + if (c_ptr->info & (CAVE_MARK)) + { + bool_ notice = TRUE; + + /* + * Examine the terrain -- conditional disturbance + * If we had more flags, we could make these customisable too + */ + switch (c_ptr->feat) + { + case FEAT_DEEP_LAVA: + case FEAT_SHAL_LAVA: + { + /* Ignore */ + if (p_ptr->invuln || p_ptr->immune_fire) notice = FALSE; + + /* Done */ + break; + } + + case FEAT_DEEP_WATER: + case FEAT_ICE: + { + /* Ignore */ + if (p_ptr->ffall || p_ptr->fly) notice = FALSE; + + /* Done */ + break; + } + + /* Open doors */ + case FEAT_OPEN: + case FEAT_BROKEN: + { + /* Option -- ignore */ + if (find_ignore_doors) notice = FALSE; + + /* Done */ + break; + } + + /* + * Stairs - too many of them, should find better ways to + * handle them (not scripting!, because it can be called + * from within the running algo) XXX XXX XXX + */ + case FEAT_LESS: + case FEAT_MORE: + case FEAT_QUEST_ENTER: + case FEAT_QUEST_EXIT: + case FEAT_QUEST_DOWN: + case FEAT_QUEST_UP: + case FEAT_SHAFT_UP: + case FEAT_SHAFT_DOWN: + case FEAT_WAY_LESS: + case FEAT_WAY_MORE: + /* XXX */ + case FEAT_BETWEEN: + case FEAT_BETWEEN2: + { + /* Option -- ignore */ + if (find_ignore_stairs) notice = FALSE; + + /* Done */ + break; + } + } + + /* Check the "don't notice running" flag */ + if (f_info[c_ptr->feat].flags1 & FF1_DONT_NOTICE_RUNNING) + { + notice = FALSE; + } + + /* A detected trap is interesting */ + if (c_ptr->info & (CAVE_TRDT)) notice = TRUE; + + /* Interesting feature */ + if (notice) return (TRUE); + + /* The grid is "visible" */ + inv = FALSE; + } + + /* Mega-Hack -- Maze code removes CAVE_MARK XXX XXX XXX */ + if (c_ptr->info & (CAVE_TRDT)) return (TRUE); + + /* Analyze unknown grids and floors */ + if (inv || cave_floor_bold(row, col)) + { + /* Looking for open area */ + if (find_openarea) + { + /* Nothing */ + } + + /* The first new direction. */ + else if (!option) + { + option = new_dir; + } + + /* Three new directions. Stop running. */ + else if (option2) + { + return (TRUE); + } + + /* Two non-adjacent new directions. Stop running. */ + else if (option != cycle[chome[prev_dir] + i - 1]) + { + return (TRUE); + } + + /* Two new (adjacent) directions (case 1) */ + else if (new_dir & 0x01) + { + check_dir = cycle[chome[prev_dir] + i - 2]; + option2 = new_dir; + } + + /* Two new (adjacent) directions (case 2) */ + else + { + check_dir = cycle[chome[prev_dir] + i + 1]; + option2 = option; + option = new_dir; + } + } + + /* Obstacle, while looking for open area */ + else + { + if (find_openarea) + { + if (i < 0) + { + /* Break to the right */ + find_breakright = TRUE; + } + + else if (i > 0) + { + /* Break to the left */ + find_breakleft = TRUE; + } + } + } + } + + + /* Looking for open area */ + if (find_openarea) + { + /* Hack -- look again */ + for (i = -max; i < 0; i++) + { + new_dir = cycle[chome[prev_dir] + i]; + + row = p_ptr->py + ddy[new_dir]; + col = p_ptr->px + ddx[new_dir]; + + /* Access grid */ + c_ptr = &cave[row][col]; + + /* Unknown grids or non-obstacle */ + if (!see_obstacle_grid(c_ptr)) + { + /* Looking to break right */ + if (find_breakright) + { + return (TRUE); + } + } + + /* Obstacle */ + else + { + /* Looking to break left */ + if (find_breakleft) + { + return (TRUE); + } + } + } + + /* Hack -- look again */ + for (i = max; i > 0; i--) + { + new_dir = cycle[chome[prev_dir] + i]; + + row = p_ptr->py + ddy[new_dir]; + col = p_ptr->px + ddx[new_dir]; + + /* Access grid */ + c_ptr = &cave[row][col]; + + /* Unknown grid or non-obstacle */ + if (!see_obstacle_grid(c_ptr)) + { + /* Looking to break left */ + if (find_breakleft) + { + return (TRUE); + } + } + + /* Obstacle */ + else + { + /* Looking to break right */ + if (find_breakright) + { + return (TRUE); + } + } + } + } + + + /* Not looking for open area */ + else + { + /* No options */ + if (!option) + { + return (TRUE); + } + + /* One option */ + else if (!option2) + { + /* Primary option */ + find_current = option; + + /* No other options */ + find_prevdir = option; + } + + /* Two options, examining corners */ + else if (find_examine && !find_cut) + { + /* Primary option */ + find_current = option; + + /* Hack -- allow curving */ + find_prevdir = option2; + } + + /* Two options, pick one */ + else + { + /* Get next location */ + row = p_ptr->py + ddy[option]; + col = p_ptr->px + ddx[option]; + + /* Don't see that it is closed off. */ + /* This could be a potential corner or an intersection. */ + if (!see_obstacle(option, row, col) || !see_obstacle(check_dir, row, col)) + { + /* Can not see anything ahead and in the direction we */ + /* are turning, assume that it is a potential corner. */ + if (find_examine && + see_nothing(option, row, col) && + see_nothing(option2, row, col)) + { + find_current = option; + find_prevdir = option2; + } + + /* STOP: we are next to an intersection or a room */ + else + { + return (TRUE); + } + } + + /* This corner is seen to be enclosed; we cut the corner. */ + else if (find_cut) + { + find_current = option2; + find_prevdir = option2; + } + + /* This corner is seen to be enclosed, and we */ + /* deliberately go the long way. */ + else + { + find_current = option; + find_prevdir = option2; + } + } + } + + + /* About to hit a known wall, stop */ + if (see_obstacle(find_current, p_ptr->py, p_ptr->px)) + { + return (TRUE); + } + + + /* Failure */ + return (FALSE); +} + + + +/* + * Take one step along the current "run" path + */ +void run_step(int dir) +{ + /* Start running */ + if (dir) + { + /* Hack -- do not start silly run */ + if (see_obstacle(dir, p_ptr->py, p_ptr->px) && + (cave[p_ptr->py + ddy[dir]][p_ptr->px + ddx[dir]].feat != FEAT_TREES)) + { + /* Message */ + msg_print("You cannot run in that direction."); + + /* Disturb */ + disturb(0); + + /* Done */ + return; + } + + /* Calculate torch radius */ + p_ptr->update |= (PU_TORCH); + + /* Initialize */ + run_init(dir); + } + + /* Keep running */ + else + { + /* Update run */ + if (run_test()) + { + /* Disturb */ + disturb(0); + + /* Done */ + return; + } + } + + /* Decrease the run counter */ + if (--running <= 0) return; + + /* Take time */ + energy_use = 100; + + + /* Move the player, using the "pickup" flag */ + move_player_aux(find_current, always_pickup, 1, TRUE); +} + + +/* + * Take care of the various things that can happen when you step + * into a space. (Objects, traps, and stores.) + */ +void step_effects(int y, int x, int do_pickup) +{ + /* Handle "objects" */ + py_pickup_floor(do_pickup); + + /* Handle "store doors" */ + if (cave[y][x].feat == FEAT_SHOP) + { + /* Disturb */ + disturb(0); + + /* Hack -- Enter store */ + command_new = KTRL('V'); + } + + /* Discover/set off traps */ + else if (cave[y][x].t_idx != 0) + { + /* Disturb */ + disturb(0); + + if (!(cave[y][x].info & CAVE_TRDT)) + { + /* Message */ + msg_print("You found a trap!"); + + /* Pick a trap */ + pick_trap(y, x); + } + + /* Hit the trap */ + hit_trap(); + } +} + +/* + * Issue a pet command + */ +void do_cmd_pet(void) +{ + int i = 0; + + int num = 0; + + int powers[36]; + + char power_desc[36][80]; + + bool_ flag, redraw; + + int ask; + + char choice; + + char out_val[160]; + + int pets = 0, pet_ctr = 0; + + bool_ all_pets = FALSE; + + monster_type *m_ptr; + + + for (num = 0; num < 36; num++) + { + powers[num] = 0; + strcpy(power_desc[num], ""); + } + + num = 0; + + if (p_ptr->confused) + { + msg_print("You are too confused to command your pets."); + energy_use = 0; + return; + } + + /* Calculate pets */ + /* Process the monsters (backwards) */ + for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) + { + /* Access the monster */ + m_ptr = &m_list[pet_ctr]; + + if (m_ptr->status >= MSTATUS_FRIEND) pets++; + } + + if (pets == 0) + { + msg_print("You have no pets/companions."); + energy_use = 0; + return; + } + else + { + strcpy(power_desc[num], "dismiss pets"); + powers[num++] = 1; + strcpy(power_desc[num], "dismiss companions"); + powers[num++] = 10; + strcpy(power_desc[num], "call pets"); + powers[num++] = 2; + strcpy(power_desc[num], "follow me"); + powers[num++] = 6; + strcpy(power_desc[num], "seek and destroy"); + powers[num++] = 3; + if (p_ptr->pet_open_doors) + strcpy(power_desc[num], "disallow open doors"); + else + strcpy(power_desc[num], "allow open doors"); + powers[num++] = 4; + if (p_ptr->pet_pickup_items) + strcpy(power_desc[num], "disallow pickup items"); + else + strcpy(power_desc[num], "allow pickup items"); + powers[num++] = 5; + strcpy(power_desc[num], "give target to a friend"); + powers[num++] = 7; + strcpy(power_desc[num], "give target to all friends"); + powers[num++] = 8; + strcpy(power_desc[num], "friend forget target"); + powers[num++] = 9; + } + + /* Nothing chosen yet */ + flag = FALSE; + + /* No redraw yet */ + redraw = FALSE; + + /* Build a prompt (accept all spells) */ + if (num <= 26) + { + /* Build a prompt (accept all spells) */ + strnfmt(out_val, 78, + "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0), + I2A(num - 1)); + } + else + { + strnfmt(out_val, 78, + "(Command %c-%c, *=List, ESC=exit) Select a command: ", I2A(0), + '0' + num - 27); + } + + /* Get a command from the user */ + while (!flag && get_com(out_val, &choice)) + { + /* Request redraw */ + if ((choice == ' ') || (choice == '*') || (choice == '?')) + { + /* Show the list */ + if (!redraw) + { + byte y = 1, x = 0; + int ctr = 0; + char dummy[80]; + + strcpy(dummy, ""); + + /* Show list */ + redraw = TRUE; + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + prt("", y++, x); + + while (ctr < num) + { + strnfmt(dummy, 80, "%c) %s", I2A(ctr), power_desc[ctr]); + prt(dummy, y + ctr, x); + ctr++; + } + + if (ctr < 17) + { + prt("", y + ctr, x); + } + else + { + prt("", y + 17, x); + } + } + + /* Hide the list */ + else + { + /* Hide list */ + redraw = FALSE; + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + } + + /* Redo asking */ + continue; + } + + if (choice == '\r' && num == 1) + { + choice = 'a'; + } + + if (isalpha(choice)) + { + /* Note verify */ + ask = (isupper(choice)); + + /* Lowercase */ + if (ask) choice = tolower(choice); + + /* Extract request */ + i = (islower(choice) ? A2I(choice) : -1); + } + else + { + ask = FALSE; /* Can't uppercase digits */ + + i = choice - '0' + 26; + } + + /* Totally Illegal */ + if ((i < 0) || (i >= num)) + { + bell(); + continue; + } + + /* Verify it */ + if (ask) + { + char tmp_val[160]; + + /* Prompt */ + strnfmt(tmp_val, 78, "Use %s? ", power_desc[i]); + + /* Belay that order */ + if (!get_check(tmp_val)) continue; + } + + /* Stop the loop */ + flag = TRUE; + } + + /* Restore the screen */ + if (redraw) + { + Term_load(); + character_icky = FALSE; + } + + /* Abort if needed */ + if (!flag) + { + energy_use = 0; + return; + } + + switch (powers[i]) + { + /* forget target */ + case 9: + { + monster_type *m_ptr; + int ii, jj; + + msg_print("Select the friendly monster:"); + if (!tgt_pt(&ii, &jj)) return; + + if (cave[jj][ii].m_idx) + { + m_ptr = &m_list[cave[jj][ii].m_idx]; + + if (m_ptr->status < MSTATUS_PET) + { + msg_print("You cannot give orders to this monster."); + return; + } + + m_ptr->target = -1; + } + break; + } + /* Give target to all */ + case 8: + { + monster_type *m_ptr; + int ii, jj, i; + + + msg_print("Select the target monster:"); + if (!tgt_pt(&ii, &jj)) return; + + if (cave[jj][ii].m_idx) + { + msg_print("Target selected."); + + for (i = m_max - 1; i >= 1; i--) + { + /* Access the monster */ + m_ptr = &m_list[i]; + + if (!m_ptr->r_idx) continue; + + if (m_ptr->status < MSTATUS_PET) continue; + + m_ptr->target = cave[jj][ii].m_idx; + } + } + else + { + msg_print("This is not a correct target."); + return; + } + break; + } + case 1: /* Dismiss pets */ + { + int Dismissed = 0; + + if (get_check("Dismiss all pets? ")) all_pets = TRUE; + + /* Process the monsters (backwards) */ + for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) + { + monster_race *r_ptr; + + /* Access the monster */ + m_ptr = &m_list[pet_ctr]; + r_ptr = &r_info[m_ptr->r_idx]; + + if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_PET) || (m_ptr->status == MSTATUS_FRIEND))) /* Get rid of it! */ + { + bool_ checked = FALSE; + char command; + bool_ delete_this = FALSE; + + if (all_pets) + { + delete_this = TRUE; + } + else + { + char friend_name[80], check_friend[80]; + monster_desc(friend_name, m_ptr, 0x80); + strnfmt(check_friend, 80, "Dismiss %s? (Escape to cancel)", friend_name); + + while (!checked) + { + if (!get_com(check_friend, &command)) + { + /* get out of loop */ + checked = TRUE; + pet_ctr = 0; + } + else switch (command) + { + case 'Y': + case 'y': + delete_this = TRUE; + checked = TRUE; + break; + case 'n': + case 'N': + checked = TRUE; + break; + default: + bell(); + break; + } + } + } + + if (delete_this) + { + delete_monster_idx(pet_ctr); + Dismissed++; + } + } + } + + msg_format("You have dismissed %d pet%s.", Dismissed, + (Dismissed == 1 ? "" : "s")); + break; + } + case 10: /* Dismiss companions */ + { + int Dismissed = 0; + + if (get_check("Dismiss all companions? ")) all_pets = TRUE; + + /* Process the monsters (backwards) */ + for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) + { + monster_race *r_ptr; + + /* Access the monster */ + m_ptr = &m_list[pet_ctr]; + r_ptr = &r_info[m_ptr->r_idx]; + + if ((!(r_ptr->flags7 & RF7_NO_DEATH)) && ((m_ptr->status == MSTATUS_COMPANION))) /* Get rid of it! */ + { + bool_ delete_this = FALSE; + + if (all_pets) + delete_this = TRUE; + else + { + char friend_name[80], check_friend[80]; + monster_desc(friend_name, m_ptr, 0x80); + strnfmt(check_friend, 80, "Dismiss %s? ", friend_name); + + if (get_check(check_friend)) + delete_this = TRUE; + } + + if (delete_this) + { + delete_monster_idx(pet_ctr); + Dismissed++; + } + } + } + + msg_format("You have dismissed %d companion%s.", Dismissed, + (Dismissed == 1 ? "" : "s")); + break; + } + /* Call pets */ + case 2: + { + p_ptr->pet_follow_distance = 1; + break; + } + /* "Seek and destroy" */ + case 3: + { + p_ptr->pet_follow_distance = 255; + break; + } + /* flag - allow pets to open doors */ + case 4: + { + p_ptr->pet_open_doors = !p_ptr->pet_open_doors; + break; + } + /* flag - allow pets to pickup items */ + case 5: + { + p_ptr->pet_pickup_items = !p_ptr->pet_pickup_items; + + /* Drop objects being carried by pets */ + if (!p_ptr->pet_pickup_items) + { + for (pet_ctr = m_max - 1; pet_ctr >= 1; pet_ctr--) + { + /* Access the monster */ + m_ptr = &m_list[pet_ctr]; + + if (m_ptr->status >= MSTATUS_PET) + { + monster_drop_carried_objects(m_ptr); + } + } + } + + break; + } + /* "Follow Me" */ + case 6: + { + p_ptr->pet_follow_distance = 6; + break; + } + } +} + +/* + * Incarnate into a body + */ +bool_ do_cmd_integrate_body() +{ + cptr q, s; + + int item; + + object_type *o_ptr; + + + if (!p_ptr->disembodied) + { + msg_print("You are already in a body."); + return FALSE; + } + + /* Restrict choices to monsters */ + item_tester_tval = TV_CORPSE; + + /* Get an item */ + q = "Incarnate in which body? "; + s = "You have no corpse to incarnate in."; + if (!get_item(&item, q, s, (USE_FLOOR))) return FALSE; + + o_ptr = &o_list[0 - item]; + + if (o_ptr->sval != SV_CORPSE_CORPSE) + { + msg_print("You must select a corpse."); + return FALSE; + } + + p_ptr->body_monster = o_ptr->pval2; + p_ptr->chp = o_ptr->pval3; + + floor_item_increase(0 - item, -1); + floor_item_describe(0 - item); + floor_item_optimize(0 - item); + + msg_print("Your spirit is incarnated in your new body."); + p_ptr->wraith_form = FALSE; + p_ptr->disembodied = FALSE; + do_cmd_redraw(); + + return TRUE; +} + +/* + * Leave a body + */ +bool_ do_cmd_leave_body(bool_ drop_body) +{ + object_type *o_ptr, forge; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + int i; + + + if (p_ptr->disembodied) + { + msg_print("You are already disembodied."); + return FALSE; + } + + for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) + { + if (p_ptr->body_parts[i - INVEN_WIELD] && p_ptr->inventory[i].k_idx && + cursed_p(&p_ptr->inventory[i])) + { + msg_print("A cursed object is preventing you from leaving your body."); + return FALSE; + } + } + + if (drop_body) + { + if (magik(25 + get_skill_scale(SKILL_POSSESSION, 25) + get_skill(SKILL_PRESERVATION))) + { + o_ptr = &forge; + object_prep(o_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE)); + o_ptr->number = 1; + o_ptr->pval = 0; + o_ptr->pval2 = p_ptr->body_monster; + o_ptr->pval3 = p_ptr->chp; + o_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1; + object_aware(o_ptr); + object_known(o_ptr); + o_ptr->ident |= IDENT_STOREB; + + /* Unique corpses are unique */ + if (r_ptr->flags1 & RF1_UNIQUE) + { + o_ptr->name1 = 201; + } + + drop_near(o_ptr, -1, p_ptr->py, p_ptr->px); + } + else + msg_print + ("You do not manage to keep the corpse from rotting away."); + } + + msg_print("Your spirit leaves your body."); + p_ptr->disembodied = TRUE; + + /* Turn into a lost soul(just for the picture) */ + p_ptr->body_monster = test_monster_name("Lost soul"); + do_cmd_redraw(); + + return (TRUE); +} + + +bool_ execute_inscription(byte i, byte y, byte x) +{ + cave_type *c_ptr = &cave[y][x]; + + + /* Not enough mana in the current grid */ + if (c_ptr->mana < inscription_info[i].mana) return (TRUE); + + + /* Reduce the grid mana -- note: it can't be restored */ + c_ptr->mana -= inscription_info[i].mana; + + /* Analyse inscription type */ + switch (i) + { + case INSCRIP_LIGHT: + { + msg_print("The inscription shines in a bright light!"); + lite_room(y, x); + + break; + } + + case INSCRIP_DARK: + { + msg_print("The inscription is enveloped in a dark aura!"); + unlite_room(y, x); + + break; + } + + case INSCRIP_STORM: + { + msg_print("The inscription releases a powerful storm!"); + project(0, 3, y, x, damroll(10, 10), + GF_ELEC, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | + PROJECT_KILL | PROJECT_JUMP); + + break; + } + + case INSCRIP_PROTECTION: + { + return (FALSE); + + break; + } + + case INSCRIP_DWARF_SUMMON: + { + int yy = y, xx = x; + + scatter(&yy, &xx, y, x, 3); + place_monster_one(yy, xx, test_monster_name("Dwarven Warrior"), + 0, FALSE, MSTATUS_FRIEND); + + break; + } + + case INSCRIP_CHASM: + { + monster_type *m_ptr; + monster_race *r_ptr; + cave_type *c_ptr; + int ii = x, ij = y; + + cave_set_feat(ij, ii, FEAT_DARK_PIT); + msg_print("A chasm appears in the floor!"); + + if (cave[ij][ii].m_idx) + { + m_ptr = &m_list[cave[ij][ii].m_idx]; + r_ptr = race_inf(m_ptr); + + if (r_ptr->flags7 & RF7_CAN_FLY) + { + msg_print("The monster simply flies over the chasm."); + } + else + { + if (!(r_ptr->flags1 & RF1_UNIQUE)) + { + msg_print("The monster falls in the chasm!"); + delete_monster_idx(cave[ij][ii].m_idx); + } + } + } + + if (cave[ij][ii].o_idx) + { + s16b this_o_idx, next_o_idx = 0; + + c_ptr = &cave[ij][ii]; + + /* Scan all objects in the grid */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; + this_o_idx = next_o_idx) + { + object_type * o_ptr; + bool_ plural = FALSE; + + char o_name[80]; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + if (o_ptr->number > 1) plural = TRUE; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Effect "observed" */ + if (o_ptr->marked) + { + object_desc(o_name, o_ptr, FALSE, 0); + } + + /* Artifacts get to resist */ + if (o_ptr->name1) + { + /* Observe the resist */ + if (o_ptr->marked) + { + msg_format("The %s %s simply fly over the chasm!", + o_name, (plural ? "are" : "is")); + } + } + + /* Kill it */ + else + { + /* Delete the object */ + delete_object_idx(this_o_idx); + + /* Redraw */ + lite_spot(ij, ii); + } + } + } + + break; + } + + case INSCRIP_BLACK_FIRE: + { + msg_print("The inscription releases a blast of hellfire!"); + project(0, 3, y, x, 200, + GF_HELL_FIRE, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | + PROJECT_KILL | PROJECT_JUMP); + + break; + } + } + + return (TRUE); +} + + +/* + * Choose an inscription and engrave it + */ +void do_cmd_engrave() +{ + char buf[41] = ""; + + byte i; + + strnfmt(buf, 41, "%s", inscription_info[cave[p_ptr->py][p_ptr->px].inscription].text); + + get_string("Engrave what? ", buf, 40); + + /* Silently do nothing when player his escape or enters an empty string */ + if (!buf[0]) return; + + for (i = 0; i < MAX_INSCRIPTIONS; i++) + { + if (!strcmp(inscription_info[i].text, buf)) + { + if (inscription_info[i].know) + { + /* Save the inscription */ + cave[p_ptr->py][p_ptr->px].inscription = i; + } + else + msg_print("You can't use this inscription for now."); + } + } + + /* Execute the inscription */ + if (inscription_info[cave[p_ptr->py][p_ptr->px].inscription].when & INSCRIP_EXEC_ENGRAVE) + { + execute_inscription(cave[p_ptr->py][p_ptr->px].inscription, p_ptr->py, p_ptr->px); + } + + energy_use += 300; +} + + +/* + * Let's do a spinning around attack: -- DG -- + * aDb + * y@k + * ooT + * Ah ... all of those will get hit. + */ +void do_spin() +{ + int i, j; + + + msg_print("You start spinning around..."); + + for (j = p_ptr->py - 1; j <= p_ptr->py + 1; j++) + { + for (i = p_ptr->px - 1; i <= p_ptr->px + 1; i++) + { + /* Avoid stupid bugs */ + if (in_bounds(j, i) && cave[j][i].m_idx) + py_attack(j, i, 1); + } + } +} diff --git a/src/cmd2.c b/src/cmd2.c deleted file mode 100644 index 67f25098..00000000 --- a/src/cmd2.c +++ /dev/null @@ -1,5182 +0,0 @@ -/* File: cmd2.c */ - -/* Purpose: Movement commands (part 2) */ - -/* - * 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 "angband.h" -#include "hooks.h" - -void do_cmd_immovable_special(void); - -/* - * Try to bash an altar - */ -static bool_ do_cmd_bash_altar(int y, int x) -{ - msg_print("Are you mad? You want to anger the gods?"); - return (FALSE); -} - - -/* - * Try to bash a fountain - */ -static bool_ do_cmd_bash_fountain(int y, int x) -{ - int bash, temp; - - bool_ more = TRUE; - - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - - if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR)) - { - msg_print("You cannot do that."); - - return (FALSE); - } - - /* Take a turn */ - energy_use = 100; - - /* Message */ - msg_print("You smash into the fountain!"); - - /* Hack -- Bash power based on strength */ - /* (Ranges from 3 to 20 to 100 to 200) */ - bash = adj_str_blow[p_ptr->stat_ind[A_STR]]; - - /* Compare bash power to door power XXX XXX XXX */ - temp = (bash - 50); - - /* Hack -- always have a chance */ - if (temp < 1) temp = 1; - - /* Hack -- attempt to bash down the door */ - if (rand_int(200) < temp) - { - /* Message */ - msg_print("The fountain breaks!"); - - fire_ball(GF_WATER, 5, damroll(6, 8), 2); - - cave_set_feat(y, x, FEAT_DEEP_WATER); - more = FALSE; - } - - return (more); -} - -/* - * Stair hooks - */ -static bool_ stair_hooks(stairs_direction direction) -{ - cptr direction_s = (direction == STAIRS_UP) ? "up" : "down"; - - /* Old-style hooks */ - if (process_hooks(HOOK_STAIR, "(s)", direction_s)) - { - return TRUE; /* Prevent movement */ - } - - /* New-style hooks */ - { - hook_stair_in in = { direction }; - hook_stair_out out = { TRUE }; /* Allow by default */ - - process_hooks_new(HOOK_STAIR, &in, &out); - - return (!out.allow); - } -} - - -/* - * Go up one level - */ -void do_cmd_go_up(void) -{ - bool_ go_up = FALSE, go_up_many = FALSE, prob_traveling = FALSE; - - cave_type *c_ptr; - - int oldl = dun_level; - - dungeon_info_type *d_ptr = &d_info[dungeon_type]; - - - /* Player grid */ - c_ptr = &cave[p_ptr->py][p_ptr->px]; - - /* Can we ? */ - if (stair_hooks(STAIRS_UP)) - { - return; - } - - /* Normal up stairs */ - if ((c_ptr->feat == FEAT_LESS) || (c_ptr->feat == FEAT_WAY_LESS)) - { - if (!dun_level) - { - go_up = TRUE; - } - else if ((dungeon_flags2 & DF2_ASK_LEAVE)) - { - go_up = get_check("Leave this unique level forever? "); - } - else if (confirm_stairs) - { - go_up = get_check("Really leave the level? "); - } - else - { - go_up = TRUE; - } - } - - /* Shaft up */ - else if (c_ptr->feat == FEAT_SHAFT_UP) - { - if (dun_level == 1) - { - go_up = TRUE; - } - else if ((dungeon_flags2 & DF2_ASK_LEAVE)) - { - go_up = get_check("Leave this unique level forever? "); - } - else if (confirm_stairs) - { - go_up_many = get_check("Really leave the level? "); - } - else - { - go_up_many = TRUE; - } - } - - /* Quest exit */ - else if (c_ptr->feat == FEAT_QUEST_EXIT) - { - leaving_quest = p_ptr->inside_quest; - - if ((dungeon_flags2 & DF2_ASK_LEAVE) && - !get_check("Leave this unique level forever? ")) - return; - - p_ptr->inside_quest = c_ptr->special; - dun_level = 0; - p_ptr->oldpx = 0; - p_ptr->oldpy = 0; - p_ptr->leaving = TRUE; - - return; - } - - /* Exits to previous area in flat terrains */ - else if (!(dungeon_flags1 & DF1_FLAT) && - p_ptr->prob_travel && !p_ptr->inside_quest) - { - if (d_ptr->mindepth == dun_level) return; - - if (dungeon_flags2 & DF2_NO_EASY_MOVE) - { - msg_print("Some powerful force prevents your from teleporting."); - return; - } - - prob_traveling = TRUE; - - if (confirm_stairs) - { - if (get_check("Really leave the level? ")) - go_up = TRUE; - } - else - { - go_up = TRUE; - } - } - else - { - msg_print("I see no up staircase here."); - return; - } - - if (go_up || go_up_many) - { - - energy_use = 0; - - /* Success */ - if (c_ptr->feat == FEAT_WAY_LESS) - msg_print("You enter the previous area."); - else - msg_print("You enter a maze of up staircases."); - - autosave_checkpoint(); - - if (p_ptr->inside_quest) - { - dun_level = 1; - leaving_quest = p_ptr->inside_quest; - - p_ptr->inside_quest = c_ptr->special; - } - - /* Create a way back */ - if (go_up_many) - create_down_shaft = TRUE; - else - create_down_stair = TRUE; - - /* New depth */ - if (go_up) - dun_level--; - else - { - dun_level -= randint(3) + 1; - if (dun_level <= 0) dun_level = 0; - } - - if (c_ptr->special && (!prob_traveling)) - { - dun_level = oldl; - dun_level = get_flevel(); - dungeon_type = c_ptr->special; - dun_level += d_info[dungeon_type].mindepth; - } - - /* Leaving */ - p_ptr->leaving = TRUE; - } -} - - -/* - * Returns TRUE if we are in the Between... - */ -static bool_ between_effect(void) -{ - byte bx, by; - - - if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN) - { - - bx = cave[p_ptr->py][p_ptr->px].special & 255; - by = cave[p_ptr->py][p_ptr->px].special >> 8; - - msg_print("You fall into the void."); - msg_print("Brrrr! It's deadly cold."); - - swap_position(by, bx); - - /* To avoid being teleported back */ - energy_use = 100; - - return (TRUE); - } - - else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN2) - { - between_exit *be_ptr = &between_exits[cave[p_ptr->py][p_ptr->px].special]; - - p_ptr->wild_mode = FALSE; - p_ptr->wilderness_x = be_ptr->wild_x; - p_ptr->wilderness_y = be_ptr->wild_y; - p_ptr->oldpx = p_ptr->px = be_ptr->px; - p_ptr->oldpy = p_ptr->py = be_ptr->py; - dungeon_type = be_ptr->d_idx; - dun_level = be_ptr->level; - p_ptr->leaving = TRUE; - - return (TRUE); - } - else - return (FALSE); -} - -/* - * Go down one level - */ -void do_cmd_go_down(void) -{ - cave_type *c_ptr; - - bool_ go_down = FALSE, go_down_many = FALSE, prob_traveling = FALSE; - - bool_ fall_trap = FALSE; - - char i; - - int old_dun = dun_level; - - dungeon_info_type *d_ptr = &d_info[dungeon_type]; - - - /* MUST be actived now */ - if (between_effect()) return; - - /* Player grid */ - c_ptr = &cave[p_ptr->py][p_ptr->px]; - - if (p_ptr->astral && (dun_level == 98)) return; - - if (c_ptr->t_idx == TRAP_OF_SINKING) fall_trap = TRUE; - - /* test if on special level */ - if ((dungeon_flags2 & DF2_ASK_LEAVE)) - { - prt("Leave this unique level forever (y/n) ? ", 0, 0); - flush(); - i = inkey(); - prt("", 0, 0); - if (i != 'y') return; - } - - /* Can we ? */ - if (stair_hooks(STAIRS_DOWN)) - { - return; - } - - /* Normal up stairs */ - if (c_ptr->feat == FEAT_SHAFT_DOWN) - { - if (!dun_level) - { - go_down = TRUE; - - /* Save old player position */ - p_ptr->oldpx = p_ptr->px; - p_ptr->oldpy = p_ptr->py; - } - else - { - if (confirm_stairs) - { - if (get_check("Really leave the level? ")) - go_down_many = TRUE; - } - else - { - go_down_many = TRUE; - } - } - } - - /* Normal stairs */ - else if ((c_ptr->feat == FEAT_MORE) || (c_ptr->feat == FEAT_WAY_MORE)) - { - if (p_ptr->prob_travel) - { - if (d_ptr->maxdepth == dun_level) return; - } - if (!dun_level) - { - go_down = TRUE; - - /* Save old player position */ - p_ptr->oldpx = p_ptr->px; - p_ptr->oldpy = p_ptr->py; - } - else - { - if (confirm_stairs) - { - if (get_check("Really leave the level? ")) - go_down = TRUE; - } - else - { - go_down = TRUE; - } - } - } - - /* Handle quest areas -KMW- */ - else if (c_ptr->feat == FEAT_QUEST_ENTER) - { - /* Enter quest level */ - enter_quest(); - - return; - } - - else if (!(dungeon_flags1 & DF1_FLAT) && - p_ptr->prob_travel && !p_ptr->inside_quest) - { - if (d_ptr->maxdepth == dun_level) return; - - if (dungeon_flags2 & DF2_NO_EASY_MOVE) - { - msg_print("Some powerfull force prevents your from teleporting."); - return; - } - - prob_traveling = TRUE; - - if (confirm_stairs) - { - if (get_check("Really leave the level? ")) - go_down = TRUE; - } - else - { - go_down = TRUE; - } - } - - else if (!(fall_trap)) - { - msg_print("I see no down staircase here."); - return; - } - - if (go_down || go_down_many) - { - energy_use = 0; - - if (fall_trap) - msg_print("You deliberately jump through the trap door."); - else - { - if (c_ptr->feat == FEAT_WAY_MORE) - msg_print("You enter the next area."); - else - msg_print("You enter a maze of down staircases."); - } - - autosave_checkpoint(); - - /* Go down */ - if (go_down) - { - dun_level++; - } - else if (go_down_many) - { - int i = randint(3) + 1, j; - - for (j = 1; j < i; j++) - { - dun_level++; - if (is_quest(dun_level + i - 1)) break; - if (d_ptr->maxdepth == dun_level) break; - } - } - - /* We change place */ - if (c_ptr->special && (!prob_traveling)) - { - if (d_info[c_ptr->special].min_plev <= p_ptr->lev) - { - dungeon_info_type *d_ptr = &d_info[c_ptr->special]; - - /* Do the lua scripts refuse ? ;) */ - if (process_hooks(HOOK_ENTER_DUNGEON, "(d)", c_ptr->special)) - { - dun_level = old_dun; - return; - } - - /* Ok go in the new dungeon */ - dungeon_type = c_ptr->special; - d_ptr = &d_info[dungeon_type]; - - if ((p_ptr->wilderness_x == d_ptr->ix) && - (p_ptr->wilderness_y == d_ptr->iy)) - { - dun_level = d_ptr->mindepth; - } - else if ((p_ptr->wilderness_x == d_ptr->ox) && - (p_ptr->wilderness_y == d_ptr->oy)) - { - dun_level = d_ptr->maxdepth; - } - else - { - dun_level = d_ptr->mindepth; - } - - msg_format("You go into %s", - d_text + d_info[dungeon_type].text); - } - else - { - msg_print - ("You don't feel yourself experienced enough to go there..."); - dun_level = old_dun; - return; - } - } - - /* Leaving */ - p_ptr->leaving = TRUE; - - if (!fall_trap) - { - /* Create a way back */ - if (go_down_many) - create_up_shaft = TRUE; - else - create_up_stair = TRUE; - } - } -} - - - -/* - * Simple command to "search" for one turn - */ -void do_cmd_search(void) -{ - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - /* Take a turn */ - energy_use = 100; - - /* Search */ - search(); -} - - -/* - * Hack -- toggle search mode - */ -void do_cmd_toggle_search(void) -{ - /* Stop searching */ - if (p_ptr->searching) - { - /* Clear the searching flag */ - p_ptr->searching = FALSE; - - /* Recalculate bonuses */ - p_ptr->update |= (PU_BONUS); - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - } - - /* Start searching */ - else - { - /* Set the searching flag */ - p_ptr->searching = TRUE; - - /* Update stuff */ - p_ptr->update |= (PU_BONUS); - - /* Redraw stuff */ - p_ptr->redraw |= (PR_STATE | PR_SPEED); - } -} - - - -/* - * Determine if a grid contains a chest - */ -static s16b chest_check(int y, int x) -{ - cave_type *c_ptr = &cave[y][x]; - - s16b this_o_idx, next_o_idx = 0; - - - /* Scan all objects in the grid */ - for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) - { - object_type * o_ptr; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Skip unknown chests XXX XXX */ - /* if (!o_ptr->marked) continue; */ - - /* Check for chest */ - if (o_ptr->tval == TV_CHEST) return (this_o_idx); - } - - /* No chest */ - return (0); -} - - -/* - * Allocates objects upon opening a chest -BEN- - * - * Disperse treasures from the given chest, centered at (x,y). - * - * Small chests often contain "gold", while Large chests always contain - * items. Wooden chests contain 2 items, Iron chests contain 4 items, - * and Steel chests contain 6 items. The "value" of the items in a - * chest is based on the "power" of the chest, which is in turn based - * on the level on which the chest is generated. - */ -static void chest_death(int y, int x, s16b o_idx) -{ - int number; - - bool_ small; - - object_type forge; - object_type *q_ptr; - - object_type *o_ptr = &o_list[o_idx]; - - - /* Small chests often hold "gold" */ - small = (o_ptr->sval < SV_CHEST_MIN_LARGE); - - /* Determine how much to drop (see above) */ - number = (o_ptr->sval % SV_CHEST_MIN_LARGE) * 2; - - /* Zero pval means empty chest */ - if (!o_ptr->pval) number = 0; - - /* Opening a chest */ - opening_chest = TRUE; - - /* Determine the "value" of the items */ - object_level = ABS(o_ptr->pval) + 10; - - /* Drop some objects (non-chests) */ - for (; number > 0; --number) - { - /* Get local object */ - q_ptr = &forge; - - /* Wipe the object */ - object_wipe(q_ptr); - - /* Small chests often drop gold */ - if (small && (rand_int(100) < 75)) - { - /* Make some gold */ - if (!make_gold(q_ptr)) continue; - } - - /* Otherwise drop an item */ - else - { - /* Make an object */ - if (!make_object(q_ptr, FALSE, FALSE, d_info[dungeon_type].objs)) - continue; - } - - /* Drop it in the dungeon */ - drop_near(q_ptr, -1, y, x); - } - - /* Reset the object level */ - object_level = dun_level; - - /* No longer opening a chest */ - opening_chest = FALSE; - - /* Empty */ - o_ptr->pval = 0; - o_ptr->pval2 = 0; - - /* Known */ - object_known(o_ptr); -} - - -/* - * Chests have traps too. - * - * Exploding chest destroys contents (and traps). - * Note that the chest itself is never destroyed. - */ -static void chest_trap(int y, int x, s16b o_idx) -{ - int trap; - - object_type *o_ptr = &o_list[o_idx]; - - bool_ ident = FALSE; - - - /* Ignore disarmed chests */ - if (o_ptr->pval <= 0) return; - - /* Obtain the trap */ - trap = o_ptr->pval; - - /* Message */ - msg_print("You found a trap!"); - - /* Set off trap */ - ident = player_activate_trap_type(y, x, o_ptr, o_idx); - if (ident) - { - t_info[o_ptr->pval].ident = TRUE; - msg_format("You identified the trap as %s.", - t_name + t_info[trap].name); - } -} - - -/* - * Attempt to open the given chest at the given location - * - * Assume there is no monster blocking the destination - * - * Returns TRUE if repeated commands may continue - */ -static bool_ do_cmd_open_chest(int y, int x, s16b o_idx) -{ - int i, j; - - bool_ flag = TRUE; - - bool_ more = FALSE; - - object_type *o_ptr = &o_list[o_idx]; - - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - - if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) - { - msg_print("You cannot open chests."); - - return (FALSE); - } - - /* Take a turn */ - energy_use = 100; - - /* Attempt to unlock it */ - if (o_ptr->pval > 0) - { - /* Assume locked, and thus not open */ - flag = FALSE; - - /* Get the "disarm" factor */ - i = p_ptr->skill_dis; - - /* Penalize some conditions */ - if (p_ptr->blind || no_lite()) i = i / 10; - if (p_ptr->confused || p_ptr->image) i = i / 10; - - /* Extract the difficulty */ - j = i - o_ptr->pval; - - /* Always have a small chance of success */ - if (j < 2) j = 2; - - /* Success -- May still have traps */ - if (rand_int(100) < j) - { - msg_print("You have picked the lock."); - gain_exp(1); - flag = TRUE; - } - - /* Failure -- Keep trying */ - else - { - /* We may continue repeating */ - more = TRUE; - - if (flush_failure) flush(); - - msg_print("You failed to pick the lock."); - } - } - - /* Allowed to open */ - if (flag) - { - /* Apply chest traps, if any */ - chest_trap(y, x, o_idx); - - /* Let the Chest drop items */ - chest_death(y, x, o_idx); - } - - /* Result */ - return (more); -} - - -/* - * Original code by TNB, improvement for Angband 2.9.3 by rr9 - * Slightly modified for ToME because of its trap implementation - */ - -/* - * Return TRUE if the given grid is an open door - */ -static bool_ is_open(cave_type *c_ptr) -{ - return (c_ptr->feat == FEAT_OPEN); -} - - -/* - * Return TRUE if the given grid is a closed door - */ -static bool_ is_closed(cave_type *c_ptr) -{ - byte feat; - - if (c_ptr->mimic) feat = c_ptr->mimic; - else feat = c_ptr->feat; - - return ((feat >= FEAT_DOOR_HEAD) && (feat <= FEAT_DOOR_TAIL)); -} - - -/* - * Return TRUE if the given grid has a trap - */ -static bool_ is_trap(cave_type *c_ptr) -{ - return ((c_ptr->info & (CAVE_TRDT)) != 0); -} - - -/* - * Return the number of doors/traps around (or under) - * the character using the filter function 'test' - */ -static int count_feats(int *y, int *x, bool_ (*test) (cave_type *c_ptr), - bool_ under) -{ - int d; - - int xx, yy; - - int count; - - - /* Clear match counter */ - count = 0; - - /* Check around (and under) the character */ - for (d = 0; d < 9; d++) - { - /* Ignore current grid if told so -- See tables.c */ - if ((d == 8) && !under) continue; - - /* Extract adjacent (legal) location */ - yy = p_ptr->py + ddy_ddd[d]; - xx = p_ptr->px + ddx_ddd[d]; - - /* Paranoia */ - if (!in_bounds(yy, xx)) continue; - - /* Must have knowledge */ - if (!(cave[yy][xx].info & (CAVE_MARK))) continue; - - /* Not looking for this feature */ - if (!(*test) (&cave[yy][xx])) continue; - - /* Count it */ - count++; - - /* Remember the location. Only meaningful if there's - exactly one match */ - *y = yy; - *x = xx; - } - - /* All done */ - return (count); -} - - -/* - * Return the number of chests around (or under) the character. - * If requested, count only trapped chests. - */ -static int count_chests(int *y, int *x, bool_ trapped) -{ - int d, count, o_idx; - - object_type *o_ptr; - - - /* Count how many matches */ - count = 0; - - /* Check around (and under) the character */ - for (d = 0; d < 9; d++) - { - - /* Extract adjacent (legal) location */ - int yy = p_ptr->py + ddy_ddd[d]; - int xx = p_ptr->px + ddx_ddd[d]; - - /* No (visible) chest is there */ - if ((o_idx = chest_check(yy, xx)) == 0) continue; - - /* Grab the object */ - o_ptr = &o_list[o_idx]; - - /* Already open */ - if (o_ptr->pval == 0) continue; - - /* No (known) traps here */ - if (trapped && (!object_known_p(o_ptr) || !o_ptr->pval)) continue; - - /* OK */ - ++count; - - /* Remember the location. Only useful if only one match */ - *y = yy; - *x = xx; - } - - /* All done */ - return (count); -} - - -/* - * Convert an adjacent location to a direction. - */ -static int coords_to_dir(int y, int x) -{ - int d[3][3] = - { - {7, 4, 1}, - {8, 5, 2}, - {9, 6, 3} }; - - int dy, dx; - - - dy = y - p_ptr->py; - dx = x - p_ptr->px; - - /* Paranoia */ - if (ABS(dx) > 1 || ABS(dy) > 1) return (0); - - return d[dx + 1][dy + 1]; -} - - -/* - * Perform the basic "open" command on doors - * - * Assume destination is a closed/locked/jammed door - * - * Assume there is no monster blocking the destination - * - * Returns TRUE if repeated commands may continue - */ -static bool_ do_cmd_open_aux(int y, int x, int dir) -{ - int i, j; - - cave_type *c_ptr; - - bool_ more = FALSE; - - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - - if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) - { - msg_print("You cannot open doors."); - - return (FALSE); - } - - /* Take a turn */ - energy_use = 100; - - /* Get requested grid */ - c_ptr = &cave[y][x]; - - /* Jammed door */ - if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08) - { - /* Stuck */ - msg_print("The door appears to be stuck."); - } - - /* Locked door */ - else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01) - { - /* Disarm factor */ - i = p_ptr->skill_dis; - - /* Penalize some conditions */ - if (p_ptr->blind || no_lite()) i = i / 10; - if (p_ptr->confused || p_ptr->image) i = i / 10; - - /* Extract the lock power */ - j = c_ptr->feat - FEAT_DOOR_HEAD; - - /* Extract the difficulty XXX XXX XXX */ - j = i - (j * 4); - - /* Always have a small chance of success */ - if (j < 2) j = 2; - - /* Success */ - if (rand_int(100) < j) - { - /* Message */ - msg_print("You have picked the lock."); - - /* Set off trap */ - if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); - - /* Open the door */ - cave_set_feat(y, x, FEAT_OPEN); - - /* Update some things */ - p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); - - /* Sound */ - sound(SOUND_OPENDOOR); - - /* Experience */ - gain_exp(1); - } - - /* Failure */ - else - { - /* Failure */ - if (flush_failure) flush(); - - /* Message */ - msg_print("You failed to pick the lock."); - - /* We may keep trying */ - more = TRUE; - } - } - - /* Closed door */ - else - { - /* Set off trap */ - if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); - - /* Open the door */ - cave_set_feat(y, x, FEAT_OPEN); - - /* Update some things */ - p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); - - /* Sound */ - sound(SOUND_OPENDOOR); - } - - /* Result */ - return (more); -} - - - -/* - * Open a closed/locked/jammed door or a closed/locked chest. - * - * Unlocking a locked door/chest is worth one experience point. - */ -void do_cmd_open(void) -{ - int y, x, dir; - - s16b o_idx; - - cave_type *c_ptr; - - bool_ more = FALSE; - - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - - if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) - { - msg_print("You cannot open doors."); - - return; - } - - /* Option: Pick a direction */ - if (easy_open) - { - int num_doors, num_chests; - - /* Count closed doors (locked or jammed) */ - num_doors = count_feats(&y, &x, is_closed, FALSE); - - /* Count chests (locked) */ - num_chests = count_chests(&y, &x, FALSE); - - /* There is nothing the player can open */ - if ((num_doors + num_chests) == 0) - { - /* Message */ - msg_print("You see nothing there to open."); - - /* Done */ - return; - } - - /* Set direction if there is only one target */ - else if ((num_doors + num_chests) == 1) - { - command_dir = coords_to_dir(y, x); - } - } - - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - /* Get a "repeated" direction */ - if (get_rep_dir(&dir)) - { - /* Get requested location */ - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - /* Get requested grid */ - c_ptr = &cave[y][x]; - - /* Check for chest */ - o_idx = chest_check(y, x); - - /* Nothing useful */ - if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && - (c_ptr->feat <= FEAT_DOOR_TAIL)) && !o_idx) - { - /* Message */ - msg_print("You see nothing there to open."); - } - - /* Monster in the way */ - else if (c_ptr->m_idx) - { - /* Take a turn */ - energy_use = 100; - - /* Message */ - msg_print("There is a monster in the way!"); - - /* Attack */ - py_attack(y, x, -1); - } - - /* Handle chests */ - else if (o_idx) - { - /* Open the chest */ - more = do_cmd_open_chest(y, x, o_idx); - } - - /* Handle doors */ - else - { - /* Open the door */ - more = do_cmd_open_aux(y, x, dir); - } - } - - /* Process the appropriate hooks */ - process_hooks(HOOK_OPEN, "(d)", is_quest(dun_level)); - - /* Cancel repeat unless we may continue */ - if (!more) disturb(0); -} - - - -/* - * Perform the basic "close" command - * - * Assume destination is an open/broken door - * - * Assume there is no monster blocking the destination - * - * Returns TRUE if repeated commands may continue - */ -static bool_ do_cmd_close_aux(int y, int x, int dir) -{ - cave_type *c_ptr; - - bool_ more = FALSE; - - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - - if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) - { - msg_print("You cannot close doors."); - - return (FALSE); - } - - /* Take a turn */ - energy_use = 100; - - /* Get grid and contents */ - c_ptr = &cave[y][x]; - - /* Set off trap */ - if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); - - /* Broken door */ - if (c_ptr->feat == FEAT_BROKEN) - { - /* Message */ - msg_print("The door appears to be broken."); - } - - /* Open door */ - else - { - /* Close the door */ - cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00); - - /* Update some things */ - p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); - - /* Sound */ - sound(SOUND_SHUTDOOR); - } - - /* Result */ - return (more); -} - - -/* - * Close an open door. - */ -void do_cmd_close(void) -{ - int y, x, dir; - - cave_type *c_ptr; - - bool_ more = FALSE; - - - /* Option: Pick a direction */ - if (easy_open) - { - int num_doors; - - /* Count open doors */ - num_doors = count_feats(&y, &x, is_open, FALSE); - - /* There are no doors the player can close */ - if (num_doors == 0) - { - /* Message */ - msg_print("You see nothing there to close."); - - /* Done */ - return; - } - - /* Exactly one closeable door */ - else if (num_doors == 1) - { - command_dir = coords_to_dir(y, x); - } - } - - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - /* Get a "repeated" direction */ - if (get_rep_dir(&dir)) - { - /* Get requested location */ - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - /* Get grid and contents */ - c_ptr = &cave[y][x]; - - /* Require open/broken door */ - if ((c_ptr->feat != FEAT_OPEN) && (c_ptr->feat != FEAT_BROKEN)) - { - /* Message */ - msg_print("You see nothing there to close."); - } - - /* Monster in the way */ - else if (c_ptr->m_idx) - { - /* Take a turn */ - energy_use = 100; - - /* Message */ - msg_print("There is a monster in the way!"); - - /* Attack */ - py_attack(y, x, -1); - } - - /* Close the door */ - else - { - /* Close the door */ - more = do_cmd_close_aux(y, x, dir); - } - } - - /* Cancel repeat unless we may continue */ - if (!more) disturb(0); -} - - -/* - * Determine if a given grid may be "tunneled" - */ -static bool_ do_cmd_tunnel_test(int y, int x) -{ - /* Must have knowledge(execpt on "forget" levels) */ - if (!(cave[y][x].info & (CAVE_MARK))) - { - /* Message */ - msg_print("You see nothing there."); - - /* Nope */ - return (FALSE); - } - - /* Must be a wall/door/etc */ - if (cave_floor_bold(y, x)) - { - /* Message */ - msg_print("You see nothing there to tunnel."); - - /* Nope */ - return (FALSE); - } - - /* Must be tunnelable */ - if (!(f_info[cave[y][x].feat].flags1 & FF1_TUNNELABLE)) - { - /* Message */ - msg_print(f_text + f_info[cave[y][x].feat].tunnel); - - /* Nope */ - return (FALSE); - } - - /* Okay */ - return (TRUE); -} - - - -/* - * Tunnel through wall. Assumes valid location. - * - * Note that it is impossible to "extend" rooms past their - * outer walls (which are actually part of the room). - * - * This will, however, produce grids which are NOT illuminated - * (or darkened) along with the rest of the room. - */ -static bool_ twall(int y, int x, byte feat) -{ - cave_type *c_ptr = &cave[y][x]; - - - /* Paranoia -- Require a wall or door or some such */ - if (cave_floor_bold(y, x)) return (FALSE); - - /* Forget the wall */ - c_ptr->info &= ~(CAVE_MARK); - - /* Remove the feature */ - cave_set_feat(y, x, feat); - - /* Update some things */ - p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE); - - /* Result */ - return (TRUE); -} - - - -/* - * Perform the basic "tunnel" command - * - * Assumes that the destination is a wall, a vein, a secret - * door, or rubble. - * - * Assumes that no monster is blocking the destination - * - * Returns TRUE if repeated commands may continue - */ -bool_ do_cmd_tunnel_aux(int y, int x, int dir) -{ - int skill_req = 0, skill_req_1pct = 0; - cave_type *c_ptr = &cave[y][x]; - - feature_type *f_ptr = &f_info[c_ptr->feat]; - - bool_ more = FALSE; - - - /* Must be have something to dig with (except for sandwalls) */ - if ((c_ptr->feat < FEAT_SANDWALL) || (c_ptr->feat > FEAT_SANDWALL_K)) - { - if (!p_ptr->inventory[INVEN_TOOL].k_idx || - (p_ptr->inventory[INVEN_TOOL].tval != TV_DIGGING)) - { - msg_print("You need to have a shovel or pick in your tool slot."); - - return (FALSE); - } - } - - /* Verify legality */ - if (!do_cmd_tunnel_test(y, x)) return (FALSE); - - /* Take a turn */ - energy_use = 100; - - /* Get grid */ - c_ptr = &cave[y][x]; - - /* Sound */ - sound(SOUND_DIG); - - /* Titanium */ - if (f_ptr->flags1 & FF1_PERMANENT) - { - msg_print(f_text + f_ptr->tunnel); - } - - else if ((c_ptr->feat == FEAT_TREES) || (c_ptr->feat == FEAT_DEAD_TREE)) - { - /* Chop Down */ - skill_req = 10; - skill_req_1pct = 14; - if ((p_ptr->skill_dig > 10 + rand_int(400)) && twall(y, x, FEAT_GRASS)) - { - msg_print("You have cleared away the trees."); - } - - /* Keep trying */ - else - { - /* We may continue chopping */ - msg_print(f_text + f_ptr->tunnel); - more = TRUE; - - /* Occasional Search XXX XXX */ - if (rand_int(100) < 25) search(); - } - } - - - /* Granite */ - else if ((c_ptr->feat >= FEAT_WALL_EXTRA) && - (c_ptr->feat <= FEAT_WALL_SOLID)) - { - /* Tunnel */ - skill_req = 40; - skill_req_1pct = 56; - if ((p_ptr->skill_dig > 40 + rand_int(1600)) && twall(y, x, FEAT_FLOOR)) - { - msg_print("You have finished the tunnel."); - } - - /* Keep trying */ - else - { - /* We may continue tunelling */ - msg_print(f_text + f_ptr->tunnel); - more = TRUE; - } - } - - - /* Quartz / Magma / Sandwall */ - else if (((c_ptr->feat >= FEAT_MAGMA) && - (c_ptr->feat <= FEAT_QUARTZ_K)) || - ((c_ptr->feat >= FEAT_SANDWALL) && - (c_ptr->feat <= FEAT_SANDWALL_K))) - { - bool_ okay = FALSE; - bool_ gold = FALSE; - bool_ hard = FALSE; - bool_ soft = FALSE; - - /* Found gold */ - if ((c_ptr->feat >= FEAT_MAGMA_H) && - (c_ptr->feat <= FEAT_QUARTZ_K)) gold = TRUE; - - if ((c_ptr->feat == FEAT_SANDWALL_H) || - (c_ptr->feat == FEAT_SANDWALL_K)) - { - gold = TRUE; - soft = TRUE; - } - else - /* Extract "quartz" flag XXX XXX XXX */ - if ((c_ptr->feat - FEAT_MAGMA) & 0x01) hard = TRUE; - - /* Quartz */ - if (hard) - { - skill_req = 20; - skill_req_1pct = 28; - okay = (p_ptr->skill_dig > 20 + rand_int(800)); - } - - /* Sandwall */ - else if (soft) - { - skill_req = 5; - skill_req_1pct = 8; - okay = (p_ptr->skill_dig > 5 + rand_int(250)); - } - - /* Magma */ - else - { - skill_req = 10; - skill_req_1pct = 14; - okay = (p_ptr->skill_dig > 10 + rand_int(400)); - } - - /* Success */ - if (okay && twall(y, x, FEAT_FLOOR)) - { - /* Found treasure */ - if (gold) - { - /* Place some gold */ - place_gold(y, x); - - /* Message */ - msg_print("You have found something!"); - } - - /* Found nothing */ - else - { - /* Message */ - msg_print("You have finished the tunnel."); - } - } - - /* Failure */ - else - { - /* Message, continue digging */ - msg_print(f_text + f_ptr->tunnel); - more = TRUE; - } - } - - /* Rubble */ - else if (c_ptr->feat == FEAT_RUBBLE) - { - /* Remove the rubble */ - skill_req = 0; - skill_req_1pct = 2; - if ((p_ptr->skill_dig > rand_int(200)) && - twall(y, x, d_info[dungeon_type].floor1)) - { - /* Message */ - msg_print("You have removed the rubble."); - - /* Hack -- place an object */ - if (rand_int(100) < 10) - { - /* Create a simple object */ - place_object(y, x, FALSE, FALSE, OBJ_FOUND_RUBBLE); - - /* Observe new object */ - if (player_can_see_bold(y, x)) - { - msg_print("You have found something!"); - } - } - } - - else - { - /* Message, keep digging */ - msg_print(f_text + f_ptr->tunnel); - more = TRUE; - } - } - - /* Secret doors */ - else if (c_ptr->feat >= FEAT_SECRET) - { - /* Tunnel */ - skill_req = 30; - skill_req_1pct = 42; - if ((p_ptr->skill_dig > 30 + rand_int(1200)) && twall(y, x, FEAT_FLOOR)) - { - msg_print("You have finished the tunnel."); - c_ptr->mimic = 0; - lite_spot(y, x); - - /* Set off trap */ - if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); - } - - /* Keep trying */ - else - { - int feat; - - if (c_ptr->mimic) feat = c_ptr->mimic; - else - feat = c_ptr->feat; - - /* We may continue tunelling */ - msg_print(f_text + f_info[feat].tunnel); - more = TRUE; - - /* Occasional Search XXX XXX */ - if (rand_int(100) < 25) search(); - } - } - - /* Doors */ - else - { - /* Tunnel */ - skill_req = 30; - skill_req_1pct = 42; - if ((p_ptr->skill_dig > 30 + rand_int(1200)) && twall(y, x, FEAT_FLOOR)) - { - msg_print("You have finished the tunnel."); - } - - /* Keep trying */ - else - { - /* We may continue tunelling */ - msg_print(f_text + f_ptr->tunnel); - more = TRUE; - } - } - - if (more && magik(2)) - { - if (p_ptr->skill_dig < skill_req) - { - msg_print("You fail to make even the slightest of progress."); - more = FALSE; - } - else if (p_ptr->skill_dig < skill_req_1pct) - { - msg_print("This will take some time."); - } - } - - /* Notice new floor grids */ - if (!cave_floor_bold(y, x)) - { - /* Update some things */ - p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE); - } - - /* Result */ - return (more); -} - - -/* - * Tunnels through "walls" (including rubble and closed doors) - * - * Note that you must tunnel in order to hit invisible monsters - * in walls, though moving into walls still takes a turn anyway. - * - * Digging is very difficult without a "digger" weapon, but can be - * accomplished by strong players using heavy weapons. - */ -void do_cmd_tunnel(void) -{ - int y, x, dir; - - cave_type *c_ptr; - - bool_ more = FALSE; - - - if (p_ptr->wild_mode) return; - - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - /* Get a direction to tunnel, or Abort */ - if (get_rep_dir(&dir)) - { - /* Get location */ - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - /* Get grid */ - c_ptr = &cave[y][x]; - - /* No tunnelling through doors */ - if (((c_ptr->feat >= FEAT_DOOR_HEAD) && - (c_ptr->feat <= FEAT_DOOR_TAIL)) || (c_ptr->feat == FEAT_SHOP)) - { - /* Message */ - msg_print("You cannot tunnel through doors."); - } - - /* No tunnelling through air */ - else if (cave_floor_grid(c_ptr)) - { - /* Message */ - msg_print("You cannot tunnel through air."); - } - - /* A monster is in the way */ - else if (c_ptr->m_idx) - { - /* Take a turn */ - energy_use = 100; - - /* Message */ - msg_print("There is a monster in the way!"); - - /* Attack */ - py_attack(y, x, -1); - } - - /* Try digging */ - else - { - /* Tunnel through walls */ - more = do_cmd_tunnel_aux(y, x, dir); - } - } - - /* Cancel repetition unless we can continue */ - if (!more) disturb(0); -} - - -/* - * easy_open_door -- - * - * If there is a jammed/closed/locked door at the given location, - * then attempt to unlock/open it. Return TRUE if an attempt was - * made (successful or not), otherwise return FALSE. - * - * The code here should be nearly identical to that in - * do_cmd_open_test() and do_cmd_open_aux(). - */ - -bool_ easy_open_door(int y, int x) -{ - int i, j; - - cave_type *c_ptr = &cave[y][x]; - - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - - if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) - { - msg_print("You cannot open doors."); - - return (FALSE); - } - - /* Must be a closed door */ - if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL))) - { - /* Nope */ - return (FALSE); - } - - /* Jammed door */ - if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08) - { - /* Stuck */ - msg_print("The door appears to be stuck."); - } - - /* Locked door */ - else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01) - { - /* Disarm factor */ - i = p_ptr->skill_dis; - - /* Penalize some conditions */ - if (p_ptr->blind || no_lite()) i = i / 10; - if (p_ptr->confused || p_ptr->image) i = i / 10; - - /* Extract the lock power */ - j = c_ptr->feat - FEAT_DOOR_HEAD; - - /* Extract the difficulty XXX XXX XXX */ - j = i - (j * 4); - - /* Always have a small chance of success */ - if (j < 2) j = 2; - - /* Success */ - if (rand_int(100) < j) - { - /* Message */ - msg_print("You have picked the lock."); - - /* Set off trap */ - if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); - - /* Open the door */ - cave_set_feat(y, x, FEAT_OPEN); - - /* Update some things */ - p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); - - /* Sound */ - sound(SOUND_OPENDOOR); - - /* Process the appropriate hooks */ - process_hooks(HOOK_OPEN, "(d)", is_quest(dun_level)); - - /* Experience */ - gain_exp(1); - } - - /* Failure */ - else - { - /* Failure */ - if (flush_failure) flush(); - - /* Message */ - msg_print("You failed to pick the lock."); - } - } - - /* Closed door */ - else - { - /* Set off trap */ - if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); - - /* Open the door */ - cave_set_feat(y, x, FEAT_OPEN); - - /* Update some things */ - p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); - - /* Sound */ - sound(SOUND_OPENDOOR); - } - - /* Result */ - return (TRUE); -} - - -/* - * Perform the basic "disarm" command - * - * Assume destination is a visible trap - * - * Assume there is no monster blocking the destination - * - * Returns TRUE if repeated commands may continue - */ -static bool_ do_cmd_disarm_chest(int y, int x, s16b o_idx) -{ - int i, j; - - bool_ more = FALSE; - - object_type *o_ptr = &o_list[o_idx]; - - trap_type *t_ptr = &t_info[o_ptr->pval]; - - - /* Take a turn */ - energy_use = 100; - - /* Get the "disarm" factor */ - i = p_ptr->skill_dis; - - /* Penalize some conditions */ - if (p_ptr->blind || no_lite()) i = i / 10; - if (p_ptr->confused || p_ptr->image) i = i / 10; - - /* Extract the difficulty */ - j = i - t_ptr->difficulty * 3; - - /* Always have a small chance of success */ - if (j < 2) j = 2; - - /* Must find the trap first. */ - if (!object_known_p(o_ptr)) - { - msg_print("I don't see any traps."); - } - - /* Already disarmed/unlocked */ - else if (o_ptr->pval <= 0) - { - msg_print("The chest is not trapped."); - } - - /* Success (get a lot of experience) */ - else if (rand_int(100) < j) - { - msg_print("You have disarmed the chest."); - gain_exp(t_ptr->difficulty * 3); - o_ptr->pval = (0 - o_ptr->pval); - } - - /* Failure -- Keep trying */ - else if ((i > 5) && (randint(i) > 5)) - { - /* We may keep trying */ - more = TRUE; - if (flush_failure) flush(); - msg_print("You failed to disarm the chest."); - } - - /* Failure -- Set off the trap */ - else - { - msg_print("You set off a trap!"); - sound(SOUND_FAIL); - chest_trap(y, x, o_idx); - } - - /* Result */ - return (more); -} - - -/* - * Perform the basic "disarm" command - * - * Assume destination is a visible trap - * - * Assume there is no monster blocking the destination - * - * Returns TRUE if repeated commands may continue - */ -bool_ do_cmd_disarm_aux(int y, int x, int dir, int do_pickup) -{ - int i, j, power; - - cave_type *c_ptr; - - cptr name; - - bool_ more = FALSE; - - - /* Take a turn */ - energy_use = 100; - - /* Get grid and contents */ - c_ptr = &cave[y][x]; - - /* Access trap name */ - if (t_info[c_ptr->t_idx].ident) - name = (t_name + t_info[c_ptr->t_idx].name); - else - name = "unknown trap"; - - /* Get the "disarm" factor */ - i = p_ptr->skill_dis; - - /* Penalize some conditions */ - if (p_ptr->blind || no_lite()) i = i / 10; - if (p_ptr->confused || p_ptr->image) i = i / 10; - - /* XXX XXX XXX Variable power? */ - - /* Extract trap "power" */ - power = t_info[c_ptr->t_idx].difficulty; - - /* Extract the difficulty */ - j = i - power; - - /* Always have a small chance of success */ - if (j < 2) j = 2; - - /* Success */ - if (rand_int(100) < j) - { - /* Message */ - msg_format("You have disarmed the %s.", name); - - /* Reward */ - gain_exp(power); - - /* Forget the trap */ - c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT); - - /* Remove the trap */ - c_ptr->t_idx = 0; - - /* Move the player onto the trap */ - if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR)) - move_player_aux(dir, do_pickup, 0, TRUE); - - /* Remove trap attr from grid */ - note_spot(y, x); - lite_spot(y, x); - } - - /* Failure -- Keep trying */ - else if ((i > 5) && (randint(i) > 5)) - { - /* Failure */ - if (flush_failure) flush(); - - /* Message */ - msg_format("You failed to disarm the %s.", name); - - /* We may keep trying */ - more = TRUE; - } - - /* Failure -- Set off the trap */ - else - { - /* Message */ - msg_format("You set off the %s!", name); - - /* Move the player onto the trap */ - if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR)) - move_player_aux(dir, do_pickup, 0, FALSE); - } - - /* Result */ - return (more); -} - - -/* - * Disamrs the monster traps(no failure) - */ -void do_cmd_disarm_mon_trap(int y, int x) -{ - msg_print("You disarm the monster trap."); - - place_floor_convert_glass(y, x); - cave[p_ptr->py][p_ptr->px].special = cave[p_ptr->py][p_ptr->px].special2 = 0; -} - - -/* - * Disarms a trap, or chest - */ -void do_cmd_disarm(void) -{ - int y, x, dir; - - s16b o_idx; - - cave_type *c_ptr; - - bool_ more = FALSE; - - - /* Option: Pick a direction */ - if (easy_disarm) - { - int num_traps, num_chests; - - /* Count visible traps */ - num_traps = count_feats(&y, &x, is_trap, TRUE); - - /* Count chests (trapped) */ - num_chests = count_chests(&y, &x, TRUE); - - /* See if only one target */ - if (num_traps || num_chests) - { - if (num_traps + num_chests <= 1) - command_dir = coords_to_dir(y, x); - } - } - - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - /* Get a direction (or abort) */ - if (get_rep_dir(&dir)) - { - /* Get location */ - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - /* Get grid and contents */ - c_ptr = &cave[y][x]; - - /* Check for chests */ - o_idx = chest_check(y, x); - - /* Disarm a trap */ - if (((c_ptr->t_idx == 0) || (!(c_ptr->info & CAVE_TRDT))) && - !o_idx && (c_ptr->feat != FEAT_MON_TRAP)) - { - /* Message */ - msg_print("You see nothing there to disarm."); - } - - /* Monster in the way */ - else if (c_ptr->m_idx) - { - /* Message */ - msg_print("There is a monster in the way!"); - - /* Attack */ - py_attack(y, x, -1); - } - - /* Disarm chest */ - else if (o_idx) - { - /* Disarm the chest */ - more = do_cmd_disarm_chest(y, x, o_idx); - } - - /* Disarm trap */ - else - { - /* Disarm the trap */ - if (c_ptr->feat == FEAT_MON_TRAP) - { - do_cmd_disarm_mon_trap(y, x); - more = FALSE; - } - else - more = do_cmd_disarm_aux(y, x, dir, always_pickup); - } - } - - /* Cancel repeat unless told not to */ - if (!more) disturb(0); -} - - -/* - * Perform the basic "bash" command - * - * Assume destination is a closed/locked/jammed door - * - * Assume there is no monster blocking the destination - * - * Returns TRUE if repeated commands may continue - */ -static bool_ do_cmd_bash_aux(int y, int x, int dir) -{ - int bash, temp; - - cave_type *c_ptr; - - bool_ more = FALSE; - - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - - if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR)) - { - msg_print("You cannot do that."); - - return (FALSE); - } - - /* Take a turn */ - energy_use = 100; - - /* Get grid */ - c_ptr = &cave[y][x]; - - /* Message */ - msg_print("You smash into the door!"); - - /* Hack -- Bash power based on strength */ - /* (Ranges from 3 to 20 to 100 to 200) */ - bash = adj_str_blow[p_ptr->stat_ind[A_STR]]; - - /* Extract door power */ - temp = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07); - - /* Compare bash power to door power XXX XXX XXX */ - temp = (bash - (temp * 10)); - - /* Hack -- always have a chance */ - if (temp < 1) temp = 1; - - /* Hack -- attempt to bash down the door */ - if (rand_int(100) < temp) - { - /* Message */ - msg_print("The door crashes open!"); - - /* Break down the door */ - if (rand_int(100) < 50) - { - /* Set off trap */ - if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); - - cave_set_feat(y, x, FEAT_BROKEN); - } - - /* Open the door */ - else - { - /* Set off trap */ - if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); - - cave_set_feat(y, x, FEAT_OPEN); - } - - /* Sound */ - sound(SOUND_OPENDOOR); - - /* Hack -- Fall through the door. Can't disarm while falling. */ - move_player_aux(dir, always_pickup, 0, FALSE); - - /* Update some things */ - p_ptr->update |= (PU_VIEW | PU_MON_LITE); - p_ptr->update |= (PU_DISTANCE); - } - - /* Saving throw against stun */ - else if (rand_int(100) < adj_dex_safe[p_ptr->stat_ind[A_DEX]] + p_ptr->lev) - { - /* Message */ - msg_print("The door holds firm."); - - /* Allow repeated bashing */ - more = TRUE; - } - - /* High dexterity yields coolness */ - else - { - /* Message */ - msg_print("You are off-balance."); - - /* Hack -- Lose balance ala paralysis */ - (void)set_paralyzed(2 + rand_int(2)); - } - - /* Result */ - return (more); -} - - -/* - * Bash open a door, success based on character strength - * - * For a closed door, pval is positive if locked; negative if stuck. - * - * For an open door, pval is positive for a broken door. - * - * A closed door can be opened - harder if locked. Any door might be - * bashed open (and thereby broken). Bashing a door is (potentially) - * faster! You move into the door way. To open a stuck door, it must - * be bashed. A closed door can be jammed (see do_cmd_spike()). - * - * Creatures can also open or bash doors, see elsewhere. - */ -void do_cmd_bash(void) -{ - int y, x, dir; - - cave_type *c_ptr; - - bool_ more = FALSE; - - monster_race *r_ptr = &r_info[p_ptr->body_monster]; - - - if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR)) - { - msg_print("You cannot do that."); - - return; - } - - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - /* Get a "repeated" direction */ - if (get_rep_dir(&dir)) - { - /* Bash location */ - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - /* Get grid */ - c_ptr = &cave[y][x]; - - /* Nothing useful */ - if ((c_ptr->feat < FEAT_DOOR_HEAD || - c_ptr->feat > FEAT_DOOR_TAIL) && - (c_ptr->feat < FEAT_ALTAR_HEAD || - c_ptr->feat > FEAT_ALTAR_TAIL) && (c_ptr->feat != FEAT_FOUNTAIN)) - { - /* Message */ - msg_print("You see nothing there to bash."); - } - - /* Monster in the way */ - else if (c_ptr->m_idx) - { - /* Take a turn */ - energy_use = 100; - - /* Message */ - msg_print("There is a monster in the way!"); - - /* Attack */ - py_attack(y, x, -1); - } - - else if (c_ptr->feat >= FEAT_ALTAR_HEAD && - c_ptr->feat <= FEAT_ALTAR_TAIL) - { - more = do_cmd_bash_altar(y, x); - } - /* Bash a closed door */ - else if (c_ptr->feat == FEAT_FOUNTAIN) - { - more = do_cmd_bash_fountain(y, x); - } - else - { - /* Bash the door */ - more = do_cmd_bash_aux(y, x, dir); - } - } - - /* Unless valid action taken, cancel bash */ - if (!more) disturb(0); -} - - - -/* - * Manipulate an adjacent grid in some way - * - * Attack monsters, tunnel through walls, disarm traps, open doors. - * - * Consider confusion XXX XXX XXX - * - * This command must always take a turn, to prevent free detection - * of invisible monsters. - */ -void do_cmd_alter(void) -{ - int y, x, dir; - - cave_type *c_ptr; - - bool_ more = FALSE; - - - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - /* Get a direction */ - if (get_rep_dir(&dir)) - { - /* Get location */ - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - /* Get grid */ - c_ptr = &cave[y][x]; - - /* Take a turn */ - energy_use = 100; - - /* Attack monsters */ - if (c_ptr->m_idx) - { - /* Attack */ - py_attack(y, x, -1); - } - - /* Open closed doors */ - else if ((c_ptr->feat >= FEAT_DOOR_HEAD) && - (c_ptr->feat <= FEAT_DOOR_TAIL)) - { - /* Tunnel */ - more = do_cmd_open_aux(y, x, dir); - } - - /* Tunnel through walls */ - else if (f_info[c_ptr->feat].flags1 & FF1_TUNNELABLE) - { - /* Tunnel */ - more = do_cmd_tunnel_aux(y, x, dir); - } - - /* Disarm traps */ - else if (c_ptr->t_idx != 0) - { - /* Tunnel */ - more = do_cmd_disarm_aux(y, x, dir, always_pickup); - } - - /* Oops */ - else - { - /* Oops */ - msg_print("You attack the empty air."); - } - } - - /* Cancel repetition unless we can continue */ - if (!more) disturb(0); -} - - -/* - * Find the index of some "spikes", if possible. - * - * XXX XXX XXX Let user choose a pile of spikes, perhaps? - */ -static bool_ get_spike(int *ip) -{ - int i; - - - /* Check every item in the pack */ - for (i = 0; i < INVEN_PACK; i++) - { - object_type *o_ptr = &p_ptr->inventory[i]; - - /* Skip non-objects */ - if (!o_ptr->k_idx) continue; - - /* Check the "tval" code */ - if (o_ptr->tval == TV_SPIKE) - { - /* Save the spike index */ - (*ip) = i; - - /* Success */ - return (TRUE); - } - } - - /* Oops */ - return (FALSE); -} - - - -/* - * Jam a closed door with a spike - * - * This command may NOT be repeated - */ -void do_cmd_spike(void) -{ - int y, x, dir, item; - - cave_type *c_ptr; - - - /* Get a "repeated" direction */ - if (get_rep_dir(&dir)) - { - /* Get location */ - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - /* Get grid and contents */ - c_ptr = &cave[y][x]; - - /* Require closed door */ - if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && - (c_ptr->feat <= FEAT_DOOR_TAIL))) - { - /* Message */ - msg_print("You see nothing there to spike."); - } - - /* Get a spike */ - else if (!get_spike(&item)) - { - /* Message */ - msg_print("You have no spikes!"); - } - - /* Is a monster in the way? */ - else if (c_ptr->m_idx) - { - /* Take a turn */ - energy_use = 100; - - /* Message */ - msg_print("There is a monster in the way!"); - - /* Attack */ - py_attack(y, x, -1); - } - - /* Go for it */ - else - { - /* Take a turn */ - energy_use = 100; - - /* Successful jamming */ - msg_print("You jam the door with a spike."); - - /* Convert "locked" to "stuck" XXX XXX XXX */ - if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08) c_ptr->feat += 0x08; - - /* Add one spike to the door */ - if (c_ptr->feat < FEAT_DOOR_TAIL) c_ptr->feat++; - - /* Use up, and describe, a single spike, from the bottom */ - inc_stack_size(item, -1); - } - } -} - - -static void do_cmd_walk_jump(int pickup, bool_ disarm) -{ - int dir; - - bool_ more = FALSE; - - - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - /* Get a "repeated" direction */ - if (get_rep_dir(&dir)) - { - /* Take a turn */ - energy_use = 100; - - /* Actually move the character */ - move_player(dir, pickup, disarm); - - /* Allow more walking */ - more = TRUE; - } - - /* Hack -- In small scale wilderness it takes MUCH more time to move */ - energy_use *= (p_ptr->wild_mode) ? ((MAX_HGT + MAX_WID) / 2) : 1; - - /* Hack again -- Is there a special encounter ??? */ - if (p_ptr->wild_mode && - magik(wf_info[wild_map[p_ptr->py][p_ptr->px].feat].level - (p_ptr->lev * 2))) - { - /* Go into large wilderness view */ - p_ptr->wilderness_x = p_ptr->px; - p_ptr->wilderness_y = p_ptr->py; - energy_use = 100; - change_wild_mode(); - - /* HACk -- set the encouter flag for the wilderness generation */ - generate_encounter = TRUE; - p_ptr->oldpx = MAX_WID / 2; - p_ptr->oldpy = MAX_HGT / 2; - - /* Inform the player of his horrible fate :=) */ - msg_print("You are ambushed!"); - } - - /* Cancel repeat unless we may continue */ - if (!more) disturb(0); -} - - -/* - * Support code for the "Walk" and "Jump" commands - */ -void do_cmd_walk(int pickup, bool_ disarm) -{ - /* Move (usually pickup) */ - - if (p_ptr->immovable) - { - do_cmd_unwalk(); - } - else - { - do_cmd_walk_jump(pickup, disarm); - } -} - - -void do_cmd_run_run() -{ - int dir; - - - /* Hack -- no running when confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - /* Get a "repeated" direction */ - if (get_rep_dir(&dir)) - { - /* Hack -- Set the run counter */ - running = (command_arg ? command_arg : 1000); - - /* First step */ - run_step(dir); - } - p_ptr->window |= (PW_OVERHEAD); -} - - -/* - * Start running. - */ -void do_cmd_run(void) -{ - if (p_ptr->immovable) - { - return; - } - else - { - do_cmd_run_run(); - } -} - - - -/* - * Stay still. Search. Enter stores. - * Pick up treasure if "pickup" is true. - */ -void do_cmd_stay(int pickup) -{ - cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; - - - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - - /* Take a turn */ - energy_use = 100; - - - /* Spontaneous Searching */ - if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos))) - { - search(); - } - - /* Continuous Searching */ - if (p_ptr->searching) - { - search(); - } - - - /* Handle "objects" */ - carry(pickup); - - - /* Hack -- enter a store if we are on one */ - if (c_ptr->feat == FEAT_SHOP) - { - /* Disturb */ - disturb(0); - - /* Hack -- enter store */ - command_new = '_'; - } -} - -/* - * Resting allows a player to safely restore his hp -RAK- - */ -void do_cmd_rest(void) -{ - /* Can't rest on a Void Jumpgate -- too dangerous */ - if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN) - { - /* 'R&\n' is one of our favourite macros, so we have to do this */ - if (flush_failure) flush(); - - /* Tell the player why */ - msg_print(format("Resting on a %s is too dangerous!", - f_name + f_info[cave[p_ptr->py][p_ptr->px].feat].name)); - - /* Done */ - return; - } - - /* Can't rest while undead, it would mean dying */ - if (p_ptr->necro_extra & CLASS_UNDEAD) - { - /* 'R&\n' is one of our favourite macros, so we have to do this */ - if (flush_failure) flush(); - - /* Tell the player why */ - msg_print("Resting is impossible while undead!"); - - /* Done */ - return; - } - - /* Prompt for time if needed */ - if (command_arg <= 0) - { - cptr p = "Rest (0-9999, '*' for HP/SP, '&' as needed): "; - - char out_val[80]; - - /* Default */ - strcpy(out_val, "&"); - - /* Ask for duration */ - if (!get_string(p, out_val, 4)) return; - - /* Rest until done */ - if (out_val[0] == '&') - { - command_arg = ( -2); - } - - /* Rest a lot */ - else if (out_val[0] == '*') - { - command_arg = ( -1); - } - - /* Rest some */ - else - { - command_arg = atoi(out_val); - if (command_arg <= 0) return; - } - } - - - /* Paranoia */ - if (command_arg > 9999) command_arg = 9999; - - - /* Take a turn XXX XXX XXX (?) */ - energy_use = 100; - - /* Save the rest code */ - resting = command_arg; - - /* Cancel searching */ - p_ptr->searching = FALSE; - - /* Recalculate bonuses */ - p_ptr->update |= (PU_BONUS); - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Handle stuff */ - handle_stuff(); - - /* Refresh */ - Term_fresh(); -} - - - - - - -/* - * Determines the odds of an object breaking when thrown at a monster - * - * Note that artifacts never break, see the "drop_near()" function. - */ -int breakage_chance(object_type *o_ptr) -{ - int reducer = - 1 + ((get_skill(SKILL_ARCHERY)) ? (get_skill_scale(SKILL_ARCHERY, 10)) : 0); - - /* Examine the item type */ - switch (o_ptr->tval) - { - /* Always break */ - case TV_FLASK: - case TV_POTION: - case TV_POTION2: - case TV_BOTTLE: - case TV_FOOD: - { - return (100); - } - - /* Often break */ - case TV_LITE: - case TV_SCROLL: - case TV_SKELETON: - { - return (50); - } - - case TV_ARROW: - { - return (50 / reducer); - } - - /* Sometimes break */ - case TV_WAND: - case TV_SPIKE: - { - return (25); - } - - case TV_SHOT: - case TV_BOLT: - { - return (25 / reducer); - } - case TV_BOOMERANG: - { - return 1; - } - } - - /* Rarely break */ - return (10); -} - -/* - * Return multiplier of an object - */ -int get_shooter_mult(object_type *o_ptr) -{ - /* Assume a base multiplier */ - int tmul = 1; - - /* Analyze the launcher */ - switch (o_ptr->sval) - { - case SV_SLING: - { - /* Sling and ammo */ - tmul = 2; - break; - } - - case SV_SHORT_BOW: - { - /* Short Bow and Arrow */ - tmul = 2; - break; - } - - case SV_LONG_BOW: - { - /* Long Bow and Arrow */ - tmul = 3; - break; - } - - /* Light Crossbow and Bolt */ - case SV_LIGHT_XBOW: - { - tmul = 3; - break; - } - - /* Heavy Crossbow and Bolt */ - case SV_HEAVY_XBOW: - { - tmul = 4; - break; - } - } - return tmul; -} - - -/* - * Fire an object from the pack or floor. - * - * You may only fire items that "match" your missile launcher. - * - * You must use slings + pebbles/shots, bows + arrows, xbows + bolts. - * - * See "calc_bonuses()" for more calculations and such. - * - * Note that "firing" a missile is MUCH better than "throwing" it. - * - * Note: "unseen" monsters are very hard to hit. - * - * Objects are more likely to break if they "attempt" to hit a monster. - * - * Rangers (with Bows) and Anyone (with "Extra Shots") get extra shots. - * - * The "extra shot" code works by decreasing the amount of energy - * required to make each shot, spreading the shots out over time. - * - * Note that when firing missiles, the launcher multiplier is applied - * after all the bonuses are added in, making multipliers very useful. - * - * Note that Bows of "Extra Might" get extra range and an extra bonus - * for the damage multiplier. - * - * Note that Bows of "Extra Shots" give an extra shot. - */ -void do_cmd_fire(void) -{ - int dir, item; - - int j, y, x, ny, nx, ty, tx, by, bx; - - int oldtdam, tdam, tdis, thits, tmul; - - int bonus, chance; - - int cur_dis, visible; - - int breakage = -1, num_pierce = 0; - - s32b special = 0; - - object_type forge; - - object_type *q_ptr; - - object_type *o_ptr; - - object_type *j_ptr; - - bool_ hit_body = FALSE; - - byte missile_attr; - - char missile_char; - - char o_name[80]; - - cptr q, s; - - int msec = delay_factor * delay_factor * delay_factor; - - - /* Get the "bow" (if any) */ - j_ptr = &p_ptr->inventory[INVEN_BOW]; - - /* Require a launcher */ - if (!j_ptr->tval) - { - msg_print("You have nothing with which to fire."); - return; - } - - /* XXX HACK */ - if (j_ptr->tval == TV_INSTRUMENT) - { - msg_print("You cannot fire with an instrument."); - return; - } - - /* Get the "ammo" (if any) */ - o_ptr = &p_ptr->inventory[INVEN_AMMO]; - - item = INVEN_AMMO; - - /* If nothing correct try to choose from the backpack */ - if ((p_ptr->tval_ammo != o_ptr->tval) || (!o_ptr->k_idx)) - { - /* Require proper missile */ - item_tester_tval = p_ptr->tval_ammo; - - /* Get an item */ - q = "Your quiver is empty. Fire which item? "; - s = "You have nothing to fire."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - - /* Access the item */ - o_ptr = get_object(item); - } - - - /* Get a direction (or cancel) */ - if (!get_aim_dir(&dir)) return; - - - /* Get local object */ - q_ptr = &forge; - - /* Obtain a local object */ - object_copy(q_ptr, o_ptr); - - /* Single object */ - q_ptr->number = 1; - - /* Reduce stack and describe */ - inc_stack_size(item, -1); - - /* Break goi/manashield */ - if (p_ptr->invuln) - { - set_invuln(0); - } - if (p_ptr->disrupt_shield) - { - set_disrupt_shield(0); - } - - - /* Sound */ - sound(SOUND_SHOOT); - - - /* Describe the object */ - object_desc(o_name, q_ptr, FALSE, 3); - - /* Find the color and symbol for the object for throwing */ - missile_attr = object_attr(q_ptr); - missile_char = object_char(q_ptr); - - - /* Use the proper number of shots */ - thits = p_ptr->num_fire; - - /* Use a base distance */ - tdis = 10; - - /* Base damage from thrown object plus launcher bonus */ - tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + j_ptr->to_d; - - /* Actually "fire" the object */ - bonus = (p_ptr->to_h + p_ptr->to_h_ranged + q_ptr->to_h + j_ptr->to_h); - - chance = (p_ptr->skill_thb + (bonus * BTH_PLUS_ADJ)); - if (chance < 5) chance = 5; - - tmul = get_shooter_mult(j_ptr); - - /* Get extra "power" from "extra might" */ - tmul += p_ptr->xtra_might; - - /* Boost the damage */ - tdam *= tmul; - - /* Add in the player damage */ - tdam += p_ptr->to_d_ranged; - - /* Base range */ - tdis = 10 + 5 * tmul; - - - /* Take a (partial) turn */ - energy_use = (100 / thits); - - /* piercing shots ? */ - if (p_ptr->use_piercing_shots) - { - num_pierce = (get_skill(SKILL_COMBAT) / 10) - 1; - num_pierce = (num_pierce < 0) ? 0 : num_pierce; - } - - /* Start at the player */ - by = p_ptr->py; - bx = p_ptr->px; - y = p_ptr->py; - x = p_ptr->px; - - /* Predict the "target" location */ - tx = p_ptr->px + 99 * ddx[dir]; - ty = p_ptr->py + 99 * ddy[dir]; - - /* Check for "target request" */ - if ((dir == 5) && target_okay()) - { - tx = target_col; - ty = target_row; - } - - - /* Hack -- Handle stuff */ - handle_stuff(); - - oldtdam = tdam; - while (TRUE) - { - /* Reset after a piercing shot */ - tdam = oldtdam; - - /* Travel until stopped */ - for (cur_dis = 0; cur_dis <= tdis; ) - { - /* Hack -- Stop at the target */ - if ((y == ty) && (x == tx)) break; - - /* Calculate the new location (see "project()") */ - ny = y; - nx = x; - mmove2(&ny, &nx, by, bx, ty, tx); - - /* Stopped by walls/doors */ - if (!cave_floor_bold(ny, nx)) break; - - /* Advance the distance */ - cur_dis++; - - /* Save the new location */ - x = nx; - y = ny; - - - /* The player can see the (on screen) missile */ - if (panel_contains(y, x) && player_can_see_bold(y, x)) - { - /* Draw, Hilite, Fresh, Pause, Erase */ - print_rel(missile_char, missile_attr, y, x); - move_cursor_relative(y, x); - Term_fresh(); - Term_xtra(TERM_XTRA_DELAY, msec); - lite_spot(y, x); - Term_fresh(); - } - - /* The player cannot see the missile */ - else - { - /* Pause anyway, for consistancy */ - Term_xtra(TERM_XTRA_DELAY, msec); - } - - - /* Monster here, Try to hit it */ - if (cave[y][x].m_idx) - { - cave_type *c_ptr = &cave[y][x]; - - monster_type *m_ptr = &m_list[c_ptr->m_idx]; - monster_race *r_ptr = race_inf(m_ptr); - - /* Check the visibility */ - visible = m_ptr->ml; - - /* Note the collision */ - hit_body = TRUE; - - /* Did we hit it (penalize range) */ - if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml)) - { - bool_ fear = FALSE; - - /* Assume a default death */ - cptr note_dies = " dies."; - - /* Some monsters get "destroyed" */ - if ((r_ptr->flags3 & (RF3_DEMON)) || - (r_ptr->flags3 & (RF3_UNDEAD)) || - (r_ptr->flags2 & (RF2_STUPID)) || - (strchr("Evg", r_ptr->d_char))) - { - /* Special note at death */ - note_dies = " is destroyed."; - } - - - /* Handle unseen monster */ - if (!visible) - { - /* Invisible monster */ - msg_format("The %s finds a mark.", o_name); - } - - /* Handle visible monster */ - else - { - char m_name[80]; - - /* Get "the monster" or "it" */ - monster_desc(m_name, m_ptr, 0); - - /* Message */ - msg_format("The %s hits %s.", o_name, m_name); - - /* Hack -- Track this monster race */ - if (m_ptr->ml) monster_race_track(m_ptr->r_idx, - m_ptr->ego); - - /* Hack -- Track this monster */ - if (m_ptr->ml) health_track(c_ptr->m_idx); - - /* Anger friends */ - { - char m_name[80]; - monster_desc(m_name, m_ptr, 0); - switch (is_friend(m_ptr)) - { - case 1: - { - msg_format("%^s gets angry!", m_name); - change_side(m_ptr); - break; - } - case 0: - { - msg_format("%^s gets angry!", m_name); - m_ptr->status = MSTATUS_NEUTRAL_M; - break; - } - } - } - } - - /* Apply special damage XXX XXX XXX */ - tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special); - tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, SKILL_ARCHERY); - - /* No negative damage */ - if (tdam < 0) tdam = 0; - - /* Complex message */ - if (wizard) - { - msg_format("You do %d (out of %d) damage.", - tdam, m_ptr->hp); - } - - /* Hit the monster, check for death */ - if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies)) - { - /* Dead monster */ - } - - /* No death */ - else - { - /* Message */ - message_pain(c_ptr->m_idx, tdam); - - if (special) attack_special(m_ptr, special, tdam); - - /* Take note */ - if (fear && m_ptr->ml) - { - char m_name[80]; - - /* Sound */ - sound(SOUND_FLEE); - - /* Get the monster name (or "it") */ - monster_desc(m_name, m_ptr, 0); - - /* Message */ - msg_format("%^s flees in terror!", m_name); - } - } - } - - /* Stop looking */ - break; - } - } - - /* Exploding arrow ? */ - if (q_ptr->pval2 != 0) - { - int rad = 0, dam = - (damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d) * 2; - int flag = - PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | - PROJECT_JUMP; - switch (q_ptr->sval) - { - case SV_AMMO_LIGHT: - rad = 2; - dam /= 2; - break; - case SV_AMMO_NORMAL: - rad = 3; - break; - case SV_AMMO_HEAVY: - rad = 4; - dam *= 2; - break; - } - - project(0, rad, y, x, dam, q_ptr->pval2, flag); - } - - /* Chance of breakage (during attacks) */ - j = (hit_body ? breakage_chance(q_ptr) : 0); - - /* Break ? */ - if ((q_ptr->pval2 != 0) || (rand_int(100) < j)) - { - breakage = 100; - break; - } - - /* If the ammo doesn't break, it can pierce through */ - if ((num_pierce) && (hit_body) && - (magik(45 + get_skill(SKILL_ARCHERY)))) - { - num_pierce--; - hit_body = FALSE; - - /* If target isn't reached, continue moving to target */ - if ( !((tx < x && x < bx) || (bx < x && x < tx)) && - !((ty < y && y < by) || (by < y && y < ty))) - { - /* Continue moving in same direction if we reached the target */ - int dx = tx - bx; - int dy = ty - by; - tx = x + 99 * dx; - ty = y + 99 * dy; - - /* New base location */ - by = y; - bx = x; - } - - msg_format("The %s pierces through!", o_name); - } - else - break; - } - - /* Drop (or break) near that location */ - drop_near(q_ptr, breakage, y, x); -} - - -/* - * Why is this here? even if it's temporary boost... - * Moved into player_type, hoping it might be useful in future extensions - * -- pelpel - */ -/* int throw_mult = 1; */ - -/* - * Throw an object from the pack or floor. - * - * Note: "unseen" monsters are very hard to hit. - * - * Should throwing a weapon do full damage? Should it allow the magic - * to hit bonus of the weapon to have an effect? Should it ever cause - * the item to be destroyed? Should it do any damage at all? - */ -void do_cmd_throw(void) -{ - int dir, item; - - s32b special = 0; - - int j, y, x, ny, nx, ty, tx; - - int chance, tdam, tdis; - - int mul, div; - - int boulder_add = 0; - int boulder_mult = 0; - - int cur_dis, visible; - - object_type forge; - - object_type *q_ptr; - - object_type *o_ptr; - - bool_ hit_body = FALSE; - - bool_ hit_wall = FALSE; - - byte missile_attr; - - char missile_char; - - char o_name[80]; - - int msec = delay_factor * delay_factor * delay_factor; - - cptr q, s; - - u32b f1, f2, f3, f4, f5, esp; - - - /* Get an item */ - q = "Throw which item? "; - s = "You have nothing to throw."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Access the item */ - o_ptr = get_object(item); - - - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Hack - Cannot throw away 'no drop' cursed items */ - if (cursed_p(o_ptr) && (f4 & TR4_CURSE_NO_DROP)) - { - /* Oops */ - msg_print("Hmmm, you seem to be unable to throw it."); - - /* Nope */ - return; - } - - /* Boulder throwing */ - if ((o_ptr->tval == TV_JUNK) && (o_ptr->sval == SV_BOULDER) && (get_skill(SKILL_BOULDER))) - { - boulder_add = get_skill_scale(SKILL_BOULDER, 80); - boulder_mult = get_skill_scale(SKILL_BOULDER, 6); - } - - /* Get a direction (or cancel) */ - if (!get_aim_dir(&dir)) return; - - /* Break goi/manashield */ - if (p_ptr->invuln) - { - set_invuln(0); - } - if (p_ptr->disrupt_shield) - { - set_disrupt_shield(0); - } - - /* Get local object */ - q_ptr = &forge; - - /* Obtain a local object */ - object_copy(q_ptr, o_ptr); - - /* - * Hack -- If rods or wands are thrown, the total maximum timeout or - * charges need to be allocated between the two stacks. - */ - if (o_ptr->tval == TV_WAND) - { - q_ptr->pval = o_ptr->pval / o_ptr->number; - - if (o_ptr->number > 1) o_ptr->pval -= q_ptr->pval; - } - - /* Single object */ - q_ptr->number = 1; - - /* Reduce stack and describe */ - inc_stack_size(item, -1); - - /* Description */ - object_desc(o_name, q_ptr, FALSE, 3); - - /* Find the color and symbol for the object for throwing */ - missile_attr = object_attr(q_ptr); - missile_char = object_char(q_ptr); - - /* Extract a "distance multiplier" */ - /* Changed for 'launcher' corruption */ - mul = 10 + (2 * (p_ptr->throw_mult - 1)) + (2 * boulder_mult); - - /* Enforce a minimum "weight" of one pound */ - div = ((q_ptr->weight > 10) ? q_ptr->weight : 10); - - /* Hack -- Distance -- Reward strength, penalize weight */ - tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div; - - /* Max distance of 10-18 */ - if (tdis > mul) tdis = mul; - - /* Hack -- Base damage from thrown object */ - tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + boulder_add; - tdam *= p_ptr->throw_mult + boulder_mult; - - /* Chance of hitting - adjusted for Weaponmasters -- Gumby */ - chance = (p_ptr->skill_tht + (p_ptr->to_h * BTH_PLUS_ADJ)); - - /* Take a turn */ - energy_use = 100; - - - /* Start at the player */ - y = p_ptr->py; - x = p_ptr->px; - - /* Predict the "target" location */ - tx = p_ptr->px + 99 * ddx[dir]; - ty = p_ptr->py + 99 * ddy[dir]; - - /* Check for "target request" */ - if ((dir == 5) && target_okay()) - { - tx = target_col; - ty = target_row; - } - - - /* Hack -- Handle stuff */ - handle_stuff(); - - - /* Travel until stopped */ - for (cur_dis = 0; cur_dis <= tdis; ) - { - /* Hack -- Stop at the target */ - if ((y == ty) && (x == tx)) break; - - /* Calculate the new location (see "project()") */ - ny = y; - nx = x; - mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx); - - /* Stopped by walls/doors */ - if (!cave_floor_bold(ny, nx)) - { - hit_wall = TRUE; - break; - } - - /* Advance the distance */ - cur_dis++; - - /* Save the new location */ - x = nx; - y = ny; - - - /* The player can see the (on screen) missile */ - if (panel_contains(y, x) && player_can_see_bold(y, x)) - { - /* Draw, Hilite, Fresh, Pause, Erase */ - print_rel(missile_char, missile_attr, y, x); - move_cursor_relative(y, x); - Term_fresh(); - Term_xtra(TERM_XTRA_DELAY, msec); - lite_spot(y, x); - Term_fresh(); - } - - /* The player cannot see the missile */ - else - { - /* Pause anyway, for consistancy */ - Term_xtra(TERM_XTRA_DELAY, msec); - } - - - /* Monster here, Try to hit it */ - if (cave[y][x].m_idx) - { - cave_type *c_ptr = &cave[y][x]; - - monster_type *m_ptr = &m_list[c_ptr->m_idx]; - monster_race *r_ptr = race_inf(m_ptr); - - /* Check the visibility */ - visible = m_ptr->ml; - - /* Note the collision */ - hit_body = TRUE; - - /* Did we hit it (penalize range) */ - if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml)) - { - bool_ fear = FALSE; - - /* Assume a default death */ - cptr note_dies = " dies."; - - /* Some monsters get "destroyed" */ - if ((r_ptr->flags3 & (RF3_DEMON)) || - (r_ptr->flags3 & (RF3_UNDEAD)) || - (r_ptr->flags2 & (RF2_STUPID)) || - (strchr("Evg", r_ptr->d_char))) - { - /* Special note at death */ - note_dies = " is destroyed."; - } - - - /* Handle unseen monster */ - if (!visible) - { - /* Invisible monster */ - msg_format("The %s finds a mark.", o_name); - } - - /* Handle visible monster */ - else - { - char m_name[80]; - - /* Get "the monster" or "it" */ - monster_desc(m_name, m_ptr, 0); - - /* Message */ - msg_format("The %s hits %s.", o_name, m_name); - - /* Hack -- Track this monster race */ - if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); - - /* Hack -- Track this monster */ - if (m_ptr->ml) health_track(c_ptr->m_idx); - } - - /* Apply special damage XXX XXX XXX */ - tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special); - tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, o_ptr->sval == SV_BOULDER ? SKILL_BOULDER : SKILL_ARCHERY); - - /* No negative damage */ - if (tdam < 0) tdam = 0; - - /* Complex message */ - if (wizard) - { - msg_format("You do %d (out of %d) damage.", - tdam, m_ptr->hp); - } - - /* Hit the monster, check for death */ - if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies)) - { - /* Dead monster */ - } - - /* No death */ - else - { - /* Message */ - message_pain(c_ptr->m_idx, tdam); - - if (special) attack_special(m_ptr, special, tdam); - - /* Anger friends */ - if (!(k_info[q_ptr->k_idx].tval == TV_POTION)) - { - char m_name[80]; - monster_desc(m_name, m_ptr, 0); - switch (is_friend(m_ptr)) - { - case 1: - msg_format("%^s gets angry!", m_name); - change_side(m_ptr); - break; - case 0: - msg_format("%^s gets angry!", m_name); - m_ptr->status = MSTATUS_NEUTRAL_M; - break; - } - } - - /* Take note */ - if (fear && m_ptr->ml) - { - char m_name[80]; - - /* Sound */ - sound(SOUND_FLEE); - - /* Get the monster name (or "it") */ - monster_desc(m_name, m_ptr, 0); - - /* Message */ - msg_format("%^s flees in terror!", m_name); - } - } - } - - /* Stop looking */ - break; - } - } - - /* Chance of breakage (during attacks) */ - j = (hit_body ? breakage_chance(q_ptr) : 0); - - /* Potions smash open */ - if (k_info[q_ptr->k_idx].tval == TV_POTION) - { - if ((hit_body) || (hit_wall) || (randint(100) < j)) - { - /* Message */ - msg_format("The %s shatters!", o_name); - - if (potion_smash_effect(0, y, x, q_ptr->sval)) - { - if (cave[y][x].m_idx) - { - char m_name[80]; - monster_desc(m_name, &m_list[cave[y][x].m_idx], 0); - switch (is_friend(&m_list[cave[y][x].m_idx])) - { - case 1: - msg_format("%^s gets angry!", m_name); - change_side(&m_list[cave[y][x].m_idx]); - break; - case 0: - msg_format("%^s gets angry!", m_name); - m_list[cave[y][x].m_idx].status = MSTATUS_NEUTRAL_M; - break; - } - } - } - - return; - } - else - { - j = 0; - } - } - - /* Drop (or break) near that location */ - drop_near(q_ptr, j, y, x); -} - - -/* - * Throw a boomerang object from the equipement(bow). - * - * Note: "unseen" monsters are very hard to hit. - * - * Should throwing a weapon do full damage? Should it allow the magic - * to hit bonus of the weapon to have an effect? Should it ever cause - * the item to be destroyed? Should it do any damage at all? - */ -void do_cmd_boomerang(void) -{ - int dir; - - int j, y, x, ny, nx, ty, tx; - - int chance, tdam, tdis; - - int mul, div; - - int cur_dis, visible; - - object_type forge; - - object_type *q_ptr; - - object_type *o_ptr; - - bool_ hit_body = FALSE; - - byte missile_attr; - - char missile_char; - - char o_name[80]; - - s32b special = 0; - - int msec = delay_factor * delay_factor * delay_factor; - - - /* Get the "bow" (if any) */ - o_ptr = &p_ptr->inventory[INVEN_BOW]; - - - /* Get a direction (or cancel) */ - if (!get_aim_dir(&dir)) return; - - - /* Get local object */ - q_ptr = &forge; - - /* Obtain a local object */ - object_copy(q_ptr, o_ptr); - - /* Single object */ - q_ptr->number = 1; - - /* Description */ - object_desc(o_name, q_ptr, FALSE, 3); - - /* Find the color and symbol for the object for throwing */ - missile_attr = object_attr(q_ptr); - missile_char = object_char(q_ptr); - - /* Extract a "distance multiplier" */ - /* Changed for 'launcher' corruption */ - mul = 10 + 2 * (p_ptr->throw_mult - 1); - - /* Enforce a minimum "weight" of one pound */ - div = ((q_ptr->weight > 10) ? q_ptr->weight : 10); - - /* Hack -- Distance -- Reward strength, penalize weight */ - tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div; - - /* Max distance of 10-18 */ - if (tdis > mul) tdis = mul; - - /* Hack -- Base damage from thrown object */ - tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d; - tdam *= p_ptr->throw_mult; - - /* Chance of hitting */ - chance = - (p_ptr->skill_tht + - ((p_ptr->to_h + p_ptr->to_h_ranged) * BTH_PLUS_ADJ)); - - chance += get_skill(SKILL_BOOMERANG); - - /* Take a turn */ - energy_use = 100; - - - /* Start at the player */ - y = p_ptr->py; - x = p_ptr->px; - - /* Predict the "target" location */ - tx = p_ptr->px + 99 * ddx[dir]; - ty = p_ptr->py + 99 * ddy[dir]; - - /* Check for "target request" */ - if ((dir == 5) && target_okay()) - { - tx = target_col; - ty = target_row; - } - - - /* Hack -- Handle stuff */ - handle_stuff(); - - - /* Travel until stopped */ - for (cur_dis = 0; cur_dis <= tdis; ) - { - /* Hack -- Stop at the target */ - if ((y == ty) && (x == tx)) break; - - /* Calculate the new location (see "project()") */ - ny = y; - nx = x; - mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx); - - /* Stopped by walls/doors */ - if (!cave_floor_bold(ny, nx)) - { - break; - } - - /* Advance the distance */ - cur_dis++; - - /* Save the new location */ - x = nx; - y = ny; - - - /* The player can see the (on screen) missile */ - if (panel_contains(y, x) && player_can_see_bold(y, x)) - { - /* Draw, Hilite, Fresh, Pause, Erase */ - print_rel(missile_char, missile_attr, y, x); - move_cursor_relative(y, x); - Term_fresh(); - Term_xtra(TERM_XTRA_DELAY, msec); - lite_spot(y, x); - Term_fresh(); - } - - /* The player cannot see the missile */ - else - { - /* Pause anyway, for consistancy */ - Term_xtra(TERM_XTRA_DELAY, msec); - } - - - /* Monster here, Try to hit it */ - if (cave[y][x].m_idx) - { - cave_type *c_ptr = &cave[y][x]; - - monster_type *m_ptr = &m_list[c_ptr->m_idx]; - monster_race *r_ptr = race_inf(m_ptr); - - /* Check the visibility */ - visible = m_ptr->ml; - - /* Note the collision */ - hit_body = TRUE; - - /* Did we hit it (penalize range) */ - if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml)) - { - bool_ fear = FALSE; - - /* Assume a default death */ - cptr note_dies = " dies."; - - /* Some monsters get "destroyed" */ - if ((r_ptr->flags3 & (RF3_DEMON)) || - (r_ptr->flags3 & (RF3_UNDEAD)) || - (r_ptr->flags2 & (RF2_STUPID)) || - (strchr("Evg", r_ptr->d_char))) - { - /* Special note at death */ - note_dies = " is destroyed."; - } - - - /* Handle unseen monster */ - if (!visible) - { - /* Invisible monster */ - msg_format("The %s finds a mark.", o_name); - } - - /* Handle visible monster */ - else - { - char m_name[80]; - - /* Get "the monster" or "it" */ - monster_desc(m_name, m_ptr, 0); - - /* Message */ - msg_format("The %s hits %s.", o_name, m_name); - - /* Hack -- Track this monster race */ - if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); - - /* Hack -- Track this monster */ - if (m_ptr->ml) health_track(c_ptr->m_idx); - } - - /* Apply special damage XXX XXX XXX */ - tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special); - tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, SKILL_ARCHERY); - - /* No negative damage */ - if (tdam < 0) tdam = 0; - - /* Complex message */ - if (wizard) - { - msg_format("You do %d (out of %d) damage.", - tdam, m_ptr->hp); - } - - /* Hit the monster, check for death */ - if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies)) - { - /* Dead monster */ - } - - /* No death */ - else - { - /* Message */ - message_pain(c_ptr->m_idx, tdam); - - if (special) attack_special(m_ptr, special, tdam); - - /* Anger friends */ - if (!(k_info[q_ptr->k_idx].tval == TV_POTION)) - { - char m_name[80]; - monster_desc(m_name, m_ptr, 0); - switch (is_friend(m_ptr)) - { - case 1: - msg_format("%^s gets angry!", m_name); - change_side(m_ptr); - break; - case 0: - msg_format("%^s gets angry!", m_name); - m_ptr->status = MSTATUS_NEUTRAL_M; - break; - } - } - - /* Take note */ - if (fear && m_ptr->ml) - { - char m_name[80]; - - /* Sound */ - sound(SOUND_FLEE); - - /* Get the monster name (or "it") */ - monster_desc(m_name, m_ptr, 0); - - /* Message */ - msg_format("%^s flees in terror!", m_name); - } - } - - /* Chance of breakage (during attacks) */ - j = (hit_body ? breakage_chance(o_ptr) : 0); - - /* Break the boomerang */ - if (!(o_ptr->art_name || artifact_p(o_ptr)) && - (rand_int(100) < j)) - { - msg_print(format("Your %s is destroyed.", o_name)); - inc_stack_size_ex(INVEN_BOW, -1, OPTIMIZE, NO_DESCRIBE); - } - } - - /* Stop looking */ - break; - } - } - - /* Travel back to the player */ - for (cur_dis = 0; cur_dis <= tdis; ) - { - /* Hack -- Stop at the target */ - if ((y == p_ptr->py) && (x == p_ptr->px)) break; - - /* Calculate the new location (see "project()") */ - ny = y; - nx = x; - mmove2(&ny, &nx, ty, tx, p_ptr->py, p_ptr->px); - - /* Advance the distance */ - cur_dis++; - - /* Save the new location */ - x = nx; - y = ny; - - - /* The player can see the (on screen) missile */ - if (panel_contains(y, x) && player_can_see_bold(y, x)) - { - /* Draw, Hilite, Fresh, Pause, Erase */ - print_rel(missile_char, missile_attr, y, x); - move_cursor_relative(y, x); - Term_fresh(); - Term_xtra(TERM_XTRA_DELAY, msec); - lite_spot(y, x); - Term_fresh(); - } - - /* The player cannot see the missile */ - else - { - /* Pause anyway, for consistancy */ - Term_xtra(TERM_XTRA_DELAY, msec); - } - } -} - - -/* - * Try to ``walk'' using phase door. - */ -void do_cmd_unwalk() -{ - int dir, y, x, feat; - - cave_type *c_ptr; - - bool_ more = FALSE; - - - if (!get_rep_dir(&dir)) return; - - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - c_ptr = &cave[y][x]; - feat = c_ptr->feat; - - /* Must have knowledge to know feature XXX XXX */ - if (!(c_ptr->info & (CAVE_MARK))) feat = FEAT_NONE; - - /* Take a turn */ - energy_use = 100; - energy_use *= (p_ptr->wild_mode) ? (5 * (MAX_HGT + MAX_WID) / 2) : 1; - - - /* Allow repeated command */ - if (command_arg) - { - /* Set repeat count */ - command_rep = command_arg - 1; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Cancel the arg */ - command_arg = 0; - } - - - /* Attack monsters */ - if (c_ptr->m_idx > 0) - { - /* Attack */ - py_attack(y, x, -1); - } - - /* Exit the area */ - else if ((!dun_level) && (!p_ptr->wild_mode) && - ((x == 0) || (x == cur_wid - 1) || (y == 0) || (y == cur_hgt - 1))) - { - /* Can the player enter the grid? */ - if (player_can_enter(c_ptr->mimic)) - { - /* Hack: move to new area */ - if ((y == 0) && (x == 0)) - { - p_ptr->wilderness_y--; - p_ptr->wilderness_x--; - p_ptr->oldpy = cur_hgt - 2; - p_ptr->oldpx = cur_wid - 2; - ambush_flag = FALSE; - } - - else if ((y == 0) && (x == MAX_WID - 1)) - { - p_ptr->wilderness_y--; - p_ptr->wilderness_x++; - p_ptr->oldpy = cur_hgt - 2; - p_ptr->oldpx = 1; - ambush_flag = FALSE; - } - - else if ((y == MAX_HGT - 1) && (x == 0)) - { - p_ptr->wilderness_y++; - p_ptr->wilderness_x--; - p_ptr->oldpy = 1; - p_ptr->oldpx = cur_wid - 2; - ambush_flag = FALSE; - } - - else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1)) - { - p_ptr->wilderness_y++; - p_ptr->wilderness_x++; - p_ptr->oldpy = 1; - p_ptr->oldpx = 1; - ambush_flag = FALSE; - } - - else if (y == 0) - { - p_ptr->wilderness_y--; - p_ptr->oldpy = cur_hgt - 2; - p_ptr->oldpx = x; - ambush_flag = FALSE; - } - - else if (y == cur_hgt - 1) - { - p_ptr->wilderness_y++; - p_ptr->oldpy = 1; - p_ptr->oldpx = x; - ambush_flag = FALSE; - } - - else if (x == 0) - { - p_ptr->wilderness_x--; - p_ptr->oldpx = cur_wid - 2; - p_ptr->oldpy = y; - ambush_flag = FALSE; - } - - else if (x == cur_wid - 1) - { - p_ptr->wilderness_x++; - p_ptr->oldpx = 1; - p_ptr->oldpy = y; - ambush_flag = FALSE; - } - - p_ptr->leaving = TRUE; - - return; - } - } - - /* Hack -- Ignore weird terrain types. */ - else if (!cave_floor_grid(c_ptr)) - { - teleport_player(10); - } - - /* Enter quests */ - else if (((feat >= FEAT_QUEST_ENTER) && (feat <= FEAT_QUEST_UP)) || - ((feat >= FEAT_LESS) && (feat <= FEAT_MORE))) - { - move_player(dir, always_pickup, TRUE); - more = FALSE; - } - - /* Hack -- Ignore wilderness mofe. */ - else if (p_ptr->wild_mode) - { - /* Chance to not blink right */ - if (magik(15)) - { - do - { - dir = rand_range(1, 9); - } - while (dir == 5); - } - - move_player(dir, always_pickup, TRUE); - } - - /* Walking semantics */ - else - { - teleport_player_directed(10, dir); - } - - /* Cancel repetition unless we can continue */ - if (!more) disturb(0); -} - - -static bool_ tport_vertically(bool_ how) -{ - /* arena or quest -KMW- */ - if ((p_ptr->inside_arena) || (p_ptr->inside_quest)) - { - msg_print("There is no effect."); - return (FALSE); - } - - if (dungeon_flags2 & DF2_NO_EASY_MOVE) - { - msg_print("Some powerful force prevents you from teleporting."); - return FALSE; - } - - /* Go down */ - if (how) - { - if (dun_level >= d_info[dungeon_type].maxdepth) - { - msg_print("The floor is impermeable."); - return (FALSE); - } - - msg_print("You sink through the floor."); - dun_level++; - p_ptr->leaving = TRUE; - } - else - { - if (dun_level < d_info[dungeon_type].mindepth) - { - msg_print("There is nothing above you but air."); - return (FALSE); - } - - msg_print("You rise through the ceiling."); - dun_level--; - p_ptr->leaving = TRUE; - } - - return (TRUE); -} - - -/* - * Do a special ``movement'' action. Meant to be used for ``immovable'' - * characters. - */ -void do_cmd_immovable_special(void) -{ - int i, ii, ij, dir; - - int foo = p_ptr->immov_cntr; - - int lose_sp = 0; - - int lose_hp = 0; - - bool_ did_act = FALSE; - - bool_ did_load = FALSE; - - - if (foo > 1) - { - if (p_ptr->csp > foo / 2) - { - - msg_format("This will drain %d mana points!", foo / 2); - if (!get_check("Proceed? ")) return; - - lose_sp = foo / 2; - - } - else if (p_ptr->chp > foo / 2) - { - - msg_format("Warning: This will drain %d hit points!", foo / 2); - if (!get_check("Proceed? ")) return; - - lose_hp = foo / 2; - - } - else - { - msg_print("You can't use your powers yet."); - return; - } - } - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - - /* Interact until done */ - while (1) - { - /* Clear screen */ - Term_clear(); - - /* Ask for a choice */ - prt("Do what special action:", 2, 0); - - /* Give some choices */ - prt("(a) Teleport to a specific place.", 4, 5); - prt("(b) Fetch an item.", 5, 5); - prt("(c) Go up 50'", 6, 5); - prt("(d) Go down 50'", 7, 5); - - /* Prompt */ - prt("Command: ", 9, 0); - - /* Prompt */ - i = inkey(); - - /* Done */ - if (i == ESCAPE) break; - - /* Tele-to */ - if (i == 'a') - { - Term_load(); - character_icky = FALSE; - did_load = TRUE; - - if (!tgt_pt(&ii, &ij)) break; - - /* Teleport to the target */ - teleport_player_to(ij, ii); - - did_act = TRUE; - break; - } - - /* Fetch item */ - else if (i == 'b') - { - Term_load(); - character_icky = FALSE; - did_load = TRUE; - - if (!get_aim_dir(&dir)) return; - fetch(dir, p_ptr->lev * 15, FALSE); - py_pickup_floor(always_pickup); - - did_act = TRUE; - break; - } - - /* Move up */ - else if (i == 'c') - { - Term_load(); - character_icky = FALSE; - did_load = TRUE; - - if (!tport_vertically(FALSE)) return; - - did_act = TRUE; - break; - } - - /* Move down */ - else if (i == 'd') - { - Term_load(); - character_icky = FALSE; - did_load = TRUE; - - if (!tport_vertically(TRUE)) return; - - did_act = TRUE; - break; - } - - /* Unknown option */ - else - { - bell(); - } - - } - - /* Check if screen was restored before */ - if (!did_load) - { - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; - } - - /* Apply stat losses if something was done */ - if (did_act) - { - p_ptr->immov_cntr += 101 - (p_ptr->lev * 2); - - if (lose_sp) - { - p_ptr->csp -= lose_sp; - p_ptr->redraw |= (PR_MANA); - } - - if (lose_hp) - { - p_ptr->chp -= lose_hp; - p_ptr->redraw |= (PR_HP); - } - - energy_use = 100; - } -} - -/* Can we sacrifice it ? */ -static bool_ item_tester_hook_sacrifiable(object_type *o_ptr) -{ - GOD(GOD_MELKOR) - { - /* Corpses are */ - if (o_ptr->tval == TV_CORPSE && o_ptr->sval == SV_CORPSE_CORPSE) - return (TRUE); - - /* Books without any udun spells */ - if ((o_ptr->tval == TV_BOOK) && udun_in_book(o_ptr->sval, o_ptr->pval) <= 0) - { - return TRUE; - } - } - - /* Assume not */ - return (FALSE); -} - -/* - * Is item eligible for sacrifice to Aule? - */ -static bool_ item_tester_hook_sacrifice_aule(object_type *o_ptr) -{ - /* perhaps restrict this only to metal armour and weapons */ - return (o_ptr->found == OBJ_FOUND_SELFMADE); -} - -/* - * Handle sacrifices to Aule - */ -static void do_cmd_sacrifice_aule() -{ - int item; - - item_tester_hook = item_tester_hook_sacrifice_aule; - if (!get_item(&item, - "Sacrifice which item? ", - "You have nothing to sacrifice.", - USE_INVEN)) - { - return; - } - - /* Increase piety by the value of the item / 10. */ - { - object_type *o_ptr = get_object(item); - s32b delta = object_value(o_ptr) / 10; - - inc_piety(GOD_ALL, delta); - } - - /* Destroy the object */ - inc_stack_size(item, -1); -} - -/* - * Handle sacrifices. - * Grace is increased by value of sacrifice. - */ -void do_cmd_sacrifice(void) -{ - byte on_what = cave[p_ptr->py][p_ptr->px].feat; - - /* Check valididty */ - if ((on_what < FEAT_ALTAR_HEAD) || (on_what > FEAT_ALTAR_TAIL)) - { - show_god_info(FALSE); - return; - } - else - { - int agod = on_what - FEAT_ALTAR_HEAD + 1; - - /* Not worshipping a god ? ahhhh! */ - GOD(GOD_NONE) - { - int i; - - for (i = 0; i < 10; i++) - { - if (deity_info[agod].desc[i] != NULL) - msg_print(deity_info[agod].desc[i]); - } - if (get_check(format("Do you want to worship %s? ", deity_info[agod].name))) - { - follow_god(agod, FALSE); - p_ptr->grace = -200; - inc_piety(p_ptr->pgod, 0); - } - } - else if (p_ptr->pgod == agod) - { - GOD(GOD_MELKOR) - { - /* One can sacrifice some HP for piety or damage */ - if ((p_ptr->mhp > 10) && (p_ptr->chp > 10) && get_check("Do you want to sacrifice a part of yourself? ")) - { - /* 10 HP = 300 * wis piety */ - if (get_check("Do you want to sacrifice for more piety instead of damage? ")) - { - int x = wisdom_scale(6); - if (x < 1) x = 1; - - p_ptr->hp_mod -= 10; - take_hit(10, "self sacrifice to Melkor"); - msg_print("Your life slips away, and Melkor seems happier."); - inc_piety(GOD_MELKOR, x * 300); - p_ptr->update |= (PU_HP); - } - /* 10 HP = +wis damage */ - else - { - take_hit(10, "self sacrifice to Melkor"); - msg_print("Your life slips away, and your arms grow stronger."); - p_ptr->melkor_sacrifice++; - p_ptr->update |= (PU_BONUS | PU_HP); - } - } - else - { - int item; - object_type *o_ptr; - - /* Restrict choices to food */ - item_tester_hook = item_tester_hook_sacrifiable; - - /* Get an item */ - if (!get_item(&item, "Sacrifice which item? ", "You have nothing to sacrifice.", (USE_INVEN))) return; - o_ptr = get_object(item); - - /* Piety for corpses is based on monster level */ - if (o_ptr->tval == TV_CORPSE) - { - inc_piety(GOD_MELKOR, 2 * r_info[o_ptr->pval2].level); - } - - /* In books it depends of the spell levels*/ - if (o_ptr->tval == TV_BOOK) - { - int x = levels_in_book(o_ptr->sval, o_ptr->pval); - - inc_piety(GOD_MELKOR, 2 * x); - } - - /* Remove the item */ - inc_stack_size(item, -1); - } - } - - GOD(GOD_AULE) - { - do_cmd_sacrifice_aule(); - } - - } - } -} - - -/* - * scan_monst -- - * - * Return a list of o_list[] indexes of items of the given monster - */ -bool_ scan_monst(int *items, int *item_num, int m_idx) -{ - int this_o_idx, next_o_idx; - - int num = 0; - - - (*item_num) = 0; - - /* Scan all objects in the grid */ - for (this_o_idx = m_list[m_idx].hold_o_idx; this_o_idx; - this_o_idx = next_o_idx) - { - object_type * o_ptr; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Accept this item */ - items[num++] = this_o_idx; - - /* XXX Hack -- Enforce limit */ - if (num == 23) break; - } - - /* Number of items */ - (*item_num) = num; - - /* Result */ - return (num != 0); -} - - -/* - * Display a list of the items that the given monster carries. - */ -byte show_monster_inven(int m_idx, int *monst_list) -{ - 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]; - - int monst_num; - - - /* Default length */ - len = 79 - 50; - - /* Maximum space allowed for descriptions */ - lim = 79 - 3; - - /* Require space for weight */ - lim -= 9; - - /* Scan for objects on the monster */ - (void)scan_monst(monst_list, &monst_num, m_idx); - - /* Display the p_ptr->inventory */ - for (k = 0, i = 0; i < monst_num; i++) - { - o_ptr = &o_list[monst_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 p_ptr->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 = monst_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 --(-- */ - strnfmt(tmp_val, 80, "%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; - strnfmt(tmp_val, 80, "%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); - - return monst_num; -} - - -/* - * Steal an object from a monster - */ -void do_cmd_steal() -{ - int x, y, dir = 0, item = -1, k = -1; - - cave_type *c_ptr; - - monster_type *m_ptr; - - object_type *o_ptr, forge; - - byte num = 0; - - bool_ done = FALSE; - - int monst_list[23]; - - - /* Only works on adjacent monsters */ - if (!get_rep_dir(&dir)) return; - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - c_ptr = &cave[y][x]; - - if (!(c_ptr->m_idx)) - { - msg_print("There is no monster there!"); - return; - } - - m_ptr = &m_list[c_ptr->m_idx]; - - /* There were no non-gold items */ - if (!m_ptr->hold_o_idx) - { - msg_print("That monster has no objects!"); - return; - } - - /* The monster is immune */ - if (r_info[m_ptr->r_idx].flags7 & (RF7_NO_THEFT)) - { - msg_print("The monster is guarding the treasures."); - return; - } - - screen_save(); - - num = show_monster_inven(c_ptr->m_idx, monst_list); - - /* Repeat until done */ - while (!done) - { - char tmp_val[80]; - char which = ' '; - - /* Build the prompt */ - strnfmt(tmp_val, 80, "Choose an item to steal (a-%c) or ESC:", - 'a' - 1 + num); - - /* Show the prompt */ - prt(tmp_val, 0, 0); - - /* Get a key */ - which = inkey(); - - /* Parse it */ - switch (which) - { - case ESCAPE: - { - done = TRUE; - - break; - } - - default: - { - int ver; - - /* Extract "query" setting */ - ver = isupper(which); - which = tolower(which); - - k = islower(which) ? A2I(which) : -1; - if (k < 0 || k >= num) - { - bell(); - - break; - } - - /* Verify the item */ - if (ver && !verify("Try", 0 - monst_list[k])) - { - done = TRUE; - - break; - } - - /* Accept that choice */ - item = monst_list[k]; - done = TRUE; - - break; - } - } - } - - if (item != -1) - { - int chance; - - chance = 40 - p_ptr->stat_ind[A_DEX]; - chance += - o_list[item].weight / (get_skill_scale(SKILL_STEALING, 19) + 1); - chance += get_skill_scale(SKILL_STEALING, 29) + 1; - chance -= (m_ptr->csleep) ? 10 : 0; - chance += m_ptr->level; - - /* Failure check */ - if (rand_int(chance) > 1 + get_skill_scale(SKILL_STEALING, 25)) - { - /* Take a turn */ - energy_use = 100; - - /* Wake up */ - m_ptr->csleep = 0; - - /* Speed up because monsters are ANGRY when you try to thief them */ - m_ptr->mspeed += 5; - - screen_load(); - - msg_print("Oops! The monster is now really *ANGRY*!"); - - return; - } - - /* Reconnect the objects list */ - if (num == 1) m_ptr->hold_o_idx = 0; - else - { - if (k > 0) o_list[monst_list[k - 1]].next_o_idx = monst_list[k + 1]; - if (k + 1 >= num) o_list[monst_list[k - 1]].next_o_idx = 0; - if (k == 0) m_ptr->hold_o_idx = monst_list[k + 1]; - } - - /* Rogues gain some xp */ - if (PRACE_FLAGS(PR1_EASE_STEAL)) - { - s32b max_point; - - /* Max XP gained from stealing */ - max_point = (o_list[item].weight / 2) + (m_ptr->level * 10); - - /* Randomise it a bit, with half a max guaranteed */ - gain_exp((max_point / 2) + (randint(max_point) / 2)); - - /* Allow escape */ - if (get_check("Phase door?")) teleport_player(10); - } - - /* Get the item */ - o_ptr = &forge; - - /* Special handling for gold */ - if (o_list[item].tval == TV_GOLD) - { - /* Collect the gold */ - p_ptr->au += o_list[item].pval; - - /* Redraw gold */ - p_ptr->redraw |= (PR_GOLD); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - } - else - { - object_copy(o_ptr, &o_list[item]); - - inven_carry(o_ptr, FALSE); - } - - /* Delete it */ - o_list[item].k_idx = 0; - } - - screen_load(); - - /* Take a turn */ - energy_use = 100; -} - - -/* - * Give an item to a monster - */ -void do_cmd_give() -{ - int dir, x, y; - - cave_type *c_ptr; - - cptr q, s; - - int item; - - - /* Get a "repeated" direction */ - if (!get_rep_dir(&dir)) return; - - /* Get requested location */ - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - /* Get requested grid */ - c_ptr = &cave[y][x]; - - /* No monster in the way */ - if (c_ptr->m_idx == 0) - { - msg_print("There is no monster there."); - return; - } - - /* Get an item */ - q = "What item do you want to offer? "; - s = "You have nothing to offer."; - if (!get_item(&item, q, s, USE_INVEN)) return; - - /* Process hooks if there are any */ - if (!process_hooks(HOOK_GIVE, "(d,d)", c_ptr->m_idx, item)) - { - hook_give_in in = { c_ptr->m_idx, item }; - if (!process_hooks_new(HOOK_GIVE, &in, NULL)) - { - msg_print("The monster does not want your item."); - } - } - - /* Take a turn, even if the offer is declined */ - energy_use = 100; -} - - -/* - * Chat with a monster - */ -void do_cmd_chat() -{ - int dir, x, y; - - cave_type *c_ptr; - - - /* Get a "repeated" direction */ - if (!get_rep_dir(&dir)) return; - - /* Get requested location */ - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - - /* Get requested grid */ - c_ptr = &cave[y][x]; - - /* No monster in the way */ - if (c_ptr->m_idx == 0) - { - msg_print("There is no monster there."); - return; - } - - /* Process hook if there are any */ - if (!process_hooks(HOOK_CHAT, "(d)", c_ptr->m_idx)) - { - msg_print("The monster does not want to chat."); - } - - /* No energy spent */ -} diff --git a/src/cmd2.cc b/src/cmd2.cc new file mode 100644 index 00000000..67f25098 --- /dev/null +++ b/src/cmd2.cc @@ -0,0 +1,5182 @@ +/* File: cmd2.c */ + +/* Purpose: Movement commands (part 2) */ + +/* + * 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 "angband.h" +#include "hooks.h" + +void do_cmd_immovable_special(void); + +/* + * Try to bash an altar + */ +static bool_ do_cmd_bash_altar(int y, int x) +{ + msg_print("Are you mad? You want to anger the gods?"); + return (FALSE); +} + + +/* + * Try to bash a fountain + */ +static bool_ do_cmd_bash_fountain(int y, int x) +{ + int bash, temp; + + bool_ more = TRUE; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + + if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR)) + { + msg_print("You cannot do that."); + + return (FALSE); + } + + /* Take a turn */ + energy_use = 100; + + /* Message */ + msg_print("You smash into the fountain!"); + + /* Hack -- Bash power based on strength */ + /* (Ranges from 3 to 20 to 100 to 200) */ + bash = adj_str_blow[p_ptr->stat_ind[A_STR]]; + + /* Compare bash power to door power XXX XXX XXX */ + temp = (bash - 50); + + /* Hack -- always have a chance */ + if (temp < 1) temp = 1; + + /* Hack -- attempt to bash down the door */ + if (rand_int(200) < temp) + { + /* Message */ + msg_print("The fountain breaks!"); + + fire_ball(GF_WATER, 5, damroll(6, 8), 2); + + cave_set_feat(y, x, FEAT_DEEP_WATER); + more = FALSE; + } + + return (more); +} + +/* + * Stair hooks + */ +static bool_ stair_hooks(stairs_direction direction) +{ + cptr direction_s = (direction == STAIRS_UP) ? "up" : "down"; + + /* Old-style hooks */ + if (process_hooks(HOOK_STAIR, "(s)", direction_s)) + { + return TRUE; /* Prevent movement */ + } + + /* New-style hooks */ + { + hook_stair_in in = { direction }; + hook_stair_out out = { TRUE }; /* Allow by default */ + + process_hooks_new(HOOK_STAIR, &in, &out); + + return (!out.allow); + } +} + + +/* + * Go up one level + */ +void do_cmd_go_up(void) +{ + bool_ go_up = FALSE, go_up_many = FALSE, prob_traveling = FALSE; + + cave_type *c_ptr; + + int oldl = dun_level; + + dungeon_info_type *d_ptr = &d_info[dungeon_type]; + + + /* Player grid */ + c_ptr = &cave[p_ptr->py][p_ptr->px]; + + /* Can we ? */ + if (stair_hooks(STAIRS_UP)) + { + return; + } + + /* Normal up stairs */ + if ((c_ptr->feat == FEAT_LESS) || (c_ptr->feat == FEAT_WAY_LESS)) + { + if (!dun_level) + { + go_up = TRUE; + } + else if ((dungeon_flags2 & DF2_ASK_LEAVE)) + { + go_up = get_check("Leave this unique level forever? "); + } + else if (confirm_stairs) + { + go_up = get_check("Really leave the level? "); + } + else + { + go_up = TRUE; + } + } + + /* Shaft up */ + else if (c_ptr->feat == FEAT_SHAFT_UP) + { + if (dun_level == 1) + { + go_up = TRUE; + } + else if ((dungeon_flags2 & DF2_ASK_LEAVE)) + { + go_up = get_check("Leave this unique level forever? "); + } + else if (confirm_stairs) + { + go_up_many = get_check("Really leave the level? "); + } + else + { + go_up_many = TRUE; + } + } + + /* Quest exit */ + else if (c_ptr->feat == FEAT_QUEST_EXIT) + { + leaving_quest = p_ptr->inside_quest; + + if ((dungeon_flags2 & DF2_ASK_LEAVE) && + !get_check("Leave this unique level forever? ")) + return; + + p_ptr->inside_quest = c_ptr->special; + dun_level = 0; + p_ptr->oldpx = 0; + p_ptr->oldpy = 0; + p_ptr->leaving = TRUE; + + return; + } + + /* Exits to previous area in flat terrains */ + else if (!(dungeon_flags1 & DF1_FLAT) && + p_ptr->prob_travel && !p_ptr->inside_quest) + { + if (d_ptr->mindepth == dun_level) return; + + if (dungeon_flags2 & DF2_NO_EASY_MOVE) + { + msg_print("Some powerful force prevents your from teleporting."); + return; + } + + prob_traveling = TRUE; + + if (confirm_stairs) + { + if (get_check("Really leave the level? ")) + go_up = TRUE; + } + else + { + go_up = TRUE; + } + } + else + { + msg_print("I see no up staircase here."); + return; + } + + if (go_up || go_up_many) + { + + energy_use = 0; + + /* Success */ + if (c_ptr->feat == FEAT_WAY_LESS) + msg_print("You enter the previous area."); + else + msg_print("You enter a maze of up staircases."); + + autosave_checkpoint(); + + if (p_ptr->inside_quest) + { + dun_level = 1; + leaving_quest = p_ptr->inside_quest; + + p_ptr->inside_quest = c_ptr->special; + } + + /* Create a way back */ + if (go_up_many) + create_down_shaft = TRUE; + else + create_down_stair = TRUE; + + /* New depth */ + if (go_up) + dun_level--; + else + { + dun_level -= randint(3) + 1; + if (dun_level <= 0) dun_level = 0; + } + + if (c_ptr->special && (!prob_traveling)) + { + dun_level = oldl; + dun_level = get_flevel(); + dungeon_type = c_ptr->special; + dun_level += d_info[dungeon_type].mindepth; + } + + /* Leaving */ + p_ptr->leaving = TRUE; + } +} + + +/* + * Returns TRUE if we are in the Between... + */ +static bool_ between_effect(void) +{ + byte bx, by; + + + if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN) + { + + bx = cave[p_ptr->py][p_ptr->px].special & 255; + by = cave[p_ptr->py][p_ptr->px].special >> 8; + + msg_print("You fall into the void."); + msg_print("Brrrr! It's deadly cold."); + + swap_position(by, bx); + + /* To avoid being teleported back */ + energy_use = 100; + + return (TRUE); + } + + else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN2) + { + between_exit *be_ptr = &between_exits[cave[p_ptr->py][p_ptr->px].special]; + + p_ptr->wild_mode = FALSE; + p_ptr->wilderness_x = be_ptr->wild_x; + p_ptr->wilderness_y = be_ptr->wild_y; + p_ptr->oldpx = p_ptr->px = be_ptr->px; + p_ptr->oldpy = p_ptr->py = be_ptr->py; + dungeon_type = be_ptr->d_idx; + dun_level = be_ptr->level; + p_ptr->leaving = TRUE; + + return (TRUE); + } + else + return (FALSE); +} + +/* + * Go down one level + */ +void do_cmd_go_down(void) +{ + cave_type *c_ptr; + + bool_ go_down = FALSE, go_down_many = FALSE, prob_traveling = FALSE; + + bool_ fall_trap = FALSE; + + char i; + + int old_dun = dun_level; + + dungeon_info_type *d_ptr = &d_info[dungeon_type]; + + + /* MUST be actived now */ + if (between_effect()) return; + + /* Player grid */ + c_ptr = &cave[p_ptr->py][p_ptr->px]; + + if (p_ptr->astral && (dun_level == 98)) return; + + if (c_ptr->t_idx == TRAP_OF_SINKING) fall_trap = TRUE; + + /* test if on special level */ + if ((dungeon_flags2 & DF2_ASK_LEAVE)) + { + prt("Leave this unique level forever (y/n) ? ", 0, 0); + flush(); + i = inkey(); + prt("", 0, 0); + if (i != 'y') return; + } + + /* Can we ? */ + if (stair_hooks(STAIRS_DOWN)) + { + return; + } + + /* Normal up stairs */ + if (c_ptr->feat == FEAT_SHAFT_DOWN) + { + if (!dun_level) + { + go_down = TRUE; + + /* Save old player position */ + p_ptr->oldpx = p_ptr->px; + p_ptr->oldpy = p_ptr->py; + } + else + { + if (confirm_stairs) + { + if (get_check("Really leave the level? ")) + go_down_many = TRUE; + } + else + { + go_down_many = TRUE; + } + } + } + + /* Normal stairs */ + else if ((c_ptr->feat == FEAT_MORE) || (c_ptr->feat == FEAT_WAY_MORE)) + { + if (p_ptr->prob_travel) + { + if (d_ptr->maxdepth == dun_level) return; + } + if (!dun_level) + { + go_down = TRUE; + + /* Save old player position */ + p_ptr->oldpx = p_ptr->px; + p_ptr->oldpy = p_ptr->py; + } + else + { + if (confirm_stairs) + { + if (get_check("Really leave the level? ")) + go_down = TRUE; + } + else + { + go_down = TRUE; + } + } + } + + /* Handle quest areas -KMW- */ + else if (c_ptr->feat == FEAT_QUEST_ENTER) + { + /* Enter quest level */ + enter_quest(); + + return; + } + + else if (!(dungeon_flags1 & DF1_FLAT) && + p_ptr->prob_travel && !p_ptr->inside_quest) + { + if (d_ptr->maxdepth == dun_level) return; + + if (dungeon_flags2 & DF2_NO_EASY_MOVE) + { + msg_print("Some powerfull force prevents your from teleporting."); + return; + } + + prob_traveling = TRUE; + + if (confirm_stairs) + { + if (get_check("Really leave the level? ")) + go_down = TRUE; + } + else + { + go_down = TRUE; + } + } + + else if (!(fall_trap)) + { + msg_print("I see no down staircase here."); + return; + } + + if (go_down || go_down_many) + { + energy_use = 0; + + if (fall_trap) + msg_print("You deliberately jump through the trap door."); + else + { + if (c_ptr->feat == FEAT_WAY_MORE) + msg_print("You enter the next area."); + else + msg_print("You enter a maze of down staircases."); + } + + autosave_checkpoint(); + + /* Go down */ + if (go_down) + { + dun_level++; + } + else if (go_down_many) + { + int i = randint(3) + 1, j; + + for (j = 1; j < i; j++) + { + dun_level++; + if (is_quest(dun_level + i - 1)) break; + if (d_ptr->maxdepth == dun_level) break; + } + } + + /* We change place */ + if (c_ptr->special && (!prob_traveling)) + { + if (d_info[c_ptr->special].min_plev <= p_ptr->lev) + { + dungeon_info_type *d_ptr = &d_info[c_ptr->special]; + + /* Do the lua scripts refuse ? ;) */ + if (process_hooks(HOOK_ENTER_DUNGEON, "(d)", c_ptr->special)) + { + dun_level = old_dun; + return; + } + + /* Ok go in the new dungeon */ + dungeon_type = c_ptr->special; + d_ptr = &d_info[dungeon_type]; + + if ((p_ptr->wilderness_x == d_ptr->ix) && + (p_ptr->wilderness_y == d_ptr->iy)) + { + dun_level = d_ptr->mindepth; + } + else if ((p_ptr->wilderness_x == d_ptr->ox) && + (p_ptr->wilderness_y == d_ptr->oy)) + { + dun_level = d_ptr->maxdepth; + } + else + { + dun_level = d_ptr->mindepth; + } + + msg_format("You go into %s", + d_text + d_info[dungeon_type].text); + } + else + { + msg_print + ("You don't feel yourself experienced enough to go there..."); + dun_level = old_dun; + return; + } + } + + /* Leaving */ + p_ptr->leaving = TRUE; + + if (!fall_trap) + { + /* Create a way back */ + if (go_down_many) + create_up_shaft = TRUE; + else + create_up_stair = TRUE; + } + } +} + + + +/* + * Simple command to "search" for one turn + */ +void do_cmd_search(void) +{ + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + /* Take a turn */ + energy_use = 100; + + /* Search */ + search(); +} + + +/* + * Hack -- toggle search mode + */ +void do_cmd_toggle_search(void) +{ + /* Stop searching */ + if (p_ptr->searching) + { + /* Clear the searching flag */ + p_ptr->searching = FALSE; + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + } + + /* Start searching */ + else + { + /* Set the searching flag */ + p_ptr->searching = TRUE; + + /* Update stuff */ + p_ptr->update |= (PU_BONUS); + + /* Redraw stuff */ + p_ptr->redraw |= (PR_STATE | PR_SPEED); + } +} + + + +/* + * Determine if a grid contains a chest + */ +static s16b chest_check(int y, int x) +{ + cave_type *c_ptr = &cave[y][x]; + + s16b this_o_idx, next_o_idx = 0; + + + /* Scan all objects in the grid */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Skip unknown chests XXX XXX */ + /* if (!o_ptr->marked) continue; */ + + /* Check for chest */ + if (o_ptr->tval == TV_CHEST) return (this_o_idx); + } + + /* No chest */ + return (0); +} + + +/* + * Allocates objects upon opening a chest -BEN- + * + * Disperse treasures from the given chest, centered at (x,y). + * + * Small chests often contain "gold", while Large chests always contain + * items. Wooden chests contain 2 items, Iron chests contain 4 items, + * and Steel chests contain 6 items. The "value" of the items in a + * chest is based on the "power" of the chest, which is in turn based + * on the level on which the chest is generated. + */ +static void chest_death(int y, int x, s16b o_idx) +{ + int number; + + bool_ small; + + object_type forge; + object_type *q_ptr; + + object_type *o_ptr = &o_list[o_idx]; + + + /* Small chests often hold "gold" */ + small = (o_ptr->sval < SV_CHEST_MIN_LARGE); + + /* Determine how much to drop (see above) */ + number = (o_ptr->sval % SV_CHEST_MIN_LARGE) * 2; + + /* Zero pval means empty chest */ + if (!o_ptr->pval) number = 0; + + /* Opening a chest */ + opening_chest = TRUE; + + /* Determine the "value" of the items */ + object_level = ABS(o_ptr->pval) + 10; + + /* Drop some objects (non-chests) */ + for (; number > 0; --number) + { + /* Get local object */ + q_ptr = &forge; + + /* Wipe the object */ + object_wipe(q_ptr); + + /* Small chests often drop gold */ + if (small && (rand_int(100) < 75)) + { + /* Make some gold */ + if (!make_gold(q_ptr)) continue; + } + + /* Otherwise drop an item */ + else + { + /* Make an object */ + if (!make_object(q_ptr, FALSE, FALSE, d_info[dungeon_type].objs)) + continue; + } + + /* Drop it in the dungeon */ + drop_near(q_ptr, -1, y, x); + } + + /* Reset the object level */ + object_level = dun_level; + + /* No longer opening a chest */ + opening_chest = FALSE; + + /* Empty */ + o_ptr->pval = 0; + o_ptr->pval2 = 0; + + /* Known */ + object_known(o_ptr); +} + + +/* + * Chests have traps too. + * + * Exploding chest destroys contents (and traps). + * Note that the chest itself is never destroyed. + */ +static void chest_trap(int y, int x, s16b o_idx) +{ + int trap; + + object_type *o_ptr = &o_list[o_idx]; + + bool_ ident = FALSE; + + + /* Ignore disarmed chests */ + if (o_ptr->pval <= 0) return; + + /* Obtain the trap */ + trap = o_ptr->pval; + + /* Message */ + msg_print("You found a trap!"); + + /* Set off trap */ + ident = player_activate_trap_type(y, x, o_ptr, o_idx); + if (ident) + { + t_info[o_ptr->pval].ident = TRUE; + msg_format("You identified the trap as %s.", + t_name + t_info[trap].name); + } +} + + +/* + * Attempt to open the given chest at the given location + * + * Assume there is no monster blocking the destination + * + * Returns TRUE if repeated commands may continue + */ +static bool_ do_cmd_open_chest(int y, int x, s16b o_idx) +{ + int i, j; + + bool_ flag = TRUE; + + bool_ more = FALSE; + + object_type *o_ptr = &o_list[o_idx]; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + + if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) + { + msg_print("You cannot open chests."); + + return (FALSE); + } + + /* Take a turn */ + energy_use = 100; + + /* Attempt to unlock it */ + if (o_ptr->pval > 0) + { + /* Assume locked, and thus not open */ + flag = FALSE; + + /* Get the "disarm" factor */ + i = p_ptr->skill_dis; + + /* Penalize some conditions */ + if (p_ptr->blind || no_lite()) i = i / 10; + if (p_ptr->confused || p_ptr->image) i = i / 10; + + /* Extract the difficulty */ + j = i - o_ptr->pval; + + /* Always have a small chance of success */ + if (j < 2) j = 2; + + /* Success -- May still have traps */ + if (rand_int(100) < j) + { + msg_print("You have picked the lock."); + gain_exp(1); + flag = TRUE; + } + + /* Failure -- Keep trying */ + else + { + /* We may continue repeating */ + more = TRUE; + + if (flush_failure) flush(); + + msg_print("You failed to pick the lock."); + } + } + + /* Allowed to open */ + if (flag) + { + /* Apply chest traps, if any */ + chest_trap(y, x, o_idx); + + /* Let the Chest drop items */ + chest_death(y, x, o_idx); + } + + /* Result */ + return (more); +} + + +/* + * Original code by TNB, improvement for Angband 2.9.3 by rr9 + * Slightly modified for ToME because of its trap implementation + */ + +/* + * Return TRUE if the given grid is an open door + */ +static bool_ is_open(cave_type *c_ptr) +{ + return (c_ptr->feat == FEAT_OPEN); +} + + +/* + * Return TRUE if the given grid is a closed door + */ +static bool_ is_closed(cave_type *c_ptr) +{ + byte feat; + + if (c_ptr->mimic) feat = c_ptr->mimic; + else feat = c_ptr->feat; + + return ((feat >= FEAT_DOOR_HEAD) && (feat <= FEAT_DOOR_TAIL)); +} + + +/* + * Return TRUE if the given grid has a trap + */ +static bool_ is_trap(cave_type *c_ptr) +{ + return ((c_ptr->info & (CAVE_TRDT)) != 0); +} + + +/* + * Return the number of doors/traps around (or under) + * the character using the filter function 'test' + */ +static int count_feats(int *y, int *x, bool_ (*test) (cave_type *c_ptr), + bool_ under) +{ + int d; + + int xx, yy; + + int count; + + + /* Clear match counter */ + count = 0; + + /* Check around (and under) the character */ + for (d = 0; d < 9; d++) + { + /* Ignore current grid if told so -- See tables.c */ + if ((d == 8) && !under) continue; + + /* Extract adjacent (legal) location */ + yy = p_ptr->py + ddy_ddd[d]; + xx = p_ptr->px + ddx_ddd[d]; + + /* Paranoia */ + if (!in_bounds(yy, xx)) continue; + + /* Must have knowledge */ + if (!(cave[yy][xx].info & (CAVE_MARK))) continue; + + /* Not looking for this feature */ + if (!(*test) (&cave[yy][xx])) continue; + + /* Count it */ + count++; + + /* Remember the location. Only meaningful if there's + exactly one match */ + *y = yy; + *x = xx; + } + + /* All done */ + return (count); +} + + +/* + * Return the number of chests around (or under) the character. + * If requested, count only trapped chests. + */ +static int count_chests(int *y, int *x, bool_ trapped) +{ + int d, count, o_idx; + + object_type *o_ptr; + + + /* Count how many matches */ + count = 0; + + /* Check around (and under) the character */ + for (d = 0; d < 9; d++) + { + + /* Extract adjacent (legal) location */ + int yy = p_ptr->py + ddy_ddd[d]; + int xx = p_ptr->px + ddx_ddd[d]; + + /* No (visible) chest is there */ + if ((o_idx = chest_check(yy, xx)) == 0) continue; + + /* Grab the object */ + o_ptr = &o_list[o_idx]; + + /* Already open */ + if (o_ptr->pval == 0) continue; + + /* No (known) traps here */ + if (trapped && (!object_known_p(o_ptr) || !o_ptr->pval)) continue; + + /* OK */ + ++count; + + /* Remember the location. Only useful if only one match */ + *y = yy; + *x = xx; + } + + /* All done */ + return (count); +} + + +/* + * Convert an adjacent location to a direction. + */ +static int coords_to_dir(int y, int x) +{ + int d[3][3] = + { + {7, 4, 1}, + {8, 5, 2}, + {9, 6, 3} }; + + int dy, dx; + + + dy = y - p_ptr->py; + dx = x - p_ptr->px; + + /* Paranoia */ + if (ABS(dx) > 1 || ABS(dy) > 1) return (0); + + return d[dx + 1][dy + 1]; +} + + +/* + * Perform the basic "open" command on doors + * + * Assume destination is a closed/locked/jammed door + * + * Assume there is no monster blocking the destination + * + * Returns TRUE if repeated commands may continue + */ +static bool_ do_cmd_open_aux(int y, int x, int dir) +{ + int i, j; + + cave_type *c_ptr; + + bool_ more = FALSE; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + + if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) + { + msg_print("You cannot open doors."); + + return (FALSE); + } + + /* Take a turn */ + energy_use = 100; + + /* Get requested grid */ + c_ptr = &cave[y][x]; + + /* Jammed door */ + if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08) + { + /* Stuck */ + msg_print("The door appears to be stuck."); + } + + /* Locked door */ + else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01) + { + /* Disarm factor */ + i = p_ptr->skill_dis; + + /* Penalize some conditions */ + if (p_ptr->blind || no_lite()) i = i / 10; + if (p_ptr->confused || p_ptr->image) i = i / 10; + + /* Extract the lock power */ + j = c_ptr->feat - FEAT_DOOR_HEAD; + + /* Extract the difficulty XXX XXX XXX */ + j = i - (j * 4); + + /* Always have a small chance of success */ + if (j < 2) j = 2; + + /* Success */ + if (rand_int(100) < j) + { + /* Message */ + msg_print("You have picked the lock."); + + /* Set off trap */ + if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); + + /* Open the door */ + cave_set_feat(y, x, FEAT_OPEN); + + /* Update some things */ + p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); + + /* Sound */ + sound(SOUND_OPENDOOR); + + /* Experience */ + gain_exp(1); + } + + /* Failure */ + else + { + /* Failure */ + if (flush_failure) flush(); + + /* Message */ + msg_print("You failed to pick the lock."); + + /* We may keep trying */ + more = TRUE; + } + } + + /* Closed door */ + else + { + /* Set off trap */ + if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); + + /* Open the door */ + cave_set_feat(y, x, FEAT_OPEN); + + /* Update some things */ + p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); + + /* Sound */ + sound(SOUND_OPENDOOR); + } + + /* Result */ + return (more); +} + + + +/* + * Open a closed/locked/jammed door or a closed/locked chest. + * + * Unlocking a locked door/chest is worth one experience point. + */ +void do_cmd_open(void) +{ + int y, x, dir; + + s16b o_idx; + + cave_type *c_ptr; + + bool_ more = FALSE; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + + if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) + { + msg_print("You cannot open doors."); + + return; + } + + /* Option: Pick a direction */ + if (easy_open) + { + int num_doors, num_chests; + + /* Count closed doors (locked or jammed) */ + num_doors = count_feats(&y, &x, is_closed, FALSE); + + /* Count chests (locked) */ + num_chests = count_chests(&y, &x, FALSE); + + /* There is nothing the player can open */ + if ((num_doors + num_chests) == 0) + { + /* Message */ + msg_print("You see nothing there to open."); + + /* Done */ + return; + } + + /* Set direction if there is only one target */ + else if ((num_doors + num_chests) == 1) + { + command_dir = coords_to_dir(y, x); + } + } + + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + /* Get a "repeated" direction */ + if (get_rep_dir(&dir)) + { + /* Get requested location */ + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + /* Get requested grid */ + c_ptr = &cave[y][x]; + + /* Check for chest */ + o_idx = chest_check(y, x); + + /* Nothing useful */ + if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && + (c_ptr->feat <= FEAT_DOOR_TAIL)) && !o_idx) + { + /* Message */ + msg_print("You see nothing there to open."); + } + + /* Monster in the way */ + else if (c_ptr->m_idx) + { + /* Take a turn */ + energy_use = 100; + + /* Message */ + msg_print("There is a monster in the way!"); + + /* Attack */ + py_attack(y, x, -1); + } + + /* Handle chests */ + else if (o_idx) + { + /* Open the chest */ + more = do_cmd_open_chest(y, x, o_idx); + } + + /* Handle doors */ + else + { + /* Open the door */ + more = do_cmd_open_aux(y, x, dir); + } + } + + /* Process the appropriate hooks */ + process_hooks(HOOK_OPEN, "(d)", is_quest(dun_level)); + + /* Cancel repeat unless we may continue */ + if (!more) disturb(0); +} + + + +/* + * Perform the basic "close" command + * + * Assume destination is an open/broken door + * + * Assume there is no monster blocking the destination + * + * Returns TRUE if repeated commands may continue + */ +static bool_ do_cmd_close_aux(int y, int x, int dir) +{ + cave_type *c_ptr; + + bool_ more = FALSE; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + + if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) + { + msg_print("You cannot close doors."); + + return (FALSE); + } + + /* Take a turn */ + energy_use = 100; + + /* Get grid and contents */ + c_ptr = &cave[y][x]; + + /* Set off trap */ + if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); + + /* Broken door */ + if (c_ptr->feat == FEAT_BROKEN) + { + /* Message */ + msg_print("The door appears to be broken."); + } + + /* Open door */ + else + { + /* Close the door */ + cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00); + + /* Update some things */ + p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); + + /* Sound */ + sound(SOUND_SHUTDOOR); + } + + /* Result */ + return (more); +} + + +/* + * Close an open door. + */ +void do_cmd_close(void) +{ + int y, x, dir; + + cave_type *c_ptr; + + bool_ more = FALSE; + + + /* Option: Pick a direction */ + if (easy_open) + { + int num_doors; + + /* Count open doors */ + num_doors = count_feats(&y, &x, is_open, FALSE); + + /* There are no doors the player can close */ + if (num_doors == 0) + { + /* Message */ + msg_print("You see nothing there to close."); + + /* Done */ + return; + } + + /* Exactly one closeable door */ + else if (num_doors == 1) + { + command_dir = coords_to_dir(y, x); + } + } + + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + /* Get a "repeated" direction */ + if (get_rep_dir(&dir)) + { + /* Get requested location */ + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + /* Get grid and contents */ + c_ptr = &cave[y][x]; + + /* Require open/broken door */ + if ((c_ptr->feat != FEAT_OPEN) && (c_ptr->feat != FEAT_BROKEN)) + { + /* Message */ + msg_print("You see nothing there to close."); + } + + /* Monster in the way */ + else if (c_ptr->m_idx) + { + /* Take a turn */ + energy_use = 100; + + /* Message */ + msg_print("There is a monster in the way!"); + + /* Attack */ + py_attack(y, x, -1); + } + + /* Close the door */ + else + { + /* Close the door */ + more = do_cmd_close_aux(y, x, dir); + } + } + + /* Cancel repeat unless we may continue */ + if (!more) disturb(0); +} + + +/* + * Determine if a given grid may be "tunneled" + */ +static bool_ do_cmd_tunnel_test(int y, int x) +{ + /* Must have knowledge(execpt on "forget" levels) */ + if (!(cave[y][x].info & (CAVE_MARK))) + { + /* Message */ + msg_print("You see nothing there."); + + /* Nope */ + return (FALSE); + } + + /* Must be a wall/door/etc */ + if (cave_floor_bold(y, x)) + { + /* Message */ + msg_print("You see nothing there to tunnel."); + + /* Nope */ + return (FALSE); + } + + /* Must be tunnelable */ + if (!(f_info[cave[y][x].feat].flags1 & FF1_TUNNELABLE)) + { + /* Message */ + msg_print(f_text + f_info[cave[y][x].feat].tunnel); + + /* Nope */ + return (FALSE); + } + + /* Okay */ + return (TRUE); +} + + + +/* + * Tunnel through wall. Assumes valid location. + * + * Note that it is impossible to "extend" rooms past their + * outer walls (which are actually part of the room). + * + * This will, however, produce grids which are NOT illuminated + * (or darkened) along with the rest of the room. + */ +static bool_ twall(int y, int x, byte feat) +{ + cave_type *c_ptr = &cave[y][x]; + + + /* Paranoia -- Require a wall or door or some such */ + if (cave_floor_bold(y, x)) return (FALSE); + + /* Forget the wall */ + c_ptr->info &= ~(CAVE_MARK); + + /* Remove the feature */ + cave_set_feat(y, x, feat); + + /* Update some things */ + p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE); + + /* Result */ + return (TRUE); +} + + + +/* + * Perform the basic "tunnel" command + * + * Assumes that the destination is a wall, a vein, a secret + * door, or rubble. + * + * Assumes that no monster is blocking the destination + * + * Returns TRUE if repeated commands may continue + */ +bool_ do_cmd_tunnel_aux(int y, int x, int dir) +{ + int skill_req = 0, skill_req_1pct = 0; + cave_type *c_ptr = &cave[y][x]; + + feature_type *f_ptr = &f_info[c_ptr->feat]; + + bool_ more = FALSE; + + + /* Must be have something to dig with (except for sandwalls) */ + if ((c_ptr->feat < FEAT_SANDWALL) || (c_ptr->feat > FEAT_SANDWALL_K)) + { + if (!p_ptr->inventory[INVEN_TOOL].k_idx || + (p_ptr->inventory[INVEN_TOOL].tval != TV_DIGGING)) + { + msg_print("You need to have a shovel or pick in your tool slot."); + + return (FALSE); + } + } + + /* Verify legality */ + if (!do_cmd_tunnel_test(y, x)) return (FALSE); + + /* Take a turn */ + energy_use = 100; + + /* Get grid */ + c_ptr = &cave[y][x]; + + /* Sound */ + sound(SOUND_DIG); + + /* Titanium */ + if (f_ptr->flags1 & FF1_PERMANENT) + { + msg_print(f_text + f_ptr->tunnel); + } + + else if ((c_ptr->feat == FEAT_TREES) || (c_ptr->feat == FEAT_DEAD_TREE)) + { + /* Chop Down */ + skill_req = 10; + skill_req_1pct = 14; + if ((p_ptr->skill_dig > 10 + rand_int(400)) && twall(y, x, FEAT_GRASS)) + { + msg_print("You have cleared away the trees."); + } + + /* Keep trying */ + else + { + /* We may continue chopping */ + msg_print(f_text + f_ptr->tunnel); + more = TRUE; + + /* Occasional Search XXX XXX */ + if (rand_int(100) < 25) search(); + } + } + + + /* Granite */ + else if ((c_ptr->feat >= FEAT_WALL_EXTRA) && + (c_ptr->feat <= FEAT_WALL_SOLID)) + { + /* Tunnel */ + skill_req = 40; + skill_req_1pct = 56; + if ((p_ptr->skill_dig > 40 + rand_int(1600)) && twall(y, x, FEAT_FLOOR)) + { + msg_print("You have finished the tunnel."); + } + + /* Keep trying */ + else + { + /* We may continue tunelling */ + msg_print(f_text + f_ptr->tunnel); + more = TRUE; + } + } + + + /* Quartz / Magma / Sandwall */ + else if (((c_ptr->feat >= FEAT_MAGMA) && + (c_ptr->feat <= FEAT_QUARTZ_K)) || + ((c_ptr->feat >= FEAT_SANDWALL) && + (c_ptr->feat <= FEAT_SANDWALL_K))) + { + bool_ okay = FALSE; + bool_ gold = FALSE; + bool_ hard = FALSE; + bool_ soft = FALSE; + + /* Found gold */ + if ((c_ptr->feat >= FEAT_MAGMA_H) && + (c_ptr->feat <= FEAT_QUARTZ_K)) gold = TRUE; + + if ((c_ptr->feat == FEAT_SANDWALL_H) || + (c_ptr->feat == FEAT_SANDWALL_K)) + { + gold = TRUE; + soft = TRUE; + } + else + /* Extract "quartz" flag XXX XXX XXX */ + if ((c_ptr->feat - FEAT_MAGMA) & 0x01) hard = TRUE; + + /* Quartz */ + if (hard) + { + skill_req = 20; + skill_req_1pct = 28; + okay = (p_ptr->skill_dig > 20 + rand_int(800)); + } + + /* Sandwall */ + else if (soft) + { + skill_req = 5; + skill_req_1pct = 8; + okay = (p_ptr->skill_dig > 5 + rand_int(250)); + } + + /* Magma */ + else + { + skill_req = 10; + skill_req_1pct = 14; + okay = (p_ptr->skill_dig > 10 + rand_int(400)); + } + + /* Success */ + if (okay && twall(y, x, FEAT_FLOOR)) + { + /* Found treasure */ + if (gold) + { + /* Place some gold */ + place_gold(y, x); + + /* Message */ + msg_print("You have found something!"); + } + + /* Found nothing */ + else + { + /* Message */ + msg_print("You have finished the tunnel."); + } + } + + /* Failure */ + else + { + /* Message, continue digging */ + msg_print(f_text + f_ptr->tunnel); + more = TRUE; + } + } + + /* Rubble */ + else if (c_ptr->feat == FEAT_RUBBLE) + { + /* Remove the rubble */ + skill_req = 0; + skill_req_1pct = 2; + if ((p_ptr->skill_dig > rand_int(200)) && + twall(y, x, d_info[dungeon_type].floor1)) + { + /* Message */ + msg_print("You have removed the rubble."); + + /* Hack -- place an object */ + if (rand_int(100) < 10) + { + /* Create a simple object */ + place_object(y, x, FALSE, FALSE, OBJ_FOUND_RUBBLE); + + /* Observe new object */ + if (player_can_see_bold(y, x)) + { + msg_print("You have found something!"); + } + } + } + + else + { + /* Message, keep digging */ + msg_print(f_text + f_ptr->tunnel); + more = TRUE; + } + } + + /* Secret doors */ + else if (c_ptr->feat >= FEAT_SECRET) + { + /* Tunnel */ + skill_req = 30; + skill_req_1pct = 42; + if ((p_ptr->skill_dig > 30 + rand_int(1200)) && twall(y, x, FEAT_FLOOR)) + { + msg_print("You have finished the tunnel."); + c_ptr->mimic = 0; + lite_spot(y, x); + + /* Set off trap */ + if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); + } + + /* Keep trying */ + else + { + int feat; + + if (c_ptr->mimic) feat = c_ptr->mimic; + else + feat = c_ptr->feat; + + /* We may continue tunelling */ + msg_print(f_text + f_info[feat].tunnel); + more = TRUE; + + /* Occasional Search XXX XXX */ + if (rand_int(100) < 25) search(); + } + } + + /* Doors */ + else + { + /* Tunnel */ + skill_req = 30; + skill_req_1pct = 42; + if ((p_ptr->skill_dig > 30 + rand_int(1200)) && twall(y, x, FEAT_FLOOR)) + { + msg_print("You have finished the tunnel."); + } + + /* Keep trying */ + else + { + /* We may continue tunelling */ + msg_print(f_text + f_ptr->tunnel); + more = TRUE; + } + } + + if (more && magik(2)) + { + if (p_ptr->skill_dig < skill_req) + { + msg_print("You fail to make even the slightest of progress."); + more = FALSE; + } + else if (p_ptr->skill_dig < skill_req_1pct) + { + msg_print("This will take some time."); + } + } + + /* Notice new floor grids */ + if (!cave_floor_bold(y, x)) + { + /* Update some things */ + p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE); + } + + /* Result */ + return (more); +} + + +/* + * Tunnels through "walls" (including rubble and closed doors) + * + * Note that you must tunnel in order to hit invisible monsters + * in walls, though moving into walls still takes a turn anyway. + * + * Digging is very difficult without a "digger" weapon, but can be + * accomplished by strong players using heavy weapons. + */ +void do_cmd_tunnel(void) +{ + int y, x, dir; + + cave_type *c_ptr; + + bool_ more = FALSE; + + + if (p_ptr->wild_mode) return; + + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + /* Get a direction to tunnel, or Abort */ + if (get_rep_dir(&dir)) + { + /* Get location */ + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + /* Get grid */ + c_ptr = &cave[y][x]; + + /* No tunnelling through doors */ + if (((c_ptr->feat >= FEAT_DOOR_HEAD) && + (c_ptr->feat <= FEAT_DOOR_TAIL)) || (c_ptr->feat == FEAT_SHOP)) + { + /* Message */ + msg_print("You cannot tunnel through doors."); + } + + /* No tunnelling through air */ + else if (cave_floor_grid(c_ptr)) + { + /* Message */ + msg_print("You cannot tunnel through air."); + } + + /* A monster is in the way */ + else if (c_ptr->m_idx) + { + /* Take a turn */ + energy_use = 100; + + /* Message */ + msg_print("There is a monster in the way!"); + + /* Attack */ + py_attack(y, x, -1); + } + + /* Try digging */ + else + { + /* Tunnel through walls */ + more = do_cmd_tunnel_aux(y, x, dir); + } + } + + /* Cancel repetition unless we can continue */ + if (!more) disturb(0); +} + + +/* + * easy_open_door -- + * + * If there is a jammed/closed/locked door at the given location, + * then attempt to unlock/open it. Return TRUE if an attempt was + * made (successful or not), otherwise return FALSE. + * + * The code here should be nearly identical to that in + * do_cmd_open_test() and do_cmd_open_aux(). + */ + +bool_ easy_open_door(int y, int x) +{ + int i, j; + + cave_type *c_ptr = &cave[y][x]; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + + if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR)) + { + msg_print("You cannot open doors."); + + return (FALSE); + } + + /* Must be a closed door */ + if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL))) + { + /* Nope */ + return (FALSE); + } + + /* Jammed door */ + if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08) + { + /* Stuck */ + msg_print("The door appears to be stuck."); + } + + /* Locked door */ + else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01) + { + /* Disarm factor */ + i = p_ptr->skill_dis; + + /* Penalize some conditions */ + if (p_ptr->blind || no_lite()) i = i / 10; + if (p_ptr->confused || p_ptr->image) i = i / 10; + + /* Extract the lock power */ + j = c_ptr->feat - FEAT_DOOR_HEAD; + + /* Extract the difficulty XXX XXX XXX */ + j = i - (j * 4); + + /* Always have a small chance of success */ + if (j < 2) j = 2; + + /* Success */ + if (rand_int(100) < j) + { + /* Message */ + msg_print("You have picked the lock."); + + /* Set off trap */ + if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); + + /* Open the door */ + cave_set_feat(y, x, FEAT_OPEN); + + /* Update some things */ + p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); + + /* Sound */ + sound(SOUND_OPENDOOR); + + /* Process the appropriate hooks */ + process_hooks(HOOK_OPEN, "(d)", is_quest(dun_level)); + + /* Experience */ + gain_exp(1); + } + + /* Failure */ + else + { + /* Failure */ + if (flush_failure) flush(); + + /* Message */ + msg_print("You failed to pick the lock."); + } + } + + /* Closed door */ + else + { + /* Set off trap */ + if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); + + /* Open the door */ + cave_set_feat(y, x, FEAT_OPEN); + + /* Update some things */ + p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE); + + /* Sound */ + sound(SOUND_OPENDOOR); + } + + /* Result */ + return (TRUE); +} + + +/* + * Perform the basic "disarm" command + * + * Assume destination is a visible trap + * + * Assume there is no monster blocking the destination + * + * Returns TRUE if repeated commands may continue + */ +static bool_ do_cmd_disarm_chest(int y, int x, s16b o_idx) +{ + int i, j; + + bool_ more = FALSE; + + object_type *o_ptr = &o_list[o_idx]; + + trap_type *t_ptr = &t_info[o_ptr->pval]; + + + /* Take a turn */ + energy_use = 100; + + /* Get the "disarm" factor */ + i = p_ptr->skill_dis; + + /* Penalize some conditions */ + if (p_ptr->blind || no_lite()) i = i / 10; + if (p_ptr->confused || p_ptr->image) i = i / 10; + + /* Extract the difficulty */ + j = i - t_ptr->difficulty * 3; + + /* Always have a small chance of success */ + if (j < 2) j = 2; + + /* Must find the trap first. */ + if (!object_known_p(o_ptr)) + { + msg_print("I don't see any traps."); + } + + /* Already disarmed/unlocked */ + else if (o_ptr->pval <= 0) + { + msg_print("The chest is not trapped."); + } + + /* Success (get a lot of experience) */ + else if (rand_int(100) < j) + { + msg_print("You have disarmed the chest."); + gain_exp(t_ptr->difficulty * 3); + o_ptr->pval = (0 - o_ptr->pval); + } + + /* Failure -- Keep trying */ + else if ((i > 5) && (randint(i) > 5)) + { + /* We may keep trying */ + more = TRUE; + if (flush_failure) flush(); + msg_print("You failed to disarm the chest."); + } + + /* Failure -- Set off the trap */ + else + { + msg_print("You set off a trap!"); + sound(SOUND_FAIL); + chest_trap(y, x, o_idx); + } + + /* Result */ + return (more); +} + + +/* + * Perform the basic "disarm" command + * + * Assume destination is a visible trap + * + * Assume there is no monster blocking the destination + * + * Returns TRUE if repeated commands may continue + */ +bool_ do_cmd_disarm_aux(int y, int x, int dir, int do_pickup) +{ + int i, j, power; + + cave_type *c_ptr; + + cptr name; + + bool_ more = FALSE; + + + /* Take a turn */ + energy_use = 100; + + /* Get grid and contents */ + c_ptr = &cave[y][x]; + + /* Access trap name */ + if (t_info[c_ptr->t_idx].ident) + name = (t_name + t_info[c_ptr->t_idx].name); + else + name = "unknown trap"; + + /* Get the "disarm" factor */ + i = p_ptr->skill_dis; + + /* Penalize some conditions */ + if (p_ptr->blind || no_lite()) i = i / 10; + if (p_ptr->confused || p_ptr->image) i = i / 10; + + /* XXX XXX XXX Variable power? */ + + /* Extract trap "power" */ + power = t_info[c_ptr->t_idx].difficulty; + + /* Extract the difficulty */ + j = i - power; + + /* Always have a small chance of success */ + if (j < 2) j = 2; + + /* Success */ + if (rand_int(100) < j) + { + /* Message */ + msg_format("You have disarmed the %s.", name); + + /* Reward */ + gain_exp(power); + + /* Forget the trap */ + c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT); + + /* Remove the trap */ + c_ptr->t_idx = 0; + + /* Move the player onto the trap */ + if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR)) + move_player_aux(dir, do_pickup, 0, TRUE); + + /* Remove trap attr from grid */ + note_spot(y, x); + lite_spot(y, x); + } + + /* Failure -- Keep trying */ + else if ((i > 5) && (randint(i) > 5)) + { + /* Failure */ + if (flush_failure) flush(); + + /* Message */ + msg_format("You failed to disarm the %s.", name); + + /* We may keep trying */ + more = TRUE; + } + + /* Failure -- Set off the trap */ + else + { + /* Message */ + msg_format("You set off the %s!", name); + + /* Move the player onto the trap */ + if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR)) + move_player_aux(dir, do_pickup, 0, FALSE); + } + + /* Result */ + return (more); +} + + +/* + * Disamrs the monster traps(no failure) + */ +void do_cmd_disarm_mon_trap(int y, int x) +{ + msg_print("You disarm the monster trap."); + + place_floor_convert_glass(y, x); + cave[p_ptr->py][p_ptr->px].special = cave[p_ptr->py][p_ptr->px].special2 = 0; +} + + +/* + * Disarms a trap, or chest + */ +void do_cmd_disarm(void) +{ + int y, x, dir; + + s16b o_idx; + + cave_type *c_ptr; + + bool_ more = FALSE; + + + /* Option: Pick a direction */ + if (easy_disarm) + { + int num_traps, num_chests; + + /* Count visible traps */ + num_traps = count_feats(&y, &x, is_trap, TRUE); + + /* Count chests (trapped) */ + num_chests = count_chests(&y, &x, TRUE); + + /* See if only one target */ + if (num_traps || num_chests) + { + if (num_traps + num_chests <= 1) + command_dir = coords_to_dir(y, x); + } + } + + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + /* Get a direction (or abort) */ + if (get_rep_dir(&dir)) + { + /* Get location */ + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + /* Get grid and contents */ + c_ptr = &cave[y][x]; + + /* Check for chests */ + o_idx = chest_check(y, x); + + /* Disarm a trap */ + if (((c_ptr->t_idx == 0) || (!(c_ptr->info & CAVE_TRDT))) && + !o_idx && (c_ptr->feat != FEAT_MON_TRAP)) + { + /* Message */ + msg_print("You see nothing there to disarm."); + } + + /* Monster in the way */ + else if (c_ptr->m_idx) + { + /* Message */ + msg_print("There is a monster in the way!"); + + /* Attack */ + py_attack(y, x, -1); + } + + /* Disarm chest */ + else if (o_idx) + { + /* Disarm the chest */ + more = do_cmd_disarm_chest(y, x, o_idx); + } + + /* Disarm trap */ + else + { + /* Disarm the trap */ + if (c_ptr->feat == FEAT_MON_TRAP) + { + do_cmd_disarm_mon_trap(y, x); + more = FALSE; + } + else + more = do_cmd_disarm_aux(y, x, dir, always_pickup); + } + } + + /* Cancel repeat unless told not to */ + if (!more) disturb(0); +} + + +/* + * Perform the basic "bash" command + * + * Assume destination is a closed/locked/jammed door + * + * Assume there is no monster blocking the destination + * + * Returns TRUE if repeated commands may continue + */ +static bool_ do_cmd_bash_aux(int y, int x, int dir) +{ + int bash, temp; + + cave_type *c_ptr; + + bool_ more = FALSE; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + + if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR)) + { + msg_print("You cannot do that."); + + return (FALSE); + } + + /* Take a turn */ + energy_use = 100; + + /* Get grid */ + c_ptr = &cave[y][x]; + + /* Message */ + msg_print("You smash into the door!"); + + /* Hack -- Bash power based on strength */ + /* (Ranges from 3 to 20 to 100 to 200) */ + bash = adj_str_blow[p_ptr->stat_ind[A_STR]]; + + /* Extract door power */ + temp = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07); + + /* Compare bash power to door power XXX XXX XXX */ + temp = (bash - (temp * 10)); + + /* Hack -- always have a chance */ + if (temp < 1) temp = 1; + + /* Hack -- attempt to bash down the door */ + if (rand_int(100) < temp) + { + /* Message */ + msg_print("The door crashes open!"); + + /* Break down the door */ + if (rand_int(100) < 50) + { + /* Set off trap */ + if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); + + cave_set_feat(y, x, FEAT_BROKEN); + } + + /* Open the door */ + else + { + /* Set off trap */ + if (c_ptr->t_idx != 0) player_activate_door_trap(y, x); + + cave_set_feat(y, x, FEAT_OPEN); + } + + /* Sound */ + sound(SOUND_OPENDOOR); + + /* Hack -- Fall through the door. Can't disarm while falling. */ + move_player_aux(dir, always_pickup, 0, FALSE); + + /* Update some things */ + p_ptr->update |= (PU_VIEW | PU_MON_LITE); + p_ptr->update |= (PU_DISTANCE); + } + + /* Saving throw against stun */ + else if (rand_int(100) < adj_dex_safe[p_ptr->stat_ind[A_DEX]] + p_ptr->lev) + { + /* Message */ + msg_print("The door holds firm."); + + /* Allow repeated bashing */ + more = TRUE; + } + + /* High dexterity yields coolness */ + else + { + /* Message */ + msg_print("You are off-balance."); + + /* Hack -- Lose balance ala paralysis */ + (void)set_paralyzed(2 + rand_int(2)); + } + + /* Result */ + return (more); +} + + +/* + * Bash open a door, success based on character strength + * + * For a closed door, pval is positive if locked; negative if stuck. + * + * For an open door, pval is positive for a broken door. + * + * A closed door can be opened - harder if locked. Any door might be + * bashed open (and thereby broken). Bashing a door is (potentially) + * faster! You move into the door way. To open a stuck door, it must + * be bashed. A closed door can be jammed (see do_cmd_spike()). + * + * Creatures can also open or bash doors, see elsewhere. + */ +void do_cmd_bash(void) +{ + int y, x, dir; + + cave_type *c_ptr; + + bool_ more = FALSE; + + monster_race *r_ptr = &r_info[p_ptr->body_monster]; + + + if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR)) + { + msg_print("You cannot do that."); + + return; + } + + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + /* Get a "repeated" direction */ + if (get_rep_dir(&dir)) + { + /* Bash location */ + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + /* Get grid */ + c_ptr = &cave[y][x]; + + /* Nothing useful */ + if ((c_ptr->feat < FEAT_DOOR_HEAD || + c_ptr->feat > FEAT_DOOR_TAIL) && + (c_ptr->feat < FEAT_ALTAR_HEAD || + c_ptr->feat > FEAT_ALTAR_TAIL) && (c_ptr->feat != FEAT_FOUNTAIN)) + { + /* Message */ + msg_print("You see nothing there to bash."); + } + + /* Monster in the way */ + else if (c_ptr->m_idx) + { + /* Take a turn */ + energy_use = 100; + + /* Message */ + msg_print("There is a monster in the way!"); + + /* Attack */ + py_attack(y, x, -1); + } + + else if (c_ptr->feat >= FEAT_ALTAR_HEAD && + c_ptr->feat <= FEAT_ALTAR_TAIL) + { + more = do_cmd_bash_altar(y, x); + } + /* Bash a closed door */ + else if (c_ptr->feat == FEAT_FOUNTAIN) + { + more = do_cmd_bash_fountain(y, x); + } + else + { + /* Bash the door */ + more = do_cmd_bash_aux(y, x, dir); + } + } + + /* Unless valid action taken, cancel bash */ + if (!more) disturb(0); +} + + + +/* + * Manipulate an adjacent grid in some way + * + * Attack monsters, tunnel through walls, disarm traps, open doors. + * + * Consider confusion XXX XXX XXX + * + * This command must always take a turn, to prevent free detection + * of invisible monsters. + */ +void do_cmd_alter(void) +{ + int y, x, dir; + + cave_type *c_ptr; + + bool_ more = FALSE; + + + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + /* Get a direction */ + if (get_rep_dir(&dir)) + { + /* Get location */ + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + /* Get grid */ + c_ptr = &cave[y][x]; + + /* Take a turn */ + energy_use = 100; + + /* Attack monsters */ + if (c_ptr->m_idx) + { + /* Attack */ + py_attack(y, x, -1); + } + + /* Open closed doors */ + else if ((c_ptr->feat >= FEAT_DOOR_HEAD) && + (c_ptr->feat <= FEAT_DOOR_TAIL)) + { + /* Tunnel */ + more = do_cmd_open_aux(y, x, dir); + } + + /* Tunnel through walls */ + else if (f_info[c_ptr->feat].flags1 & FF1_TUNNELABLE) + { + /* Tunnel */ + more = do_cmd_tunnel_aux(y, x, dir); + } + + /* Disarm traps */ + else if (c_ptr->t_idx != 0) + { + /* Tunnel */ + more = do_cmd_disarm_aux(y, x, dir, always_pickup); + } + + /* Oops */ + else + { + /* Oops */ + msg_print("You attack the empty air."); + } + } + + /* Cancel repetition unless we can continue */ + if (!more) disturb(0); +} + + +/* + * Find the index of some "spikes", if possible. + * + * XXX XXX XXX Let user choose a pile of spikes, perhaps? + */ +static bool_ get_spike(int *ip) +{ + int i; + + + /* Check every item in the pack */ + for (i = 0; i < INVEN_PACK; i++) + { + object_type *o_ptr = &p_ptr->inventory[i]; + + /* Skip non-objects */ + if (!o_ptr->k_idx) continue; + + /* Check the "tval" code */ + if (o_ptr->tval == TV_SPIKE) + { + /* Save the spike index */ + (*ip) = i; + + /* Success */ + return (TRUE); + } + } + + /* Oops */ + return (FALSE); +} + + + +/* + * Jam a closed door with a spike + * + * This command may NOT be repeated + */ +void do_cmd_spike(void) +{ + int y, x, dir, item; + + cave_type *c_ptr; + + + /* Get a "repeated" direction */ + if (get_rep_dir(&dir)) + { + /* Get location */ + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + /* Get grid and contents */ + c_ptr = &cave[y][x]; + + /* Require closed door */ + if (!((c_ptr->feat >= FEAT_DOOR_HEAD) && + (c_ptr->feat <= FEAT_DOOR_TAIL))) + { + /* Message */ + msg_print("You see nothing there to spike."); + } + + /* Get a spike */ + else if (!get_spike(&item)) + { + /* Message */ + msg_print("You have no spikes!"); + } + + /* Is a monster in the way? */ + else if (c_ptr->m_idx) + { + /* Take a turn */ + energy_use = 100; + + /* Message */ + msg_print("There is a monster in the way!"); + + /* Attack */ + py_attack(y, x, -1); + } + + /* Go for it */ + else + { + /* Take a turn */ + energy_use = 100; + + /* Successful jamming */ + msg_print("You jam the door with a spike."); + + /* Convert "locked" to "stuck" XXX XXX XXX */ + if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08) c_ptr->feat += 0x08; + + /* Add one spike to the door */ + if (c_ptr->feat < FEAT_DOOR_TAIL) c_ptr->feat++; + + /* Use up, and describe, a single spike, from the bottom */ + inc_stack_size(item, -1); + } + } +} + + +static void do_cmd_walk_jump(int pickup, bool_ disarm) +{ + int dir; + + bool_ more = FALSE; + + + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + /* Get a "repeated" direction */ + if (get_rep_dir(&dir)) + { + /* Take a turn */ + energy_use = 100; + + /* Actually move the character */ + move_player(dir, pickup, disarm); + + /* Allow more walking */ + more = TRUE; + } + + /* Hack -- In small scale wilderness it takes MUCH more time to move */ + energy_use *= (p_ptr->wild_mode) ? ((MAX_HGT + MAX_WID) / 2) : 1; + + /* Hack again -- Is there a special encounter ??? */ + if (p_ptr->wild_mode && + magik(wf_info[wild_map[p_ptr->py][p_ptr->px].feat].level - (p_ptr->lev * 2))) + { + /* Go into large wilderness view */ + p_ptr->wilderness_x = p_ptr->px; + p_ptr->wilderness_y = p_ptr->py; + energy_use = 100; + change_wild_mode(); + + /* HACk -- set the encouter flag for the wilderness generation */ + generate_encounter = TRUE; + p_ptr->oldpx = MAX_WID / 2; + p_ptr->oldpy = MAX_HGT / 2; + + /* Inform the player of his horrible fate :=) */ + msg_print("You are ambushed!"); + } + + /* Cancel repeat unless we may continue */ + if (!more) disturb(0); +} + + +/* + * Support code for the "Walk" and "Jump" commands + */ +void do_cmd_walk(int pickup, bool_ disarm) +{ + /* Move (usually pickup) */ + + if (p_ptr->immovable) + { + do_cmd_unwalk(); + } + else + { + do_cmd_walk_jump(pickup, disarm); + } +} + + +void do_cmd_run_run() +{ + int dir; + + + /* Hack -- no running when confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + /* Get a "repeated" direction */ + if (get_rep_dir(&dir)) + { + /* Hack -- Set the run counter */ + running = (command_arg ? command_arg : 1000); + + /* First step */ + run_step(dir); + } + p_ptr->window |= (PW_OVERHEAD); +} + + +/* + * Start running. + */ +void do_cmd_run(void) +{ + if (p_ptr->immovable) + { + return; + } + else + { + do_cmd_run_run(); + } +} + + + +/* + * Stay still. Search. Enter stores. + * Pick up treasure if "pickup" is true. + */ +void do_cmd_stay(int pickup) +{ + cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; + + + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + + /* Take a turn */ + energy_use = 100; + + + /* Spontaneous Searching */ + if ((p_ptr->skill_fos >= 50) || (0 == rand_int(50 - p_ptr->skill_fos))) + { + search(); + } + + /* Continuous Searching */ + if (p_ptr->searching) + { + search(); + } + + + /* Handle "objects" */ + carry(pickup); + + + /* Hack -- enter a store if we are on one */ + if (c_ptr->feat == FEAT_SHOP) + { + /* Disturb */ + disturb(0); + + /* Hack -- enter store */ + command_new = '_'; + } +} + +/* + * Resting allows a player to safely restore his hp -RAK- + */ +void do_cmd_rest(void) +{ + /* Can't rest on a Void Jumpgate -- too dangerous */ + if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN) + { + /* 'R&\n' is one of our favourite macros, so we have to do this */ + if (flush_failure) flush(); + + /* Tell the player why */ + msg_print(format("Resting on a %s is too dangerous!", + f_name + f_info[cave[p_ptr->py][p_ptr->px].feat].name)); + + /* Done */ + return; + } + + /* Can't rest while undead, it would mean dying */ + if (p_ptr->necro_extra & CLASS_UNDEAD) + { + /* 'R&\n' is one of our favourite macros, so we have to do this */ + if (flush_failure) flush(); + + /* Tell the player why */ + msg_print("Resting is impossible while undead!"); + + /* Done */ + return; + } + + /* Prompt for time if needed */ + if (command_arg <= 0) + { + cptr p = "Rest (0-9999, '*' for HP/SP, '&' as needed): "; + + char out_val[80]; + + /* Default */ + strcpy(out_val, "&"); + + /* Ask for duration */ + if (!get_string(p, out_val, 4)) return; + + /* Rest until done */ + if (out_val[0] == '&') + { + command_arg = ( -2); + } + + /* Rest a lot */ + else if (out_val[0] == '*') + { + command_arg = ( -1); + } + + /* Rest some */ + else + { + command_arg = atoi(out_val); + if (command_arg <= 0) return; + } + } + + + /* Paranoia */ + if (command_arg > 9999) command_arg = 9999; + + + /* Take a turn XXX XXX XXX (?) */ + energy_use = 100; + + /* Save the rest code */ + resting = command_arg; + + /* Cancel searching */ + p_ptr->searching = FALSE; + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Handle stuff */ + handle_stuff(); + + /* Refresh */ + Term_fresh(); +} + + + + + + +/* + * Determines the odds of an object breaking when thrown at a monster + * + * Note that artifacts never break, see the "drop_near()" function. + */ +int breakage_chance(object_type *o_ptr) +{ + int reducer = + 1 + ((get_skill(SKILL_ARCHERY)) ? (get_skill_scale(SKILL_ARCHERY, 10)) : 0); + + /* Examine the item type */ + switch (o_ptr->tval) + { + /* Always break */ + case TV_FLASK: + case TV_POTION: + case TV_POTION2: + case TV_BOTTLE: + case TV_FOOD: + { + return (100); + } + + /* Often break */ + case TV_LITE: + case TV_SCROLL: + case TV_SKELETON: + { + return (50); + } + + case TV_ARROW: + { + return (50 / reducer); + } + + /* Sometimes break */ + case TV_WAND: + case TV_SPIKE: + { + return (25); + } + + case TV_SHOT: + case TV_BOLT: + { + return (25 / reducer); + } + case TV_BOOMERANG: + { + return 1; + } + } + + /* Rarely break */ + return (10); +} + +/* + * Return multiplier of an object + */ +int get_shooter_mult(object_type *o_ptr) +{ + /* Assume a base multiplier */ + int tmul = 1; + + /* Analyze the launcher */ + switch (o_ptr->sval) + { + case SV_SLING: + { + /* Sling and ammo */ + tmul = 2; + break; + } + + case SV_SHORT_BOW: + { + /* Short Bow and Arrow */ + tmul = 2; + break; + } + + case SV_LONG_BOW: + { + /* Long Bow and Arrow */ + tmul = 3; + break; + } + + /* Light Crossbow and Bolt */ + case SV_LIGHT_XBOW: + { + tmul = 3; + break; + } + + /* Heavy Crossbow and Bolt */ + case SV_HEAVY_XBOW: + { + tmul = 4; + break; + } + } + return tmul; +} + + +/* + * Fire an object from the pack or floor. + * + * You may only fire items that "match" your missile launcher. + * + * You must use slings + pebbles/shots, bows + arrows, xbows + bolts. + * + * See "calc_bonuses()" for more calculations and such. + * + * Note that "firing" a missile is MUCH better than "throwing" it. + * + * Note: "unseen" monsters are very hard to hit. + * + * Objects are more likely to break if they "attempt" to hit a monster. + * + * Rangers (with Bows) and Anyone (with "Extra Shots") get extra shots. + * + * The "extra shot" code works by decreasing the amount of energy + * required to make each shot, spreading the shots out over time. + * + * Note that when firing missiles, the launcher multiplier is applied + * after all the bonuses are added in, making multipliers very useful. + * + * Note that Bows of "Extra Might" get extra range and an extra bonus + * for the damage multiplier. + * + * Note that Bows of "Extra Shots" give an extra shot. + */ +void do_cmd_fire(void) +{ + int dir, item; + + int j, y, x, ny, nx, ty, tx, by, bx; + + int oldtdam, tdam, tdis, thits, tmul; + + int bonus, chance; + + int cur_dis, visible; + + int breakage = -1, num_pierce = 0; + + s32b special = 0; + + object_type forge; + + object_type *q_ptr; + + object_type *o_ptr; + + object_type *j_ptr; + + bool_ hit_body = FALSE; + + byte missile_attr; + + char missile_char; + + char o_name[80]; + + cptr q, s; + + int msec = delay_factor * delay_factor * delay_factor; + + + /* Get the "bow" (if any) */ + j_ptr = &p_ptr->inventory[INVEN_BOW]; + + /* Require a launcher */ + if (!j_ptr->tval) + { + msg_print("You have nothing with which to fire."); + return; + } + + /* XXX HACK */ + if (j_ptr->tval == TV_INSTRUMENT) + { + msg_print("You cannot fire with an instrument."); + return; + } + + /* Get the "ammo" (if any) */ + o_ptr = &p_ptr->inventory[INVEN_AMMO]; + + item = INVEN_AMMO; + + /* If nothing correct try to choose from the backpack */ + if ((p_ptr->tval_ammo != o_ptr->tval) || (!o_ptr->k_idx)) + { + /* Require proper missile */ + item_tester_tval = p_ptr->tval_ammo; + + /* Get an item */ + q = "Your quiver is empty. Fire which item? "; + s = "You have nothing to fire."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + + /* Access the item */ + o_ptr = get_object(item); + } + + + /* Get a direction (or cancel) */ + if (!get_aim_dir(&dir)) return; + + + /* Get local object */ + q_ptr = &forge; + + /* Obtain a local object */ + object_copy(q_ptr, o_ptr); + + /* Single object */ + q_ptr->number = 1; + + /* Reduce stack and describe */ + inc_stack_size(item, -1); + + /* Break goi/manashield */ + if (p_ptr->invuln) + { + set_invuln(0); + } + if (p_ptr->disrupt_shield) + { + set_disrupt_shield(0); + } + + + /* Sound */ + sound(SOUND_SHOOT); + + + /* Describe the object */ + object_desc(o_name, q_ptr, FALSE, 3); + + /* Find the color and symbol for the object for throwing */ + missile_attr = object_attr(q_ptr); + missile_char = object_char(q_ptr); + + + /* Use the proper number of shots */ + thits = p_ptr->num_fire; + + /* Use a base distance */ + tdis = 10; + + /* Base damage from thrown object plus launcher bonus */ + tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + j_ptr->to_d; + + /* Actually "fire" the object */ + bonus = (p_ptr->to_h + p_ptr->to_h_ranged + q_ptr->to_h + j_ptr->to_h); + + chance = (p_ptr->skill_thb + (bonus * BTH_PLUS_ADJ)); + if (chance < 5) chance = 5; + + tmul = get_shooter_mult(j_ptr); + + /* Get extra "power" from "extra might" */ + tmul += p_ptr->xtra_might; + + /* Boost the damage */ + tdam *= tmul; + + /* Add in the player damage */ + tdam += p_ptr->to_d_ranged; + + /* Base range */ + tdis = 10 + 5 * tmul; + + + /* Take a (partial) turn */ + energy_use = (100 / thits); + + /* piercing shots ? */ + if (p_ptr->use_piercing_shots) + { + num_pierce = (get_skill(SKILL_COMBAT) / 10) - 1; + num_pierce = (num_pierce < 0) ? 0 : num_pierce; + } + + /* Start at the player */ + by = p_ptr->py; + bx = p_ptr->px; + y = p_ptr->py; + x = p_ptr->px; + + /* Predict the "target" location */ + tx = p_ptr->px + 99 * ddx[dir]; + ty = p_ptr->py + 99 * ddy[dir]; + + /* Check for "target request" */ + if ((dir == 5) && target_okay()) + { + tx = target_col; + ty = target_row; + } + + + /* Hack -- Handle stuff */ + handle_stuff(); + + oldtdam = tdam; + while (TRUE) + { + /* Reset after a piercing shot */ + tdam = oldtdam; + + /* Travel until stopped */ + for (cur_dis = 0; cur_dis <= tdis; ) + { + /* Hack -- Stop at the target */ + if ((y == ty) && (x == tx)) break; + + /* Calculate the new location (see "project()") */ + ny = y; + nx = x; + mmove2(&ny, &nx, by, bx, ty, tx); + + /* Stopped by walls/doors */ + if (!cave_floor_bold(ny, nx)) break; + + /* Advance the distance */ + cur_dis++; + + /* Save the new location */ + x = nx; + y = ny; + + + /* The player can see the (on screen) missile */ + if (panel_contains(y, x) && player_can_see_bold(y, x)) + { + /* Draw, Hilite, Fresh, Pause, Erase */ + print_rel(missile_char, missile_attr, y, x); + move_cursor_relative(y, x); + Term_fresh(); + Term_xtra(TERM_XTRA_DELAY, msec); + lite_spot(y, x); + Term_fresh(); + } + + /* The player cannot see the missile */ + else + { + /* Pause anyway, for consistancy */ + Term_xtra(TERM_XTRA_DELAY, msec); + } + + + /* Monster here, Try to hit it */ + if (cave[y][x].m_idx) + { + cave_type *c_ptr = &cave[y][x]; + + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + monster_race *r_ptr = race_inf(m_ptr); + + /* Check the visibility */ + visible = m_ptr->ml; + + /* Note the collision */ + hit_body = TRUE; + + /* Did we hit it (penalize range) */ + if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml)) + { + bool_ fear = FALSE; + + /* Assume a default death */ + cptr note_dies = " dies."; + + /* Some monsters get "destroyed" */ + if ((r_ptr->flags3 & (RF3_DEMON)) || + (r_ptr->flags3 & (RF3_UNDEAD)) || + (r_ptr->flags2 & (RF2_STUPID)) || + (strchr("Evg", r_ptr->d_char))) + { + /* Special note at death */ + note_dies = " is destroyed."; + } + + + /* Handle unseen monster */ + if (!visible) + { + /* Invisible monster */ + msg_format("The %s finds a mark.", o_name); + } + + /* Handle visible monster */ + else + { + char m_name[80]; + + /* Get "the monster" or "it" */ + monster_desc(m_name, m_ptr, 0); + + /* Message */ + msg_format("The %s hits %s.", o_name, m_name); + + /* Hack -- Track this monster race */ + if (m_ptr->ml) monster_race_track(m_ptr->r_idx, + m_ptr->ego); + + /* Hack -- Track this monster */ + if (m_ptr->ml) health_track(c_ptr->m_idx); + + /* Anger friends */ + { + char m_name[80]; + monster_desc(m_name, m_ptr, 0); + switch (is_friend(m_ptr)) + { + case 1: + { + msg_format("%^s gets angry!", m_name); + change_side(m_ptr); + break; + } + case 0: + { + msg_format("%^s gets angry!", m_name); + m_ptr->status = MSTATUS_NEUTRAL_M; + break; + } + } + } + } + + /* Apply special damage XXX XXX XXX */ + tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special); + tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, SKILL_ARCHERY); + + /* No negative damage */ + if (tdam < 0) tdam = 0; + + /* Complex message */ + if (wizard) + { + msg_format("You do %d (out of %d) damage.", + tdam, m_ptr->hp); + } + + /* Hit the monster, check for death */ + if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies)) + { + /* Dead monster */ + } + + /* No death */ + else + { + /* Message */ + message_pain(c_ptr->m_idx, tdam); + + if (special) attack_special(m_ptr, special, tdam); + + /* Take note */ + if (fear && m_ptr->ml) + { + char m_name[80]; + + /* Sound */ + sound(SOUND_FLEE); + + /* Get the monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + /* Message */ + msg_format("%^s flees in terror!", m_name); + } + } + } + + /* Stop looking */ + break; + } + } + + /* Exploding arrow ? */ + if (q_ptr->pval2 != 0) + { + int rad = 0, dam = + (damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d) * 2; + int flag = + PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL | + PROJECT_JUMP; + switch (q_ptr->sval) + { + case SV_AMMO_LIGHT: + rad = 2; + dam /= 2; + break; + case SV_AMMO_NORMAL: + rad = 3; + break; + case SV_AMMO_HEAVY: + rad = 4; + dam *= 2; + break; + } + + project(0, rad, y, x, dam, q_ptr->pval2, flag); + } + + /* Chance of breakage (during attacks) */ + j = (hit_body ? breakage_chance(q_ptr) : 0); + + /* Break ? */ + if ((q_ptr->pval2 != 0) || (rand_int(100) < j)) + { + breakage = 100; + break; + } + + /* If the ammo doesn't break, it can pierce through */ + if ((num_pierce) && (hit_body) && + (magik(45 + get_skill(SKILL_ARCHERY)))) + { + num_pierce--; + hit_body = FALSE; + + /* If target isn't reached, continue moving to target */ + if ( !((tx < x && x < bx) || (bx < x && x < tx)) && + !((ty < y && y < by) || (by < y && y < ty))) + { + /* Continue moving in same direction if we reached the target */ + int dx = tx - bx; + int dy = ty - by; + tx = x + 99 * dx; + ty = y + 99 * dy; + + /* New base location */ + by = y; + bx = x; + } + + msg_format("The %s pierces through!", o_name); + } + else + break; + } + + /* Drop (or break) near that location */ + drop_near(q_ptr, breakage, y, x); +} + + +/* + * Why is this here? even if it's temporary boost... + * Moved into player_type, hoping it might be useful in future extensions + * -- pelpel + */ +/* int throw_mult = 1; */ + +/* + * Throw an object from the pack or floor. + * + * Note: "unseen" monsters are very hard to hit. + * + * Should throwing a weapon do full damage? Should it allow the magic + * to hit bonus of the weapon to have an effect? Should it ever cause + * the item to be destroyed? Should it do any damage at all? + */ +void do_cmd_throw(void) +{ + int dir, item; + + s32b special = 0; + + int j, y, x, ny, nx, ty, tx; + + int chance, tdam, tdis; + + int mul, div; + + int boulder_add = 0; + int boulder_mult = 0; + + int cur_dis, visible; + + object_type forge; + + object_type *q_ptr; + + object_type *o_ptr; + + bool_ hit_body = FALSE; + + bool_ hit_wall = FALSE; + + byte missile_attr; + + char missile_char; + + char o_name[80]; + + int msec = delay_factor * delay_factor * delay_factor; + + cptr q, s; + + u32b f1, f2, f3, f4, f5, esp; + + + /* Get an item */ + q = "Throw which item? "; + s = "You have nothing to throw."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Access the item */ + o_ptr = get_object(item); + + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Hack - Cannot throw away 'no drop' cursed items */ + if (cursed_p(o_ptr) && (f4 & TR4_CURSE_NO_DROP)) + { + /* Oops */ + msg_print("Hmmm, you seem to be unable to throw it."); + + /* Nope */ + return; + } + + /* Boulder throwing */ + if ((o_ptr->tval == TV_JUNK) && (o_ptr->sval == SV_BOULDER) && (get_skill(SKILL_BOULDER))) + { + boulder_add = get_skill_scale(SKILL_BOULDER, 80); + boulder_mult = get_skill_scale(SKILL_BOULDER, 6); + } + + /* Get a direction (or cancel) */ + if (!get_aim_dir(&dir)) return; + + /* Break goi/manashield */ + if (p_ptr->invuln) + { + set_invuln(0); + } + if (p_ptr->disrupt_shield) + { + set_disrupt_shield(0); + } + + /* Get local object */ + q_ptr = &forge; + + /* Obtain a local object */ + object_copy(q_ptr, o_ptr); + + /* + * Hack -- If rods or wands are thrown, the total maximum timeout or + * charges need to be allocated between the two stacks. + */ + if (o_ptr->tval == TV_WAND) + { + q_ptr->pval = o_ptr->pval / o_ptr->number; + + if (o_ptr->number > 1) o_ptr->pval -= q_ptr->pval; + } + + /* Single object */ + q_ptr->number = 1; + + /* Reduce stack and describe */ + inc_stack_size(item, -1); + + /* Description */ + object_desc(o_name, q_ptr, FALSE, 3); + + /* Find the color and symbol for the object for throwing */ + missile_attr = object_attr(q_ptr); + missile_char = object_char(q_ptr); + + /* Extract a "distance multiplier" */ + /* Changed for 'launcher' corruption */ + mul = 10 + (2 * (p_ptr->throw_mult - 1)) + (2 * boulder_mult); + + /* Enforce a minimum "weight" of one pound */ + div = ((q_ptr->weight > 10) ? q_ptr->weight : 10); + + /* Hack -- Distance -- Reward strength, penalize weight */ + tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div; + + /* Max distance of 10-18 */ + if (tdis > mul) tdis = mul; + + /* Hack -- Base damage from thrown object */ + tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + boulder_add; + tdam *= p_ptr->throw_mult + boulder_mult; + + /* Chance of hitting - adjusted for Weaponmasters -- Gumby */ + chance = (p_ptr->skill_tht + (p_ptr->to_h * BTH_PLUS_ADJ)); + + /* Take a turn */ + energy_use = 100; + + + /* Start at the player */ + y = p_ptr->py; + x = p_ptr->px; + + /* Predict the "target" location */ + tx = p_ptr->px + 99 * ddx[dir]; + ty = p_ptr->py + 99 * ddy[dir]; + + /* Check for "target request" */ + if ((dir == 5) && target_okay()) + { + tx = target_col; + ty = target_row; + } + + + /* Hack -- Handle stuff */ + handle_stuff(); + + + /* Travel until stopped */ + for (cur_dis = 0; cur_dis <= tdis; ) + { + /* Hack -- Stop at the target */ + if ((y == ty) && (x == tx)) break; + + /* Calculate the new location (see "project()") */ + ny = y; + nx = x; + mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx); + + /* Stopped by walls/doors */ + if (!cave_floor_bold(ny, nx)) + { + hit_wall = TRUE; + break; + } + + /* Advance the distance */ + cur_dis++; + + /* Save the new location */ + x = nx; + y = ny; + + + /* The player can see the (on screen) missile */ + if (panel_contains(y, x) && player_can_see_bold(y, x)) + { + /* Draw, Hilite, Fresh, Pause, Erase */ + print_rel(missile_char, missile_attr, y, x); + move_cursor_relative(y, x); + Term_fresh(); + Term_xtra(TERM_XTRA_DELAY, msec); + lite_spot(y, x); + Term_fresh(); + } + + /* The player cannot see the missile */ + else + { + /* Pause anyway, for consistancy */ + Term_xtra(TERM_XTRA_DELAY, msec); + } + + + /* Monster here, Try to hit it */ + if (cave[y][x].m_idx) + { + cave_type *c_ptr = &cave[y][x]; + + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + monster_race *r_ptr = race_inf(m_ptr); + + /* Check the visibility */ + visible = m_ptr->ml; + + /* Note the collision */ + hit_body = TRUE; + + /* Did we hit it (penalize range) */ + if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml)) + { + bool_ fear = FALSE; + + /* Assume a default death */ + cptr note_dies = " dies."; + + /* Some monsters get "destroyed" */ + if ((r_ptr->flags3 & (RF3_DEMON)) || + (r_ptr->flags3 & (RF3_UNDEAD)) || + (r_ptr->flags2 & (RF2_STUPID)) || + (strchr("Evg", r_ptr->d_char))) + { + /* Special note at death */ + note_dies = " is destroyed."; + } + + + /* Handle unseen monster */ + if (!visible) + { + /* Invisible monster */ + msg_format("The %s finds a mark.", o_name); + } + + /* Handle visible monster */ + else + { + char m_name[80]; + + /* Get "the monster" or "it" */ + monster_desc(m_name, m_ptr, 0); + + /* Message */ + msg_format("The %s hits %s.", o_name, m_name); + + /* Hack -- Track this monster race */ + if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); + + /* Hack -- Track this monster */ + if (m_ptr->ml) health_track(c_ptr->m_idx); + } + + /* Apply special damage XXX XXX XXX */ + tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special); + tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, o_ptr->sval == SV_BOULDER ? SKILL_BOULDER : SKILL_ARCHERY); + + /* No negative damage */ + if (tdam < 0) tdam = 0; + + /* Complex message */ + if (wizard) + { + msg_format("You do %d (out of %d) damage.", + tdam, m_ptr->hp); + } + + /* Hit the monster, check for death */ + if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies)) + { + /* Dead monster */ + } + + /* No death */ + else + { + /* Message */ + message_pain(c_ptr->m_idx, tdam); + + if (special) attack_special(m_ptr, special, tdam); + + /* Anger friends */ + if (!(k_info[q_ptr->k_idx].tval == TV_POTION)) + { + char m_name[80]; + monster_desc(m_name, m_ptr, 0); + switch (is_friend(m_ptr)) + { + case 1: + msg_format("%^s gets angry!", m_name); + change_side(m_ptr); + break; + case 0: + msg_format("%^s gets angry!", m_name); + m_ptr->status = MSTATUS_NEUTRAL_M; + break; + } + } + + /* Take note */ + if (fear && m_ptr->ml) + { + char m_name[80]; + + /* Sound */ + sound(SOUND_FLEE); + + /* Get the monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + /* Message */ + msg_format("%^s flees in terror!", m_name); + } + } + } + + /* Stop looking */ + break; + } + } + + /* Chance of breakage (during attacks) */ + j = (hit_body ? breakage_chance(q_ptr) : 0); + + /* Potions smash open */ + if (k_info[q_ptr->k_idx].tval == TV_POTION) + { + if ((hit_body) || (hit_wall) || (randint(100) < j)) + { + /* Message */ + msg_format("The %s shatters!", o_name); + + if (potion_smash_effect(0, y, x, q_ptr->sval)) + { + if (cave[y][x].m_idx) + { + char m_name[80]; + monster_desc(m_name, &m_list[cave[y][x].m_idx], 0); + switch (is_friend(&m_list[cave[y][x].m_idx])) + { + case 1: + msg_format("%^s gets angry!", m_name); + change_side(&m_list[cave[y][x].m_idx]); + break; + case 0: + msg_format("%^s gets angry!", m_name); + m_list[cave[y][x].m_idx].status = MSTATUS_NEUTRAL_M; + break; + } + } + } + + return; + } + else + { + j = 0; + } + } + + /* Drop (or break) near that location */ + drop_near(q_ptr, j, y, x); +} + + +/* + * Throw a boomerang object from the equipement(bow). + * + * Note: "unseen" monsters are very hard to hit. + * + * Should throwing a weapon do full damage? Should it allow the magic + * to hit bonus of the weapon to have an effect? Should it ever cause + * the item to be destroyed? Should it do any damage at all? + */ +void do_cmd_boomerang(void) +{ + int dir; + + int j, y, x, ny, nx, ty, tx; + + int chance, tdam, tdis; + + int mul, div; + + int cur_dis, visible; + + object_type forge; + + object_type *q_ptr; + + object_type *o_ptr; + + bool_ hit_body = FALSE; + + byte missile_attr; + + char missile_char; + + char o_name[80]; + + s32b special = 0; + + int msec = delay_factor * delay_factor * delay_factor; + + + /* Get the "bow" (if any) */ + o_ptr = &p_ptr->inventory[INVEN_BOW]; + + + /* Get a direction (or cancel) */ + if (!get_aim_dir(&dir)) return; + + + /* Get local object */ + q_ptr = &forge; + + /* Obtain a local object */ + object_copy(q_ptr, o_ptr); + + /* Single object */ + q_ptr->number = 1; + + /* Description */ + object_desc(o_name, q_ptr, FALSE, 3); + + /* Find the color and symbol for the object for throwing */ + missile_attr = object_attr(q_ptr); + missile_char = object_char(q_ptr); + + /* Extract a "distance multiplier" */ + /* Changed for 'launcher' corruption */ + mul = 10 + 2 * (p_ptr->throw_mult - 1); + + /* Enforce a minimum "weight" of one pound */ + div = ((q_ptr->weight > 10) ? q_ptr->weight : 10); + + /* Hack -- Distance -- Reward strength, penalize weight */ + tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div; + + /* Max distance of 10-18 */ + if (tdis > mul) tdis = mul; + + /* Hack -- Base damage from thrown object */ + tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d; + tdam *= p_ptr->throw_mult; + + /* Chance of hitting */ + chance = + (p_ptr->skill_tht + + ((p_ptr->to_h + p_ptr->to_h_ranged) * BTH_PLUS_ADJ)); + + chance += get_skill(SKILL_BOOMERANG); + + /* Take a turn */ + energy_use = 100; + + + /* Start at the player */ + y = p_ptr->py; + x = p_ptr->px; + + /* Predict the "target" location */ + tx = p_ptr->px + 99 * ddx[dir]; + ty = p_ptr->py + 99 * ddy[dir]; + + /* Check for "target request" */ + if ((dir == 5) && target_okay()) + { + tx = target_col; + ty = target_row; + } + + + /* Hack -- Handle stuff */ + handle_stuff(); + + + /* Travel until stopped */ + for (cur_dis = 0; cur_dis <= tdis; ) + { + /* Hack -- Stop at the target */ + if ((y == ty) && (x == tx)) break; + + /* Calculate the new location (see "project()") */ + ny = y; + nx = x; + mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx); + + /* Stopped by walls/doors */ + if (!cave_floor_bold(ny, nx)) + { + break; + } + + /* Advance the distance */ + cur_dis++; + + /* Save the new location */ + x = nx; + y = ny; + + + /* The player can see the (on screen) missile */ + if (panel_contains(y, x) && player_can_see_bold(y, x)) + { + /* Draw, Hilite, Fresh, Pause, Erase */ + print_rel(missile_char, missile_attr, y, x); + move_cursor_relative(y, x); + Term_fresh(); + Term_xtra(TERM_XTRA_DELAY, msec); + lite_spot(y, x); + Term_fresh(); + } + + /* The player cannot see the missile */ + else + { + /* Pause anyway, for consistancy */ + Term_xtra(TERM_XTRA_DELAY, msec); + } + + + /* Monster here, Try to hit it */ + if (cave[y][x].m_idx) + { + cave_type *c_ptr = &cave[y][x]; + + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + monster_race *r_ptr = race_inf(m_ptr); + + /* Check the visibility */ + visible = m_ptr->ml; + + /* Note the collision */ + hit_body = TRUE; + + /* Did we hit it (penalize range) */ + if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml)) + { + bool_ fear = FALSE; + + /* Assume a default death */ + cptr note_dies = " dies."; + + /* Some monsters get "destroyed" */ + if ((r_ptr->flags3 & (RF3_DEMON)) || + (r_ptr->flags3 & (RF3_UNDEAD)) || + (r_ptr->flags2 & (RF2_STUPID)) || + (strchr("Evg", r_ptr->d_char))) + { + /* Special note at death */ + note_dies = " is destroyed."; + } + + + /* Handle unseen monster */ + if (!visible) + { + /* Invisible monster */ + msg_format("The %s finds a mark.", o_name); + } + + /* Handle visible monster */ + else + { + char m_name[80]; + + /* Get "the monster" or "it" */ + monster_desc(m_name, m_ptr, 0); + + /* Message */ + msg_format("The %s hits %s.", o_name, m_name); + + /* Hack -- Track this monster race */ + if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego); + + /* Hack -- Track this monster */ + if (m_ptr->ml) health_track(c_ptr->m_idx); + } + + /* Apply special damage XXX XXX XXX */ + tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special); + tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, SKILL_ARCHERY); + + /* No negative damage */ + if (tdam < 0) tdam = 0; + + /* Complex message */ + if (wizard) + { + msg_format("You do %d (out of %d) damage.", + tdam, m_ptr->hp); + } + + /* Hit the monster, check for death */ + if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies)) + { + /* Dead monster */ + } + + /* No death */ + else + { + /* Message */ + message_pain(c_ptr->m_idx, tdam); + + if (special) attack_special(m_ptr, special, tdam); + + /* Anger friends */ + if (!(k_info[q_ptr->k_idx].tval == TV_POTION)) + { + char m_name[80]; + monster_desc(m_name, m_ptr, 0); + switch (is_friend(m_ptr)) + { + case 1: + msg_format("%^s gets angry!", m_name); + change_side(m_ptr); + break; + case 0: + msg_format("%^s gets angry!", m_name); + m_ptr->status = MSTATUS_NEUTRAL_M; + break; + } + } + + /* Take note */ + if (fear && m_ptr->ml) + { + char m_name[80]; + + /* Sound */ + sound(SOUND_FLEE); + + /* Get the monster name (or "it") */ + monster_desc(m_name, m_ptr, 0); + + /* Message */ + msg_format("%^s flees in terror!", m_name); + } + } + + /* Chance of breakage (during attacks) */ + j = (hit_body ? breakage_chance(o_ptr) : 0); + + /* Break the boomerang */ + if (!(o_ptr->art_name || artifact_p(o_ptr)) && + (rand_int(100) < j)) + { + msg_print(format("Your %s is destroyed.", o_name)); + inc_stack_size_ex(INVEN_BOW, -1, OPTIMIZE, NO_DESCRIBE); + } + } + + /* Stop looking */ + break; + } + } + + /* Travel back to the player */ + for (cur_dis = 0; cur_dis <= tdis; ) + { + /* Hack -- Stop at the target */ + if ((y == p_ptr->py) && (x == p_ptr->px)) break; + + /* Calculate the new location (see "project()") */ + ny = y; + nx = x; + mmove2(&ny, &nx, ty, tx, p_ptr->py, p_ptr->px); + + /* Advance the distance */ + cur_dis++; + + /* Save the new location */ + x = nx; + y = ny; + + + /* The player can see the (on screen) missile */ + if (panel_contains(y, x) && player_can_see_bold(y, x)) + { + /* Draw, Hilite, Fresh, Pause, Erase */ + print_rel(missile_char, missile_attr, y, x); + move_cursor_relative(y, x); + Term_fresh(); + Term_xtra(TERM_XTRA_DELAY, msec); + lite_spot(y, x); + Term_fresh(); + } + + /* The player cannot see the missile */ + else + { + /* Pause anyway, for consistancy */ + Term_xtra(TERM_XTRA_DELAY, msec); + } + } +} + + +/* + * Try to ``walk'' using phase door. + */ +void do_cmd_unwalk() +{ + int dir, y, x, feat; + + cave_type *c_ptr; + + bool_ more = FALSE; + + + if (!get_rep_dir(&dir)) return; + + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + c_ptr = &cave[y][x]; + feat = c_ptr->feat; + + /* Must have knowledge to know feature XXX XXX */ + if (!(c_ptr->info & (CAVE_MARK))) feat = FEAT_NONE; + + /* Take a turn */ + energy_use = 100; + energy_use *= (p_ptr->wild_mode) ? (5 * (MAX_HGT + MAX_WID) / 2) : 1; + + + /* Allow repeated command */ + if (command_arg) + { + /* Set repeat count */ + command_rep = command_arg - 1; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Cancel the arg */ + command_arg = 0; + } + + + /* Attack monsters */ + if (c_ptr->m_idx > 0) + { + /* Attack */ + py_attack(y, x, -1); + } + + /* Exit the area */ + else if ((!dun_level) && (!p_ptr->wild_mode) && + ((x == 0) || (x == cur_wid - 1) || (y == 0) || (y == cur_hgt - 1))) + { + /* Can the player enter the grid? */ + if (player_can_enter(c_ptr->mimic)) + { + /* Hack: move to new area */ + if ((y == 0) && (x == 0)) + { + p_ptr->wilderness_y--; + p_ptr->wilderness_x--; + p_ptr->oldpy = cur_hgt - 2; + p_ptr->oldpx = cur_wid - 2; + ambush_flag = FALSE; + } + + else if ((y == 0) && (x == MAX_WID - 1)) + { + p_ptr->wilderness_y--; + p_ptr->wilderness_x++; + p_ptr->oldpy = cur_hgt - 2; + p_ptr->oldpx = 1; + ambush_flag = FALSE; + } + + else if ((y == MAX_HGT - 1) && (x == 0)) + { + p_ptr->wilderness_y++; + p_ptr->wilderness_x--; + p_ptr->oldpy = 1; + p_ptr->oldpx = cur_wid - 2; + ambush_flag = FALSE; + } + + else if ((y == MAX_HGT - 1) && (x == MAX_WID - 1)) + { + p_ptr->wilderness_y++; + p_ptr->wilderness_x++; + p_ptr->oldpy = 1; + p_ptr->oldpx = 1; + ambush_flag = FALSE; + } + + else if (y == 0) + { + p_ptr->wilderness_y--; + p_ptr->oldpy = cur_hgt - 2; + p_ptr->oldpx = x; + ambush_flag = FALSE; + } + + else if (y == cur_hgt - 1) + { + p_ptr->wilderness_y++; + p_ptr->oldpy = 1; + p_ptr->oldpx = x; + ambush_flag = FALSE; + } + + else if (x == 0) + { + p_ptr->wilderness_x--; + p_ptr->oldpx = cur_wid - 2; + p_ptr->oldpy = y; + ambush_flag = FALSE; + } + + else if (x == cur_wid - 1) + { + p_ptr->wilderness_x++; + p_ptr->oldpx = 1; + p_ptr->oldpy = y; + ambush_flag = FALSE; + } + + p_ptr->leaving = TRUE; + + return; + } + } + + /* Hack -- Ignore weird terrain types. */ + else if (!cave_floor_grid(c_ptr)) + { + teleport_player(10); + } + + /* Enter quests */ + else if (((feat >= FEAT_QUEST_ENTER) && (feat <= FEAT_QUEST_UP)) || + ((feat >= FEAT_LESS) && (feat <= FEAT_MORE))) + { + move_player(dir, always_pickup, TRUE); + more = FALSE; + } + + /* Hack -- Ignore wilderness mofe. */ + else if (p_ptr->wild_mode) + { + /* Chance to not blink right */ + if (magik(15)) + { + do + { + dir = rand_range(1, 9); + } + while (dir == 5); + } + + move_player(dir, always_pickup, TRUE); + } + + /* Walking semantics */ + else + { + teleport_player_directed(10, dir); + } + + /* Cancel repetition unless we can continue */ + if (!more) disturb(0); +} + + +static bool_ tport_vertically(bool_ how) +{ + /* arena or quest -KMW- */ + if ((p_ptr->inside_arena) || (p_ptr->inside_quest)) + { + msg_print("There is no effect."); + return (FALSE); + } + + if (dungeon_flags2 & DF2_NO_EASY_MOVE) + { + msg_print("Some powerful force prevents you from teleporting."); + return FALSE; + } + + /* Go down */ + if (how) + { + if (dun_level >= d_info[dungeon_type].maxdepth) + { + msg_print("The floor is impermeable."); + return (FALSE); + } + + msg_print("You sink through the floor."); + dun_level++; + p_ptr->leaving = TRUE; + } + else + { + if (dun_level < d_info[dungeon_type].mindepth) + { + msg_print("There is nothing above you but air."); + return (FALSE); + } + + msg_print("You rise through the ceiling."); + dun_level--; + p_ptr->leaving = TRUE; + } + + return (TRUE); +} + + +/* + * Do a special ``movement'' action. Meant to be used for ``immovable'' + * characters. + */ +void do_cmd_immovable_special(void) +{ + int i, ii, ij, dir; + + int foo = p_ptr->immov_cntr; + + int lose_sp = 0; + + int lose_hp = 0; + + bool_ did_act = FALSE; + + bool_ did_load = FALSE; + + + if (foo > 1) + { + if (p_ptr->csp > foo / 2) + { + + msg_format("This will drain %d mana points!", foo / 2); + if (!get_check("Proceed? ")) return; + + lose_sp = foo / 2; + + } + else if (p_ptr->chp > foo / 2) + { + + msg_format("Warning: This will drain %d hit points!", foo / 2); + if (!get_check("Proceed? ")) return; + + lose_hp = foo / 2; + + } + else + { + msg_print("You can't use your powers yet."); + return; + } + } + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + + /* Interact until done */ + while (1) + { + /* Clear screen */ + Term_clear(); + + /* Ask for a choice */ + prt("Do what special action:", 2, 0); + + /* Give some choices */ + prt("(a) Teleport to a specific place.", 4, 5); + prt("(b) Fetch an item.", 5, 5); + prt("(c) Go up 50'", 6, 5); + prt("(d) Go down 50'", 7, 5); + + /* Prompt */ + prt("Command: ", 9, 0); + + /* Prompt */ + i = inkey(); + + /* Done */ + if (i == ESCAPE) break; + + /* Tele-to */ + if (i == 'a') + { + Term_load(); + character_icky = FALSE; + did_load = TRUE; + + if (!tgt_pt(&ii, &ij)) break; + + /* Teleport to the target */ + teleport_player_to(ij, ii); + + did_act = TRUE; + break; + } + + /* Fetch item */ + else if (i == 'b') + { + Term_load(); + character_icky = FALSE; + did_load = TRUE; + + if (!get_aim_dir(&dir)) return; + fetch(dir, p_ptr->lev * 15, FALSE); + py_pickup_floor(always_pickup); + + did_act = TRUE; + break; + } + + /* Move up */ + else if (i == 'c') + { + Term_load(); + character_icky = FALSE; + did_load = TRUE; + + if (!tport_vertically(FALSE)) return; + + did_act = TRUE; + break; + } + + /* Move down */ + else if (i == 'd') + { + Term_load(); + character_icky = FALSE; + did_load = TRUE; + + if (!tport_vertically(TRUE)) return; + + did_act = TRUE; + break; + } + + /* Unknown option */ + else + { + bell(); + } + + } + + /* Check if screen was restored before */ + if (!did_load) + { + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; + } + + /* Apply stat losses if something was done */ + if (did_act) + { + p_ptr->immov_cntr += 101 - (p_ptr->lev * 2); + + if (lose_sp) + { + p_ptr->csp -= lose_sp; + p_ptr->redraw |= (PR_MANA); + } + + if (lose_hp) + { + p_ptr->chp -= lose_hp; + p_ptr->redraw |= (PR_HP); + } + + energy_use = 100; + } +} + +/* Can we sacrifice it ? */ +static bool_ item_tester_hook_sacrifiable(object_type *o_ptr) +{ + GOD(GOD_MELKOR) + { + /* Corpses are */ + if (o_ptr->tval == TV_CORPSE && o_ptr->sval == SV_CORPSE_CORPSE) + return (TRUE); + + /* Books without any udun spells */ + if ((o_ptr->tval == TV_BOOK) && udun_in_book(o_ptr->sval, o_ptr->pval) <= 0) + { + return TRUE; + } + } + + /* Assume not */ + return (FALSE); +} + +/* + * Is item eligible for sacrifice to Aule? + */ +static bool_ item_tester_hook_sacrifice_aule(object_type *o_ptr) +{ + /* perhaps restrict this only to metal armour and weapons */ + return (o_ptr->found == OBJ_FOUND_SELFMADE); +} + +/* + * Handle sacrifices to Aule + */ +static void do_cmd_sacrifice_aule() +{ + int item; + + item_tester_hook = item_tester_hook_sacrifice_aule; + if (!get_item(&item, + "Sacrifice which item? ", + "You have nothing to sacrifice.", + USE_INVEN)) + { + return; + } + + /* Increase piety by the value of the item / 10. */ + { + object_type *o_ptr = get_object(item); + s32b delta = object_value(o_ptr) / 10; + + inc_piety(GOD_ALL, delta); + } + + /* Destroy the object */ + inc_stack_size(item, -1); +} + +/* + * Handle sacrifices. + * Grace is increased by value of sacrifice. + */ +void do_cmd_sacrifice(void) +{ + byte on_what = cave[p_ptr->py][p_ptr->px].feat; + + /* Check valididty */ + if ((on_what < FEAT_ALTAR_HEAD) || (on_what > FEAT_ALTAR_TAIL)) + { + show_god_info(FALSE); + return; + } + else + { + int agod = on_what - FEAT_ALTAR_HEAD + 1; + + /* Not worshipping a god ? ahhhh! */ + GOD(GOD_NONE) + { + int i; + + for (i = 0; i < 10; i++) + { + if (deity_info[agod].desc[i] != NULL) + msg_print(deity_info[agod].desc[i]); + } + if (get_check(format("Do you want to worship %s? ", deity_info[agod].name))) + { + follow_god(agod, FALSE); + p_ptr->grace = -200; + inc_piety(p_ptr->pgod, 0); + } + } + else if (p_ptr->pgod == agod) + { + GOD(GOD_MELKOR) + { + /* One can sacrifice some HP for piety or damage */ + if ((p_ptr->mhp > 10) && (p_ptr->chp > 10) && get_check("Do you want to sacrifice a part of yourself? ")) + { + /* 10 HP = 300 * wis piety */ + if (get_check("Do you want to sacrifice for more piety instead of damage? ")) + { + int x = wisdom_scale(6); + if (x < 1) x = 1; + + p_ptr->hp_mod -= 10; + take_hit(10, "self sacrifice to Melkor"); + msg_print("Your life slips away, and Melkor seems happier."); + inc_piety(GOD_MELKOR, x * 300); + p_ptr->update |= (PU_HP); + } + /* 10 HP = +wis damage */ + else + { + take_hit(10, "self sacrifice to Melkor"); + msg_print("Your life slips away, and your arms grow stronger."); + p_ptr->melkor_sacrifice++; + p_ptr->update |= (PU_BONUS | PU_HP); + } + } + else + { + int item; + object_type *o_ptr; + + /* Restrict choices to food */ + item_tester_hook = item_tester_hook_sacrifiable; + + /* Get an item */ + if (!get_item(&item, "Sacrifice which item? ", "You have nothing to sacrifice.", (USE_INVEN))) return; + o_ptr = get_object(item); + + /* Piety for corpses is based on monster level */ + if (o_ptr->tval == TV_CORPSE) + { + inc_piety(GOD_MELKOR, 2 * r_info[o_ptr->pval2].level); + } + + /* In books it depends of the spell levels*/ + if (o_ptr->tval == TV_BOOK) + { + int x = levels_in_book(o_ptr->sval, o_ptr->pval); + + inc_piety(GOD_MELKOR, 2 * x); + } + + /* Remove the item */ + inc_stack_size(item, -1); + } + } + + GOD(GOD_AULE) + { + do_cmd_sacrifice_aule(); + } + + } + } +} + + +/* + * scan_monst -- + * + * Return a list of o_list[] indexes of items of the given monster + */ +bool_ scan_monst(int *items, int *item_num, int m_idx) +{ + int this_o_idx, next_o_idx; + + int num = 0; + + + (*item_num) = 0; + + /* Scan all objects in the grid */ + for (this_o_idx = m_list[m_idx].hold_o_idx; this_o_idx; + this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Accept this item */ + items[num++] = this_o_idx; + + /* XXX Hack -- Enforce limit */ + if (num == 23) break; + } + + /* Number of items */ + (*item_num) = num; + + /* Result */ + return (num != 0); +} + + +/* + * Display a list of the items that the given monster carries. + */ +byte show_monster_inven(int m_idx, int *monst_list) +{ + 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]; + + int monst_num; + + + /* Default length */ + len = 79 - 50; + + /* Maximum space allowed for descriptions */ + lim = 79 - 3; + + /* Require space for weight */ + lim -= 9; + + /* Scan for objects on the monster */ + (void)scan_monst(monst_list, &monst_num, m_idx); + + /* Display the p_ptr->inventory */ + for (k = 0, i = 0; i < monst_num; i++) + { + o_ptr = &o_list[monst_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 p_ptr->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 = monst_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 --(-- */ + strnfmt(tmp_val, 80, "%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; + strnfmt(tmp_val, 80, "%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); + + return monst_num; +} + + +/* + * Steal an object from a monster + */ +void do_cmd_steal() +{ + int x, y, dir = 0, item = -1, k = -1; + + cave_type *c_ptr; + + monster_type *m_ptr; + + object_type *o_ptr, forge; + + byte num = 0; + + bool_ done = FALSE; + + int monst_list[23]; + + + /* Only works on adjacent monsters */ + if (!get_rep_dir(&dir)) return; + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + c_ptr = &cave[y][x]; + + if (!(c_ptr->m_idx)) + { + msg_print("There is no monster there!"); + return; + } + + m_ptr = &m_list[c_ptr->m_idx]; + + /* There were no non-gold items */ + if (!m_ptr->hold_o_idx) + { + msg_print("That monster has no objects!"); + return; + } + + /* The monster is immune */ + if (r_info[m_ptr->r_idx].flags7 & (RF7_NO_THEFT)) + { + msg_print("The monster is guarding the treasures."); + return; + } + + screen_save(); + + num = show_monster_inven(c_ptr->m_idx, monst_list); + + /* Repeat until done */ + while (!done) + { + char tmp_val[80]; + char which = ' '; + + /* Build the prompt */ + strnfmt(tmp_val, 80, "Choose an item to steal (a-%c) or ESC:", + 'a' - 1 + num); + + /* Show the prompt */ + prt(tmp_val, 0, 0); + + /* Get a key */ + which = inkey(); + + /* Parse it */ + switch (which) + { + case ESCAPE: + { + done = TRUE; + + break; + } + + default: + { + int ver; + + /* Extract "query" setting */ + ver = isupper(which); + which = tolower(which); + + k = islower(which) ? A2I(which) : -1; + if (k < 0 || k >= num) + { + bell(); + + break; + } + + /* Verify the item */ + if (ver && !verify("Try", 0 - monst_list[k])) + { + done = TRUE; + + break; + } + + /* Accept that choice */ + item = monst_list[k]; + done = TRUE; + + break; + } + } + } + + if (item != -1) + { + int chance; + + chance = 40 - p_ptr->stat_ind[A_DEX]; + chance += + o_list[item].weight / (get_skill_scale(SKILL_STEALING, 19) + 1); + chance += get_skill_scale(SKILL_STEALING, 29) + 1; + chance -= (m_ptr->csleep) ? 10 : 0; + chance += m_ptr->level; + + /* Failure check */ + if (rand_int(chance) > 1 + get_skill_scale(SKILL_STEALING, 25)) + { + /* Take a turn */ + energy_use = 100; + + /* Wake up */ + m_ptr->csleep = 0; + + /* Speed up because monsters are ANGRY when you try to thief them */ + m_ptr->mspeed += 5; + + screen_load(); + + msg_print("Oops! The monster is now really *ANGRY*!"); + + return; + } + + /* Reconnect the objects list */ + if (num == 1) m_ptr->hold_o_idx = 0; + else + { + if (k > 0) o_list[monst_list[k - 1]].next_o_idx = monst_list[k + 1]; + if (k + 1 >= num) o_list[monst_list[k - 1]].next_o_idx = 0; + if (k == 0) m_ptr->hold_o_idx = monst_list[k + 1]; + } + + /* Rogues gain some xp */ + if (PRACE_FLAGS(PR1_EASE_STEAL)) + { + s32b max_point; + + /* Max XP gained from stealing */ + max_point = (o_list[item].weight / 2) + (m_ptr->level * 10); + + /* Randomise it a bit, with half a max guaranteed */ + gain_exp((max_point / 2) + (randint(max_point) / 2)); + + /* Allow escape */ + if (get_check("Phase door?")) teleport_player(10); + } + + /* Get the item */ + o_ptr = &forge; + + /* Special handling for gold */ + if (o_list[item].tval == TV_GOLD) + { + /* Collect the gold */ + p_ptr->au += o_list[item].pval; + + /* Redraw gold */ + p_ptr->redraw |= (PR_GOLD); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + } + else + { + object_copy(o_ptr, &o_list[item]); + + inven_carry(o_ptr, FALSE); + } + + /* Delete it */ + o_list[item].k_idx = 0; + } + + screen_load(); + + /* Take a turn */ + energy_use = 100; +} + + +/* + * Give an item to a monster + */ +void do_cmd_give() +{ + int dir, x, y; + + cave_type *c_ptr; + + cptr q, s; + + int item; + + + /* Get a "repeated" direction */ + if (!get_rep_dir(&dir)) return; + + /* Get requested location */ + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + /* Get requested grid */ + c_ptr = &cave[y][x]; + + /* No monster in the way */ + if (c_ptr->m_idx == 0) + { + msg_print("There is no monster there."); + return; + } + + /* Get an item */ + q = "What item do you want to offer? "; + s = "You have nothing to offer."; + if (!get_item(&item, q, s, USE_INVEN)) return; + + /* Process hooks if there are any */ + if (!process_hooks(HOOK_GIVE, "(d,d)", c_ptr->m_idx, item)) + { + hook_give_in in = { c_ptr->m_idx, item }; + if (!process_hooks_new(HOOK_GIVE, &in, NULL)) + { + msg_print("The monster does not want your item."); + } + } + + /* Take a turn, even if the offer is declined */ + energy_use = 100; +} + + +/* + * Chat with a monster + */ +void do_cmd_chat() +{ + int dir, x, y; + + cave_type *c_ptr; + + + /* Get a "repeated" direction */ + if (!get_rep_dir(&dir)) return; + + /* Get requested location */ + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + + /* Get requested grid */ + c_ptr = &cave[y][x]; + + /* No monster in the way */ + if (c_ptr->m_idx == 0) + { + msg_print("There is no monster there."); + return; + } + + /* Process hook if there are any */ + if (!process_hooks(HOOK_CHAT, "(d)", c_ptr->m_idx)) + { + msg_print("The monster does not want to chat."); + } + + /* No energy spent */ +} diff --git a/src/cmd3.c b/src/cmd3.c deleted file mode 100644 index 9f4170bc..00000000 --- a/src/cmd3.c +++ /dev/null @@ -1,2333 +0,0 @@ -/* File: cmd3.c */ - -/* Purpose: Inventory commands */ - -/* - * 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 "angband.h" - -#include "quark.h" -#include "hooks.h" - -/* - * Display p_ptr->inventory - */ -void do_cmd_inven(void) -{ - char out_val[160]; - - - /* Note that we are in "p_ptr->inventory" mode */ - command_wrk = FALSE; - - /* Save the screen */ - character_icky = TRUE; - Term_save(); - - /* Hack -- show empty slots */ - item_tester_full = TRUE; - - /* Display the p_ptr->inventory */ - show_inven(); - - /* Hack -- hide empty slots */ - item_tester_full = FALSE; - - - { - s32b total_weight = calc_total_weight(); - - strnfmt(out_val, 160, - "Inventory: carrying %ld.%ld pounds (%ld%% of capacity). Command: ", - total_weight / 10, total_weight % 10, - (total_weight * 100) / ((weight_limit()) / 2)); - } - - /* Get a command */ - prt(out_val, 0, 0); - - /* Get a new command */ - command_new = inkey(); - - /* Restore the screen */ - Term_load(); - character_icky = FALSE; - - - /* Process "Escape" */ - if (command_new == ESCAPE) - { - /* Reset stuff */ - command_new = 0; - } - - /* Process normal keys */ - else - { - /* Mega-Hack -- Don't disable keymaps for this key */ - request_command_inven_mode = TRUE; - } -} - - -/* - * Display equipment - */ -void do_cmd_equip(void) -{ - char out_val[160]; - - - /* Note that we are in "equipment" mode */ - command_wrk = TRUE; - - /* Save the screen */ - character_icky = TRUE; - Term_save(); - - /* Hack -- show empty slots */ - item_tester_full = TRUE; - - /* Display the equipment */ - show_equip(); - - /* Hack -- undo the hack above */ - item_tester_full = FALSE; - - /* Build a prompt */ - { - s32b total_weight = calc_total_weight(); - - /* Build a prompt */ - strnfmt(out_val, 160, - "Equipment: carrying %ld.%ld pounds (%ld%% of capacity). Command: ", - total_weight / 10, total_weight % 10, - (total_weight * 100) / ((weight_limit()) / 2)); - } - - /* Get a command */ - prt(out_val, 0, 0); - - /* Get a new command */ - command_new = inkey(); - - /* Restore the screen */ - Term_load(); - character_icky = FALSE; - - - /* Process "Escape" */ - if (command_new == ESCAPE) - { - /* Reset stuff */ - command_new = 0; - } - - /* Process normal keys */ - else - { - /* Mega-Hack -- Don't disable keymaps for this key */ - request_command_inven_mode = TRUE; - } -} - - -/* - * The "wearable" tester - */ -static bool_ item_tester_hook_wear(object_type *o_ptr) -{ - u32b f1, f2, f3, f4, f5, esp; - int slot = wield_slot(o_ptr); - - - /* Extract the flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Only one ultimate at a time */ - if (f4 & TR4_ULTIMATE) - { - int i; - - for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) - { - object_type *q_ptr = &p_ptr->inventory[i]; - - /* Extract the flags */ - object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if (!q_ptr->k_idx) continue; - - if (f4 & TR4_ULTIMATE) return (FALSE); - } - } - - if ((slot < INVEN_WIELD) || ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_WIELD) && (p_ptr->melee_style != SKILL_MASTERY))) - return (FALSE); - - /* Check for a usable slot */ - if (slot >= INVEN_WIELD) return (TRUE); - - /* Assume not wearable */ - return (FALSE); -} - - -bool_ is_slot_ok(int slot) -{ - if ((slot >= INVEN_WIELD) && (slot < INVEN_TOTAL)) - { - return (TRUE); - } - else - { - return (FALSE); - } -} - - -/* - * Wield or wear a single item from the pack or floor - */ -void do_cmd_wield(void) -{ - int item, slot, num = 1; - - object_type forge; - - object_type *q_ptr; - - object_type *o_ptr, *i_ptr; - - cptr act; - - char o_name[80]; - - cptr q, s; - - u32b f1, f2, f3, f4, f5, esp; - - - /* Restrict the choices */ - item_tester_hook = item_tester_hook_wear; - - /* Get an item */ - q = "Wear/Wield which item? "; - s = "You have nothing you can wear or wield."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Check the slot */ - slot = wield_slot(o_ptr); - - /* Prevent wielding into a cursed slot */ - if (cursed_p(&p_ptr->inventory[slot])) - { - /* Describe it */ - object_desc(o_name, &p_ptr->inventory[slot], FALSE, 0); - - /* Message */ - msg_format("The %s you are %s appears to be cursed.", - o_name, describe_use(slot)); - - /* Cancel the command */ - return; - } - - if ((cursed_p(o_ptr)) && (wear_confirm) - && (object_known_p(o_ptr) || (o_ptr->ident & (IDENT_SENSE)))) - { - char dummy[512]; - - /* Describe it */ - object_desc(o_name, o_ptr, FALSE, 0); - - strnfmt(dummy, 512, "Really use the %s {cursed}? ", o_name); - if (!(get_check(dummy))) - return; - } - - /* Can we wield */ - if (process_hooks(HOOK_WIELD, "(d)", item)) return; - - /* Extract the flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Two handed weapons can't be wielded with a shield */ - if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) && - (f4 & TR4_MUST2H) && - (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0)) - { - object_desc(o_name, o_ptr, FALSE, 0); - msg_format("You cannot wield your %s with a shield.", o_name); - return; - } - - if (is_slot_ok(slot - INVEN_ARM + INVEN_WIELD)) - { - i_ptr = &p_ptr->inventory[slot - INVEN_ARM + INVEN_WIELD]; - - /* Extract the flags */ - object_flags(i_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Prevent shield from being put on if wielding 2H */ - if ((f4 & TR4_MUST2H) && (i_ptr->k_idx) && - (p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM)) - { - object_desc(o_name, o_ptr, FALSE, 0); - msg_format("You cannot wield your %s with a two-handed weapon.", o_name); - return; - } - - if ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM) && - (f4 & TR4_COULD2H)) - { - if (!get_check("Are you sure you want to restrict your fighting? ")) - { - return; - } - } - } - - - /* Extract the flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) && - (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0) && - (f4 & TR4_COULD2H)) - { - if (!get_check("Are you sure you want to use this weapon with a shield?")) - { - return; - } - } - - /* Can we take off existing item */ - if (slot != INVEN_AMMO) - { - if (p_ptr->inventory[slot].k_idx) - if (process_hooks(HOOK_TAKEOFF, "(d)", slot)) return; - } - else - { - if (p_ptr->inventory[slot].k_idx) - if (!object_similar(&p_ptr->inventory[slot], o_ptr)) - if (process_hooks(HOOK_TAKEOFF, "(d)", slot)) return; - } - - /* Take a turn */ - energy_use = 100; - - /* Get local object */ - q_ptr = &forge; - - /* Obtain local object */ - object_copy(q_ptr, o_ptr); - - if (slot == INVEN_AMMO) num = o_ptr->number; - - /* Modify quantity */ - q_ptr->number = num; - - /* Decrease the item */ - inc_stack_size_ex(item, -num, OPTIMIZE, NO_DESCRIBE); - - /* Access the wield slot */ - o_ptr = &p_ptr->inventory[slot]; - - /* Take off existing item */ - if (slot != INVEN_AMMO) - { - if (o_ptr->k_idx) - { - /* Take off existing item */ - (void)inven_takeoff(slot, 255, FALSE); - } - } - else - { - if (o_ptr->k_idx) - { - if (!object_similar(o_ptr, q_ptr)) - { - /* Take off existing item */ - (void)inven_takeoff(slot, 255, FALSE); - } - else - { - q_ptr->number += o_ptr->number; - } - } - } - - - /* Wear the new stuff */ - object_copy(o_ptr, q_ptr); - - /* Increment the equip counter by hand */ - equip_cnt++; - - /* Where is the item now */ - if (slot == INVEN_WIELD) - { - act = "You are wielding"; - } - else if (( slot == INVEN_BOW ) && (o_ptr->tval == TV_INSTRUMENT)) - { - act = "You are holding"; - } - else if (slot == INVEN_BOW) - { - act = "You are shooting with"; - } - else if (slot == INVEN_LITE) - { - act = "Your light source is"; - } - else if (slot == INVEN_AMMO) - { - act = "In your quiver you have"; - } - else if (slot == INVEN_TOOL) - { - act = "You are using"; - } - else - { - act = "You are wearing"; - } - - /* Describe the result */ - object_desc(o_name, o_ptr, TRUE, 3); - - /* Message */ - msg_format("%s %s (%c).", act, o_name, index_to_label(slot)); - - /* 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; - } - - /* Take care of item sets */ - if (o_ptr->name1) - { - wield_set(o_ptr->name1, a_info[o_ptr->name1].set, FALSE); - } - - /* 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 | PU_SPELLS); - - /* Redraw monster hitpoint */ - p_ptr->redraw |= (PR_MH); - - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); -} - - - -/* - * Take off an item - */ -void do_cmd_takeoff(void) -{ - int item; - - object_type *o_ptr; - - cptr q, s; - - - /* Get an item */ - q = "Take off which item? "; - s = "You are not wearing anything to take off."; - if (!get_item(&item, q, s, (USE_EQUIP))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Can we take it off */ - if (process_hooks(HOOK_TAKEOFF, "(d)", item)) return; - - /* Item is cursed */ - if (cursed_p(o_ptr) && (!wizard)) - { - /* Oops */ - msg_print("Hmmm, it seems to be cursed."); - - /* Nope */ - return; - } - - - /* Take a partial turn */ - energy_use = 50; - - /* Take off the item */ - (void)inven_takeoff(item, 255, FALSE); - - /* Recalculate hitpoint */ - p_ptr->update |= (PU_HP); - - p_ptr->redraw |= (PR_MH); -} - - -/* - * Drop an item - */ -void do_cmd_drop(void) -{ - int item, amt = 1; - - object_type *o_ptr; - - u32b f1, f2, f3, f4, f5, esp; - - cptr q, s; - - - /* Get an item */ - q = "Drop which item? "; - s = "You have nothing to drop."; - if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return; - - /* Get the item */ - o_ptr = get_object(item); - - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Can we drop */ - if (process_hooks(HOOK_DROP, "(d)", item)) return; - - /* Hack -- Cannot remove cursed items */ - if (cursed_p(o_ptr)) - { - if (item >= INVEN_WIELD) - { - /* Oops */ - msg_print("Hmmm, it seems to be cursed."); - - /* Nope */ - return; - } - else - { - if (f4 & TR4_CURSE_NO_DROP) - { - /* Oops */ - msg_print("Hmmm, you seem to be unable to drop it."); - - /* Nope */ - return; - } - } - } - - - /* See how many items */ - if (o_ptr->number > 1) - { - /* Get a quantity */ - amt = get_quantity(NULL, o_ptr->number); - - /* Allow user abort */ - if (amt <= 0) return; - } - - /* Take a partial turn */ - energy_use = 50; - - /* Drop (some of) the item */ - inven_drop(item, amt, p_ptr->py, p_ptr->px, FALSE); -} - - -/* - * Destroy an item - */ -void do_cmd_destroy(void) -{ - int item, amt = 1; - - int old_number; - - bool_ force = FALSE; - - object_type *o_ptr; - - char o_name[80]; - - char out_val[160]; - - cptr q, s; - - u32b f1, f2, f3, f4, f5, esp; - - - /* Hack -- force destruction */ - if (command_arg > 0) force = TRUE; - - - /* Get an item */ - q = "Destroy which item? "; - s = "You have nothing to destroy."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_AUTO))) return; - - /* Get the item */ - o_ptr = get_object(item); - - - /* See how many items */ - if (o_ptr->number > 1) - { - /* Get a quantity */ - amt = get_quantity(NULL, o_ptr->number); - - /* Allow user abort */ - if (amt <= 0) return; - } - - - /* Describe the object */ - old_number = o_ptr->number; - o_ptr->number = amt; - object_desc(o_name, o_ptr, TRUE, 3); - o_ptr->number = old_number; - - /* Verify unless quantity given */ - if (!force) - { - if (!((auto_destroy) && (object_value(o_ptr) < 1))) - { - /* Make a verification */ - strnfmt(out_val, 160, "Really destroy %s? ", o_name); - if (!get_check(out_val)) return; - } - } - - /* Take no time, just like the automatizer */ - energy_use = 0; - - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if ((f4 & TR4_CURSE_NO_DROP) && cursed_p(o_ptr)) - { - /* Oops */ - msg_print("Hmmm, you seem to be unable to destroy it."); - - /* Nope */ - return; - } - - - /* Artifacts cannot be destroyed */ - if (artifact_p(o_ptr) || o_ptr->art_name) - { - byte feel = SENSE_SPECIAL; - - energy_use = 0; - - /* Message */ - msg_format("You cannot destroy %s.", o_name); - - /* Hack -- Handle icky artifacts */ - if (cursed_p(o_ptr)) feel = SENSE_TERRIBLE; - - /* Hack -- inscribe the artifact */ - o_ptr->sense = feel; - - /* We have "felt" it (again) */ - o_ptr->ident |= (IDENT_SENSE); - - /* Combine the pack */ - p_ptr->notice |= (PN_COMBINE); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - /* Done */ - return; - } - - /* Message */ - msg_format("You destroy %s.", o_name); - sound(SOUND_DESTITEM); - - /* Create an automatizer rule */ - if (automatizer_create) - { - automatizer_add_rule(o_ptr, TRUE); - } - - /* - * Hack -- If rods or wand are destroyed, the total maximum timeout or - * charges of the stack needs to be reduced, unless all the items are - * being destroyed. -LM- - */ - if ((o_ptr->tval == TV_WAND) && (amt < o_ptr->number)) - { - o_ptr->pval -= o_ptr->pval * amt / o_ptr->number; - } - - /* Eru wont be happy */ - if (f3 & TR3_BLESSED) - inc_piety(GOD_ERU, -10 * k_info[o_ptr->k_idx].level); - - /* Eliminate the item */ - inc_stack_size(item, -amt); -} - - -/* - * Observe an item which has been *identify*-ed - */ -void do_cmd_observe(void) -{ - int item; - - object_type *o_ptr; - - char o_name[80]; - - cptr q, s; - - - /* Get an item */ - q = "Examine which item? "; - s = "You have nothing to examine."; - if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Description */ - object_desc(o_name, o_ptr, TRUE, 3); - - /* Describe */ - cmsg_format(TERM_L_BLUE, "%s", o_name); - - /* Describe it fully */ - if (!object_out_desc(o_ptr, NULL, FALSE, TRUE)) msg_print("You see nothing special."); -} - - - -/* - * Remove the inscription from an object - * XXX Mention item (when done)? - */ -void do_cmd_uninscribe(void) -{ - int item; - - object_type *o_ptr; - - cptr q, s; - - - /* Get an item */ - q = "Un-inscribe which item? "; - s = "You have nothing to un-inscribe."; - if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Nothing to remove */ - if (!o_ptr->note) - { - msg_print("That item had no inscription to remove."); - return; - } - - /* Message */ - msg_print("Inscription removed."); - - /* Remove the incription */ - o_ptr->note = 0; - - /* Combine the pack */ - p_ptr->notice |= (PN_COMBINE); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); -} - - -/* - * Inscribe an object with a comment - */ -void do_cmd_inscribe(void) -{ - int item; - - object_type *o_ptr; - - char o_name[80]; - - char out_val[80]; - - cptr q, s; - - - /* Get an item */ - q = "Inscribe which item? "; - s = "You have nothing to inscribe."; - if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Describe the activity */ - object_desc(o_name, o_ptr, TRUE, 3); - - /* Message */ - msg_format("Inscribing %s.", o_name); - msg_print(NULL); - - /* Start with nothing */ - strcpy(out_val, ""); - - /* Use old inscription */ - if (o_ptr->note) - { - /* Start with the old inscription */ - strcpy(out_val, quark_str(o_ptr->note)); - } - - /* Get a new inscription (possibly empty) */ - if (get_string("Inscription: ", out_val, 80)) - { - /* Save the inscription */ - o_ptr->note = quark_add(out_val); - - /* Combine the pack */ - p_ptr->notice |= (PN_COMBINE); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - } -} - - - -/* - * An "item_tester_hook" for refilling lanterns - */ -static bool_ item_tester_refill_lantern(object_type *o_ptr) -{ - /* Flasks of oil are okay */ - if (o_ptr->tval == TV_FLASK) return (TRUE); - - /* Lanterns are okay */ - if ((o_ptr->tval == TV_LITE) && - (o_ptr->sval == SV_LITE_LANTERN)) return (TRUE); - - /* Assume not okay */ - return (FALSE); -} - - -/* - * Refill the players lamp (from the pack or floor) - */ -static void do_cmd_refill_lamp(void) -{ - int item; - - object_type *o_ptr; - object_type *j_ptr; - - cptr q, s; - - - /* Restrict the choices */ - item_tester_hook = item_tester_refill_lantern; - - /* Get an item */ - q = "Refill with which flask? "; - s = "You have no flasks of oil."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Take a partial turn */ - energy_use = 50; - - /* Access the lantern */ - j_ptr = &p_ptr->inventory[INVEN_LITE]; - - /* Refuel */ - if (o_ptr->tval == TV_FLASK) - j_ptr->timeout += o_ptr->pval; - else - j_ptr->timeout += o_ptr->timeout; - - /* Message */ - msg_print("You fuel your lamp."); - - /* Comment */ - if (j_ptr->timeout >= FUEL_LAMP) - { - j_ptr->timeout = FUEL_LAMP; - msg_print("Your lamp is full."); - } - - /* Decrease the item stack */ - inc_stack_size(item, -1); - - /* Recalculate torch */ - p_ptr->update |= (PU_TORCH); -} - - -/* - * An "item_tester_hook" for refilling torches - */ -static bool_ item_tester_refill_torch(object_type *o_ptr) -{ - /* Torches are okay */ - if ((o_ptr->tval == TV_LITE) && - (o_ptr->sval == SV_LITE_TORCH)) return (TRUE); - - /* Assume not okay */ - return (FALSE); -} - - -/* - * Refuel the players torch (from the pack or floor) - */ -static void do_cmd_refill_torch(void) -{ - int item; - - object_type *o_ptr; - - object_type *j_ptr; - - cptr q, s; - - - /* Restrict the choices */ - item_tester_hook = item_tester_refill_torch; - - /* Get an item */ - q = "Refuel with which torch? "; - s = "You have no extra torches."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Take a partial turn */ - energy_use = 50; - - /* Access the primary torch */ - j_ptr = &p_ptr->inventory[INVEN_LITE]; - - /* Refuel */ - j_ptr->timeout += o_ptr->timeout + 5; - - /* Message */ - msg_print("You combine the torches."); - - /* Over-fuel message */ - if (j_ptr->timeout >= FUEL_TORCH) - { - j_ptr->timeout = FUEL_TORCH; - msg_print("Your torch is fully fueled."); - } - - /* Refuel message */ - else - { - msg_print("Your torch glows more brightly."); - } - - /* Decrease the item stack */ - inc_stack_size(item, -1); - - /* Recalculate torch */ - p_ptr->update |= (PU_TORCH); -} - - -/* - * Refill the players lamp, or restock his torches - */ -void do_cmd_refill(void) -{ - object_type *o_ptr; - - u32b f1, f2, f3, f4, f5, esp; - - - /* Get the light */ - o_ptr = &p_ptr->inventory[INVEN_LITE]; - - /* It is nothing */ - if (o_ptr->tval != TV_LITE) - { - msg_print("You are not wielding a light."); - return; - } - - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if (f4 & TR4_FUEL_LITE) - { - /* It's a torch */ - if (o_ptr->sval == SV_LITE_TORCH || - o_ptr->sval == SV_LITE_TORCH_EVER) - { - do_cmd_refill_torch(); - } - - /* It's a lamp */ - else if (o_ptr->sval == SV_LITE_LANTERN || - o_ptr->sval == SV_LITE_DWARVEN || - o_ptr->sval == SV_LITE_FEANORIAN) - { - do_cmd_refill_lamp(); - } - } - - /* No torch to refill */ - else - { - msg_print("Your light cannot be refilled."); - } -} - - -/* - * Target command - */ -void do_cmd_target(void) -{ - /* Target set */ - if (target_set(TARGET_KILL)) - { - msg_print("Target Selected."); - } - - /* Target aborted */ - else - { - msg_print("Target Aborted."); - } -} - - - -/* - * Look command - */ -void do_cmd_look(void) -{ - /* Look around */ - if (target_set(TARGET_LOOK)) - { - msg_print("Target Selected."); - } -} - - - -/* - * Allow the player to examine other sectors on the map - */ -void do_cmd_locate(void) -{ - int dir, y1, x1, y2, x2; - int panel_hgt, panel_wid; - char tmp_val[80]; - char out_val[160]; - - - /* Retrieve size of the Angband window */ - Term_get_size(&panel_wid, &panel_hgt); - - /* Calcurate size of the dungeon map area */ - panel_hgt = (panel_hgt - (ROW_MAP + 1)) / 2; - panel_wid = (panel_wid - (COL_MAP + 1)) / 2; - - /* Start at current panel */ - y2 = y1 = panel_row_min; - x2 = x1 = panel_col_min; - - /* Show panels until done */ - while (1) - { - /* Describe the location */ - if ((y2 == y1) && (x2 == x1)) - { - tmp_val[0] = '\0'; - } - else - { - strnfmt(tmp_val, 80, "%s%s of", - ((y2 < y1) ? " North" : (y2 > y1) ? " South" : ""), - ((x2 < x1) ? " West" : (x2 > x1) ? " East" : "")); - } - - /* Prepare to ask which way to look */ - if ((panel_hgt == PANEL_HGT) && (panel_wid == PANEL_WID)) - { - /* Avoid surprising the standard screen users */ - strnfmt(out_val, 160, - "Map sector [%d,%d], which is%s your sector. Direction?", - y2 / panel_hgt, x2 / panel_wid, tmp_val); - } - - /* Big screen */ - else - { - /* Panels are measured by current map area size */ - strnfmt(out_val, 160, - "Map sector [%d(%02d),%d(%02d)], which is%s your sector. Direction?", - y2 / panel_hgt, y2 % panel_hgt, - x2 / panel_wid, x2 % panel_wid, tmp_val); - } - - /* Assume no direction */ - dir = 0; - - /* Get a direction */ - while (!dir) - { - char ch; - - /* Get a command (or cancel) */ - if (!get_com(out_val, &ch)) break; - - /* Extract the action (if any) */ - dir = get_keymap_dir(ch); - - /* Error */ - if (!dir) bell(); - } - - /* No direction */ - if (!dir) break; - - /* Apply the motion */ - if (change_panel(ddy[dir], ddx[dir])) - { - y2 = panel_row_min; - x2 = panel_col_min; - } - } - - /* Recenter the map around the player */ - verify_panel(); - - /* Update stuff */ - p_ptr->update |= (PU_MONSTERS); - - /* Redraw map */ - p_ptr->redraw |= (PR_MAP); - - /* Window stuff */ - p_ptr->window |= (PW_OVERHEAD); - - /* Handle stuff */ - handle_stuff(); -} - - - - - - -/* - * The table of "symbol info" -- each entry is a string of the form - * "X:desc" where "X" is the trigger, and "desc" is the "info". - */ -static cptr ident_info[] = -{ - " :A dark grid", - "!:A potion (or oil)", - "\":An amulet (or necklace)", - "#:A wall (or secret door)", - "$:Treasure (gold or gems)", - "%:A vein (magma or quartz)", - /* "&:unused", */ - "':An open door", - "(:Soft armor", - "):A shield", - "*:A vein with treasure", - "+:A closed door", - ",:Food (or mushroom patch)", - "-:A wand (or rod)", - ".:Floor", - "/:A polearm (Axe/Pike/etc)", - "0:An altar", - "1:Entrance to General Store", - "2:Entrance to Armory", - "3:Entrance to Weaponsmith", - "4:Entrance to Temple", - "5:Entrance to Alchemy shop", - "6:Entrance to Magic store", - "7:Entrance to Black Market", - "8:Entrance to your home", - "9:Entrance to Bookstore", - "::Rubble", - ";:A glyph of warding / explosive rune", - "<:An up staircase", - "=:A ring", - ">:A down staircase", - "?:A scroll", - "@:You", - "A:Angel", - "B:Bird", - "C:Canine", - "D:Ancient Dragon/Wyrm", - "E:Elemental", - "F:Dragon Fly", - "G:Ghost", - "H:Hybrid", - "I:Insect", - "J:Snake", - "K:Killer Beetle", - "L:Lich", - "M:Multi-Headed Reptile", - /* "N:unused", */ - "O:Ogre", - "P:Giant Humanoid", - "Q:Quylthulg (Pulsing Flesh Mound)", - "R:Reptile/Amphibian", - "S:Spider/Scorpion/Tick", - "T:Troll", - "U:Major Demon", - "V:Vampire", - "W:Wight/Wraith/etc", - "X:Xorn/Xaren/etc", - "Y:Yeti", - "Z:Zephyr Hound", - "[:Hard armor", - "\\:A hafted weapon (mace/whip/etc)", - "]:Misc. armor", - "^:A trap", - "_:A staff", - /* "`:unused", */ - "a:Ant", - "b:Bat", - "c:Centipede", - "d:Dragon", - "e:Floating Eye", - "f:Feline", - "g:Golem", - "h:Hobbit/Elf/Dwarf", - "i:Icky Thing", - "j:Jelly", - "k:Kobold", - "l:Louse", - "m:Mold", - "n:Naga", - "o:Orc", - "p:Person/Human", - "q:Quadruped", - "r:Rodent", - "s:Skeleton", - "t:Townsperson", - "u:Minor Demon", - "v:Vortex", - "w:Worm/Worm-Mass", - /* "x:unused", */ - "y:Yeek", - "z:Zombie/Mummy", - "{:A missile (arrow/bolt/shot)", - "|:An edged weapon (sword/dagger/etc)", - "}:A launcher (bow/crossbow/sling)", - "~:A tool (or miscellaneous item)", - NULL -}; - - - -/* - * Sorting hook -- Comp function -- see below - * - * We use "u" to point to array of monster indexes, - * and "v" to select the type of sorting to perform on "u". - */ -static bool_ ang_sort_comp_hook(vptr u, vptr v, int a, int b) -{ - u16b *who = (u16b*)(u); - - u16b *why = (u16b*)(v); - - int w1 = who[a]; - - int w2 = who[b]; - - int z1, z2; - - - /* Sort by player kills */ - if (*why >= 4) - { - /* Extract player kills */ - z1 = r_info[w1].r_pkills; - z2 = r_info[w2].r_pkills; - - /* Compare player kills */ - if (z1 < z2) return (TRUE); - if (z1 > z2) return (FALSE); - } - - - /* Sort by total kills */ - if (*why >= 3) - { - /* Extract total kills */ - z1 = r_info[w1].r_tkills; - z2 = r_info[w2].r_tkills; - - /* Compare total kills */ - if (z1 < z2) return (TRUE); - if (z1 > z2) return (FALSE); - } - - - /* Sort by monster level */ - if (*why >= 2) - { - /* Extract levels */ - z1 = r_info[w1].level; - z2 = r_info[w2].level; - - /* Compare levels */ - if (z1 < z2) return (TRUE); - if (z1 > z2) return (FALSE); - } - - - /* Sort by monster experience */ - if (*why >= 1) - { - /* Extract experience */ - z1 = r_info[w1].mexp; - z2 = r_info[w2].mexp; - - /* Compare experience */ - if (z1 < z2) return (TRUE); - if (z1 > z2) return (FALSE); - } - - - /* Compare indexes */ - return (w1 <= w2); -} - - -/* - * Sorting hook -- Swap function -- see below - * - * We use "u" to point to array of monster indexes, - * and "v" to select the type of sorting to perform. - */ -static void ang_sort_swap_hook(vptr u, vptr v, int a, int b) -{ - u16b *who = (u16b*)(u); - - u16b holder; - - - /* XXX XXX */ - v = v ? v : 0; - - /* Swap */ - holder = who[a]; - who[a] = who[b]; - who[b] = holder; -} - - - -/* - * Hack -- Display the "name" and "attr/chars" of a monster race - */ -static void roff_top(int r_idx) -{ - monster_race *r_ptr = &r_info[r_idx]; - - byte a1, a2; - - char c1, c2; - - - /* Access the chars */ - c1 = r_ptr->d_char; - c2 = r_ptr->x_char; - - /* Access the attrs */ - a1 = r_ptr->d_attr; - a2 = r_ptr->x_attr; - - - /* Clear the top line */ - Term_erase(0, 0, 255); - - /* Reset the cursor */ - Term_gotoxy(0, 0); - - /* A title (use "The" for non-uniques) */ - if (!(r_ptr->flags1 & (RF1_UNIQUE))) - { - Term_addstr( -1, TERM_WHITE, "The "); - } - - /* Dump the name */ - Term_addstr( -1, TERM_WHITE, (r_name + r_ptr->name)); - - /* Append the "standard" attr/char info */ - Term_addstr( -1, TERM_WHITE, " ('"); - Term_addch(a1, c1); - if (use_bigtile && (a1 & 0x80)) Term_addch(255, 255); - Term_addstr( -1, TERM_WHITE, "')"); - - /* Append the "optional" attr/char info */ - Term_addstr( -1, TERM_WHITE, "/('"); - Term_addch(a2, c2); - if (use_bigtile && (a2 & 0x80)) Term_addch(255, 255); - Term_addstr( -1, TERM_WHITE, "'):"); -} - - -/* - * Identify a character, allow recall of monsters - * - * Several "special" responses recall "multiple" monsters: - * ^A (all monsters) - * ^U (all unique monsters) - * ^N (all non-unique monsters) - * ^M (case insensitive name search) - * - * The responses may be sorted in several ways, see below. - * - * Note that the player ghosts are ignored. XXX XXX XXX - */ -void do_cmd_query_symbol(void) -{ - int i, n, r_idx; - - char sym, query; - - char buf[128]; - - - bool_ all = FALSE; - - bool_ uniq = FALSE; - - bool_ norm = FALSE; - - - bool_ name = FALSE; - - char temp[80] = ""; - - - bool_ recall = FALSE; - - - u16b why = 0; - - u16b *who; - - - /* Get a character, or abort */ - if (!get_com("Enter character to be identified, " - "or (Ctrl-A, Ctrl-U, Ctrl-N, Ctrl-M):", &sym)) return; - - /* Find that character info, and describe it */ - for (i = 0; ident_info[i]; ++i) - { - if (sym == ident_info[i][0]) break; - } - - /* Describe */ - if (sym == KTRL('A')) - { - all = TRUE; - strcpy(buf, "Full monster list."); - } - else if (sym == KTRL('U')) - { - all = uniq = TRUE; - strcpy(buf, "Unique monster list."); - } - else if (sym == KTRL('N')) - { - all = norm = TRUE; - strcpy(buf, "Non-unique monster list."); - } - else if (sym == KTRL('M')) - { - all = name = TRUE; - if (!get_string("Name:", temp, 70)) return; - strnfmt(buf, 128, "Monsters with a name \"%s\"", temp); - strlower(temp); - } - else if (ident_info[i]) - { - strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2); - } - else - { - strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol"); - } - - /* Display the result */ - prt(buf, 0, 0); - - /* Allocate the "who" array */ - C_MAKE(who, max_r_idx, u16b); - - /* Collect matching monsters */ - for (n = 0, i = 1; i < max_r_idx; i++) - { - monster_race *r_ptr = &r_info[i]; - - /* Nothing to recall */ - if (!cheat_know && !r_ptr->r_sights) continue; - - /* Require non-unique monsters if needed */ - if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue; - - /* Require unique monsters if needed */ - if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue; - - /* Require monsters with the name requested if needed */ - if (name) - { - char mon_name[80]; - - strcpy(mon_name, r_name + r_ptr->name); - strlower(mon_name); - - if (!strstr(mon_name, temp)) continue; - } - - /* Collect "appropriate" monsters */ - if (all || (r_ptr->d_char == sym)) who[n++] = i; - } - - /* Nothing to recall */ - if (!n) - { - /* Free the "who" array */ - C_KILL(who, max_r_idx, u16b); - - return; - } - - - /* Prompt XXX XXX XXX */ - put_str("Recall details? (k/p/y/n): ", 0, 40); - - /* Query */ - query = inkey(); - - /* Restore */ - prt(buf, 0, 0); - - - /* Sort by kills (and level) */ - if (query == 'k') - { - why = 4; - query = 'y'; - } - - /* Sort by level */ - if (query == 'p') - { - why = 2; - query = 'y'; - } - - /* Catch "escape" */ - if (query != 'y') - { - /* Free the "who" array */ - C_KILL(who, max_r_idx, u16b); - - return; - } - - - /* Sort if needed */ - if (why) - { - /* Select the sort method */ - ang_sort_comp = ang_sort_comp_hook; - ang_sort_swap = ang_sort_swap_hook; - - /* Sort the array */ - ang_sort(who, &why, n); - } - - - /* Start at the end */ - i = n - 1; - - /* Scan the monster memory */ - while (1) - { - /* Extract a race */ - r_idx = who[i]; - - /* Hack -- Auto-recall */ - monster_race_track(r_idx, 0); - - /* Hack -- Handle stuff */ - handle_stuff(); - - /* Hack -- Begin the prompt */ - roff_top(r_idx); - - /* Hack -- Complete the prompt */ - Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]"); - - /* Interact */ - while (1) - { - /* Recall */ - if (recall) - { - /* Save the screen */ - character_icky = TRUE; - Term_save(); - - /* Recall on screen */ - screen_roff(who[i], 0, 0); - - /* Hack -- Complete the prompt (again) */ - Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]"); - } - - /* Command */ - query = inkey(); - - /* Unrecall */ - if (recall) - { - /* Restore */ - Term_load(); - character_icky = FALSE; - } - - /* Normal commands */ - if (query != 'r') break; - - /* Toggle recall */ - recall = !recall; - } - - /* Stop scanning */ - if (query == ESCAPE) break; - - /* Move to "prev" monster */ - if (query == '-') - { - if (++i == n) - { - i = 0; - if (!expand_list) break; - } - } - - /* Move to "next" monster */ - else - { - if (i-- == 0) - { - i = n - 1; - if (!expand_list) break; - } - } - } - - /* Re-display the identity */ - prt(buf, 0, 0); - - /* Free the "who" array */ - C_KILL(who, max_r_idx, u16b); -} - - -/* - * research_mon - * -KMW- - */ -bool_ research_mon() -{ - int i, n, r_idx; - - char sym, query; - - char buf[128]; - - - s16b oldkills; - - byte oldwake; - - bool_ oldcheat; - - - bool_ all = FALSE; - - bool_ uniq = FALSE; - - bool_ norm = FALSE; - - bool_ notpicked; - - - bool_ recall = FALSE; - - u16b why = 0; - - monster_race *r2_ptr; - - u16b *who; - - - /* Hack -- Remember "cheat_know" flag */ - oldcheat = cheat_know; - - - /* Get a character, or abort */ - if (!get_com("Enter character of monster: ", &sym)) return (TRUE); - - /* Allocate the "who" array */ - C_MAKE(who, max_r_idx, u16b); - - /* Find that character info, and describe it */ - for (i = 0; ident_info[i]; ++i) - { - if (sym == ident_info[i][0]) break; - } - - if (ident_info[i]) - { - strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2); - } - else - { - strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol"); - } - - /* Display the result */ - prt(buf, 16, 10); - - - /* Collect matching monsters */ - for (n = 0, i = 1; i < max_r_idx; i++) - { - monster_race *r_ptr = &r_info[i]; - - /* Hack -- Force "cheat_know" */ - cheat_know = TRUE; - - /* Nothing to recall */ - if (!cheat_know && !r_ptr->r_sights) continue; - - /* Require non-unique monsters if needed */ - if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue; - - /* Require unique monsters if needed */ - if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue; - - /* Collect "appropriate" monsters */ - if (all || (r_ptr->d_char == sym)) who[n++] = i; - } - - /* Nothing to recall */ - if (!n) - { - /* Free the "who" array */ - C_KILL(who, max_r_idx, u16b); - - /* Restore the "cheat_know" flag */ - cheat_know = oldcheat; - - return (TRUE); - } - - - /* Sort by level */ - why = 2; - query = 'y'; - - /* Sort if needed */ - if (why) - { - /* Select the sort method */ - ang_sort_comp = ang_sort_comp_hook; - ang_sort_swap = ang_sort_swap_hook; - - /* Sort the array */ - ang_sort(who, &why, n); - } - - - /* Start at the end */ - i = n - 1; - - notpicked = TRUE; - - /* Scan the monster memory */ - while (notpicked) - { - /* Extract a race */ - r_idx = who[i]; - - /* Hack -- Auto-recall */ - monster_race_track(r_idx, 0); - - /* Hack -- Handle stuff */ - handle_stuff(); - - /* Hack -- Begin the prompt */ - roff_top(r_idx); - - /* Hack -- Complete the prompt */ - Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC, space to continue]"); - - /* Interact */ - while (1) - { - /* Recall */ - if (recall) - { - /* Save the screen */ - character_icky = TRUE; - Term_save(); - - /* Recall on screen */ - r2_ptr = &r_info[r_idx]; - - oldkills = r2_ptr->r_tkills; - oldwake = r2_ptr->r_wake; - screen_roff(who[i], 0, 1); - r2_ptr->r_tkills = oldkills; - r2_ptr->r_wake = oldwake; - r2_ptr->r_sights = 1; - cheat_know = oldcheat; - notpicked = FALSE; - break; - - } - - /* Command */ - query = inkey(); - - /* Unrecall */ - if (recall) - { - /* Restore */ - Term_load(); - character_icky = FALSE; - } - - /* Normal commands */ - if (query != 'r') break; - - /* Toggle recall */ - recall = !recall; - } - - /* Stop scanning */ - if (query == ESCAPE) break; - - /* Move to "prev" monster */ - if (query == '-') - { - if (++i == n) - { - i = 0; - if (!expand_list) break; - } - } - - /* Move to "next" monster */ - else - { - if (i-- == 0) - { - i = n - 1; - if (!expand_list) break; - } - } - } - - - /* Re-display the identity */ - /* prt(buf, 5, 5);*/ - - /* Free the "who" array */ - C_KILL(who, max_r_idx, u16b); - - /* Restore the "cheat_know" flag */ - cheat_know = oldcheat; - - return (notpicked); -} - - -/* - * Try to "sense" the grid's mana - */ -bool_ do_cmd_sense_grid_mana() -{ - int chance, i; - - - /* Take (a lot of) time */ - energy_use = 200; - - /* Base chance of success */ - chance = p_ptr->skill_dev; - - /* Confusion hurts skill */ - if (p_ptr->confused) chance = chance / 2; - - /* Hight mana grids are harder */ - chance = chance - (cave[p_ptr->py][p_ptr->px].mana / 10); - - /* Give everyone a (slight) chance */ - if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0)) - { - chance = USE_DEVICE; - } - - /* Roll for usage */ - if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE)) - { - if (flush_failure) flush(); - msg_print("You failed to sense the grid's mana."); - sound(SOUND_FAIL); - return FALSE; - } - - /* Try to give an "average" value */ - i = (101 - p_ptr->skill_dev) / 2; - i = (i < 1) ? 1 : (i > 50) ? 50 : i; - - if (wizard) - { - msg_format("Grid's mana: %d.", cave[p_ptr->py][p_ptr->px].mana); - msg_format("Average grid's mana: %d.", (cave[p_ptr->py][p_ptr->px].mana / i) * i); - } - else - { - msg_format("Average Area's mana: %d", (cave[p_ptr->py][p_ptr->px].mana / i) * i); - } - return TRUE; -} - - -/* - * Calculate the weight of the portable holes - */ -s32b portable_hole_weight(void) -{ - s32b weight, i; - - store_type *st_ptr = &town_info[TOWN_RANDOM].store[STORE_HOME]; - - - /* Sum the objects in the appropriate home */ - for (i = 0, weight = 0; i < st_ptr->stock_num; i++) - { - object_type *o_ptr = &st_ptr->stock[i]; - - weight += (o_ptr->weight * o_ptr->number); - } - - /* Multiply the sum with 1.5 */ - weight = (weight * 3) / 2 + 2; - - return (weight); -} - - -/* - * Calculate and set the weight of the portable holes - */ -void set_portable_hole_weight(void) -{ - s32b weight, i, j; - - /* Calculate the weight of items in home */ - weight = portable_hole_weight(); - - /* Set the weight of portable holes in the shops, ... */ - for (i = 1; i < max_towns; i++) - { - for (j = 0; j < max_st_idx; j++) - { - store_type *st_ptr = &town_info[i].store[j]; - int k; - - for (k = 0; k < st_ptr->stock_num; k++) - { - object_type *o_ptr = &st_ptr->stock[k]; - - if ((o_ptr->tval == TV_TOOL) && - (o_ptr->sval == SV_PORTABLE_HOLE)) - o_ptr->weight = weight; - } - } - } - - /* ... in the object list, ... */ - for (i = 1; i < o_max; i++) - { - object_type *o_ptr = &o_list[i]; - - if ((o_ptr->tval == TV_TOOL) && - (o_ptr->sval == SV_PORTABLE_HOLE)) o_ptr->weight = weight; - } - - /* ... and in the p_ptr->inventory to the appropriate value */ - for (i = 0; i < INVEN_TOTAL; i++) - { - object_type *o_ptr = &p_ptr->inventory[i]; - - /* Skip non-objects */ - if ((o_ptr->tval == TV_TOOL) && - (o_ptr->sval == SV_PORTABLE_HOLE)) o_ptr->weight = weight; - } -} - - -/* - * Use a portable hole - */ -void do_cmd_portable_hole(void) -{ - cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; - - int feat, special, town_num; - - /* Is it currently wielded? */ - if (!p_ptr->inventory[INVEN_TOOL].k_idx || - (p_ptr->inventory[INVEN_TOOL].tval != TV_TOOL) || - (p_ptr->inventory[INVEN_TOOL].sval != SV_PORTABLE_HOLE)) - { - /* No, it isn't */ - msg_print("You have to wield a portable hole to use your abilities"); - return; - } - - /* Mega-hack: Saving the old values, and then... */ - feat = c_ptr->feat; - special = c_ptr->special; - town_num = p_ptr->town_num; - - /* ... change the current grid to the home in town #1 */ - /* DG -- use the first random town, since random towns cannot have houses */ - /* - * pelpel -- This doesn't affect LoS, so we can manipulate - * terrain feature without calling cave_set_feat() - */ - c_ptr->feat = FEAT_SHOP; - c_ptr->special = STORE_HOME; - p_ptr->town_num = TOWN_RANDOM; - - /* Now use the portable hole */ - do_cmd_store(); - - /* Mega-hack part II: change the current grid to the original value */ - c_ptr->feat = feat; - c_ptr->special = special; - p_ptr->town_num = town_num; - - set_portable_hole_weight(); - - /* Recalculate bonuses */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - p_ptr->update |= (PU_BONUS); -} - - -/* - * Try to add a CLI action. - */ -void cli_add(cptr active, cptr trigger, cptr descr) -{ - s16b num; - cli_comm *cli_ptr, *old_ptr; - - /* Too many macros. */ - if (cli_total >= CLI_MAX) return; - - /* First try to read active as a number. */ - if (strtol(active, 0, 0)) - { - num = strtol(active, 0, 0); - } - /* Then try to read it as a character. */ - else if (strlen(active) == 1) - { - num = active[0]; - } - /* Give up if it doesn't work. */ - else - { - return; - } - - /* Dump the macro. */ - cli_ptr = cli_info + cli_total; - old_ptr = cli_info + cli_total - 1; - - /* - * Trim 's from the ends of a token. This turns '@' into @ and - * ''' into '. This may be the intent of the code in tokenize(), - * but I've left it for lack of comments to back me up. - */ - if (strchr(trigger, '\'')) - { - char temp[80], *t; - cptr s; - for (s = trigger, t = temp; ; s++, t++) - { - /* tokenize() causes each ' to be followed by another character, - * and then another '. Trim the 's here. */ - if (*s == '\'') - { - *t = *(++s); - s++; - } - else - { - *t = *s; - } - if (*t == '\0') break; - } - cli_ptr->comm = string_make(temp); - } - else - { - cli_ptr->comm = string_make(trigger); - } - - /* First try copying everything across. */ - cli_ptr->key = num; - cli_ptr->descrip = string_make(descr); - - /* Take description for the previous record if appropriate. */ - if ((cli_total > 0) && (old_ptr->key == cli_ptr->key) && (cli_ptr->descrip == 0)) - { - cli_ptr->descrip = old_ptr->descrip; - } - - /* Accept the macro. */ - if (cli_ptr->key && cli_ptr->comm && cli_ptr->descrip) cli_total++; -} - - - -/* - * Get a string using CLI completion. - */ -bool_ get_string_cli(cptr prompt, char *buf, int len) -{ - bool_ res; - - - /* Paranoia XXX XXX XXX */ - msg_print(NULL); - - /* Display prompt */ - prt(prompt, 0, 0); - - /* Ask the user for a string */ - askfor_aux_complete = TRUE; - res = askfor_aux(buf, len); - askfor_aux_complete = FALSE; - - /* Clear prompt */ - prt("", 0, 0); - - /* Result */ - return (res); -} - - -/* - * Do a command line command - * - * This is a wrapper around process command to provide a "reverse keymap" - * whereby a set of keypresses is mapped to one. - * - * This is useful because command_cmd is a s16b, and so allows each command a - * unique representation. - * - * See defines.h for a list of the codes used. - */ -void do_cmd_cli(void) -{ - char buff[80]; - - cli_comm *cli_ptr; - - /* Clear the input buffer */ - strcpy(buff, ""); - - /* Accept command */ - if (!get_string_cli("Command: ", buff, 30)) return; - - - /* Analyse the input */ - for (cli_ptr = cli_info; cli_ptr->comm; cli_ptr++) - { - if (!strcmp(buff, cli_ptr->comm)) - { - /* Process the command without keymaps or macros. */ - command_new = cli_ptr->key; - return; - } - } - - msg_format("No such command: %s", buff); -} - - -/* - * Display on-line help for the CLI commands - */ -void do_cmd_cli_help() -{ - int i, j; - - FILE *fff; - - char file_name[1024]; - - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - for (i = 0, j = -1; i < cli_total; i++) - { - if (j < i - 1) fprintf(fff, "/"); - fprintf(fff, "[[[[[G%s]", cli_info[i].comm); - if (cli_info[i].descrip != cli_info[i + 1].descrip) - { - fprintf(fff, " %s\n", cli_info[i].descrip); - j = i; - } - } - - /* Close the file */ - my_fclose(fff); - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - /* Display the file contents */ - show_file(file_name, "Command line help", 0, 0); - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; - - /* Remove the file */ - fd_kill(file_name); -} - - -/* - * Dump screen shot in HTML - */ -void do_cmd_html_dump() -{ - char tmp_val[81]; - bool_ html = TRUE; - term_win *save; - - /* Save the screen */ - save = Term_save_to(); - - if (wizard && get_check("WIZARD MODE: Do an help file dump?")) - html = FALSE; - - /* Ask for a file */ - if (html) - { - strcpy(tmp_val, "dummy.htm"); - if (!get_string("File(you can post it to http://angband.oook.cz/): ", tmp_val, 80)) - { - /* Now restore the screen to initial state */ - Term_load_from(save, TRUE); - Term_fresh(); - return; - } - } - else - { - strcpy(tmp_val, "dummy.txt"); - if (!get_string("File: ", tmp_val, 80)) - { - /* Now restore the screen to initial state */ - Term_load_from(save, TRUE); - Term_fresh(); - return; - } - } - - /* Now restore the screen to dump it */ - Term_load_from(save, TRUE); - - if (html) - html_screenshot(tmp_val); - else - help_file_screenshot(tmp_val); - - Term_erase(0, 0, 255); - msg_print("Dump saved."); - Term_fresh(); - fix_message(); -} diff --git a/src/cmd3.cc b/src/cmd3.cc new file mode 100644 index 00000000..6b618cb8 --- /dev/null +++ b/src/cmd3.cc @@ -0,0 +1,2267 @@ +/* File: cmd3.c */ + +/* Purpose: Inventory commands */ + +/* + * 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 "angband.h" + +#include "quark.h" +#include "hooks.h" + +#include +#include +#include + +/* + * Display p_ptr->inventory + */ +void do_cmd_inven(void) +{ + char out_val[160]; + + + /* Note that we are in "p_ptr->inventory" mode */ + command_wrk = FALSE; + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Hack -- show empty slots */ + item_tester_full = TRUE; + + /* Display the p_ptr->inventory */ + show_inven(); + + /* Hack -- hide empty slots */ + item_tester_full = FALSE; + + + { + s32b total_weight = calc_total_weight(); + + strnfmt(out_val, 160, + "Inventory: carrying %ld.%ld pounds (%ld%% of capacity). Command: ", + total_weight / 10, total_weight % 10, + (total_weight * 100) / ((weight_limit()) / 2)); + } + + /* Get a command */ + prt(out_val, 0, 0); + + /* Get a new command */ + command_new = inkey(); + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + + + /* Process "Escape" */ + if (command_new == ESCAPE) + { + /* Reset stuff */ + command_new = 0; + } + + /* Process normal keys */ + else + { + /* Mega-Hack -- Don't disable keymaps for this key */ + request_command_inven_mode = TRUE; + } +} + + +/* + * Display equipment + */ +void do_cmd_equip(void) +{ + char out_val[160]; + + + /* Note that we are in "equipment" mode */ + command_wrk = TRUE; + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Hack -- show empty slots */ + item_tester_full = TRUE; + + /* Display the equipment */ + show_equip(); + + /* Hack -- undo the hack above */ + item_tester_full = FALSE; + + /* Build a prompt */ + { + s32b total_weight = calc_total_weight(); + + /* Build a prompt */ + strnfmt(out_val, 160, + "Equipment: carrying %ld.%ld pounds (%ld%% of capacity). Command: ", + total_weight / 10, total_weight % 10, + (total_weight * 100) / ((weight_limit()) / 2)); + } + + /* Get a command */ + prt(out_val, 0, 0); + + /* Get a new command */ + command_new = inkey(); + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + + + /* Process "Escape" */ + if (command_new == ESCAPE) + { + /* Reset stuff */ + command_new = 0; + } + + /* Process normal keys */ + else + { + /* Mega-Hack -- Don't disable keymaps for this key */ + request_command_inven_mode = TRUE; + } +} + + +/* + * The "wearable" tester + */ +static bool_ item_tester_hook_wear(object_type *o_ptr) +{ + u32b f1, f2, f3, f4, f5, esp; + int slot = wield_slot(o_ptr); + + + /* Extract the flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Only one ultimate at a time */ + if (f4 & TR4_ULTIMATE) + { + int i; + + for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) + { + object_type *q_ptr = &p_ptr->inventory[i]; + + /* Extract the flags */ + object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if (!q_ptr->k_idx) continue; + + if (f4 & TR4_ULTIMATE) return (FALSE); + } + } + + if ((slot < INVEN_WIELD) || ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_WIELD) && (p_ptr->melee_style != SKILL_MASTERY))) + return (FALSE); + + /* Check for a usable slot */ + if (slot >= INVEN_WIELD) return (TRUE); + + /* Assume not wearable */ + return (FALSE); +} + + +bool_ is_slot_ok(int slot) +{ + if ((slot >= INVEN_WIELD) && (slot < INVEN_TOTAL)) + { + return (TRUE); + } + else + { + return (FALSE); + } +} + + +/* + * Wield or wear a single item from the pack or floor + */ +void do_cmd_wield(void) +{ + int item, slot, num = 1; + + object_type forge; + + object_type *q_ptr; + + object_type *o_ptr, *i_ptr; + + cptr act; + + char o_name[80]; + + cptr q, s; + + u32b f1, f2, f3, f4, f5, esp; + + + /* Restrict the choices */ + item_tester_hook = item_tester_hook_wear; + + /* Get an item */ + q = "Wear/Wield which item? "; + s = "You have nothing you can wear or wield."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Check the slot */ + slot = wield_slot(o_ptr); + + /* Prevent wielding into a cursed slot */ + if (cursed_p(&p_ptr->inventory[slot])) + { + /* Describe it */ + object_desc(o_name, &p_ptr->inventory[slot], FALSE, 0); + + /* Message */ + msg_format("The %s you are %s appears to be cursed.", + o_name, describe_use(slot)); + + /* Cancel the command */ + return; + } + + if ((cursed_p(o_ptr)) && (wear_confirm) + && (object_known_p(o_ptr) || (o_ptr->ident & (IDENT_SENSE)))) + { + char dummy[512]; + + /* Describe it */ + object_desc(o_name, o_ptr, FALSE, 0); + + strnfmt(dummy, 512, "Really use the %s {cursed}? ", o_name); + if (!(get_check(dummy))) + return; + } + + /* Can we wield */ + if (process_hooks(HOOK_WIELD, "(d)", item)) return; + + /* Extract the flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Two handed weapons can't be wielded with a shield */ + if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) && + (f4 & TR4_MUST2H) && + (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0)) + { + object_desc(o_name, o_ptr, FALSE, 0); + msg_format("You cannot wield your %s with a shield.", o_name); + return; + } + + if (is_slot_ok(slot - INVEN_ARM + INVEN_WIELD)) + { + i_ptr = &p_ptr->inventory[slot - INVEN_ARM + INVEN_WIELD]; + + /* Extract the flags */ + object_flags(i_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Prevent shield from being put on if wielding 2H */ + if ((f4 & TR4_MUST2H) && (i_ptr->k_idx) && + (p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM)) + { + object_desc(o_name, o_ptr, FALSE, 0); + msg_format("You cannot wield your %s with a two-handed weapon.", o_name); + return; + } + + if ((p_ptr->body_parts[slot - INVEN_WIELD] == INVEN_ARM) && + (f4 & TR4_COULD2H)) + { + if (!get_check("Are you sure you want to restrict your fighting? ")) + { + return; + } + } + } + + + /* Extract the flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if ((is_slot_ok(slot - INVEN_WIELD + INVEN_ARM)) && + (p_ptr->inventory[slot - INVEN_WIELD + INVEN_ARM].k_idx != 0) && + (f4 & TR4_COULD2H)) + { + if (!get_check("Are you sure you want to use this weapon with a shield?")) + { + return; + } + } + + /* Can we take off existing item */ + if (slot != INVEN_AMMO) + { + if (p_ptr->inventory[slot].k_idx) + if (process_hooks(HOOK_TAKEOFF, "(d)", slot)) return; + } + else + { + if (p_ptr->inventory[slot].k_idx) + if (!object_similar(&p_ptr->inventory[slot], o_ptr)) + if (process_hooks(HOOK_TAKEOFF, "(d)", slot)) return; + } + + /* Take a turn */ + energy_use = 100; + + /* Get local object */ + q_ptr = &forge; + + /* Obtain local object */ + object_copy(q_ptr, o_ptr); + + if (slot == INVEN_AMMO) num = o_ptr->number; + + /* Modify quantity */ + q_ptr->number = num; + + /* Decrease the item */ + inc_stack_size_ex(item, -num, OPTIMIZE, NO_DESCRIBE); + + /* Access the wield slot */ + o_ptr = &p_ptr->inventory[slot]; + + /* Take off existing item */ + if (slot != INVEN_AMMO) + { + if (o_ptr->k_idx) + { + /* Take off existing item */ + (void)inven_takeoff(slot, 255, FALSE); + } + } + else + { + if (o_ptr->k_idx) + { + if (!object_similar(o_ptr, q_ptr)) + { + /* Take off existing item */ + (void)inven_takeoff(slot, 255, FALSE); + } + else + { + q_ptr->number += o_ptr->number; + } + } + } + + + /* Wear the new stuff */ + object_copy(o_ptr, q_ptr); + + /* Increment the equip counter by hand */ + equip_cnt++; + + /* Where is the item now */ + if (slot == INVEN_WIELD) + { + act = "You are wielding"; + } + else if (( slot == INVEN_BOW ) && (o_ptr->tval == TV_INSTRUMENT)) + { + act = "You are holding"; + } + else if (slot == INVEN_BOW) + { + act = "You are shooting with"; + } + else if (slot == INVEN_LITE) + { + act = "Your light source is"; + } + else if (slot == INVEN_AMMO) + { + act = "In your quiver you have"; + } + else if (slot == INVEN_TOOL) + { + act = "You are using"; + } + else + { + act = "You are wearing"; + } + + /* Describe the result */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Message */ + msg_format("%s %s (%c).", act, o_name, index_to_label(slot)); + + /* 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; + } + + /* Take care of item sets */ + if (o_ptr->name1) + { + wield_set(o_ptr->name1, a_info[o_ptr->name1].set, FALSE); + } + + /* 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 | PU_SPELLS); + + /* Redraw monster hitpoint */ + p_ptr->redraw |= (PR_MH); + + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); +} + + + +/* + * Take off an item + */ +void do_cmd_takeoff(void) +{ + int item; + + object_type *o_ptr; + + cptr q, s; + + + /* Get an item */ + q = "Take off which item? "; + s = "You are not wearing anything to take off."; + if (!get_item(&item, q, s, (USE_EQUIP))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Can we take it off */ + if (process_hooks(HOOK_TAKEOFF, "(d)", item)) return; + + /* Item is cursed */ + if (cursed_p(o_ptr) && (!wizard)) + { + /* Oops */ + msg_print("Hmmm, it seems to be cursed."); + + /* Nope */ + return; + } + + + /* Take a partial turn */ + energy_use = 50; + + /* Take off the item */ + (void)inven_takeoff(item, 255, FALSE); + + /* Recalculate hitpoint */ + p_ptr->update |= (PU_HP); + + p_ptr->redraw |= (PR_MH); +} + + +/* + * Drop an item + */ +void do_cmd_drop(void) +{ + int item, amt = 1; + + object_type *o_ptr; + + u32b f1, f2, f3, f4, f5, esp; + + cptr q, s; + + + /* Get an item */ + q = "Drop which item? "; + s = "You have nothing to drop."; + if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return; + + /* Get the item */ + o_ptr = get_object(item); + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Can we drop */ + if (process_hooks(HOOK_DROP, "(d)", item)) return; + + /* Hack -- Cannot remove cursed items */ + if (cursed_p(o_ptr)) + { + if (item >= INVEN_WIELD) + { + /* Oops */ + msg_print("Hmmm, it seems to be cursed."); + + /* Nope */ + return; + } + else + { + if (f4 & TR4_CURSE_NO_DROP) + { + /* Oops */ + msg_print("Hmmm, you seem to be unable to drop it."); + + /* Nope */ + return; + } + } + } + + + /* See how many items */ + if (o_ptr->number > 1) + { + /* Get a quantity */ + amt = get_quantity(NULL, o_ptr->number); + + /* Allow user abort */ + if (amt <= 0) return; + } + + /* Take a partial turn */ + energy_use = 50; + + /* Drop (some of) the item */ + inven_drop(item, amt, p_ptr->py, p_ptr->px, FALSE); +} + + +/* + * Destroy an item + */ +void do_cmd_destroy(void) +{ + int item, amt = 1; + + int old_number; + + bool_ force = FALSE; + + object_type *o_ptr; + + char o_name[80]; + + char out_val[160]; + + cptr q, s; + + u32b f1, f2, f3, f4, f5, esp; + + + /* Hack -- force destruction */ + if (command_arg > 0) force = TRUE; + + + /* Get an item */ + q = "Destroy which item? "; + s = "You have nothing to destroy."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_AUTO))) return; + + /* Get the item */ + o_ptr = get_object(item); + + + /* See how many items */ + if (o_ptr->number > 1) + { + /* Get a quantity */ + amt = get_quantity(NULL, o_ptr->number); + + /* Allow user abort */ + if (amt <= 0) return; + } + + + /* Describe the object */ + old_number = o_ptr->number; + o_ptr->number = amt; + object_desc(o_name, o_ptr, TRUE, 3); + o_ptr->number = old_number; + + /* Verify unless quantity given */ + if (!force) + { + if (!((auto_destroy) && (object_value(o_ptr) < 1))) + { + /* Make a verification */ + strnfmt(out_val, 160, "Really destroy %s? ", o_name); + if (!get_check(out_val)) return; + } + } + + /* Take no time, just like the automatizer */ + energy_use = 0; + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if ((f4 & TR4_CURSE_NO_DROP) && cursed_p(o_ptr)) + { + /* Oops */ + msg_print("Hmmm, you seem to be unable to destroy it."); + + /* Nope */ + return; + } + + + /* Artifacts cannot be destroyed */ + if (artifact_p(o_ptr) || o_ptr->art_name) + { + byte feel = SENSE_SPECIAL; + + energy_use = 0; + + /* Message */ + msg_format("You cannot destroy %s.", o_name); + + /* Hack -- Handle icky artifacts */ + if (cursed_p(o_ptr)) feel = SENSE_TERRIBLE; + + /* Hack -- inscribe the artifact */ + o_ptr->sense = feel; + + /* We have "felt" it (again) */ + o_ptr->ident |= (IDENT_SENSE); + + /* Combine the pack */ + p_ptr->notice |= (PN_COMBINE); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Done */ + return; + } + + /* Message */ + msg_format("You destroy %s.", o_name); + sound(SOUND_DESTITEM); + + /* Create an automatizer rule */ + if (automatizer_create) + { + automatizer_add_rule(o_ptr, TRUE); + } + + /* + * Hack -- If rods or wand are destroyed, the total maximum timeout or + * charges of the stack needs to be reduced, unless all the items are + * being destroyed. -LM- + */ + if ((o_ptr->tval == TV_WAND) && (amt < o_ptr->number)) + { + o_ptr->pval -= o_ptr->pval * amt / o_ptr->number; + } + + /* Eru wont be happy */ + if (f3 & TR3_BLESSED) + inc_piety(GOD_ERU, -10 * k_info[o_ptr->k_idx].level); + + /* Eliminate the item */ + inc_stack_size(item, -amt); +} + + +/* + * Observe an item which has been *identify*-ed + */ +void do_cmd_observe(void) +{ + int item; + + object_type *o_ptr; + + char o_name[80]; + + cptr q, s; + + + /* Get an item */ + q = "Examine which item? "; + s = "You have nothing to examine."; + if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Description */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Describe */ + cmsg_format(TERM_L_BLUE, "%s", o_name); + + /* Describe it fully */ + if (!object_out_desc(o_ptr, NULL, FALSE, TRUE)) msg_print("You see nothing special."); +} + + + +/* + * Remove the inscription from an object + * XXX Mention item (when done)? + */ +void do_cmd_uninscribe(void) +{ + int item; + + object_type *o_ptr; + + cptr q, s; + + + /* Get an item */ + q = "Un-inscribe which item? "; + s = "You have nothing to un-inscribe."; + if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Nothing to remove */ + if (!o_ptr->note) + { + msg_print("That item had no inscription to remove."); + return; + } + + /* Message */ + msg_print("Inscription removed."); + + /* Remove the incription */ + o_ptr->note = 0; + + /* Combine the pack */ + p_ptr->notice |= (PN_COMBINE); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); +} + + +/* + * Inscribe an object with a comment + */ +void do_cmd_inscribe(void) +{ + int item; + + object_type *o_ptr; + + char o_name[80]; + + char out_val[80]; + + cptr q, s; + + + /* Get an item */ + q = "Inscribe which item? "; + s = "You have nothing to inscribe."; + if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Describe the activity */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Message */ + msg_format("Inscribing %s.", o_name); + msg_print(NULL); + + /* Start with nothing */ + strcpy(out_val, ""); + + /* Use old inscription */ + if (o_ptr->note) + { + /* Start with the old inscription */ + strcpy(out_val, quark_str(o_ptr->note)); + } + + /* Get a new inscription (possibly empty) */ + if (get_string("Inscription: ", out_val, 80)) + { + /* Save the inscription */ + o_ptr->note = quark_add(out_val); + + /* Combine the pack */ + p_ptr->notice |= (PN_COMBINE); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + } +} + + + +/* + * An "item_tester_hook" for refilling lanterns + */ +static bool_ item_tester_refill_lantern(object_type *o_ptr) +{ + /* Flasks of oil are okay */ + if (o_ptr->tval == TV_FLASK) return (TRUE); + + /* Lanterns are okay */ + if ((o_ptr->tval == TV_LITE) && + (o_ptr->sval == SV_LITE_LANTERN)) return (TRUE); + + /* Assume not okay */ + return (FALSE); +} + + +/* + * Refill the players lamp (from the pack or floor) + */ +static void do_cmd_refill_lamp(void) +{ + int item; + + object_type *o_ptr; + object_type *j_ptr; + + cptr q, s; + + + /* Restrict the choices */ + item_tester_hook = item_tester_refill_lantern; + + /* Get an item */ + q = "Refill with which flask? "; + s = "You have no flasks of oil."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Take a partial turn */ + energy_use = 50; + + /* Access the lantern */ + j_ptr = &p_ptr->inventory[INVEN_LITE]; + + /* Refuel */ + if (o_ptr->tval == TV_FLASK) + j_ptr->timeout += o_ptr->pval; + else + j_ptr->timeout += o_ptr->timeout; + + /* Message */ + msg_print("You fuel your lamp."); + + /* Comment */ + if (j_ptr->timeout >= FUEL_LAMP) + { + j_ptr->timeout = FUEL_LAMP; + msg_print("Your lamp is full."); + } + + /* Decrease the item stack */ + inc_stack_size(item, -1); + + /* Recalculate torch */ + p_ptr->update |= (PU_TORCH); +} + + +/* + * An "item_tester_hook" for refilling torches + */ +static bool_ item_tester_refill_torch(object_type *o_ptr) +{ + /* Torches are okay */ + if ((o_ptr->tval == TV_LITE) && + (o_ptr->sval == SV_LITE_TORCH)) return (TRUE); + + /* Assume not okay */ + return (FALSE); +} + + +/* + * Refuel the players torch (from the pack or floor) + */ +static void do_cmd_refill_torch(void) +{ + int item; + + object_type *o_ptr; + + object_type *j_ptr; + + cptr q, s; + + + /* Restrict the choices */ + item_tester_hook = item_tester_refill_torch; + + /* Get an item */ + q = "Refuel with which torch? "; + s = "You have no extra torches."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Take a partial turn */ + energy_use = 50; + + /* Access the primary torch */ + j_ptr = &p_ptr->inventory[INVEN_LITE]; + + /* Refuel */ + j_ptr->timeout += o_ptr->timeout + 5; + + /* Message */ + msg_print("You combine the torches."); + + /* Over-fuel message */ + if (j_ptr->timeout >= FUEL_TORCH) + { + j_ptr->timeout = FUEL_TORCH; + msg_print("Your torch is fully fueled."); + } + + /* Refuel message */ + else + { + msg_print("Your torch glows more brightly."); + } + + /* Decrease the item stack */ + inc_stack_size(item, -1); + + /* Recalculate torch */ + p_ptr->update |= (PU_TORCH); +} + + +/* + * Refill the players lamp, or restock his torches + */ +void do_cmd_refill(void) +{ + object_type *o_ptr; + + u32b f1, f2, f3, f4, f5, esp; + + + /* Get the light */ + o_ptr = &p_ptr->inventory[INVEN_LITE]; + + /* It is nothing */ + if (o_ptr->tval != TV_LITE) + { + msg_print("You are not wielding a light."); + return; + } + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if (f4 & TR4_FUEL_LITE) + { + /* It's a torch */ + if (o_ptr->sval == SV_LITE_TORCH || + o_ptr->sval == SV_LITE_TORCH_EVER) + { + do_cmd_refill_torch(); + } + + /* It's a lamp */ + else if (o_ptr->sval == SV_LITE_LANTERN || + o_ptr->sval == SV_LITE_DWARVEN || + o_ptr->sval == SV_LITE_FEANORIAN) + { + do_cmd_refill_lamp(); + } + } + + /* No torch to refill */ + else + { + msg_print("Your light cannot be refilled."); + } +} + + +/* + * Target command + */ +void do_cmd_target(void) +{ + /* Target set */ + if (target_set(TARGET_KILL)) + { + msg_print("Target Selected."); + } + + /* Target aborted */ + else + { + msg_print("Target Aborted."); + } +} + + + +/* + * Look command + */ +void do_cmd_look(void) +{ + /* Look around */ + if (target_set(TARGET_LOOK)) + { + msg_print("Target Selected."); + } +} + + + +/* + * Allow the player to examine other sectors on the map + */ +void do_cmd_locate(void) +{ + int dir, y1, x1, y2, x2; + int panel_hgt, panel_wid; + char tmp_val[80]; + char out_val[160]; + + + /* Retrieve size of the Angband window */ + Term_get_size(&panel_wid, &panel_hgt); + + /* Calcurate size of the dungeon map area */ + panel_hgt = (panel_hgt - (ROW_MAP + 1)) / 2; + panel_wid = (panel_wid - (COL_MAP + 1)) / 2; + + /* Start at current panel */ + y2 = y1 = panel_row_min; + x2 = x1 = panel_col_min; + + /* Show panels until done */ + while (1) + { + /* Describe the location */ + if ((y2 == y1) && (x2 == x1)) + { + tmp_val[0] = '\0'; + } + else + { + strnfmt(tmp_val, 80, "%s%s of", + ((y2 < y1) ? " North" : (y2 > y1) ? " South" : ""), + ((x2 < x1) ? " West" : (x2 > x1) ? " East" : "")); + } + + /* Prepare to ask which way to look */ + if ((panel_hgt == PANEL_HGT) && (panel_wid == PANEL_WID)) + { + /* Avoid surprising the standard screen users */ + strnfmt(out_val, 160, + "Map sector [%d,%d], which is%s your sector. Direction?", + y2 / panel_hgt, x2 / panel_wid, tmp_val); + } + + /* Big screen */ + else + { + /* Panels are measured by current map area size */ + strnfmt(out_val, 160, + "Map sector [%d(%02d),%d(%02d)], which is%s your sector. Direction?", + y2 / panel_hgt, y2 % panel_hgt, + x2 / panel_wid, x2 % panel_wid, tmp_val); + } + + /* Assume no direction */ + dir = 0; + + /* Get a direction */ + while (!dir) + { + char ch; + + /* Get a command (or cancel) */ + if (!get_com(out_val, &ch)) break; + + /* Extract the action (if any) */ + dir = get_keymap_dir(ch); + + /* Error */ + if (!dir) bell(); + } + + /* No direction */ + if (!dir) break; + + /* Apply the motion */ + if (change_panel(ddy[dir], ddx[dir])) + { + y2 = panel_row_min; + x2 = panel_col_min; + } + } + + /* Recenter the map around the player */ + verify_panel(); + + /* Update stuff */ + p_ptr->update |= (PU_MONSTERS); + + /* Redraw map */ + p_ptr->redraw |= (PR_MAP); + + /* Window stuff */ + p_ptr->window |= (PW_OVERHEAD); + + /* Handle stuff */ + handle_stuff(); +} + + + + + + +/* + * The table of "symbol info" -- each entry is a string of the form + * "X:desc" where "X" is the trigger, and "desc" is the "info". + */ +static cptr ident_info[] = +{ + " :A dark grid", + "!:A potion (or oil)", + "\":An amulet (or necklace)", + "#:A wall (or secret door)", + "$:Treasure (gold or gems)", + "%:A vein (magma or quartz)", + /* "&:unused", */ + "':An open door", + "(:Soft armor", + "):A shield", + "*:A vein with treasure", + "+:A closed door", + ",:Food (or mushroom patch)", + "-:A wand (or rod)", + ".:Floor", + "/:A polearm (Axe/Pike/etc)", + "0:An altar", + "1:Entrance to General Store", + "2:Entrance to Armory", + "3:Entrance to Weaponsmith", + "4:Entrance to Temple", + "5:Entrance to Alchemy shop", + "6:Entrance to Magic store", + "7:Entrance to Black Market", + "8:Entrance to your home", + "9:Entrance to Bookstore", + "::Rubble", + ";:A glyph of warding / explosive rune", + "<:An up staircase", + "=:A ring", + ">:A down staircase", + "?:A scroll", + "@:You", + "A:Angel", + "B:Bird", + "C:Canine", + "D:Ancient Dragon/Wyrm", + "E:Elemental", + "F:Dragon Fly", + "G:Ghost", + "H:Hybrid", + "I:Insect", + "J:Snake", + "K:Killer Beetle", + "L:Lich", + "M:Multi-Headed Reptile", + /* "N:unused", */ + "O:Ogre", + "P:Giant Humanoid", + "Q:Quylthulg (Pulsing Flesh Mound)", + "R:Reptile/Amphibian", + "S:Spider/Scorpion/Tick", + "T:Troll", + "U:Major Demon", + "V:Vampire", + "W:Wight/Wraith/etc", + "X:Xorn/Xaren/etc", + "Y:Yeti", + "Z:Zephyr Hound", + "[:Hard armor", + "\\:A hafted weapon (mace/whip/etc)", + "]:Misc. armor", + "^:A trap", + "_:A staff", + /* "`:unused", */ + "a:Ant", + "b:Bat", + "c:Centipede", + "d:Dragon", + "e:Floating Eye", + "f:Feline", + "g:Golem", + "h:Hobbit/Elf/Dwarf", + "i:Icky Thing", + "j:Jelly", + "k:Kobold", + "l:Louse", + "m:Mold", + "n:Naga", + "o:Orc", + "p:Person/Human", + "q:Quadruped", + "r:Rodent", + "s:Skeleton", + "t:Townsperson", + "u:Minor Demon", + "v:Vortex", + "w:Worm/Worm-Mass", + /* "x:unused", */ + "y:Yeek", + "z:Zombie/Mummy", + "{:A missile (arrow/bolt/shot)", + "|:An edged weapon (sword/dagger/etc)", + "}:A launcher (bow/crossbow/sling)", + "~:A tool (or miscellaneous item)", + NULL +}; + + +/** + * Sort by monster experience. + */ +static bool compare_monster_experience(int w1, int w2) +{ + /* Extract experience */ + s32b z1 = r_info[w1].mexp; + s32b z2 = r_info[w2].mexp; + + /* Compare experience */ + if (z1 < z2) return true; + if (z1 > z2) return false; + + /* Punt to index */ + return w1 < w2; +} + +/** + * Sort by monster level. + */ +static bool compare_monster_level(int w1, int w2) +{ + /* Extract levels */ + byte z1 = r_info[w1].level; + byte z2 = r_info[w2].level; + + /* Compare levels */ + if (z1 < z2) return true; + if (z1 > z2) return false; + + /* Punt to monster experience. */ + return compare_monster_experience(w1, w2); +} + +/** + * Sort by total number of kills + */ +static bool compare_total_kills(int w1, int w2) +{ + /* Extract total kills */ + s16b z1 = r_info[w1].r_tkills; + s16b z2 = r_info[w2].r_tkills; + + /* Compare total kills */ + if (z1 < z2) return true; + if (z1 > z2) return false; + + /* Punt to monster level. */ + return compare_monster_level(w1, w2); +} + +/* + * Sort by player kills + */ +static bool compare_player_kills(int w1, int w2) +{ + /* Extract player kills */ + s16b z1 = r_info[w1].r_pkills; + s16b z2 = r_info[w2].r_pkills; + + /* Compare player kills */ + if (z1 < z2) return true; + if (z1 > z2) return false; + + /* Punt to total number of kills. */ + return compare_total_kills(w1, w2); +} + + +/* + * Hack -- Display the "name" and "attr/chars" of a monster race + */ +static void roff_top(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + byte a1, a2; + + char c1, c2; + + + /* Access the chars */ + c1 = r_ptr->d_char; + c2 = r_ptr->x_char; + + /* Access the attrs */ + a1 = r_ptr->d_attr; + a2 = r_ptr->x_attr; + + + /* Clear the top line */ + Term_erase(0, 0, 255); + + /* Reset the cursor */ + Term_gotoxy(0, 0); + + /* A title (use "The" for non-uniques) */ + if (!(r_ptr->flags1 & (RF1_UNIQUE))) + { + Term_addstr( -1, TERM_WHITE, "The "); + } + + /* Dump the name */ + Term_addstr( -1, TERM_WHITE, (r_name + r_ptr->name)); + + /* Append the "standard" attr/char info */ + Term_addstr( -1, TERM_WHITE, " ('"); + Term_addch(a1, c1); + if (use_bigtile && (a1 & 0x80)) Term_addch(255, 255); + Term_addstr( -1, TERM_WHITE, "')"); + + /* Append the "optional" attr/char info */ + Term_addstr( -1, TERM_WHITE, "/('"); + Term_addch(a2, c2); + if (use_bigtile && (a2 & 0x80)) Term_addch(255, 255); + Term_addstr( -1, TERM_WHITE, "'):"); +} + + +/* + * Identify a character, allow recall of monsters + * + * Several "special" responses recall "multiple" monsters: + * ^A (all monsters) + * ^U (all unique monsters) + * ^N (all non-unique monsters) + * ^M (case insensitive name search) + * + * The responses may be sorted in several ways, see below. + * + * Note that the player ghosts are ignored. XXX XXX XXX + */ +void do_cmd_query_symbol(void) +{ + int i, r_idx; + + char sym, query; + + char buf[128]; + + + bool_ all = FALSE; + + bool_ uniq = FALSE; + + bool_ norm = FALSE; + + + bool_ name = FALSE; + + char temp[80] = ""; + + + bool_ recall = FALSE; + + bool (*sort_by)(int,int) = nullptr; + + /* Get a character, or abort */ + if (!get_com("Enter character to be identified, " + "or (Ctrl-A, Ctrl-U, Ctrl-N, Ctrl-M):", &sym)) return; + + /* Find that character info, and describe it */ + for (i = 0; ident_info[i]; ++i) + { + if (sym == ident_info[i][0]) break; + } + + /* Describe */ + if (sym == KTRL('A')) + { + all = TRUE; + strcpy(buf, "Full monster list."); + } + else if (sym == KTRL('U')) + { + all = uniq = TRUE; + strcpy(buf, "Unique monster list."); + } + else if (sym == KTRL('N')) + { + all = norm = TRUE; + strcpy(buf, "Non-unique monster list."); + } + else if (sym == KTRL('M')) + { + all = name = TRUE; + if (!get_string("Name:", temp, 70)) return; + strnfmt(buf, 128, "Monsters with a name \"%s\"", temp); + strlower(temp); + } + else if (ident_info[i]) + { + strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2); + } + else + { + strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol"); + } + + /* Display the result */ + prt(buf, 0, 0); + + /* Collect matching monsters */ + std::vector who; + for (i = 1; i < max_r_idx; i++) + { + monster_race *r_ptr = &r_info[i]; + + /* Nothing to recall */ + if (!cheat_know && !r_ptr->r_sights) continue; + + /* Require non-unique monsters if needed */ + if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue; + + /* Require unique monsters if needed */ + if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue; + + /* Require monsters with the name requested if needed */ + if (name) + { + char mon_name[80]; + + strcpy(mon_name, r_name + r_ptr->name); + strlower(mon_name); + + if (!strstr(mon_name, temp)) continue; + } + + /* Collect "appropriate" monsters */ + if (all || (r_ptr->d_char == sym)) { + who.push_back(i); + } + } + + /* Nothing to recall */ + if (who.empty()) + { + return; + } + + + /* Prompt XXX XXX XXX */ + put_str("Recall details? (k/p/y/n): ", 0, 40); + + /* Query */ + query = inkey(); + + /* Restore */ + prt(buf, 0, 0); + + + /* Sort by kills (and level) */ + if (query == 'k') + { + sort_by = compare_player_kills; + query = 'y'; + } + + /* Sort by level */ + if (query == 'p') + { + sort_by = compare_monster_level; + query = 'y'; + } + + /* Catch "escape" */ + if (query != 'y') + { + return; + } + + + /* Sort if needed */ + if (sort_by) + { + /* Sort the array */ + std::sort(std::begin(who), std::end(who), sort_by); + } + + + /* Start at the end */ + i = who.size() - 1; + + /* Scan the monster memory */ + while (1) + { + /* Extract a race */ + r_idx = who[i]; + + /* Hack -- Auto-recall */ + monster_race_track(r_idx, 0); + + /* Hack -- Handle stuff */ + handle_stuff(); + + /* Hack -- Begin the prompt */ + roff_top(r_idx); + + /* Hack -- Complete the prompt */ + Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]"); + + /* Interact */ + while (1) + { + /* Recall */ + if (recall) + { + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Recall on screen */ + screen_roff(who[i], 0, 0); + + /* Hack -- Complete the prompt (again) */ + Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC]"); + } + + /* Command */ + query = inkey(); + + /* Unrecall */ + if (recall) + { + /* Restore */ + Term_load(); + character_icky = FALSE; + } + + /* Normal commands */ + if (query != 'r') break; + + /* Toggle recall */ + recall = !recall; + } + + /* Stop scanning */ + if (query == ESCAPE) break; + + /* Move to "prev" monster */ + if (query == '-') + { + if (++i == who.size()) + { + i = 0; + if (!expand_list) break; + } + } + + /* Move to "next" monster */ + else + { + if (i-- == 0) + { + i = who.size() - 1; + if (!expand_list) break; + } + } + } + + /* Re-display the identity */ + prt(buf, 0, 0); +} + + +/* + * research_mon + * -KMW- + */ +bool_ research_mon() +{ + int i, r_idx; + + char sym, query; + + char buf[128]; + + + s16b oldkills; + + byte oldwake; + + bool_ oldcheat; + + + bool_ all = FALSE; + + bool_ uniq = FALSE; + + bool_ norm = FALSE; + + bool_ notpicked; + + + bool_ recall = FALSE; + + monster_race *r2_ptr; + + /* Hack -- Remember "cheat_know" flag */ + oldcheat = cheat_know; + + + /* Get a character, or abort */ + if (!get_com("Enter character of monster: ", &sym)) return (TRUE); + + /* Find that character info, and describe it */ + for (i = 0; ident_info[i]; ++i) + { + if (sym == ident_info[i][0]) break; + } + + if (ident_info[i]) + { + strnfmt(buf, 128, "%c - %s.", sym, ident_info[i] + 2); + } + else + { + strnfmt(buf, 128, "%c - %s.", sym, "Unknown Symbol"); + } + + /* Display the result */ + prt(buf, 16, 10); + + + /* Collect matching monsters */ + std::vector who; + for (i = 1; i < max_r_idx; i++) + { + monster_race *r_ptr = &r_info[i]; + + /* Hack -- Force "cheat_know" */ + cheat_know = TRUE; + + /* Nothing to recall */ + if (!cheat_know && !r_ptr->r_sights) continue; + + /* Require non-unique monsters if needed */ + if (norm && (r_ptr->flags1 & (RF1_UNIQUE))) continue; + + /* Require unique monsters if needed */ + if (uniq && !(r_ptr->flags1 & (RF1_UNIQUE))) continue; + + /* Collect "appropriate" monsters */ + if (all || (r_ptr->d_char == sym)) { + who.push_back(i); + } + } + + /* Nothing to recall */ + if (who.empty()) + { + /* Restore the "cheat_know" flag */ + cheat_know = oldcheat; + + return (TRUE); + } + + + query = 'y'; + + /* Sort by level */ + std::sort(std::begin(who), std::end(who), compare_monster_level); + + + /* Start at the end */ + i = who.size() - 1; + + notpicked = TRUE; + + /* Scan the monster memory */ + while (notpicked) + { + /* Extract a race */ + r_idx = who[i]; + + /* Hack -- Auto-recall */ + monster_race_track(r_idx, 0); + + /* Hack -- Handle stuff */ + handle_stuff(); + + /* Hack -- Begin the prompt */ + roff_top(r_idx); + + /* Hack -- Complete the prompt */ + Term_addstr( -1, TERM_WHITE, " [(r)ecall, ESC, space to continue]"); + + /* Interact */ + while (1) + { + /* Recall */ + if (recall) + { + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Recall on screen */ + r2_ptr = &r_info[r_idx]; + + oldkills = r2_ptr->r_tkills; + oldwake = r2_ptr->r_wake; + screen_roff(who[i], 0, 1); + r2_ptr->r_tkills = oldkills; + r2_ptr->r_wake = oldwake; + r2_ptr->r_sights = 1; + cheat_know = oldcheat; + notpicked = FALSE; + break; + + } + + /* Command */ + query = inkey(); + + /* Unrecall */ + if (recall) + { + /* Restore */ + Term_load(); + character_icky = FALSE; + } + + /* Normal commands */ + if (query != 'r') break; + + /* Toggle recall */ + recall = !recall; + } + + /* Stop scanning */ + if (query == ESCAPE) break; + + /* Move to "prev" monster */ + if (query == '-') + { + if (++i == who.size()) + { + i = 0; + if (!expand_list) break; + } + } + + /* Move to "next" monster */ + else + { + if (i-- == 0) + { + i = who.size() - 1; + if (!expand_list) break; + } + } + } + + + /* Re-display the identity */ + /* prt(buf, 5, 5);*/ + + /* Restore the "cheat_know" flag */ + cheat_know = oldcheat; + + return (notpicked); +} + + +/* + * Try to "sense" the grid's mana + */ +bool_ do_cmd_sense_grid_mana() +{ + int chance, i; + + + /* Take (a lot of) time */ + energy_use = 200; + + /* Base chance of success */ + chance = p_ptr->skill_dev; + + /* Confusion hurts skill */ + if (p_ptr->confused) chance = chance / 2; + + /* Hight mana grids are harder */ + chance = chance - (cave[p_ptr->py][p_ptr->px].mana / 10); + + /* Give everyone a (slight) chance */ + if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0)) + { + chance = USE_DEVICE; + } + + /* Roll for usage */ + if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE)) + { + if (flush_failure) flush(); + msg_print("You failed to sense the grid's mana."); + sound(SOUND_FAIL); + return FALSE; + } + + /* Try to give an "average" value */ + i = (101 - p_ptr->skill_dev) / 2; + i = (i < 1) ? 1 : (i > 50) ? 50 : i; + + if (wizard) + { + msg_format("Grid's mana: %d.", cave[p_ptr->py][p_ptr->px].mana); + msg_format("Average grid's mana: %d.", (cave[p_ptr->py][p_ptr->px].mana / i) * i); + } + else + { + msg_format("Average Area's mana: %d", (cave[p_ptr->py][p_ptr->px].mana / i) * i); + } + return TRUE; +} + + +/* + * Calculate the weight of the portable holes + */ +s32b portable_hole_weight(void) +{ + s32b weight, i; + + store_type *st_ptr = &town_info[TOWN_RANDOM].store[STORE_HOME]; + + + /* Sum the objects in the appropriate home */ + for (i = 0, weight = 0; i < st_ptr->stock_num; i++) + { + object_type *o_ptr = &st_ptr->stock[i]; + + weight += (o_ptr->weight * o_ptr->number); + } + + /* Multiply the sum with 1.5 */ + weight = (weight * 3) / 2 + 2; + + return (weight); +} + + +/* + * Calculate and set the weight of the portable holes + */ +void set_portable_hole_weight(void) +{ + s32b weight, i, j; + + /* Calculate the weight of items in home */ + weight = portable_hole_weight(); + + /* Set the weight of portable holes in the shops, ... */ + for (i = 1; i < max_towns; i++) + { + for (j = 0; j < max_st_idx; j++) + { + store_type *st_ptr = &town_info[i].store[j]; + int k; + + for (k = 0; k < st_ptr->stock_num; k++) + { + object_type *o_ptr = &st_ptr->stock[k]; + + if ((o_ptr->tval == TV_TOOL) && + (o_ptr->sval == SV_PORTABLE_HOLE)) + o_ptr->weight = weight; + } + } + } + + /* ... in the object list, ... */ + for (i = 1; i < o_max; i++) + { + object_type *o_ptr = &o_list[i]; + + if ((o_ptr->tval == TV_TOOL) && + (o_ptr->sval == SV_PORTABLE_HOLE)) o_ptr->weight = weight; + } + + /* ... and in the p_ptr->inventory to the appropriate value */ + for (i = 0; i < INVEN_TOTAL; i++) + { + object_type *o_ptr = &p_ptr->inventory[i]; + + /* Skip non-objects */ + if ((o_ptr->tval == TV_TOOL) && + (o_ptr->sval == SV_PORTABLE_HOLE)) o_ptr->weight = weight; + } +} + + +/* + * Use a portable hole + */ +void do_cmd_portable_hole(void) +{ + cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; + + int feat, special, town_num; + + /* Is it currently wielded? */ + if (!p_ptr->inventory[INVEN_TOOL].k_idx || + (p_ptr->inventory[INVEN_TOOL].tval != TV_TOOL) || + (p_ptr->inventory[INVEN_TOOL].sval != SV_PORTABLE_HOLE)) + { + /* No, it isn't */ + msg_print("You have to wield a portable hole to use your abilities"); + return; + } + + /* Mega-hack: Saving the old values, and then... */ + feat = c_ptr->feat; + special = c_ptr->special; + town_num = p_ptr->town_num; + + /* ... change the current grid to the home in town #1 */ + /* DG -- use the first random town, since random towns cannot have houses */ + /* + * pelpel -- This doesn't affect LoS, so we can manipulate + * terrain feature without calling cave_set_feat() + */ + c_ptr->feat = FEAT_SHOP; + c_ptr->special = STORE_HOME; + p_ptr->town_num = TOWN_RANDOM; + + /* Now use the portable hole */ + do_cmd_store(); + + /* Mega-hack part II: change the current grid to the original value */ + c_ptr->feat = feat; + c_ptr->special = special; + p_ptr->town_num = town_num; + + set_portable_hole_weight(); + + /* Recalculate bonuses */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + p_ptr->update |= (PU_BONUS); +} + + +/* + * Try to add a CLI action. + */ +void cli_add(cptr active, cptr trigger, cptr descr) +{ + s16b num; + cli_comm *cli_ptr, *old_ptr; + + /* Too many macros. */ + if (cli_total >= CLI_MAX) return; + + /* First try to read active as a number. */ + if (strtol(active, 0, 0)) + { + num = strtol(active, 0, 0); + } + /* Then try to read it as a character. */ + else if (strlen(active) == 1) + { + num = active[0]; + } + /* Give up if it doesn't work. */ + else + { + return; + } + + /* Dump the macro. */ + cli_ptr = cli_info + cli_total; + old_ptr = cli_info + cli_total - 1; + + /* + * Trim 's from the ends of a token. This turns '@' into @ and + * ''' into '. This may be the intent of the code in tokenize(), + * but I've left it for lack of comments to back me up. + */ + if (strchr(trigger, '\'')) + { + char temp[80], *t; + cptr s; + for (s = trigger, t = temp; ; s++, t++) + { + /* tokenize() causes each ' to be followed by another character, + * and then another '. Trim the 's here. */ + if (*s == '\'') + { + *t = *(++s); + s++; + } + else + { + *t = *s; + } + if (*t == '\0') break; + } + cli_ptr->comm = string_make(temp); + } + else + { + cli_ptr->comm = string_make(trigger); + } + + /* First try copying everything across. */ + cli_ptr->key = num; + cli_ptr->descrip = string_make(descr); + + /* Take description for the previous record if appropriate. */ + if ((cli_total > 0) && (old_ptr->key == cli_ptr->key) && (cli_ptr->descrip == 0)) + { + cli_ptr->descrip = old_ptr->descrip; + } + + /* Accept the macro. */ + if (cli_ptr->key && cli_ptr->comm && cli_ptr->descrip) cli_total++; +} + + + +/* + * Get a string using CLI completion. + */ +bool_ get_string_cli(cptr prompt, char *buf, int len) +{ + bool_ res; + + + /* Paranoia XXX XXX XXX */ + msg_print(NULL); + + /* Display prompt */ + prt(prompt, 0, 0); + + /* Ask the user for a string */ + askfor_aux_complete = TRUE; + res = askfor_aux(buf, len); + askfor_aux_complete = FALSE; + + /* Clear prompt */ + prt("", 0, 0); + + /* Result */ + return (res); +} + + +/* + * Do a command line command + * + * This is a wrapper around process command to provide a "reverse keymap" + * whereby a set of keypresses is mapped to one. + * + * This is useful because command_cmd is a s16b, and so allows each command a + * unique representation. + * + * See defines.h for a list of the codes used. + */ +void do_cmd_cli(void) +{ + char buff[80]; + + cli_comm *cli_ptr; + + /* Clear the input buffer */ + strcpy(buff, ""); + + /* Accept command */ + if (!get_string_cli("Command: ", buff, 30)) return; + + + /* Analyse the input */ + for (cli_ptr = cli_info; cli_ptr->comm; cli_ptr++) + { + if (!strcmp(buff, cli_ptr->comm)) + { + /* Process the command without keymaps or macros. */ + command_new = cli_ptr->key; + return; + } + } + + msg_format("No such command: %s", buff); +} + + +/* + * Display on-line help for the CLI commands + */ +void do_cmd_cli_help() +{ + int i, j; + + FILE *fff; + + char file_name[1024]; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + for (i = 0, j = -1; i < cli_total; i++) + { + if (j < i - 1) fprintf(fff, "/"); + fprintf(fff, "[[[[[G%s]", cli_info[i].comm); + if (cli_info[i].descrip != cli_info[i + 1].descrip) + { + fprintf(fff, " %s\n", cli_info[i].descrip); + j = i; + } + } + + /* Close the file */ + my_fclose(fff); + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + /* Display the file contents */ + show_file(file_name, "Command line help", 0, 0); + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * Dump screen shot in HTML + */ +void do_cmd_html_dump() +{ + char tmp_val[81]; + bool_ html = TRUE; + term_win *save; + + /* Save the screen */ + save = Term_save_to(); + + if (wizard && get_check("WIZARD MODE: Do an help file dump?")) + html = FALSE; + + /* Ask for a file */ + if (html) + { + strcpy(tmp_val, "dummy.htm"); + if (!get_string("File(you can post it to http://angband.oook.cz/): ", tmp_val, 80)) + { + /* Now restore the screen to initial state */ + Term_load_from(save, TRUE); + Term_fresh(); + return; + } + } + else + { + strcpy(tmp_val, "dummy.txt"); + if (!get_string("File: ", tmp_val, 80)) + { + /* Now restore the screen to initial state */ + Term_load_from(save, TRUE); + Term_fresh(); + return; + } + } + + /* Now restore the screen to dump it */ + Term_load_from(save, TRUE); + + if (html) + html_screenshot(tmp_val); + else + help_file_screenshot(tmp_val); + + Term_erase(0, 0, 255); + msg_print("Dump saved."); + Term_fresh(); + fix_message(); +} diff --git a/src/cmd4.c b/src/cmd4.c deleted file mode 100644 index 3a6ef2cc..00000000 --- a/src/cmd4.c +++ /dev/null @@ -1,4632 +0,0 @@ -/* File: cmd4.c */ - -/* Purpose: Interface commands */ - -/* - * 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 "angband.h" - -#include "messages.h" -#include "hooks.h" - -/* - * Hack -- redraw the screen - * - * This command performs various low level updates, clears all the "extra" - * windows, does a total redraw of the main window, and requests all of the - * interesting updates and redraws that I can think of. - * - * This command is also used to "instantiate" the results of the user - * selecting various things, such as graphics mode, so it must call - * the "TERM_XTRA_REACT" hook before redrawing the windows. - */ -void do_cmd_redraw(void) -{ - int j; - - term *old = Term; - - - /* Hack -- react to changes */ - Term_xtra(TERM_XTRA_REACT, 0); - - - /* Combine and Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - - /* Update torch */ - p_ptr->update |= (PU_TORCH); - - /* Update stuff */ - p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | - PU_SANITY | PU_BODY); - - /* Forget view */ - p_ptr->update |= (PU_UN_VIEW); - - /* Update view */ - p_ptr->update |= (PU_VIEW); - - /* Update monster light */ - p_ptr->update |= (PU_MON_LITE); - - /* Update monsters */ - p_ptr->update |= (PU_MONSTERS); - - /* Redraw everything */ - p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER | PW_M_LIST); - - /* Window stuff */ - p_ptr->window |= (PW_MESSAGE | PW_OVERHEAD | PW_MONSTER | PW_OBJECT); - - /* Hack -- update */ - handle_stuff(); - - - /* Redraw every window */ - for (j = 0; j < 8; j++) - { - /* Dead window */ - if (!angband_term[j]) continue; - - /* Activate */ - Term_activate(angband_term[j]); - - /* Redraw */ - Term_redraw(); - - /* Refresh */ - Term_fresh(); - - /* Restore */ - Term_activate(old); - } -} - - -/* - * Hack -- change name - */ -void do_cmd_change_name(void) -{ - char c; - - int mode = 0; - - char tmp[160]; - - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - /* Forever */ - while (1) - { - /* keep mode below 7 */ - mode = (mode + 6) % 6; - - /* Display the player */ - display_player(mode); - - /* Prompt */ - if (mode == 0) - { - Term_putstr(14, 22, -1, TERM_WHITE, - "['t/T' to change tactics, 'e/E' to change movement]"); - } - - Term_putstr(4, 23, -1, TERM_WHITE, - "['c' to change name, 'f' to file, 'p' for previous, 'n' for next, or ESC]"); - - /* Query */ - c = inkey(); - - /* Exit */ - if (c == ESCAPE) break; - - /* Change name */ - if (c == 'c') - { - get_name(); - } - - /* File dump */ - else if (c == 'f') - { - strnfmt(tmp, 160, "%s.txt", player_name); - if (get_string("Filename(you can post it to http://angband.oook.cz/): ", tmp, 80)) - { - if (tmp[0] && (tmp[0] != ' ')) - { - file_character(tmp, FALSE); - } - } - } - - /* Toggle mode */ - else if (c == 'n') - { - mode++; - } - else if (c == 'p') - { - mode--; - } - - else if (mode == 0) - { - /* Change tactic */ - if (c == 't') - { - (void)do_cmd_change_tactic( -1); - } - else if (c == 'T') - { - (void)do_cmd_change_tactic(1); - } - - /* Change movement */ - else if (c == 'e') - { - do_cmd_change_movement( -1); - } - else if (c == 'E') - { - do_cmd_change_movement(1); - } - else - { - bell(); - } - } - /* Oops */ - else - { - bell(); - } - - /* Flush messages */ - msg_print(NULL); - } - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; - - - /* Redraw everything */ - p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP); - - handle_stuff(); -} - - -/* - * Recall the most recent message - */ -void do_cmd_message_one(void) -{ - cptr msg = format("> %s", message_str(0)); - - /* Recall one message XXX XXX XXX */ - display_message(0, 0, strlen(msg), message_color(0), msg); -} - - -/* - * Show previous messages to the user -BEN- - * - * The screen format uses line 0 and (Term->hgt - 1) for headers and prompts, - * skips line 1 and (Term->hgt - 2), and uses line 2 thru (Term->hgt - 3) for - * old messages. - * - * This command shows you which commands you are viewing, and allows - * you to "search" for strings in the recall. - * - * Note that messages may be longer than 80 characters, but they are - * displayed using "infinite" length, with a special sub-command to - * "slide" the virtual display to the left or right. - * - * Attempt to only hilite the matching portions of the string. - * - * Now taking advantages of big-screen. -pav- - */ -void do_cmd_messages(void) -{ - int i, j, k, n; - u32b q; - int wid, hgt; - - char shower[80]; - char finder[80]; - - /* Wipe finder */ - strcpy(finder, ""); - - /* Wipe shower */ - strcpy(shower, ""); - - - /* Total messages */ - n = message_num(); - - /* Start on first message */ - i = 0; - - /* Start at leftmost edge */ - q = 0; - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - /* Process requests until done */ - while (1) - { - /* Clear screen */ - Term_clear(); - - /* Retrieve current screen size */ - Term_get_size(&wid, &hgt); - - /* Dump up to 20 (or more in bigscreen) lines of messages */ - for (j = 0; (j < (hgt - 4)) && (i + j < n); j++) - { - cptr msg = message_str(i + j); - byte color = message_color(i + j); - - /* Apply horizontal scroll */ - msg = (strlen(msg) >= q) ? (msg + q) : ""; - - /* Dump the messages, bottom to top */ - display_message(0, (hgt - 3) - j, strlen(msg), color, msg); - - /* Hilite "shower" */ - if (shower[0]) - { - cptr str = msg; - - /* Display matches */ - while ((str = strstr(str, shower)) != NULL) - { - int len = strlen(shower); - - /* Display the match */ - Term_putstr(str - msg, (hgt - 3) - j, len, TERM_YELLOW, shower); - - /* Advance */ - str += len; - } - } - } - - /* Display header XXX XXX XXX */ - prt(format("Message Recall (%d-%d of %d), Offset %d", - i, i + j - 1, n, q), 0, 0); - - /* Display prompt (not very informative) */ - prt("[Press 'p' for older, 'n' for newer, ..., or ESCAPE]", hgt - 1, 0); - - /* Get a command */ - k = inkey(); - - /* Exit on Escape */ - if (k == ESCAPE) break; - - /* Hack -- Save the old index */ - j = i; - - /* Horizontal scroll */ - if (k == '4') - { - /* Scroll left */ - q = (q >= ((u32b)wid / 2)) ? (q - wid / 2) : 0; - - /* Success */ - continue; - } - - /* Horizontal scroll */ - if (k == '6') - { - /* Scroll right */ - q = q + wid / 2; - - /* Success */ - continue; - } - - /* Hack -- handle show */ - if (k == '=') - { - /* Prompt */ - prt("Show: ", hgt - 1, 0); - - /* Get a "shower" string, or continue */ - if (!askfor_aux(shower, 80)) continue; - - /* Okay */ - continue; - } - - /* Hack -- handle find */ - if (k == '/') - { - s16b z; - - /* Prompt */ - prt("Find: ", hgt - 1, 0); - - /* Get a "finder" string, or continue */ - if (!askfor_aux(finder, 80)) continue; - - /* Show it */ - strcpy(shower, finder); - - /* Scan messages */ - for (z = i + 1; z < n; z++) - { - cptr msg = message_str(z); - - /* Search for it */ - if (strstr(msg, finder)) - { - /* New location */ - i = z; - - /* Done */ - break; - } - } - } - - /* Recall 1 older message */ - if ((k == '8') || (k == '\n') || (k == '\r')) - { - /* Go newer if legal */ - if (i + 1 < n) i += 1; - } - - /* Recall 10 older messages */ - if (k == '+') - { - /* Go older if legal */ - if (i + 10 < n) i += 10; - } - - /* Recall one screen of older messages */ - if ((k == 'p') || (k == KTRL('P')) || (k == ' ')) - { - /* Go older if legal */ - if (i + (hgt - 4) < n) i += (hgt - 4); - } - - /* Recall one screen of newer messages */ - if ((k == 'n') || (k == KTRL('N'))) - { - /* Go newer (if able) */ - i = (i >= (hgt - 4)) ? (i - (hgt - 4)) : 0; - } - - /* Recall 10 newer messages */ - if (k == '-') - { - /* Go newer (if able) */ - i = (i >= 10) ? (i - 10) : 0; - } - - /* Recall 1 newer messages */ - if (k == '2') - { - /* Go newer (if able) */ - i = (i >= 1) ? (i - 1) : 0; - } - - /* Hack -- Error of some kind */ - if (i == j) bell(); - } - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; -} - - - -/* - * Number of cheating options - */ -#define CHEAT_MAX 6 - -/* - * Cheating options - */ -static option_type cheat_info[CHEAT_MAX] = -{ - { &cheat_peek, FALSE, 0, 0, "cheat_peek", "Peek into object creation" }, - { &cheat_hear, FALSE, 0, 1, "cheat_hear", "Peek into monster creation" }, - { &cheat_room, FALSE, 0, 2, "cheat_room", "Peek into dungeon creation" }, - { &cheat_xtra, FALSE, 0, 3, "cheat_xtra", "Peek into something else" }, - { &cheat_know, FALSE, 0, 4, "cheat_know", "Know complete monster info" }, - { &cheat_live, FALSE, 0, 5, "cheat_live", "Allow player to avoid death" } -}; - -/* - * Interact with some options for cheating - */ -static void do_cmd_options_cheat(cptr info) -{ - char ch; - - int i, k = 0, n = CHEAT_MAX; - - int dir; - - char buf[80]; - - - /* Clear screen */ - Term_clear(); - - /* Interact with the player */ - while (TRUE) - { - /* Prompt XXX XXX XXX */ - strnfmt(buf, 80, "%s (RET to advance, y/n to set, ESC to accept) ", info); - prt(buf, 0, 0); - - /* Display the options */ - for (i = 0; i < n; i++) - { - byte a = TERM_WHITE; - - /* Color current option */ - if (i == k) a = TERM_L_BLUE; - - /* Display the option text */ - strnfmt(buf, 80, "%-48s: %s (%s)", - cheat_info[i].o_desc, - (*cheat_info[i].o_var ? "yes" : "no "), - cheat_info[i].o_text); - c_prt(a, buf, i + 2, 0); - } - - /* Hilite current option */ - move_cursor(k + 2, 50); - - /* Get a key */ - ch = inkey(); - - /* - * Hack -- Try to translate the key into a direction - * to allow the use of roguelike keys for navigation - */ - dir = get_keymap_dir(ch); - if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir); - - - /* Analyze */ - switch (ch) - { - case ESCAPE: - { - return; - } - - case '-': - case '8': - { - k = (n + k - 1) % n; - - break; - } - - case ' ': - case '\n': - case '\r': - case '2': - { - k = (k + 1) % n; - - break; - } - - case 'y': - case 'Y': - case '6': - { - noscore |= (cheat_info[k].o_page * 256 + cheat_info[k].o_bit); - (*cheat_info[k].o_var) = TRUE; - k = (k + 1) % n; - - break; - } - - case 'n': - case 'N': - case '4': - { - (*cheat_info[k].o_var) = FALSE; - k = (k + 1) % n; - - break; - } - - default: - { - bell(); - - break; - } - } - } -} - - -static option_type autosave_info[2] = -{ - { &autosave_l, FALSE, 0, 6, "autosave_l", "Autosave when entering new levels" }, - { &autosave_t, FALSE, 0, 7, "autosave_t", "Timed autosave" }, -}; - -s16b toggle_frequency(s16b current) -{ - if (current == 0) return (50); - if (current == 50) return (100); - if (current == 100) return (250); - if (current == 250) return (500); - if (current == 500) return (1000); - if (current == 1000) return (2500); - if (current == 2500) return (5000); - if (current == 5000) return (10000); - if (current == 10000) return (25000); - - return (0); -} - - -/* - * Interact with some options for cheating - */ -static void do_cmd_options_autosave(cptr info) -{ - char ch; - - int i, k = 0, n = 2; - - int dir; - - char buf[80]; - - - /* Clear screen */ - Term_clear(); - - /* Interact with the player */ - while (TRUE) - { - /* Prompt XXX XXX XXX */ - strnfmt(buf, 80, - "%s (RET to advance, y/n to set, 'F' for frequency, ESC to accept) ", - info); - prt(buf, 0, 0); - - /* Display the options */ - for (i = 0; i < n; i++) - { - byte a = TERM_WHITE; - - /* Color current option */ - if (i == k) a = TERM_L_BLUE; - - /* Display the option text */ - strnfmt(buf, 80, "%-48s: %s (%s)", - autosave_info[i].o_desc, - (*autosave_info[i].o_var ? "yes" : "no "), - autosave_info[i].o_text); - c_prt(a, buf, i + 2, 0); - } - - prt(format("Timed autosave frequency: every %d turns", autosave_freq), 5, 0); - - - /* Hilite current option */ - move_cursor(k + 2, 50); - - /* Get a key */ - ch = inkey(); - - /* - * Hack -- Try to translate the key into a direction - * to allow the use of roguelike keys for navigation - */ - dir = get_keymap_dir(ch); - if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir); - - /* Analyze */ - switch (ch) - { - case ESCAPE: - { - return; - } - - case '-': - case '8': - { - k = (n + k - 1) % n; - - break; - } - - case ' ': - case '\n': - case '\r': - case '2': - { - k = (k + 1) % n; - - break; - } - - case 'y': - case 'Y': - case '6': - { - - (*autosave_info[k].o_var) = TRUE; - k = (k + 1) % n; - - break; - } - - case 'n': - case 'N': - case '4': - { - (*autosave_info[k].o_var) = FALSE; - k = (k + 1) % n; - - break; - } - - case 'f': - case 'F': - { - autosave_freq = toggle_frequency(autosave_freq); - prt(format("Timed autosave frequency: every %d turns", - autosave_freq), 5, 0); - - break; - } - - default: - { - bell(); - - break; - } - } - } -} - -/* Switch an option by only knowing its name */ -bool_ change_option(cptr name, bool_ value) -{ - int i; - - /* Scan the options */ - for (i = 0; option_info[i].o_desc; i++) - { - if (!strcmp(option_info[i].o_text, name)) - { - bool_ old = (*option_info[i].o_var); - - (*option_info[i].o_var) = value; - - return old; - } - } - - cmsg_format(TERM_VIOLET, "Warning, change_option couldn't find option '%s'.", name); - return FALSE; -} - -/* - * Interact with some options - */ -void do_cmd_options_aux(int page, cptr info, bool_ read_only) -{ - char ch; - - int i, k = 0, n = 0; - - int dir; - - int opt[24]; - - char buf[80]; - - - /* Lookup the options */ - for (i = 0; i < 24; i++) opt[i] = 0; - - /* Scan the options */ - for (i = 0; option_info[i].o_desc; i++) - { - /* Notice options on this "page" */ - if (option_info[i].o_page == page) opt[n++] = i; - } - - - /* Clear screen */ - Term_clear(); - - /* Interact with the player */ - while (TRUE) - { - /* Prompt XXX XXX XXX */ - strnfmt(buf, 80, "%s (RET to advance, y/n to set, ESC to accept) ", info); - prt(buf, 0, 0); - - /* Display the options */ - for (i = 0; i < n; i++) - { - byte a = TERM_WHITE; - - /* Color current option */ - if (i == k) a = TERM_L_BLUE; - - /* Display the option text */ - strnfmt(buf, 80, "%-48s: %s (%s)", - option_info[opt[i]].o_desc, - (*option_info[opt[i]].o_var ? "yes" : "no "), - option_info[opt[i]].o_text); - c_prt(a, buf, i + 2, 0); - } - - /* Hilite current option */ - move_cursor(k + 2, 50); - - /* Get a key */ - ch = inkey(); - - /* - * Hack -- Try to translate the key into a direction - * to allow the use of roguelike keys for navigation - */ - dir = get_keymap_dir(ch); - if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir); - - /* Analyze */ - switch (ch) - { - case ESCAPE: - { - return; - } - - case '-': - case '8': - { - k = (n + k - 1) % n; - - break; - } - - case ' ': - case '\n': - case '\r': - case '2': - { - k = (k + 1) % n; - - break; - } - - case 'y': - case 'Y': - case '6': - { - if (read_only) break; - - (*option_info[opt[k]].o_var) = TRUE; - k = (k + 1) % n; - - break; - } - - case 'n': - case 'N': - case '4': - { - if (read_only) break; - - (*option_info[opt[k]].o_var) = FALSE; - k = (k + 1) % n; - - break; - } - - default: - { - bell(); - - break; - } - } - } -} - - -/* - * Modify the "window" options - */ -static void do_cmd_options_win(void) -{ - int i, j, d; - - int y = 0; - - int x = 0; - - char ch; - - bool_ go = TRUE; - - u32b old_flag[8]; - - - /* Memorize old flags */ - for (j = 0; j < 8; j++) - { - /* Acquire current flags */ - old_flag[j] = window_flag[j]; - } - - - /* Clear screen */ - Term_clear(); - - /* Interact */ - while (go) - { - /* Prompt XXX XXX XXX */ - prt("Window Flags (, t, y, n, ESC) ", 0, 0); - - /* Display the windows */ - for (j = 0; j < 8; j++) - { - byte a = TERM_WHITE; - - cptr s = angband_term_name[j]; - - /* Use color */ - if (j == x) a = TERM_L_BLUE; - - /* Window name, staggered, centered */ - Term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s); - } - - /* Display the options */ - for (i = 0; i < 16; i++) - { - byte a = TERM_WHITE; - - cptr str = window_flag_desc[i]; - - /* Use color */ - if (i == y) a = TERM_L_BLUE; - - /* Unused option */ - if (!str) str = "(Unused option)"; - - /* Flag name */ - Term_putstr(0, i + 5, -1, a, str); - - /* Display the windows */ - for (j = 0; j < 8; j++) - { - byte a = TERM_WHITE; - - char c = '.'; - - /* Use color */ - if ((i == y) && (j == x)) a = TERM_L_BLUE; - - /* Active flag */ - if (window_flag[j] & (1L << i)) c = 'X'; - - /* Flag value */ - Term_putch(35 + j * 5, i + 5, a, c); - } - } - - /* Place Cursor */ - Term_gotoxy(35 + x * 5, y + 5); - - /* Get key */ - ch = inkey(); - - /* Analyze */ - switch (ch) - { - case ESCAPE: - { - go = FALSE; - - break; - } - - case 'T': - case 't': - { - /* Clear windows */ - for (j = 0; j < 8; j++) - { - window_flag[j] &= ~(1L << y); - } - - /* Clear flags */ - for (i = 0; i < 16; i++) - { - window_flag[x] &= ~(1L << i); - } - - /* Fall through */ - } - - case 'y': - case 'Y': - { - /* Ignore screen */ - if (x == 0) break; - - /* Set flag */ - window_flag[x] |= (1L << y); - - break; - } - - case 'n': - case 'N': - { - /* Clear flag */ - window_flag[x] &= ~(1L << y); - - break; - } - - default: - { - d = get_keymap_dir(ch); - - x = (x + ddx[d] + 8) % 8; - y = (y + ddy[d] + 16) % 16; - - if (!d) bell(); - - break; - } - } - } - - /* Notice changes */ - for (j = 0; j < 8; j++) - { - term *old = Term; - - /* Dead window */ - if (!angband_term[j]) continue; - - /* Ignore non-changes */ - if (window_flag[j] == old_flag[j]) continue; - - /* Activate */ - Term_activate(angband_term[j]); - - /* Erase */ - Term_clear(); - - /* Refresh */ - Term_fresh(); - - /* Restore */ - Term_activate(old); - } -} - - -/* - * Write all current options to the given preference file in the - * lib/user directory. Modified from KAmband 1.8. - */ -static errr option_dump(cptr fname) -{ - int i, j; - - FILE *fff; - - char buf[1024]; - - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_USER, fname); - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Append to the file */ - fff = my_fopen(buf, "a"); - - /* Failure */ - if (!fff) return ( -1); - - - /* Skip some lines */ - fprintf(fff, "\n\n"); - - /* Start dumping */ - fprintf(fff, "# Automatic option dump\n\n"); - - /* Dump options (skip cheat, adult, score) */ - for (i = 0; option_info[i].o_var != NULL; i++) - { - /* Require a real option */ - if (!option_info[i].o_text) continue; - - /* No birth options */ - if (option_info[i].o_page == 6) continue; - - /* Comment */ - fprintf(fff, "# Option '%s'\n", option_info[i].o_desc); - - /* Dump the option */ - if ((*option_info[i].o_var)) - { - fprintf(fff, "Y:%s\n", option_info[i].o_text); - } - else - { - fprintf(fff, "X:%s\n", option_info[i].o_text); - } - - /* Skip a line */ - fprintf(fff, "\n"); - } - - /* Dump window flags */ - for (i = 1; i < ANGBAND_TERM_MAX; i++) - { - /* Require a real window */ - if (!angband_term[i]) continue; - - /* Check each flag */ - for (j = 0; j < 32; j++) - { - /* Require a real flag */ - if (!window_flag_desc[j]) continue; - - /* Comment */ - fprintf(fff, "# Window '%s', Flag '%s'\n", - angband_term_name[i], window_flag_desc[j]); - - /* Dump the flag */ - if (window_flag[i] & (1L << j)) - { - fprintf(fff, "W:%d:%d:1\n", i, j); - } - else - { - fprintf(fff, "W:%d:%d:0\n", i, j); - } - - /* Skip a line */ - fprintf(fff, "\n"); - } - } - - /* Close */ - my_fclose(fff); - - /* Success */ - return (0); -} - - -/* - * Ask for a "user pref file" and process it. - * - * This function should only be used by standard interaction commands, - * in which a standard "Command:" prompt is present on the given row. - * - * Allow absolute file names? XXX XXX XXX - */ -static void do_cmd_pref_file_hack(int row) -{ - char ftmp[80]; - - - /* Prompt */ - prt("Command: Load a user pref file", row, 0); - - /* Prompt */ - prt("File: ", row + 2, 0); - - /* Default filename */ - strnfmt(ftmp, 80, "%s.prf", player_base); - - /* Ask for a file (or cancel) */ - if (!askfor_aux(ftmp, 80)) return; - - /* Process the given filename */ - if (process_pref_file(ftmp)) - { - /* Mention failure */ - msg_format("Failed to load '%s'!", ftmp); - } - else - { - /* Mention success */ - msg_format("Loaded '%s'.", ftmp); - } -} - - -/* - * Set or unset various options. - * - * The user must use the "Ctrl-R" command to "adapt" to changes - * in any options which control "visual" aspects of the game. - */ -void do_cmd_options(void) -{ - int k; - - - /* Save the screen */ - screen_save(); - - /* Interact */ - while (1) - { - /* Clear screen */ - Term_clear(); - - /* Why are we here */ - prt("Options", 2, 0); - - /* Give some choices */ - prt("(1) User Interface Options", 4, 5); - prt("(2) Disturbance Options", 5, 5); - prt("(3) Game-Play Options", 6, 5); - prt("(4) Efficiency Options", 7, 5); - prt("(5) ToME Options", 8, 5); - prt("(6) Birth Options(read only)", 9, 5); - - /* Special choices */ - prt("(D) Base Delay Factor", 10, 5); - prt("(H) Hitpoint Warning", 11, 5); - prt("(A) Autosave Options", 12, 5); - - /* Automatizer */ - prt("(T) Automatizer", 14, 5); - - - /* Window flags */ - prt("(W) Window Flags", 16, 5); - - /* Cheating */ - prt("(C) Cheating Options", 18, 5); - - /* Dump */ - prt("(U) Dump Options setting", 20, 5); - prt("(O) Load Options setting", 21, 5); - - /* Prompt */ - prt("Command: ", 22, 0); - - /* Get command */ - k = inkey(); - - /* Exit */ - if (k == ESCAPE) break; - - /* Analyze */ - switch (k) - { - /* Load a user pref file */ - case 'o': - case 'O': - { - /* Ask for and load a user pref file */ - do_cmd_pref_file_hack(21); - - break; - } - - /* Append options to a file */ - case 'u': - case 'U': - { - char ftmp[80]; - - /* Prompt */ - prt("Command: Append options to a file", 21, 0); - - /* Prompt */ - prt("File: ", 21, 0); - - /* Default filename */ - strnfmt(ftmp, 80, "%s.prf", player_base); - - /* Ask for a file */ - if (!askfor_aux(ftmp, 80)) continue; - - /* Dump the options */ - if (option_dump(ftmp)) - { - /* Failure */ - msg_print("Failed!"); - } - else - { - /* Success */ - msg_print("Done."); - } - - break; - } - - /* General Options */ - case '1': - { - /* Process the general options */ - do_cmd_options_aux(1, "User Interface Options", FALSE); - - break; - } - - /* Disturbance Options */ - case '2': - { - /* Spawn */ - do_cmd_options_aux(2, "Disturbance Options", FALSE); - - break; - } - - /* Inventory Options */ - case '3': - { - /* Spawn */ - do_cmd_options_aux(3, "Game-Play Options", FALSE); - - break; - } - - /* Efficiency Options */ - case '4': - { - /* Spawn */ - do_cmd_options_aux(4, "Efficiency Options", FALSE); - - break; - } - - /* ToME Options */ - case '5': - { - do_cmd_options_aux(5, "ToME Options", FALSE); - - break; - } - - /* Birth Options - read only */ - case '6': - { - do_cmd_options_aux(6, "Birth Options(read only)", TRUE); - - break; - } - /* Cheating Options */ - case 'C': - { - /* Spawn */ - do_cmd_options_cheat("Cheaters never win"); - - break; - } - - case 't': - case 'T': - { - do_cmd_automatizer(); - break; - } - - case 'a': - case 'A': - { - do_cmd_options_autosave("Autosave"); - - break; - } - - /* Window flags */ - case 'W': - case 'w': - { - /* Spawn */ - do_cmd_options_win(); - - break; - } - - /* Hack -- Delay Speed */ - case 'D': - case 'd': - { - /* Prompt */ - prt("Command: Base Delay Factor", 21, 0); - - /* Get a new value */ - while (1) - { - int msec = delay_factor * delay_factor * delay_factor; - prt(format("Current base delay factor: %d (%d msec)", - delay_factor, msec), 22, 0); - prt("Delay Factor (0-9 or ESC to accept): ", 23, 0); - k = inkey(); - if (k == ESCAPE) break; - if (isdigit(k)) delay_factor = D2I(k); - else bell(); - } - - break; - } - - /* Hack -- hitpoint warning factor */ - case 'H': - case 'h': - { - /* Prompt */ - prt("Command: Hitpoint Warning", 18, 0); - - /* Get a new value */ - while (1) - { - prt(format("Current hitpoint warning: %d0%%", - hitpoint_warn), 22, 0); - prt("Hitpoint Warning (0-9 or ESC to accept): ", 20, 0); - k = inkey(); - if (k == ESCAPE) break; - if (isdigit(k)) hitpoint_warn = D2I(k); - else bell(); - } - - break; - } - - /* Unknown option */ - default: - { - /* Oops */ - bell(); - - break; - } - } - - /* Flush messages */ - msg_print(NULL); - } - - - /* Restore the screen */ - screen_load(); -} - - - -/* - * Ask for a "user pref line" and process it - * - * XXX XXX XXX Allow absolute file names? - */ -void do_cmd_pref(void) -{ - char buf[80]; - - - /* Default */ - strcpy(buf, ""); - - /* Ask for a "user pref command" */ - if (!get_string("Pref: ", buf, 80)) return; - - /* Process that pref command */ - (void)process_pref_file_aux(buf); -} - - -/* - * Hack -- append all current macros to the given file - */ -static errr macro_dump(cptr fname) -{ - int i; - - FILE *fff; - - char buf[1024]; - - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_USER, fname); - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Append to the file */ - fff = my_fopen(buf, "a"); - - /* Failure */ - if (!fff) return ( -1); - - - /* Skip space */ - fprintf(fff, "\n\n"); - - /* Start dumping */ - fprintf(fff, "# Automatic macro dump\n\n"); - - /* Dump them */ - for (i = 0; i < macro__num; i++) - { - /* Start the macro */ - fprintf(fff, "# Macro '%d'\n\n", i); - - /* Extract the action */ - ascii_to_text(buf, macro__act[i]); - - /* Dump the macro */ - fprintf(fff, "A:%s\n", buf); - - /* Extract the action */ - ascii_to_text(buf, macro__pat[i]); - - /* Dump normal macros */ - fprintf(fff, "P:%s\n", buf); - - /* End the macro */ - fprintf(fff, "\n\n"); - } - - /* Start dumping */ - fprintf(fff, "\n\n\n\n"); - - - /* Close */ - my_fclose(fff); - - /* Success */ - return (0); -} - - -/* - * Hack -- ask for a "trigger" (see below) - * - * Note the complex use of the "inkey()" function from "util.c". - * - * Note that both "flush()" calls are extremely important. - */ -static void do_cmd_macro_aux(char *buf, bool_ macro_screen) -{ - int i, n = 0; - - char tmp[1024]; - - - /* Flush */ - flush(); - - /* Do not process macros */ - inkey_base = TRUE; - - /* First key */ - i = inkey(); - - /* Read the pattern */ - while (i) - { - /* Save the key */ - buf[n++] = i; - - /* Do not process macros */ - inkey_base = TRUE; - - /* Do not wait for keys */ - inkey_scan = TRUE; - - /* Attempt to read a key */ - i = inkey(); - } - - /* Terminate */ - buf[n] = '\0'; - - /* Flush */ - flush(); - - - if (macro_screen) - { - /* Convert the trigger */ - ascii_to_text(tmp, buf); - - /* Hack -- display the trigger */ - Term_addstr( -1, TERM_WHITE, tmp); - } -} - -/* - * Hack -- ask for a keymap "trigger" (see below) - * - * Note that both "flush()" calls are extremely important. This may - * no longer be true, since "util.c" is much simpler now. XXX XXX XXX - */ -static void do_cmd_macro_aux_keymap(char *buf) -{ - char tmp[1024]; - - - /* Flush */ - flush(); - - - /* Get a key */ - buf[0] = inkey(); - buf[1] = '\0'; - - - /* Convert to ascii */ - ascii_to_text(tmp, buf); - - /* Hack -- display the trigger */ - Term_addstr( -1, TERM_WHITE, tmp); - - - /* Flush */ - flush(); -} - - -/* - * Hack -- append all keymaps to the given file - */ -static errr keymap_dump(cptr fname) -{ - int i; - - FILE *fff; - - char key[1024]; - char buf[1024]; - - int mode; - - - /* Keymap mode */ - mode = get_keymap_mode(); - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_USER, fname); - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Append to the file */ - fff = my_fopen(buf, "a"); - - /* Failure */ - if (!fff) return ( -1); - - - /* Skip space */ - fprintf(fff, "\n\n"); - - /* Start dumping */ - fprintf(fff, "# Automatic keymap dump\n\n"); - - /* Dump them */ - for (i = 0; i < 256; i++) - { - cptr act; - - /* Loop up the keymap */ - act = keymap_act[mode][i]; - - /* Skip empty keymaps */ - if (!act) continue; - - /* Encode the key */ - buf[0] = i; - buf[1] = '\0'; - ascii_to_text(key, buf); - - /* Encode the action */ - ascii_to_text(buf, act); - - /* Dump the macro */ - fprintf(fff, "A:%s\n", buf); - fprintf(fff, "C:%d:%s\n", mode, key); - } - - /* Start dumping */ - fprintf(fff, "\n\n\n"); - - - /* Close */ - my_fclose(fff); - - /* Success */ - return (0); -} - - - -/* - * Interact with "macros" - * - * Note that the macro "action" must be defined before the trigger. - * - * Could use some helpful instructions on this page. XXX XXX XXX - */ -void do_cmd_macros(void) -{ - int i; - - char tmp[1024]; - - char buf[1024]; - - int mode; - - - /* Keymap mode */ - mode = get_keymap_mode(); - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save screen */ - Term_save(); - - - /* Process requests until done */ - while (1) - { - /* Clear screen */ - Term_clear(); - - /* Describe */ - prt("Interact with Macros", 2, 0); - - - /* Describe that action */ - prt("Current action (if any) shown below:", 20, 0); - - /* Analyze the current action */ - ascii_to_text(buf, macro__buf); - - /* Display the current action */ - prt(buf, 22, 0); - - - /* Selections */ - prt("(1) Load a user pref file", 4, 5); - prt("(2) Append macros to a file", 5, 5); - prt("(3) Query a macro", 6, 5); - prt("(4) Create a macro", 7, 5); - prt("(5) Remove a macro", 8, 5); - prt("(6) Append keymaps to a file", 9, 5); - prt("(7) Query a keymap", 10, 5); - prt("(8) Create a keymap", 11, 5); - prt("(9) Remove a keymap", 12, 5); - prt("(0) Enter a new action", 13, 5); - - /* Prompt */ - prt("Command: ", 16, 0); - - /* Get a command */ - i = inkey(); - - /* Leave */ - if (i == ESCAPE) break; - - /* Load a 'macro' file */ - else if (i == '1') - { - /* Prompt */ - prt("Command: Load a user pref file", 16, 0); - - /* Prompt */ - prt("File: ", 18, 0); - - /* Default filename */ - strnfmt(tmp, 1024, "%s.prf", player_name); - - /* Ask for a file */ - if (!askfor_aux(tmp, 80)) continue; - - /* Process the given filename */ - if (0 != process_pref_file(tmp)) - { - /* Prompt */ - msg_print("Could not load file!"); - } - } - - /* Save macros */ - else if (i == '2') - { - /* Prompt */ - prt("Command: Append macros to a file", 16, 0); - - /* Prompt */ - prt("File: ", 18, 0); - - /* Default filename */ - strnfmt(tmp, 1024, "%s.prf", player_name); - - /* Ask for a file */ - if (!askfor_aux(tmp, 80)) continue; - - /* Dump the macros */ - (void)macro_dump(tmp); - - /* Prompt */ - msg_print("Appended macros."); - } - - /* Query a macro */ - else if (i == '3') - { - int k; - - /* Prompt */ - prt("Command: Query a macro", 16, 0); - - /* Prompt */ - prt("Trigger: ", 18, 0); - - /* Get a macro trigger */ - do_cmd_macro_aux(buf, TRUE); - - /* Acquire action */ - k = macro_find_exact(buf); - - /* Nothing found */ - if (k < 0) - { - /* Prompt */ - msg_print("Found no macro."); - } - - /* Found one */ - else - { - /* Obtain the action */ - strcpy(macro__buf, macro__act[k]); - - /* Analyze the current action */ - ascii_to_text(buf, macro__buf); - - /* Display the current action */ - prt(buf, 22, 0); - - /* Prompt */ - msg_print("Found a macro."); - } - } - - /* Create a macro */ - else if (i == '4') - { - /* Prompt */ - prt("Command: Create a macro", 16, 0); - - /* Prompt */ - prt("Trigger: ", 18, 0); - - /* Get a macro trigger */ - do_cmd_macro_aux(buf, TRUE); - - /* Clear */ - clear_from(20); - - /* Prompt */ - prt("Action: ", 20, 0); - - /* Convert to text */ - ascii_to_text(tmp, macro__buf); - - /* Get an encoded action */ - if (askfor_aux(tmp, 80)) - { - /* Convert to ascii */ - text_to_ascii(macro__buf, tmp); - - /* Link the macro */ - macro_add(buf, macro__buf); - - /* Prompt */ - msg_print("Added a macro."); - } - } - - /* Remove a macro */ - else if (i == '5') - { - /* Prompt */ - prt("Command: Remove a macro", 16, 0); - - /* Prompt */ - prt("Trigger: ", 18, 0); - - /* Get a macro trigger */ - do_cmd_macro_aux(buf, TRUE); - - /* Link the macro */ - macro_add(buf, buf); - - /* Prompt */ - msg_print("Removed a macro."); - } - - /* Save keymaps */ - else if (i == '6') - { - /* Prompt */ - prt("Command: Append keymaps to a file", 16, 0); - - /* Prompt */ - prt("File: ", 18, 0); - - /* Default filename */ - strnfmt(tmp, 1024, "%s.prf", player_name); - - /* Ask for a file */ - if (!askfor_aux(tmp, 80)) continue; - - /* Dump the macros */ - (void)keymap_dump(tmp); - - /* Prompt */ - msg_print("Appended keymaps."); - } - - /* Query a keymap */ - else if (i == '7') - { - cptr act; - - /* Prompt */ - prt("Command: Query a keymap", 16, 0); - - /* Prompt */ - prt("Keypress: ", 18, 0); - - /* Get a keymap trigger */ - do_cmd_macro_aux_keymap(buf); - - /* Look up the keymap */ - act = keymap_act[mode][(byte)(buf[0])]; - - /* Nothing found */ - if (!act) - { - /* Prompt */ - msg_print("Found no keymap."); - } - - /* Found one */ - else - { - /* Obtain the action */ - strcpy(macro__buf, act); - - /* Analyze the current action */ - ascii_to_text(buf, macro__buf); - - /* Display the current action */ - prt(buf, 22, 0); - - /* Prompt */ - msg_print("Found a keymap."); - } - } - - /* Create a keymap */ - else if (i == '8') - { - /* Prompt */ - prt("Command: Create a keymap", 16, 0); - - /* Prompt */ - prt("Keypress: ", 18, 0); - - /* Get a keymap trigger */ - do_cmd_macro_aux_keymap(buf); - - /* Clear */ - clear_from(20); - - /* Prompt */ - prt("Action: ", 20, 0); - - /* Convert to text */ - ascii_to_text(tmp, macro__buf); - - /* Get an encoded action */ - if (askfor_aux(tmp, 80)) - { - /* Convert to ascii */ - text_to_ascii(macro__buf, tmp); - - /* Free old keymap */ - string_free(keymap_act[mode][(byte)(buf[0])]); - - /* Make new keymap */ - keymap_act[mode][(byte)(buf[0])] = string_make(macro__buf); - - /* Prompt */ - msg_print("Added a keymap."); - } - } - - /* Remove a keymap */ - else if (i == '9') - { - /* Prompt */ - prt("Command: Remove a keymap", 16, 0); - - /* Prompt */ - prt("Keypress: ", 18, 0); - - /* Get a keymap trigger */ - do_cmd_macro_aux_keymap(buf); - - /* Free old keymap */ - string_free(keymap_act[mode][(byte)(buf[0])]); - - /* Make new keymap */ - keymap_act[mode][(byte)(buf[0])] = NULL; - - /* Prompt */ - msg_print("Removed a keymap."); - } - - /* Enter a new action */ - else if (i == '0') - { - /* Prompt */ - prt("Command: Enter a new action", 16, 0); - - /* Go to the correct location */ - Term_gotoxy(0, 22); - - /* Hack -- limit the value */ - tmp[80] = '\0'; - - /* Get an encoded action */ - if (!askfor_aux(buf, 80)) continue; - - /* Extract an action */ - text_to_ascii(macro__buf, buf); - } - - /* Oops */ - else - { - /* Oops */ - bell(); - } - - /* Flush messages */ - msg_print(NULL); - } - - /* Load screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; -} - - -/* - * Interact with "visuals" - */ -void do_cmd_visuals(void) -{ - int i; - - FILE *fff; - - char tmp[160]; - - char buf[1024]; - - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - - /* Interact until done */ - while (1) - { - /* Clear screen */ - Term_clear(); - - /* Ask for a choice */ - prt("Interact with Visuals", 2, 0); - - /* Give some choices */ - prt("(1) Load a user pref file", 4, 5); - prt("(2) Dump monster attr/chars", 5, 5); - prt("(3) Dump object attr/chars", 6, 5); - prt("(4) Dump feature attr/chars", 7, 5); - prt("(5) (unused)", 8, 5); - prt("(6) Change monster attr/chars", 9, 5); - prt("(7) Change object attr/chars", 10, 5); - prt("(8) Change feature attr/chars", 11, 5); - prt("(9) (unused)", 12, 5); - prt("(0) Reset visuals", 13, 5); - - /* Prompt */ - prt("Command: ", 15, 0); - - /* Prompt */ - i = inkey(); - - /* Done */ - if (i == ESCAPE) break; - - /* Load a 'pref' file */ - else if (i == '1') - { - /* Prompt */ - prt("Command: Load a user pref file", 15, 0); - - /* Prompt */ - prt("File: ", 17, 0); - - /* Default filename */ - strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); - - /* Query */ - if (!askfor_aux(tmp, 70)) continue; - - /* Process the given filename */ - (void)process_pref_file(tmp); - } - - /* Dump monster attr/chars */ - else if (i == '2') - { - /* Prompt */ - prt("Command: Dump monster attr/chars", 15, 0); - - /* Prompt */ - prt("File: ", 17, 0); - - /* Default filename */ - strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); - - /* Get a filename */ - if (!askfor_aux(tmp, 70)) continue; - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_USER, tmp); - - /* Append to the file */ - fff = my_fopen(buf, "a"); - - /* Failure */ - if (!fff) continue; - - /* Start dumping */ - fprintf(fff, "\n\n"); - fprintf(fff, "# Monster attr/char definitions\n\n"); - - /* Dump monsters */ - for (i = 0; i < max_r_idx; i++) - { - monster_race *r_ptr = &r_info[i]; - - /* Skip non-entries */ - if (!r_ptr->name) continue; - - /* Dump a comment */ - fprintf(fff, "# %s\n", (r_name + r_ptr->name)); - - /* Dump the monster attr/char info */ - fprintf(fff, "R:%d:0x%02X:0x%02X\n\n", i, - (byte)(r_ptr->x_attr), (byte)(r_ptr->x_char)); - } - - /* All done */ - fprintf(fff, "\n\n\n\n"); - - /* Close */ - my_fclose(fff); - - /* Message */ - msg_print("Dumped monster attr/chars."); - } - - /* Dump object attr/chars */ - else if (i == '3') - { - /* Prompt */ - prt("Command: Dump object attr/chars", 15, 0); - - /* Prompt */ - prt("File: ", 17, 0); - - /* Default filename */ - strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); - - /* Get a filename */ - if (!askfor_aux(tmp, 70)) continue; - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_USER, tmp); - - /* Append to the file */ - fff = my_fopen(buf, "a"); - - /* Failure */ - if (!fff) continue; - - /* Start dumping */ - fprintf(fff, "\n\n"); - fprintf(fff, "# Object attr/char definitions\n\n"); - - /* Dump objects */ - for (i = 0; i < max_k_idx; i++) - { - object_kind *k_ptr = &k_info[i]; - - /* Skip non-entries */ - if (!k_ptr->name) continue; - - /* Dump a comment */ - fprintf(fff, "# %s\n", (k_name + k_ptr->name)); - - /* Dump the object attr/char info */ - fprintf(fff, "K:%d:0x%02X:0x%02X\n\n", i, - (byte)(k_ptr->x_attr), (byte)(k_ptr->x_char)); - } - - /* All done */ - fprintf(fff, "\n\n\n\n"); - - /* Close */ - my_fclose(fff); - - /* Message */ - msg_print("Dumped object attr/chars."); - } - - /* Dump feature attr/chars */ - else if (i == '4') - { - /* Prompt */ - prt("Command: Dump feature attr/chars", 15, 0); - - /* Prompt */ - prt("File: ", 17, 0); - - /* Default filename */ - strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); - - /* Get a filename */ - if (!askfor_aux(tmp, 70)) continue; - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_USER, tmp); - - /* Append to the file */ - fff = my_fopen(buf, "a"); - - /* Failure */ - if (!fff) continue; - - /* Start dumping */ - fprintf(fff, "\n\n"); - fprintf(fff, "# Feature attr/char definitions\n\n"); - - /* Dump features */ - for (i = 0; i < max_f_idx; i++) - { - feature_type *f_ptr = &f_info[i]; - - /* Skip non-entries */ - if (!f_ptr->name) continue; - - /* Dump a comment */ - fprintf(fff, "# %s\n", (f_name + f_ptr->name)); - - /* Dump the feature attr/char info */ - fprintf(fff, "F:%d:0x%02X:0x%02X\n\n", i, - (byte)(f_ptr->x_attr), (byte)(f_ptr->x_char)); - } - - /* All done */ - fprintf(fff, "\n\n\n\n"); - - /* Close */ - my_fclose(fff); - - /* Message */ - msg_print("Dumped feature attr/chars."); - } - - /* Modify monster attr/chars */ - else if (i == '6') - { - static int r = 0; - - /* Prompt */ - prt("Command: Change monster attr/chars", 15, 0); - - /* Hack -- query until done */ - while (1) - { - monster_race *r_ptr = &r_info[r]; - - byte da = (r_ptr->d_attr); - char dc = (r_ptr->d_char); - byte ca = (r_ptr->x_attr); - char cc = (r_ptr->x_char); - - /* Label the object */ - Term_putstr(5, 17, -1, TERM_WHITE, - format("Monster = %d, Name = %-40.40s", - r, (r_name + r_ptr->name))); - - /* Label the Default values */ - Term_putstr(10, 19, -1, TERM_WHITE, - format("Default attr/char = %3u / %3u", da, (dc & 0xFF))); - Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>"); - Term_putch(43, 19, da, dc); - if (use_bigtile) - { - if (da & 0x80) - Term_putch(44, 19, 255, 255); - else - Term_putch(44, 19, 0, ' '); - } - - /* Label the Current values */ - Term_putstr(10, 20, -1, TERM_WHITE, - format("Current attr/char = %3u / %3u", ca, (cc & 0xFF))); - Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>"); - Term_putch(43, 20, ca, cc); - if (use_bigtile) - { - if (ca & 0x80) - Term_putch(44, 20, 255, 255); - else - Term_putch(44, 20, 0, ' '); - } - - /* Prompt */ - Term_putstr(0, 22, -1, TERM_WHITE, - "Command (n/N/a/A/c/C): "); - - /* Get a command */ - i = inkey(); - - /* All done */ - if (i == ESCAPE) break; - - /* Analyze */ - if (i == 'n') r = (r + max_r_idx + 1) % max_r_idx; - if (i == 'N') r = (r + max_r_idx - 1) % max_r_idx; - if (i == 'a') r_ptr->x_attr = (ca + 1); - if (i == 'A') r_ptr->x_attr = (ca - 1); - if (i == 'c') r_ptr->x_char = (cc + 1); - if (i == 'C') r_ptr->x_char = (cc - 1); - } - } - - /* Modify object attr/chars */ - else if (i == '7') - { - static int k = 0; - - /* Prompt */ - prt("Command: Change object attr/chars", 15, 0); - - /* Hack -- query until done */ - while (1) - { - object_kind *k_ptr = &k_info[k]; - - byte da = k_ptr->d_attr; - char dc = k_ptr->d_char; - byte ca = k_ptr->x_attr; - char cc = k_ptr->x_char; - - /* Label the object */ - Term_putstr(5, 17, -1, TERM_WHITE, - format("Object = %d, Name = %-40.40s", - k, (k_name + k_ptr->name))); - - /* Label the Default values */ - Term_putstr(10, 19, -1, TERM_WHITE, - format("Default attr/char = %3u / %3u", da, (dc & 0xFF))); - Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>"); - Term_putch(43, 19, da, dc); - if (use_bigtile) - { - if (da & 0x80) - Term_putch(44, 19, 255, 255); - else - Term_putch(44, 19, 0, ' '); - } - - /* Label the Current values */ - Term_putstr(10, 20, -1, TERM_WHITE, - format("Current attr/char = %3u / %3u", ca, (cc & 0xFF))); - Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>"); - Term_putch(43, 20, ca, cc); - if (use_bigtile) - { - if (ca & 0x80) - Term_putch(44, 20, 255, 255); - else - Term_putch(44, 20, 0, ' '); - } - - /* Prompt */ - Term_putstr(0, 22, -1, TERM_WHITE, - "Command (n/N/a/A/c/C): "); - - /* Get a command */ - i = inkey(); - - /* All done */ - if (i == ESCAPE) break; - - /* Analyze */ - if (i == 'n') k = (k + max_k_idx + 1) % max_k_idx; - if (i == 'N') k = (k + max_k_idx - 1) % max_k_idx; - if (i == 'a') k_info[k].x_attr = (ca + 1); - if (i == 'A') k_info[k].x_attr = (ca - 1); - if (i == 'c') k_info[k].x_char = (cc + 1); - if (i == 'C') k_info[k].x_char = (cc - 1); - } - } - - /* Modify feature attr/chars */ - else if (i == '8') - { - static int f = 0; - - /* Prompt */ - prt("Command: Change feature attr/chars", 15, 0); - - /* Hack -- query until done */ - while (1) - { - feature_type *f_ptr = &f_info[f]; - - byte da = f_ptr->d_attr; - char dc = f_ptr->d_char; - byte ca = f_ptr->x_attr; - char cc = f_ptr->x_char; - - /* Label the object */ - Term_putstr(5, 17, -1, TERM_WHITE, - format("Terrain = %d, Name = %-40.40s", - f, (f_name + f_ptr->name))); - - /* Label the Default values */ - Term_putstr(10, 19, -1, TERM_WHITE, - format("Default attr/char = %3u / %3u", da, (dc & 0xFF))); - Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>"); - Term_putch(43, 19, da, dc); - if (use_bigtile) - { - if (da & 0x80) - Term_putch(44, 19, 255, 255); - else - Term_putch(44, 19, 0, ' '); - } - - /* Label the Current values */ - Term_putstr(10, 20, -1, TERM_WHITE, - format("Current attr/char = %3u / %3u", ca, (cc & 0xFF))); - Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>"); - Term_putch(43, 20, ca, cc); - if (use_bigtile) - { - if (ca & 0x80) - Term_putch(44, 20, 255, 255); - else - Term_putch(44, 20, 0, ' '); - } - - /* Prompt */ - Term_putstr(0, 22, -1, TERM_WHITE, - "Command (n/N/a/A/c/C/d): "); - - /* Get a command */ - i = inkey(); - - /* All done */ - if (i == ESCAPE) break; - - /* Analyze */ - if (i == 'n') f = (f + max_f_idx + 1) % max_f_idx; - if (i == 'N') f = (f + max_f_idx - 1) % max_f_idx; - if (i == 'a') f_info[f].x_attr = (ca + 1); - if (i == 'A') f_info[f].x_attr = (ca - 1); - if (i == 'c') f_info[f].x_char = (cc + 1); - if (i == 'C') f_info[f].x_char = (cc - 1); - if (i == 'd') - { - f_info[f].x_char = f_ptr->d_char; - f_info[f].x_attr = f_ptr->d_attr; - } - } - } - - /* Reset visuals */ - else if (i == '0') - { - /* Reset */ - reset_visuals(); - - /* Message */ - msg_print("Visual attr/char tables reset."); - } - - /* Unknown option */ - else - { - bell(); - } - - /* Flush messages */ - msg_print(NULL); - } - - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; -} - - -/* - * Interact with "colors" - */ -void do_cmd_colors(void) -{ - int i; - - FILE *fff; - - char tmp[160]; - - char buf[1024]; - - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - - /* Interact until done */ - while (1) - { - /* Clear screen */ - Term_clear(); - - /* Ask for a choice */ - prt("Interact with Colors", 2, 0); - - /* Give some choices */ - prt("(1) Load a user pref file", 4, 5); - prt("(2) Dump colors", 5, 5); - prt("(3) Modify colors", 6, 5); - - /* Prompt */ - prt("Command: ", 8, 0); - - /* Prompt */ - i = inkey(); - - /* Done */ - if (i == ESCAPE) break; - - /* Load a 'pref' file */ - if (i == '1') - { - /* Prompt */ - prt("Command: Load a user pref file", 8, 0); - - /* Prompt */ - prt("File: ", 10, 0); - - /* Default file */ - strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); - - /* Query */ - if (!askfor_aux(tmp, 70)) continue; - - /* Process the given filename */ - (void)process_pref_file(tmp); - - /* Mega-Hack -- react to changes */ - Term_xtra(TERM_XTRA_REACT, 0); - - /* Mega-Hack -- redraw */ - Term_redraw(); - } - - /* Dump colors */ - else if (i == '2') - { - /* Prompt */ - prt("Command: Dump colors", 8, 0); - - /* Prompt */ - prt("File: ", 10, 0); - - /* Default filename */ - strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); - - /* Get a filename */ - if (!askfor_aux(tmp, 70)) continue; - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_USER, tmp); - - /* Append to the file */ - fff = my_fopen(buf, "a"); - - /* Failure */ - if (!fff) continue; - - /* Start dumping */ - fprintf(fff, "\n\n"); - fprintf(fff, "# Color redefinitions\n\n"); - - /* Dump colors */ - for (i = 0; i < 256; i++) - { - int kv = angband_color_table[i][0]; - int rv = angband_color_table[i][1]; - int gv = angband_color_table[i][2]; - int bv = angband_color_table[i][3]; - - cptr name = "unknown"; - - /* Skip non-entries */ - if (!kv && !rv && !gv && !bv) continue; - - /* Extract the color name */ - if (i < 16) name = color_names[i]; - - /* Dump a comment */ - fprintf(fff, "# Color '%s'\n", name); - - /* Dump the monster attr/char info */ - fprintf(fff, "V:%d:0x%02X:0x%02X:0x%02X:0x%02X\n\n", - i, kv, rv, gv, bv); - } - - /* All done */ - fprintf(fff, "\n\n\n\n"); - - /* Close */ - my_fclose(fff); - - /* Message */ - msg_print("Dumped color redefinitions."); - } - - /* Edit colors */ - else if (i == '3') - { - static byte a = 0; - - /* Prompt */ - prt("Command: Modify colors", 8, 0); - - /* Hack -- query until done */ - while (1) - { - cptr name; - - /* Clear */ - clear_from(10); - - /* Exhibit the normal colors */ - for (i = 0; i < 16; i++) - { - /* Exhibit this color */ - Term_putstr(i*4, 20, -1, a, "###"); - - /* Exhibit all colors */ - Term_putstr(i*4, 22, -1, i, format("%3d", i)); - } - - /* Describe the color */ - name = ((a < 16) ? color_names[a] : "undefined"); - - /* Describe the color */ - Term_putstr(5, 10, -1, TERM_WHITE, - format("Color = %d, Name = %s", a, name)); - - /* Label the Current values */ - Term_putstr(5, 12, -1, TERM_WHITE, - format("K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x", - angband_color_table[a][0], - angband_color_table[a][1], - angband_color_table[a][2], - angband_color_table[a][3])); - - /* Prompt */ - Term_putstr(0, 14, -1, TERM_WHITE, - "Command (n/N/k/K/r/R/g/G/b/B): "); - - /* Get a command */ - i = inkey(); - - /* All done */ - if (i == ESCAPE) break; - - /* Analyze */ - if (i == 'n') a = (a + 1); - if (i == 'N') a = (a - 1); - if (i == 'k') angband_color_table[a][0] = (angband_color_table[a][0] + 1); - if (i == 'K') angband_color_table[a][0] = (angband_color_table[a][0] - 1); - if (i == 'r') angband_color_table[a][1] = (angband_color_table[a][1] + 1); - if (i == 'R') angband_color_table[a][1] = (angband_color_table[a][1] - 1); - if (i == 'g') angband_color_table[a][2] = (angband_color_table[a][2] + 1); - if (i == 'G') angband_color_table[a][2] = (angband_color_table[a][2] - 1); - if (i == 'b') angband_color_table[a][3] = (angband_color_table[a][3] + 1); - if (i == 'B') angband_color_table[a][3] = (angband_color_table[a][3] - 1); - - /* Hack -- react to changes */ - Term_xtra(TERM_XTRA_REACT, 0); - - /* Hack -- redraw */ - Term_redraw(); - } - } - - /* Unknown option */ - else - { - bell(); - } - - /* Flush messages */ - msg_print(NULL); - } - - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; -} - - -/* - * Take notes. There are two ways this can happen, either in the message - * recall or a file. - */ -void do_cmd_note(void) -{ - char buf[80]; - - - /* Default */ - strcpy(buf, ""); - - if (!get_string("Note: ", buf, 60)) return; - - /* Ignore empty notes */ - if (!buf[0] || (buf[0] == ' ')) return; - - /* Add note to file */ - add_note(buf, ' '); -} - - -/* - * Mention the current version - */ -void do_cmd_version(void) -{ - /* Silly message */ - msg_format("You are playing %s made by %s (%s).", - get_version_string(), - modules[game_module_idx].meta.author.name, - modules[game_module_idx].meta.author.email); -} - - - -/* - * Array of feeling strings - */ -static cptr do_cmd_feeling_text[11] = -{ - "Looks like any other level.", - "You feel there is something special about this level.", - "You have a superb feeling about this level.", - "You have an excellent feeling...", - "You have a very good feeling...", - "You have a good feeling...", - "You feel strangely lucky...", - "You feel your luck is turning...", - "You like the look of this place...", - "This level can't be all bad...", - "What a boring place..." -}; - - -/* - * Note that "feeling" is set to zero unless some time has passed. - * Note that this is done when the level is GENERATED, not entered. - */ -void do_cmd_feeling(void) -{ - /* Verify the feeling */ - if (feeling < 0) feeling = 0; - if (feeling > 10) feeling = 10; - - /* Feeling of the fate */ - if (fate_flag && !(dungeon_flags2 & DF2_SPECIAL) && !p_ptr->inside_quest) - { - msg_print("You feel that you will meet your fate here."); - } - - /* Hooked feelings ? */ - if (process_hooks(HOOK_FEELING, "(d)", is_quest(dun_level))) - { - return; - } - - /* No useful feeling in special levels */ - if (dungeon_flags2 & DF2_DESC) - { - char buf[1024]; - - if ((get_dungeon_save(buf)) || (generate_special_feeling) || (dungeon_flags2 & DF2_DESC_ALWAYS)) - { - if (!get_level_desc(buf)) msg_print("Someone forgot to describe this level!"); - else msg_print(buf); - return; - } - } - - /* No useful feeling in quests */ - if (p_ptr->inside_quest) - { - msg_print("Looks like a typical quest level."); - return; - } - - /* Display feelings in the dungeon, nothing on the surface */ - if (dun_level) - { - /* This could be simplified with a correct p_ptr->town_num */ - int i, town_level = 0; - dungeon_info_type *d_ptr = &d_info[dungeon_type]; - - /* Is it a town level ? */ - for (i = 0; i < TOWN_DUNGEON; i++) - { - if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i]; - } - - if (town_level) - msg_print("You hear the sound of a market."); - else - msg_print(do_cmd_feeling_text[feeling]); - } - return; -} - - - -/* - * Encode the screen colors - */ -static char hack[17] = "dwsorgbuDWvyRGBU"; - - -/* - * Hack -- load a screen dump from a file - */ -void do_cmd_load_screen(void) -{ - int i, y, x; - - int wid, hgt; - int len; - - byte a = 0; - char c = ' '; - - bool_ okay = TRUE; - - FILE *fff; - - char buf[1024]; - - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_USER, "dump.txt"); - - /* Append to the file */ - fff = my_fopen(buf, "r"); - - /* Oops */ - if (!fff) return; - - - /* Retrieve the current screen size */ - Term_get_size(&wid, &hgt); - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - /* Clear the screen */ - Term_clear(); - - - /* Load the screen */ - for (y = 0; okay; y++) - { - /* Get a line of data */ - if (my_fgets(fff, buf, 1024)) okay = FALSE; - - /* Stop on blank line */ - if (!buf[0]) break; - - /* Ignore off screen lines */ - if (y >= hgt) continue; - - /* Get width */ - len = strlen(buf); - - /* Truncate if it's longer than current screen width */ - if (len > wid) len = wid; - - /* Show each row */ - for (x = 0; x < len; x++) - { - /* Put the attr/char */ - Term_draw(x, y, TERM_WHITE, buf[x]); - } - } - - /* Dump the screen */ - for (y = 0; okay; y++) - { - /* Get a line of data */ - if (my_fgets(fff, buf, 1024)) okay = FALSE; - - /* Stop on blank line */ - if (!buf[0]) break; - - /* Ignore off screen lines */ - if (y >= hgt) continue; - - /* Get width */ - len = strlen(buf); - - /* Truncate if it's longer than current screen width */ - if (len > wid) len = wid; - - /* Dump each row */ - for (x = 0; x < len; x++) - { - /* Get the attr/char */ - (void)(Term_what(x, y, &a, &c)); - - /* Look up the attr */ - for (i = 0; i < 16; i++) - { - /* Use attr matches */ - if (hack[i] == buf[x]) a = i; - } - - /* Put the attr/char */ - Term_draw(x, y, a, c); - } - } - - - /* Close it */ - my_fclose(fff); - - - /* Message */ - msg_print("Screen dump loaded."); - msg_print(NULL); - - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; -} - - - -/* - * Redefinable "save_screen" action - */ -void (*screendump_aux)(void) = NULL; - - - - - - -/* - * Hack -- save a screen dump to a file - */ -void do_cmd_save_screen(void) -{ - /* Do we use a special screendump function ? */ - if (screendump_aux) - { - /* Dump the screen to a graphics file */ - (*screendump_aux)(); - } - - /* Dump the screen as text */ - else - { - int y, x; - int wid, hgt; - - byte a = 0; - char c = ' '; - - FILE *fff; - - char buf[1024]; - - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_USER, "dump.txt"); - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Append to the file */ - fff = my_fopen(buf, "w"); - - /* Oops */ - if (!fff) return; - - - /* Retrieve the current screen size */ - Term_get_size(&wid, &hgt); - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - - /* Dump the screen */ - for (y = 0; y < hgt; y++) - { - /* Dump each row */ - for (x = 0; x < wid; x++) - { - /* Get the attr/char */ - (void)(Term_what(x, y, &a, &c)); - - /* Dump it */ - buf[x] = c; - } - - /* Terminate */ - buf[x] = '\0'; - - /* End the row */ - fprintf(fff, "%s\n", buf); - } - - /* Skip a line */ - fprintf(fff, "\n"); - - - /* Dump the screen */ - for (y = 0; y < hgt; y++) - { - /* Dump each row */ - for (x = 0; x < wid; x++) - { - /* Get the attr/char */ - (void)(Term_what(x, y, &a, &c)); - - /* Dump it */ - buf[x] = hack[a & 0x0F]; - } - - /* Terminate */ - buf[x] = '\0'; - - /* End the row */ - fprintf(fff, "%s\n", buf); - } - - /* Skip a line */ - fprintf(fff, "\n"); - - - /* Close it */ - my_fclose(fff); - - - /* Message */ - msg_print("Screen dump saved."); - msg_print(NULL); - - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; - } -} - - -/* - * Check the status of "artifacts" - */ -void do_cmd_knowledge_artifacts(void) -{ - int i, k, z, x, y; - - FILE *fff; - - char file_name[1024]; - - char base_name[80]; - - bool_ *okay, *okayk; - - - /* Allocate the "okay" array */ - C_MAKE(okay, max_a_idx, bool_); - C_MAKE(okayk, max_k_idx, bool_); - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - /* Scan the artifacts */ - for (k = 0; k < max_a_idx; k++) - { - artifact_type *a_ptr = &a_info[k]; - - /* Default */ - okay[k] = FALSE; - - /* Skip "empty" artifacts */ - if (!a_ptr->name) continue; - - /* Skip "uncreated" artifacts */ - if (!a_ptr->cur_num) continue; - - /* Assume okay */ - okay[k] = TRUE; - } - - for (k = 0; k < max_k_idx; k++) - { - object_kind *k_ptr = &k_info[k]; - - /* Default */ - okayk[k] = FALSE; - - /* Skip "empty" artifacts */ - if (!(k_ptr->flags3 & TR3_NORM_ART)) continue; - - /* Skip "uncreated" artifacts */ - if (!k_ptr->artifact) continue; - - /* Assume okay */ - okayk[k] = TRUE; - } - - /* Check the dungeon */ - for (y = 0; y < cur_hgt; y++) - { - for (x = 0; x < cur_wid; x++) - { - cave_type *c_ptr = &cave[y][x]; - - s16b this_o_idx, next_o_idx = 0; - - /* Scan all objects in the grid */ - for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) - { - object_type * o_ptr; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Ignore random artifacts */ - if (o_ptr->tval == TV_RANDART) continue; - - /* Ignore non-artifacts */ - if (!artifact_p(o_ptr)) continue; - - /* Ignore known items */ - if (object_known_p(o_ptr)) continue; - - /* Note the artifact */ - if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART) - { - okayk[o_ptr->k_idx] = FALSE; - } - else - { - okay[o_ptr->name1] = FALSE; - } - } - } - } - - /* Check monsters in the dungeon */ - for (i = 0; i < m_max; i++) - { - monster_type *m_ptr = &m_list[i]; - - s16b this_o_idx, next_o_idx = 0; - - /* Scan all objects the monster carries */ - for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) - { - object_type * o_ptr; - - /* Acquire object */ - o_ptr = &o_list[this_o_idx]; - - /* Acquire next object */ - next_o_idx = o_ptr->next_o_idx; - - /* Ignore random artifacts */ - if (o_ptr->tval == TV_RANDART) continue; - - /* Ignore non-artifacts */ - if (!artifact_p(o_ptr)) continue; - - /* Ignore known items */ - if (object_known_p(o_ptr)) continue; - - /* Note the artifact */ - if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART) - { - okayk[o_ptr->k_idx] = FALSE; - } - else - { - okay[o_ptr->name1] = FALSE; - } - } - } - - /* Check the p_ptr->inventory and equipment */ - for (i = 0; i < INVEN_TOTAL; i++) - { - object_type *o_ptr = &p_ptr->inventory[i]; - - /* Ignore non-objects */ - if (!o_ptr->k_idx) continue; - - /* Ignore random artifacts */ - if (o_ptr->tval == TV_RANDART) continue; - - /* Ignore non-artifacts */ - if (!artifact_p(o_ptr)) continue; - - /* Ignore known items */ - if (object_known_p(o_ptr)) continue; - - /* Note the artifact */ - if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART) - { - okayk[o_ptr->k_idx] = FALSE; - } - else - { - okay[o_ptr->name1] = FALSE; - } - } - - /* Scan the artifacts */ - for (k = 0; k < max_a_idx; k++) - { - artifact_type *a_ptr = &a_info[k]; - - /* List "dead" ones */ - if (!okay[k]) continue; - - /* Paranoia */ - strcpy(base_name, "Unknown Artifact"); - - /* Obtain the base object type */ - z = lookup_kind(a_ptr->tval, a_ptr->sval); - - /* Real object */ - if (z) - { - object_type forge; - object_type *q_ptr; - u32b f1, f2, f3, f4, f5, esp; - - /* Get local object */ - q_ptr = &forge; - - /* Create fake object */ - object_prep(q_ptr, z); - - /* Make it an artifact */ - q_ptr->name1 = k; - - /* Spell in it ? no ! */ - object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - if (f5 & TR5_SPELL_CONTAIN) - q_ptr->pval2 = -1; - - /* Describe the artifact */ - object_desc_store(base_name, q_ptr, FALSE, 0); - } - - /* Hack -- Build the artifact name */ - fprintf(fff, " The %s\n", base_name); - } - - for (k = 0; k < max_k_idx; k++) - { - /* List "dead" ones */ - if (!okayk[k]) continue; - - /* Paranoia */ - strcpy(base_name, "Unknown Artifact"); - - /* Real object */ - if (k) - { - object_type forge; - object_type *q_ptr; - - /* Get local object */ - q_ptr = &forge; - - /* Create fake object */ - object_prep(q_ptr, k); - - /* Describe the artifact */ - object_desc_store(base_name, q_ptr, FALSE, 0); - } - - /* Hack -- Build the artifact name */ - fprintf(fff, " The %s\n", base_name); - } - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Artifacts Seen", 0, 0); - - /* Remove the file */ - fd_kill(file_name); - - C_FREE(okay, max_a_idx, bool_); - C_FREE(okayk, max_k_idx, bool_); -} - - -/* - * Check the status of traps - */ -void do_cmd_knowledge_traps(void) -{ - int k; - - FILE *fff; - - trap_type *t_ptr; - - char file_name[1024]; - - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - /* Scan the traps */ - for (k = 0; k < max_t_idx; k++) - { - /* Get the trap */ - t_ptr = &t_info[k]; - - /* Skip "empty" traps */ - if (!t_ptr->name) continue; - - /* Skip unidentified traps */ - if (!t_ptr->ident) continue; - - /* Hack -- Build the trap name */ - fprintf(fff, " %s\n", t_name + t_ptr->name); - } - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Traps known", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - -/* - * Display known uniques - * - * Note that the player ghosts are ignored. XXX XXX XXX - */ -static void insert_sort_unique(int *sort_uniques, int *num, int r_idx) -{ - int i, j; - - monster_race *r_ptr = &r_info[r_idx]; - - int level = r_ptr->level; - - - /* Hack -- Morgoth is always at the bottom of the list */ - if (r_idx == 862) level = 20000; - - /* Find the place */ - for (i = 0; i < *num; i++) - { - monster_race *r2_ptr = &r_info[sort_uniques[i]]; - int level2 = r2_ptr->level; - - if (sort_uniques[i] == 862) level2 = 20000; - - if (level < level2) break; - } - - /* Move the remaining items */ - for (j = *num - 1; j >= i; j--) - { - sort_uniques[j + 1] = sort_uniques[j]; - } - - /* Insert it */ - sort_uniques[i] = r_idx; - (*num)++; -} - - -static void do_cmd_knowledge_uniques(void) -{ - int k; - - int *sort_uniques; - - int num = 0; - - FILE *fff; - - char file_name[1024]; - - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - C_MAKE(sort_uniques, max_r_idx, int); - - /* Sort the monster races */ - for (k = 1; k < max_r_idx; k++) - { - monster_race *r_ptr = &r_info[k]; - - /* Only print Uniques */ - if (r_ptr->flags1 & (RF1_UNIQUE) && - !(r_ptr->flags7 & RF7_PET) && - !(r_ptr->flags7 & RF7_NEUTRAL)) - { - insert_sort_unique(sort_uniques, &num, k); - } - } - - /* Scan the monster races -- sorted */ - for (k = 0; k < num; k++) - { - monster_race *r_ptr = &r_info[sort_uniques[k]]; - - /* Only print Uniques */ - if (r_ptr->flags1 & (RF1_UNIQUE)) - { - bool_ dead = (r_ptr->max_num == 0); - - /* Only display "known" uniques */ - if (dead || cheat_know || r_ptr->r_sights) - { - /* Print a message */ - if (dead) - { - /* Don't print the unique's ASCII symbol - * if use_graphics is on. */ - if (use_graphics) - { - fprintf(fff, "[[[[[R%-70s is dead]\n", - (r_name + r_ptr->name)); - } - else - { - fprintf(fff, "[[[[[%c%c] [[[[[R%-68s is dead]\n", - conv_color[r_ptr->d_attr], - r_ptr->d_char, - (r_name + r_ptr->name)); - } - } - else - { - /* Don't print the unique's ASCII symbol - * if use_graphics is on. */ - if (use_graphics) - { - fprintf(fff, "[[[[[w%-70s is alive]\n", - (r_name + r_ptr->name)); - } - else - { - fprintf(fff, "[[[[[%c%c] [[[[[w%-68s is alive]\n", - conv_color[r_ptr->d_attr], - r_ptr->d_char, - (r_name + r_ptr->name)); - } - } - } - } - } - - C_FREE(sort_uniques, max_r_idx, int); - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Known Uniques", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - -void plural_aux(char *name) -{ - int name_len = strlen(name); - - /* Hack -- Precedent must be pluralised for this one */ - if (strstr(name, "Disembodied hand")) - { - strcpy(name, "Disembodied hands that strangled people"); - } - - /* "someone of something" */ - else if (strstr(name, " of ")) - { - cptr aider = strstr(name, " of "); - char dummy[80]; - int i = 0; - cptr ctr = name; - - while (ctr < aider) - { - dummy[i] = *ctr; - ctr++; - i++; - } - - if (dummy[i - 1] == 's') - { - strcpy(&dummy[i], "es"); - i++; - } - else - { - strcpy(&dummy[i], "s"); - } - - strcpy(&dummy[i + 1], aider); - strcpy(name, dummy); - } - - /* Creeping coins */ - else if (strstr(name, "coins")) - { - char dummy[80]; - strcpy(dummy, "piles of "); - strcat(dummy, name); - strcpy(name, dummy); - return; - } - - /* Manes stay manes */ - else if (strstr(name, "Manes")) - { - return; - } - - /* Broken plurals are, well, broken */ - else if (name[name_len - 1] == 'y') - { - strcpy(&name[name_len - 1], "ies"); - } - else if (streq(&name[name_len - 4], "ouse")) - { - strcpy(&name[name_len - 4], "ice"); - } - else if (streq(&name[name_len - 6], "kelman")) - { - strcpy(&name[name_len - 6], "kelmen"); - } - else if (streq(&name[name_len - 2], "ex")) - { - strcpy(&name[name_len - 2], "ices"); - } - else if (streq(&name[name_len - 3], "olf")) - { - strcpy(&name[name_len - 3], "olves"); - } - - /* Now begins sane cases */ - else if ((streq(&name[name_len - 2], "ch")) || (name[name_len - 1] == 's')) - { - strcpy(&name[name_len], "es"); - } - else - { - strcpy(&name[name_len], "s"); - } -} - - -/* - * Display current pets - */ -static void do_cmd_knowledge_pets(void) -{ - int i; - - FILE *fff; - - monster_type *m_ptr; - - int t_friends = 0; - - int t_levels = 0; - - int show_upkeep = 0; - - int upkeep_divider = 20; - - char file_name[1024]; - - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - if (has_ability(AB_PERFECT_CASTING)) upkeep_divider = 15; - - /* Process the monsters (backwards) */ - for (i = m_max - 1; i >= 1; i--) - { - /* Access the monster */ - m_ptr = &m_list[i]; - - /* Ignore "dead" monsters */ - if (!m_ptr->r_idx) continue; - - /* Calculate "upkeep" for friendly monsters */ - if (m_ptr->status >= MSTATUS_PET) - { - char pet_name[80]; - monster_race *r_ptr = race_inf(m_ptr); - - t_friends++; - t_levels += m_ptr->level; - monster_desc(pet_name, m_ptr, 0x88); - fprintf(fff, "%s%s (%s)\n", - (r_ptr->flags1 & RF1_UNIQUE) ? "#####G" : "", - pet_name, - (m_ptr->status < MSTATUS_COMPANION) ? "pet" : "companion"); - } - } - - if (t_friends > 1 + (p_ptr->lev / (upkeep_divider))) - { - show_upkeep = (t_levels); - - if (show_upkeep > 100) show_upkeep = 100; - else if (show_upkeep < 10) show_upkeep = 10; - } - - - fprintf(fff, "----------------------------------------------\n"); - fprintf(fff, " Total: %d pet%s.\n", t_friends, (t_friends == 1 ? "" : "s")); - fprintf(fff, " Upkeep: %d%% mana.\n", show_upkeep); - - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Current Pets", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - - -/* - * Total kill count - * - * Note that the player ghosts are ignored. XXX XXX XXX - */ -static void do_cmd_knowledge_kill_count(void) -{ - int k; - - FILE *fff; - - char file_name[1024]; - - s32b Total = 0; - - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - { - /* Monsters slain */ - int kk; - - /* For all monsters */ - for (kk = 1; kk < max_r_idx; kk++) - { - monster_race *r_ptr = &r_info[kk]; - - if (r_ptr->flags1 & (RF1_UNIQUE)) - { - bool_ dead = (r_ptr->max_num == 0); - - if (dead) - { - Total++; - } - } - else - { - s16b This = r_ptr->r_pkills; - - if (This > 0) - { - Total += This; - } - } - } - - if (Total < 1) - { - fprintf(fff, "You have defeated no enemies yet.\n\n"); - } - else if (Total == 1) - { - fprintf(fff, "You have defeated one enemy.\n\n"); - } - else - { - fprintf(fff, "You have defeated " FMTs32b " enemies.\n\n", Total); - } - } - - Total = 0; - - /* Scan the monster races */ - for (k = 0; k < max_r_idx; k++) - { - monster_race *r_ptr = &r_info[k]; - - if (r_ptr->flags1 & (RF1_UNIQUE)) - { - bool_ dead = (r_ptr->max_num == 0); - - if (dead) - { - /* Print a message */ - fprintf(fff, " %s\n", - (r_name + r_ptr->name)); - Total++; - } - } - else - { - s16b This = r_ptr->r_pkills; - - if (This > 0) - { - if (This < 2) - { - if (strstr(r_name + r_ptr->name, "coins")) - { - fprintf(fff, " 1 pile of %s\n", (r_name + r_ptr->name)); - } - else - { - fprintf(fff, " 1 %s\n", (r_name + r_ptr->name)); - } - } - else - { - char to_plural[80]; - strcpy(to_plural, (r_name + r_ptr->name)); - plural_aux(to_plural); - fprintf(fff, " %d %s\n", This, to_plural); - } - - Total += This; - } - } - } - - fprintf(fff, "----------------------------------------------\n"); - fprintf(fff, " Total: " FMTs32b " creature%s killed.\n", Total, (Total == 1 ? "" : "s")); - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Kill Count", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - -/* - * Display known objects - */ -static void do_cmd_knowledge_objects(void) -{ - int k; - - FILE *fff; - - char o_name[80]; - - char file_name[1024]; - - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - /* Scan the object kinds */ - for (k = 1; k < max_k_idx; k++) - { - object_kind *k_ptr = &k_info[k]; - - /* Hack -- skip artifacts */ - if (k_ptr->flags3 & (TR3_INSTA_ART)) continue; - - /* List known flavored objects */ - if (k_ptr->flavor && k_ptr->aware) - { - object_type *i_ptr; - object_type object_type_body; - - /* Get local object */ - i_ptr = &object_type_body; - - /* Create fake object */ - object_prep(i_ptr, k); - - /* Describe the object */ - object_desc_store(o_name, i_ptr, FALSE, 0); - - /* Print a message */ - fprintf(fff, " %s\n", o_name); - } - } - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Known Objects", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - -/* - * List recall depths - */ -static void do_cmd_knowledge_dungeons(void) -{ - int y; - char file_name[1024]; - FILE *fff; - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - /* Oops */ - if (fff == NULL) return; - - /* Scan all dungeons */ - for (y = 1; y < max_d_idx; y++) - { - /* The dungeon has a valid recall depth set */ - if (max_dlv[y]) - { - /* Describe the recall depth */ - fprintf(fff, " %c%s: Level %d (%d')\n", - (p_ptr->recall_dungeon == y) ? '*' : ' ', - d_name + d_info[y].name, - max_dlv[y], 50 * (max_dlv[y])); - } - } - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Recall Depths", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - -/* - * List known towns - */ -void do_cmd_knowledge_towns(void) -{ - int i, j; - char file_name[1024]; - FILE *fff; - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - /* Oops */ - if (fff == NULL) return; - - /* Scan all dungeons */ - for (i = 0; i < max_d_idx; i++) - { - dungeon_info_type *d_ptr = &d_info[i]; - - /* Scan all dungeon town slots */ - for (j = 0; j < TOWN_DUNGEON; j++) - { - int town_idx = d_ptr->t_idx[j]; - - /* Ignore non-existent towns */ - if (!(town_info[town_idx].flags & (TOWN_REAL))) continue; - - /* Ignore unknown towns */ - if (!(town_info[town_idx].flags & (TOWN_KNOWN))) continue; - - /* Describe the dungeon town */ - fprintf(fff, " %s: Level %d (%d')\n", - d_name + d_ptr->name, - d_ptr->t_level[j], - 50 * d_ptr->t_level[j]); - } - } - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Dungeon Towns", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - -/* - * List corruptions - */ -void do_cmd_knowledge_corruptions(void) -{ - FILE *fff; - - char file_name[1024]; - - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - /* Dump the corruptions to file */ - if (fff) - { - dump_corruptions(fff, TRUE, FALSE); - } - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Corruptions", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - -/* - * Helper function for do_cmd_knowledge_quests - */ -static void insert_sort_quest(int *order, int *num, int q_idx) -{ - int i, j; - - quest_type *q_ptr = &quest[q_idx]; - - int level = q_ptr->level; - - - /* Find the place */ - for (i = 0; i < *num; i++) - { - quest_type *q2_ptr = &quest[order[i]]; - int level2 = q2_ptr->level; - - if (level < level2) break; - } - - /* Move the remaining items */ - for (j = *num - 1; j >= i; j--) - { - order[j + 1] = order[j]; - } - - /* Insert it */ - order[i] = q_idx; - (*num)++; -} - - -/* - * Print quest status of all active quests - */ -static void do_cmd_knowledge_quests(void) -{ - FILE *fff; - - char file_name[1024]; - - int order[MAX_Q_IDX] = { }; - - int num = 0; - - int i, j, z; - - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - for (i = 0; i < MAX_Q_IDX; i++) - { - insert_sort_quest(order, &num, i); - } - - for (z = 0; z < MAX_Q_IDX; z++) - { - i = order[z]; - - /* Dynamic descriptions */ - if (quest[i].gen_desc != NULL) - { - if (!quest[i].gen_desc(fff)) - { - continue; - } - } - - /* Fixed quests (only known ones) */ - else if (!quest[i].silent) - { - if (quest[i].status == QUEST_STATUS_TAKEN) - { - /* Print the quest info */ - fprintf(fff, "#####y%s (Danger level: %d)\n", - quest[i].name, quest[i].level); - - j = 0; - while ((j < 10) && (quest[i].desc[j][0] != '\0')) - { - fprintf(fff, "%s\n", quest[i].desc[j++]); - } - fprintf(fff, "\n"); - } - else if (quest[i].status == QUEST_STATUS_COMPLETED) - { - fprintf(fff , "#####G%s Completed - Unrewarded\n", quest[i].name); - fprintf(fff, "\n"); - } - } - } - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Quest status", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - -/* - * Print fate status - */ -static void do_cmd_knowledge_fates(void) -{ - FILE *fff; - - char file_name[1024]; - - - /* Temporary file */ - if (path_temp(file_name, 1024)) return; - - /* Open a new file */ - fff = my_fopen(file_name, "w"); - - dump_fates(fff); - - /* Close the file */ - my_fclose(fff); - - /* Display the file contents */ - show_file(file_name, "Fate status", 0, 0); - - /* Remove the file */ - fd_kill(file_name); -} - - -/* - * Print the note file - */ -void do_cmd_knowledge_notes(void) -{ - /* Spawn */ - show_notes_file(); - - /* Done */ - return; -} - - -/* - * Interact with "knowledge" - */ -void do_cmd_knowledge(void) -{ - int i; - - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - /* Interact until done */ - while (1) - { - /* Clear screen */ - Term_clear(); - - /* Ask for a choice */ - prt("Display current knowledge", 2, 0); - - /* Give some choices */ - prt("(1) Display known artifacts", 4, 5); - prt("(2) Display known uniques", 5, 5); - prt("(3) Display known objects", 6, 5); - prt("(4) Display kill count", 7, 5); - prt("(5) Display recall depths", 8, 5); - prt("(6) Display corruptions", 9, 5); - prt("(7) Display current pets", 10, 5); - prt("(8) Display current quests", 11, 5); - prt("(9) Display current fates", 12, 5); - prt("(0) Display known traps", 13, 5); - prt("(A) Display known dungeon towns", 14, 5); - prt("(B) Display notes", 15, 5); - - /* Prompt */ - prt("Command: ", 17, 0); - - /* Prompt */ - i = inkey(); - - /* Done */ - if (i == ESCAPE) break; - - switch (i) - { - /* Artifacts */ - case '1': - { - do_cmd_knowledge_artifacts(); - - break; - } - - /* Uniques */ - case '2': - { - do_cmd_knowledge_uniques(); - - break; - } - - /* Objects */ - case '3': - { - do_cmd_knowledge_objects(); - - break; - } - - /* Kill count */ - case '4': - { - do_cmd_knowledge_kill_count(); - - break; - } - - /* Recall depths */ - case '5': - { - do_cmd_knowledge_dungeons(); - - break; - } - - /* corruptions */ - case '6': - { - do_cmd_knowledge_corruptions(); - - break; - } - - /* Pets */ - case '7': - { - do_cmd_knowledge_pets(); - - break; - } - - /* Quests */ - case '8': - { - do_cmd_knowledge_quests(); - - break; - } - - /* Fates */ - case '9': - { - do_cmd_knowledge_fates(); - - break; - } - - /* Traps */ - case '0': - { - do_cmd_knowledge_traps(); - - break; - } - - /* Dungeon towns */ - case 'A': - case 'a': - { - do_cmd_knowledge_towns(); - - break; - } - - /* Notes */ - case 'B': - case 'b': - { - do_cmd_knowledge_notes(); - - break; - } - - /* Unknown option */ - default: - { - bell(); - - break; - } - } - - /* Flush messages */ - msg_print(NULL); - } - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; -} - - -/* - * Check on the status of an active quest -KMW- - * TODO: Spill out status when not a simple kill # monster. - */ -void do_cmd_checkquest(void) -{ - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - /* Quest info */ - do_cmd_knowledge_quests(); - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; -} - - -/* - * Change player's "tactic" setting - */ -void do_cmd_change_tactic(int i) -{ - p_ptr->tactic += i; - if (p_ptr->tactic > 8) p_ptr->tactic = 0; - if (p_ptr->tactic < 0) p_ptr->tactic = 8; - - p_ptr->update |= (PU_BONUS); - update_stuff(); - prt("", 0, 0); -} - - -/* - * Change player's "movement" setting - */ -void do_cmd_change_movement(int i) -{ - p_ptr->movement += i; - if (p_ptr->movement > 8) p_ptr->movement = 0; - if (p_ptr->movement < 0) p_ptr->movement = 8; - - p_ptr->update |= (PU_BONUS); - update_stuff(); - prt("", 0, 0); -} - - -/* - * Display the time and date - */ -void do_cmd_time() -{ - int day = bst(DAY, turn); - - int hour = bst(HOUR, turn); - - int min = bst(MINUTE, turn); - - int full = hour * 100 + min; - - char buf2[20]; - - int start = 9999; - - int end = -9999; - - int num = 0; - - char desc[1024]; - - char buf[1024]; - - FILE *fff; - - - /* Note */ - strcpy(desc, "It is a strange time."); - - /* Format time of the day */ - strnfmt(buf2, 20, get_day(bst(YEAR, turn) + START_YEAR)); - - /* Display current date in the Elvish calendar */ - msg_format("This is %s of the %s year of the third age.", - get_month_name(day, wizard, FALSE), buf2); - - /* Message */ - msg_format("The time is %d:%02d %s.", - (hour % 12 == 0) ? 12 : (hour % 12), - min, (hour < 12) ? "AM" : "PM"); - - /* Find the path */ - if (!rand_int(10) || p_ptr->image) - { - path_build(buf, 1024, ANGBAND_DIR_FILE, "timefun.txt"); - } - else - { - path_build(buf, 1024, ANGBAND_DIR_FILE, "timenorm.txt"); - } - - /* Open this file */ - fff = my_fopen(buf, "rt"); - - /* Oops */ - if (!fff) return; - - /* Find this time */ - while (!my_fgets(fff, buf, 1024)) - { - /* Ignore comments */ - if (!buf[0] || (buf[0] == '#')) continue; - - /* Ignore invalid lines */ - if (buf[1] != ':') continue; - - /* Process 'Start' */ - if (buf[0] == 'S') - { - /* Extract the starting time */ - start = atoi(buf + 2); - - /* Assume valid for an hour */ - end = start + 59; - - /* Next... */ - continue; - } - - /* Process 'End' */ - if (buf[0] == 'E') - { - /* Extract the ending time */ - end = atoi(buf + 2); - - /* Next... */ - continue; - } - - /* Ignore incorrect range */ - if ((start > full) || (full > end)) continue; - - /* Process 'Description' */ - if (buf[0] == 'D') - { - num++; - - /* Apply the randomizer */ - if (!rand_int(num)) strcpy(desc, buf + 2); - - /* Next... */ - continue; - } - } - - /* Message */ - msg_print(desc); - - /* Close the file */ - my_fclose(fff); -} - -/* - * Macro recorder! - * It records all keypresses and then put them in a macro - * Not as powerful as the macro screen, but much easier for newbies - */ -char *macro_recorder_current = NULL; -void macro_recorder_start() -{ - msg_print("Starting macro recording, press this key again to stop. Note that if the action you want to record accepts the @ key, use it; it will remove your the need to inscribe stuff."); - C_MAKE(macro_recorder_current, 1, char); - macro_recorder_current[0] = '\0'; -} - -void macro_recorder_add(char c) -{ - char *old_macro_recorder_current = macro_recorder_current; - - if (macro_recorder_current == NULL) return; - - C_MAKE(macro_recorder_current, strlen(macro_recorder_current) + 1 + 1, char); - sprintf(macro_recorder_current, "%s%c", old_macro_recorder_current, c); - C_FREE(old_macro_recorder_current, strlen(old_macro_recorder_current) + 1, char); -} - -void macro_recorder_stop() -{ - char *str, *macro; - char buf[1024]; - - /* Ok we remove the last key, because it is the key to stop recording */ - macro_recorder_current[strlen(macro_recorder_current) - 1] = '\0'; - - /* Stop the recording */ - macro = macro_recorder_current; - macro_recorder_current = NULL; - - /* Add it */ - if (get_check("Are you satisfied and want to create the macro? ")) - { - prt("Trigger: ", 0, 0); - - /* Get a macro trigger */ - do_cmd_macro_aux(buf, FALSE); - - /* Link the macro */ - macro_add(buf, macro); - - /* Prompt */ - C_MAKE(str, (strlen(macro) + 1) * 3, char); - ascii_to_text(str, macro); - msg_format("Added a macro '%s'. If you want it to stay permanently, press @ now and dump macros to a file.", str); - C_FREE(str, (strlen(macro) + 1) * 3, char); - } - - /* Ok now rid of useless stuff */ - C_FREE(macro, strlen(macro) + 1, char); -} - -void do_cmd_macro_recorder() -{ - if (macro_recorder_current == NULL) - macro_recorder_start(); - else - macro_recorder_stop(); -} diff --git a/src/cmd4.cc b/src/cmd4.cc new file mode 100644 index 00000000..e00f5835 --- /dev/null +++ b/src/cmd4.cc @@ -0,0 +1,4607 @@ +/* File: cmd4.c */ + +/* Purpose: Interface commands */ + +/* + * 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 "angband.h" + +#include "messages.h" +#include "hooks.h" + +#include +#include +#include +#include + +/* + * Hack -- redraw the screen + * + * This command performs various low level updates, clears all the "extra" + * windows, does a total redraw of the main window, and requests all of the + * interesting updates and redraws that I can think of. + * + * This command is also used to "instantiate" the results of the user + * selecting various things, such as graphics mode, so it must call + * the "TERM_XTRA_REACT" hook before redrawing the windows. + */ +void do_cmd_redraw(void) +{ + int j; + + term *old = Term; + + + /* Hack -- react to changes */ + Term_xtra(TERM_XTRA_REACT, 0); + + + /* Combine and Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + + /* Update torch */ + p_ptr->update |= (PU_TORCH); + + /* Update stuff */ + p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | + PU_SANITY | PU_BODY); + + /* Forget view */ + p_ptr->update |= (PU_UN_VIEW); + + /* Update view */ + p_ptr->update |= (PU_VIEW); + + /* Update monster light */ + p_ptr->update |= (PU_MON_LITE); + + /* Update monsters */ + p_ptr->update |= (PU_MONSTERS); + + /* Redraw everything */ + p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER | PW_M_LIST); + + /* Window stuff */ + p_ptr->window |= (PW_MESSAGE | PW_OVERHEAD | PW_MONSTER | PW_OBJECT); + + /* Hack -- update */ + handle_stuff(); + + + /* Redraw every window */ + for (j = 0; j < 8; j++) + { + /* Dead window */ + if (!angband_term[j]) continue; + + /* Activate */ + Term_activate(angband_term[j]); + + /* Redraw */ + Term_redraw(); + + /* Refresh */ + Term_fresh(); + + /* Restore */ + Term_activate(old); + } +} + + +/* + * Hack -- change name + */ +void do_cmd_change_name(void) +{ + char c; + + int mode = 0; + + char tmp[160]; + + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + /* Forever */ + while (1) + { + /* keep mode below 7 */ + mode = (mode + 6) % 6; + + /* Display the player */ + display_player(mode); + + /* Prompt */ + if (mode == 0) + { + Term_putstr(14, 22, -1, TERM_WHITE, + "['t/T' to change tactics, 'e/E' to change movement]"); + } + + Term_putstr(4, 23, -1, TERM_WHITE, + "['c' to change name, 'f' to file, 'p' for previous, 'n' for next, or ESC]"); + + /* Query */ + c = inkey(); + + /* Exit */ + if (c == ESCAPE) break; + + /* Change name */ + if (c == 'c') + { + get_name(); + } + + /* File dump */ + else if (c == 'f') + { + strnfmt(tmp, 160, "%s.txt", player_name); + if (get_string("Filename(you can post it to http://angband.oook.cz/): ", tmp, 80)) + { + if (tmp[0] && (tmp[0] != ' ')) + { + file_character(tmp, FALSE); + } + } + } + + /* Toggle mode */ + else if (c == 'n') + { + mode++; + } + else if (c == 'p') + { + mode--; + } + + else if (mode == 0) + { + /* Change tactic */ + if (c == 't') + { + (void)do_cmd_change_tactic( -1); + } + else if (c == 'T') + { + (void)do_cmd_change_tactic(1); + } + + /* Change movement */ + else if (c == 'e') + { + do_cmd_change_movement( -1); + } + else if (c == 'E') + { + do_cmd_change_movement(1); + } + else + { + bell(); + } + } + /* Oops */ + else + { + bell(); + } + + /* Flush messages */ + msg_print(NULL); + } + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; + + + /* Redraw everything */ + p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP); + + handle_stuff(); +} + + +/* + * Recall the most recent message + */ +void do_cmd_message_one(void) +{ + cptr msg = format("> %s", message_str(0)); + + /* Recall one message XXX XXX XXX */ + display_message(0, 0, strlen(msg), message_color(0), msg); +} + + +/* + * Show previous messages to the user -BEN- + * + * The screen format uses line 0 and (Term->hgt - 1) for headers and prompts, + * skips line 1 and (Term->hgt - 2), and uses line 2 thru (Term->hgt - 3) for + * old messages. + * + * This command shows you which commands you are viewing, and allows + * you to "search" for strings in the recall. + * + * Note that messages may be longer than 80 characters, but they are + * displayed using "infinite" length, with a special sub-command to + * "slide" the virtual display to the left or right. + * + * Attempt to only hilite the matching portions of the string. + * + * Now taking advantages of big-screen. -pav- + */ +void do_cmd_messages(void) +{ + int i, j, k, n; + u32b q; + int wid, hgt; + + char shower[80]; + char finder[80]; + + /* Wipe finder */ + strcpy(finder, ""); + + /* Wipe shower */ + strcpy(shower, ""); + + + /* Total messages */ + n = message_num(); + + /* Start on first message */ + i = 0; + + /* Start at leftmost edge */ + q = 0; + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + /* Process requests until done */ + while (1) + { + /* Clear screen */ + Term_clear(); + + /* Retrieve current screen size */ + Term_get_size(&wid, &hgt); + + /* Dump up to 20 (or more in bigscreen) lines of messages */ + for (j = 0; (j < (hgt - 4)) && (i + j < n); j++) + { + cptr msg = message_str(i + j); + byte color = message_color(i + j); + + /* Apply horizontal scroll */ + msg = (strlen(msg) >= q) ? (msg + q) : ""; + + /* Dump the messages, bottom to top */ + display_message(0, (hgt - 3) - j, strlen(msg), color, msg); + + /* Hilite "shower" */ + if (shower[0]) + { + cptr str = msg; + + /* Display matches */ + while ((str = strstr(str, shower)) != NULL) + { + int len = strlen(shower); + + /* Display the match */ + Term_putstr(str - msg, (hgt - 3) - j, len, TERM_YELLOW, shower); + + /* Advance */ + str += len; + } + } + } + + /* Display header XXX XXX XXX */ + prt(format("Message Recall (%d-%d of %d), Offset %d", + i, i + j - 1, n, q), 0, 0); + + /* Display prompt (not very informative) */ + prt("[Press 'p' for older, 'n' for newer, ..., or ESCAPE]", hgt - 1, 0); + + /* Get a command */ + k = inkey(); + + /* Exit on Escape */ + if (k == ESCAPE) break; + + /* Hack -- Save the old index */ + j = i; + + /* Horizontal scroll */ + if (k == '4') + { + /* Scroll left */ + q = (q >= ((u32b)wid / 2)) ? (q - wid / 2) : 0; + + /* Success */ + continue; + } + + /* Horizontal scroll */ + if (k == '6') + { + /* Scroll right */ + q = q + wid / 2; + + /* Success */ + continue; + } + + /* Hack -- handle show */ + if (k == '=') + { + /* Prompt */ + prt("Show: ", hgt - 1, 0); + + /* Get a "shower" string, or continue */ + if (!askfor_aux(shower, 80)) continue; + + /* Okay */ + continue; + } + + /* Hack -- handle find */ + if (k == '/') + { + s16b z; + + /* Prompt */ + prt("Find: ", hgt - 1, 0); + + /* Get a "finder" string, or continue */ + if (!askfor_aux(finder, 80)) continue; + + /* Show it */ + strcpy(shower, finder); + + /* Scan messages */ + for (z = i + 1; z < n; z++) + { + cptr msg = message_str(z); + + /* Search for it */ + if (strstr(msg, finder)) + { + /* New location */ + i = z; + + /* Done */ + break; + } + } + } + + /* Recall 1 older message */ + if ((k == '8') || (k == '\n') || (k == '\r')) + { + /* Go newer if legal */ + if (i + 1 < n) i += 1; + } + + /* Recall 10 older messages */ + if (k == '+') + { + /* Go older if legal */ + if (i + 10 < n) i += 10; + } + + /* Recall one screen of older messages */ + if ((k == 'p') || (k == KTRL('P')) || (k == ' ')) + { + /* Go older if legal */ + if (i + (hgt - 4) < n) i += (hgt - 4); + } + + /* Recall one screen of newer messages */ + if ((k == 'n') || (k == KTRL('N'))) + { + /* Go newer (if able) */ + i = (i >= (hgt - 4)) ? (i - (hgt - 4)) : 0; + } + + /* Recall 10 newer messages */ + if (k == '-') + { + /* Go newer (if able) */ + i = (i >= 10) ? (i - 10) : 0; + } + + /* Recall 1 newer messages */ + if (k == '2') + { + /* Go newer (if able) */ + i = (i >= 1) ? (i - 1) : 0; + } + + /* Hack -- Error of some kind */ + if (i == j) bell(); + } + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; +} + + + +/* + * Number of cheating options + */ +#define CHEAT_MAX 6 + +/* + * Cheating options + */ +static option_type cheat_info[CHEAT_MAX] = +{ + { &cheat_peek, FALSE, 0, 0, "cheat_peek", "Peek into object creation" }, + { &cheat_hear, FALSE, 0, 1, "cheat_hear", "Peek into monster creation" }, + { &cheat_room, FALSE, 0, 2, "cheat_room", "Peek into dungeon creation" }, + { &cheat_xtra, FALSE, 0, 3, "cheat_xtra", "Peek into something else" }, + { &cheat_know, FALSE, 0, 4, "cheat_know", "Know complete monster info" }, + { &cheat_live, FALSE, 0, 5, "cheat_live", "Allow player to avoid death" } +}; + +/* + * Interact with some options for cheating + */ +static void do_cmd_options_cheat(cptr info) +{ + char ch; + + int i, k = 0, n = CHEAT_MAX; + + int dir; + + char buf[80]; + + + /* Clear screen */ + Term_clear(); + + /* Interact with the player */ + while (TRUE) + { + /* Prompt XXX XXX XXX */ + strnfmt(buf, 80, "%s (RET to advance, y/n to set, ESC to accept) ", info); + prt(buf, 0, 0); + + /* Display the options */ + for (i = 0; i < n; i++) + { + byte a = TERM_WHITE; + + /* Color current option */ + if (i == k) a = TERM_L_BLUE; + + /* Display the option text */ + strnfmt(buf, 80, "%-48s: %s (%s)", + cheat_info[i].o_desc, + (*cheat_info[i].o_var ? "yes" : "no "), + cheat_info[i].o_text); + c_prt(a, buf, i + 2, 0); + } + + /* Hilite current option */ + move_cursor(k + 2, 50); + + /* Get a key */ + ch = inkey(); + + /* + * Hack -- Try to translate the key into a direction + * to allow the use of roguelike keys for navigation + */ + dir = get_keymap_dir(ch); + if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir); + + + /* Analyze */ + switch (ch) + { + case ESCAPE: + { + return; + } + + case '-': + case '8': + { + k = (n + k - 1) % n; + + break; + } + + case ' ': + case '\n': + case '\r': + case '2': + { + k = (k + 1) % n; + + break; + } + + case 'y': + case 'Y': + case '6': + { + noscore |= (cheat_info[k].o_page * 256 + cheat_info[k].o_bit); + (*cheat_info[k].o_var) = TRUE; + k = (k + 1) % n; + + break; + } + + case 'n': + case 'N': + case '4': + { + (*cheat_info[k].o_var) = FALSE; + k = (k + 1) % n; + + break; + } + + default: + { + bell(); + + break; + } + } + } +} + + +static option_type autosave_info[2] = +{ + { &autosave_l, FALSE, 0, 6, "autosave_l", "Autosave when entering new levels" }, + { &autosave_t, FALSE, 0, 7, "autosave_t", "Timed autosave" }, +}; + +s16b toggle_frequency(s16b current) +{ + if (current == 0) return (50); + if (current == 50) return (100); + if (current == 100) return (250); + if (current == 250) return (500); + if (current == 500) return (1000); + if (current == 1000) return (2500); + if (current == 2500) return (5000); + if (current == 5000) return (10000); + if (current == 10000) return (25000); + + return (0); +} + + +/* + * Interact with some options for cheating + */ +static void do_cmd_options_autosave(cptr info) +{ + char ch; + + int i, k = 0, n = 2; + + int dir; + + char buf[80]; + + + /* Clear screen */ + Term_clear(); + + /* Interact with the player */ + while (TRUE) + { + /* Prompt XXX XXX XXX */ + strnfmt(buf, 80, + "%s (RET to advance, y/n to set, 'F' for frequency, ESC to accept) ", + info); + prt(buf, 0, 0); + + /* Display the options */ + for (i = 0; i < n; i++) + { + byte a = TERM_WHITE; + + /* Color current option */ + if (i == k) a = TERM_L_BLUE; + + /* Display the option text */ + strnfmt(buf, 80, "%-48s: %s (%s)", + autosave_info[i].o_desc, + (*autosave_info[i].o_var ? "yes" : "no "), + autosave_info[i].o_text); + c_prt(a, buf, i + 2, 0); + } + + prt(format("Timed autosave frequency: every %d turns", autosave_freq), 5, 0); + + + /* Hilite current option */ + move_cursor(k + 2, 50); + + /* Get a key */ + ch = inkey(); + + /* + * Hack -- Try to translate the key into a direction + * to allow the use of roguelike keys for navigation + */ + dir = get_keymap_dir(ch); + if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir); + + /* Analyze */ + switch (ch) + { + case ESCAPE: + { + return; + } + + case '-': + case '8': + { + k = (n + k - 1) % n; + + break; + } + + case ' ': + case '\n': + case '\r': + case '2': + { + k = (k + 1) % n; + + break; + } + + case 'y': + case 'Y': + case '6': + { + + (*autosave_info[k].o_var) = TRUE; + k = (k + 1) % n; + + break; + } + + case 'n': + case 'N': + case '4': + { + (*autosave_info[k].o_var) = FALSE; + k = (k + 1) % n; + + break; + } + + case 'f': + case 'F': + { + autosave_freq = toggle_frequency(autosave_freq); + prt(format("Timed autosave frequency: every %d turns", + autosave_freq), 5, 0); + + break; + } + + default: + { + bell(); + + break; + } + } + } +} + +/* Switch an option by only knowing its name */ +bool_ change_option(cptr name, bool_ value) +{ + int i; + + /* Scan the options */ + for (i = 0; option_info[i].o_desc; i++) + { + if (!strcmp(option_info[i].o_text, name)) + { + bool_ old = (*option_info[i].o_var); + + (*option_info[i].o_var) = value; + + return old; + } + } + + cmsg_format(TERM_VIOLET, "Warning, change_option couldn't find option '%s'.", name); + return FALSE; +} + +/* + * Interact with some options + */ +void do_cmd_options_aux(int page, cptr info, bool_ read_only) +{ + char ch; + + int i, k = 0, n = 0; + + int dir; + + int opt[24]; + + char buf[80]; + + + /* Lookup the options */ + for (i = 0; i < 24; i++) opt[i] = 0; + + /* Scan the options */ + for (i = 0; option_info[i].o_desc; i++) + { + /* Notice options on this "page" */ + if (option_info[i].o_page == page) opt[n++] = i; + } + + + /* Clear screen */ + Term_clear(); + + /* Interact with the player */ + while (TRUE) + { + /* Prompt XXX XXX XXX */ + strnfmt(buf, 80, "%s (RET to advance, y/n to set, ESC to accept) ", info); + prt(buf, 0, 0); + + /* Display the options */ + for (i = 0; i < n; i++) + { + byte a = TERM_WHITE; + + /* Color current option */ + if (i == k) a = TERM_L_BLUE; + + /* Display the option text */ + strnfmt(buf, 80, "%-48s: %s (%s)", + option_info[opt[i]].o_desc, + (*option_info[opt[i]].o_var ? "yes" : "no "), + option_info[opt[i]].o_text); + c_prt(a, buf, i + 2, 0); + } + + /* Hilite current option */ + move_cursor(k + 2, 50); + + /* Get a key */ + ch = inkey(); + + /* + * Hack -- Try to translate the key into a direction + * to allow the use of roguelike keys for navigation + */ + dir = get_keymap_dir(ch); + if ((dir == 2) || (dir == 4) || (dir == 6) || (dir == 8)) ch = I2D(dir); + + /* Analyze */ + switch (ch) + { + case ESCAPE: + { + return; + } + + case '-': + case '8': + { + k = (n + k - 1) % n; + + break; + } + + case ' ': + case '\n': + case '\r': + case '2': + { + k = (k + 1) % n; + + break; + } + + case 'y': + case 'Y': + case '6': + { + if (read_only) break; + + (*option_info[opt[k]].o_var) = TRUE; + k = (k + 1) % n; + + break; + } + + case 'n': + case 'N': + case '4': + { + if (read_only) break; + + (*option_info[opt[k]].o_var) = FALSE; + k = (k + 1) % n; + + break; + } + + default: + { + bell(); + + break; + } + } + } +} + + +/* + * Modify the "window" options + */ +static void do_cmd_options_win(void) +{ + int i, j, d; + + int y = 0; + + int x = 0; + + char ch; + + bool_ go = TRUE; + + u32b old_flag[8]; + + + /* Memorize old flags */ + for (j = 0; j < 8; j++) + { + /* Acquire current flags */ + old_flag[j] = window_flag[j]; + } + + + /* Clear screen */ + Term_clear(); + + /* Interact */ + while (go) + { + /* Prompt XXX XXX XXX */ + prt("Window Flags (, t, y, n, ESC) ", 0, 0); + + /* Display the windows */ + for (j = 0; j < 8; j++) + { + byte a = TERM_WHITE; + + cptr s = angband_term_name[j]; + + /* Use color */ + if (j == x) a = TERM_L_BLUE; + + /* Window name, staggered, centered */ + Term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s); + } + + /* Display the options */ + for (i = 0; i < 16; i++) + { + byte a = TERM_WHITE; + + cptr str = window_flag_desc[i]; + + /* Use color */ + if (i == y) a = TERM_L_BLUE; + + /* Unused option */ + if (!str) str = "(Unused option)"; + + /* Flag name */ + Term_putstr(0, i + 5, -1, a, str); + + /* Display the windows */ + for (j = 0; j < 8; j++) + { + byte a = TERM_WHITE; + + char c = '.'; + + /* Use color */ + if ((i == y) && (j == x)) a = TERM_L_BLUE; + + /* Active flag */ + if (window_flag[j] & (1L << i)) c = 'X'; + + /* Flag value */ + Term_putch(35 + j * 5, i + 5, a, c); + } + } + + /* Place Cursor */ + Term_gotoxy(35 + x * 5, y + 5); + + /* Get key */ + ch = inkey(); + + /* Analyze */ + switch (ch) + { + case ESCAPE: + { + go = FALSE; + + break; + } + + case 'T': + case 't': + { + /* Clear windows */ + for (j = 0; j < 8; j++) + { + window_flag[j] &= ~(1L << y); + } + + /* Clear flags */ + for (i = 0; i < 16; i++) + { + window_flag[x] &= ~(1L << i); + } + + /* Fall through */ + } + + case 'y': + case 'Y': + { + /* Ignore screen */ + if (x == 0) break; + + /* Set flag */ + window_flag[x] |= (1L << y); + + break; + } + + case 'n': + case 'N': + { + /* Clear flag */ + window_flag[x] &= ~(1L << y); + + break; + } + + default: + { + d = get_keymap_dir(ch); + + x = (x + ddx[d] + 8) % 8; + y = (y + ddy[d] + 16) % 16; + + if (!d) bell(); + + break; + } + } + } + + /* Notice changes */ + for (j = 0; j < 8; j++) + { + term *old = Term; + + /* Dead window */ + if (!angband_term[j]) continue; + + /* Ignore non-changes */ + if (window_flag[j] == old_flag[j]) continue; + + /* Activate */ + Term_activate(angband_term[j]); + + /* Erase */ + Term_clear(); + + /* Refresh */ + Term_fresh(); + + /* Restore */ + Term_activate(old); + } +} + + +/* + * Write all current options to the given preference file in the + * lib/user directory. Modified from KAmband 1.8. + */ +static errr option_dump(cptr fname) +{ + int i, j; + + FILE *fff; + + char buf[1024]; + + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, fname); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Append to the file */ + fff = my_fopen(buf, "a"); + + /* Failure */ + if (!fff) return ( -1); + + + /* Skip some lines */ + fprintf(fff, "\n\n"); + + /* Start dumping */ + fprintf(fff, "# Automatic option dump\n\n"); + + /* Dump options (skip cheat, adult, score) */ + for (i = 0; option_info[i].o_var != NULL; i++) + { + /* Require a real option */ + if (!option_info[i].o_text) continue; + + /* No birth options */ + if (option_info[i].o_page == 6) continue; + + /* Comment */ + fprintf(fff, "# Option '%s'\n", option_info[i].o_desc); + + /* Dump the option */ + if ((*option_info[i].o_var)) + { + fprintf(fff, "Y:%s\n", option_info[i].o_text); + } + else + { + fprintf(fff, "X:%s\n", option_info[i].o_text); + } + + /* Skip a line */ + fprintf(fff, "\n"); + } + + /* Dump window flags */ + for (i = 1; i < ANGBAND_TERM_MAX; i++) + { + /* Require a real window */ + if (!angband_term[i]) continue; + + /* Check each flag */ + for (j = 0; j < 32; j++) + { + /* Require a real flag */ + if (!window_flag_desc[j]) continue; + + /* Comment */ + fprintf(fff, "# Window '%s', Flag '%s'\n", + angband_term_name[i], window_flag_desc[j]); + + /* Dump the flag */ + if (window_flag[i] & (1L << j)) + { + fprintf(fff, "W:%d:%d:1\n", i, j); + } + else + { + fprintf(fff, "W:%d:%d:0\n", i, j); + } + + /* Skip a line */ + fprintf(fff, "\n"); + } + } + + /* Close */ + my_fclose(fff); + + /* Success */ + return (0); +} + + +/* + * Ask for a "user pref file" and process it. + * + * This function should only be used by standard interaction commands, + * in which a standard "Command:" prompt is present on the given row. + * + * Allow absolute file names? XXX XXX XXX + */ +static void do_cmd_pref_file_hack(int row) +{ + char ftmp[80]; + + + /* Prompt */ + prt("Command: Load a user pref file", row, 0); + + /* Prompt */ + prt("File: ", row + 2, 0); + + /* Default filename */ + strnfmt(ftmp, 80, "%s.prf", player_base); + + /* Ask for a file (or cancel) */ + if (!askfor_aux(ftmp, 80)) return; + + /* Process the given filename */ + if (process_pref_file(ftmp)) + { + /* Mention failure */ + msg_format("Failed to load '%s'!", ftmp); + } + else + { + /* Mention success */ + msg_format("Loaded '%s'.", ftmp); + } +} + + +/* + * Set or unset various options. + * + * The user must use the "Ctrl-R" command to "adapt" to changes + * in any options which control "visual" aspects of the game. + */ +void do_cmd_options(void) +{ + int k; + + + /* Save the screen */ + screen_save(); + + /* Interact */ + while (1) + { + /* Clear screen */ + Term_clear(); + + /* Why are we here */ + prt("Options", 2, 0); + + /* Give some choices */ + prt("(1) User Interface Options", 4, 5); + prt("(2) Disturbance Options", 5, 5); + prt("(3) Game-Play Options", 6, 5); + prt("(4) Efficiency Options", 7, 5); + prt("(5) ToME Options", 8, 5); + prt("(6) Birth Options(read only)", 9, 5); + + /* Special choices */ + prt("(D) Base Delay Factor", 10, 5); + prt("(H) Hitpoint Warning", 11, 5); + prt("(A) Autosave Options", 12, 5); + + /* Automatizer */ + prt("(T) Automatizer", 14, 5); + + + /* Window flags */ + prt("(W) Window Flags", 16, 5); + + /* Cheating */ + prt("(C) Cheating Options", 18, 5); + + /* Dump */ + prt("(U) Dump Options setting", 20, 5); + prt("(O) Load Options setting", 21, 5); + + /* Prompt */ + prt("Command: ", 22, 0); + + /* Get command */ + k = inkey(); + + /* Exit */ + if (k == ESCAPE) break; + + /* Analyze */ + switch (k) + { + /* Load a user pref file */ + case 'o': + case 'O': + { + /* Ask for and load a user pref file */ + do_cmd_pref_file_hack(21); + + break; + } + + /* Append options to a file */ + case 'u': + case 'U': + { + char ftmp[80]; + + /* Prompt */ + prt("Command: Append options to a file", 21, 0); + + /* Prompt */ + prt("File: ", 21, 0); + + /* Default filename */ + strnfmt(ftmp, 80, "%s.prf", player_base); + + /* Ask for a file */ + if (!askfor_aux(ftmp, 80)) continue; + + /* Dump the options */ + if (option_dump(ftmp)) + { + /* Failure */ + msg_print("Failed!"); + } + else + { + /* Success */ + msg_print("Done."); + } + + break; + } + + /* General Options */ + case '1': + { + /* Process the general options */ + do_cmd_options_aux(1, "User Interface Options", FALSE); + + break; + } + + /* Disturbance Options */ + case '2': + { + /* Spawn */ + do_cmd_options_aux(2, "Disturbance Options", FALSE); + + break; + } + + /* Inventory Options */ + case '3': + { + /* Spawn */ + do_cmd_options_aux(3, "Game-Play Options", FALSE); + + break; + } + + /* Efficiency Options */ + case '4': + { + /* Spawn */ + do_cmd_options_aux(4, "Efficiency Options", FALSE); + + break; + } + + /* ToME Options */ + case '5': + { + do_cmd_options_aux(5, "ToME Options", FALSE); + + break; + } + + /* Birth Options - read only */ + case '6': + { + do_cmd_options_aux(6, "Birth Options(read only)", TRUE); + + break; + } + /* Cheating Options */ + case 'C': + { + /* Spawn */ + do_cmd_options_cheat("Cheaters never win"); + + break; + } + + case 't': + case 'T': + { + do_cmd_automatizer(); + break; + } + + case 'a': + case 'A': + { + do_cmd_options_autosave("Autosave"); + + break; + } + + /* Window flags */ + case 'W': + case 'w': + { + /* Spawn */ + do_cmd_options_win(); + + break; + } + + /* Hack -- Delay Speed */ + case 'D': + case 'd': + { + /* Prompt */ + prt("Command: Base Delay Factor", 21, 0); + + /* Get a new value */ + while (1) + { + int msec = delay_factor * delay_factor * delay_factor; + prt(format("Current base delay factor: %d (%d msec)", + delay_factor, msec), 22, 0); + prt("Delay Factor (0-9 or ESC to accept): ", 23, 0); + k = inkey(); + if (k == ESCAPE) break; + if (isdigit(k)) delay_factor = D2I(k); + else bell(); + } + + break; + } + + /* Hack -- hitpoint warning factor */ + case 'H': + case 'h': + { + /* Prompt */ + prt("Command: Hitpoint Warning", 18, 0); + + /* Get a new value */ + while (1) + { + prt(format("Current hitpoint warning: %d0%%", + hitpoint_warn), 22, 0); + prt("Hitpoint Warning (0-9 or ESC to accept): ", 20, 0); + k = inkey(); + if (k == ESCAPE) break; + if (isdigit(k)) hitpoint_warn = D2I(k); + else bell(); + } + + break; + } + + /* Unknown option */ + default: + { + /* Oops */ + bell(); + + break; + } + } + + /* Flush messages */ + msg_print(NULL); + } + + + /* Restore the screen */ + screen_load(); +} + + + +/* + * Ask for a "user pref line" and process it + * + * XXX XXX XXX Allow absolute file names? + */ +void do_cmd_pref(void) +{ + char buf[80]; + + + /* Default */ + strcpy(buf, ""); + + /* Ask for a "user pref command" */ + if (!get_string("Pref: ", buf, 80)) return; + + /* Process that pref command */ + (void)process_pref_file_aux(buf); +} + + +/* + * Hack -- append all current macros to the given file + */ +static errr macro_dump(cptr fname) +{ + int i; + + FILE *fff; + + char buf[1024]; + + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, fname); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Append to the file */ + fff = my_fopen(buf, "a"); + + /* Failure */ + if (!fff) return ( -1); + + + /* Skip space */ + fprintf(fff, "\n\n"); + + /* Start dumping */ + fprintf(fff, "# Automatic macro dump\n\n"); + + /* Dump them */ + for (i = 0; i < macro__num; i++) + { + /* Start the macro */ + fprintf(fff, "# Macro '%d'\n\n", i); + + /* Extract the action */ + ascii_to_text(buf, macro__act[i]); + + /* Dump the macro */ + fprintf(fff, "A:%s\n", buf); + + /* Extract the action */ + ascii_to_text(buf, macro__pat[i]); + + /* Dump normal macros */ + fprintf(fff, "P:%s\n", buf); + + /* End the macro */ + fprintf(fff, "\n\n"); + } + + /* Start dumping */ + fprintf(fff, "\n\n\n\n"); + + + /* Close */ + my_fclose(fff); + + /* Success */ + return (0); +} + + +/* + * Hack -- ask for a "trigger" (see below) + * + * Note the complex use of the "inkey()" function from "util.c". + * + * Note that both "flush()" calls are extremely important. + */ +static void do_cmd_macro_aux(char *buf, bool_ macro_screen) +{ + int i, n = 0; + + char tmp[1024]; + + + /* Flush */ + flush(); + + /* Do not process macros */ + inkey_base = TRUE; + + /* First key */ + i = inkey(); + + /* Read the pattern */ + while (i) + { + /* Save the key */ + buf[n++] = i; + + /* Do not process macros */ + inkey_base = TRUE; + + /* Do not wait for keys */ + inkey_scan = TRUE; + + /* Attempt to read a key */ + i = inkey(); + } + + /* Terminate */ + buf[n] = '\0'; + + /* Flush */ + flush(); + + + if (macro_screen) + { + /* Convert the trigger */ + ascii_to_text(tmp, buf); + + /* Hack -- display the trigger */ + Term_addstr( -1, TERM_WHITE, tmp); + } +} + +/* + * Hack -- ask for a keymap "trigger" (see below) + * + * Note that both "flush()" calls are extremely important. This may + * no longer be true, since "util.c" is much simpler now. XXX XXX XXX + */ +static void do_cmd_macro_aux_keymap(char *buf) +{ + char tmp[1024]; + + + /* Flush */ + flush(); + + + /* Get a key */ + buf[0] = inkey(); + buf[1] = '\0'; + + + /* Convert to ascii */ + ascii_to_text(tmp, buf); + + /* Hack -- display the trigger */ + Term_addstr( -1, TERM_WHITE, tmp); + + + /* Flush */ + flush(); +} + + +/* + * Hack -- append all keymaps to the given file + */ +static errr keymap_dump(cptr fname) +{ + int i; + + FILE *fff; + + char key[1024]; + char buf[1024]; + + int mode; + + + /* Keymap mode */ + mode = get_keymap_mode(); + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, fname); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Append to the file */ + fff = my_fopen(buf, "a"); + + /* Failure */ + if (!fff) return ( -1); + + + /* Skip space */ + fprintf(fff, "\n\n"); + + /* Start dumping */ + fprintf(fff, "# Automatic keymap dump\n\n"); + + /* Dump them */ + for (i = 0; i < 256; i++) + { + cptr act; + + /* Loop up the keymap */ + act = keymap_act[mode][i]; + + /* Skip empty keymaps */ + if (!act) continue; + + /* Encode the key */ + buf[0] = i; + buf[1] = '\0'; + ascii_to_text(key, buf); + + /* Encode the action */ + ascii_to_text(buf, act); + + /* Dump the macro */ + fprintf(fff, "A:%s\n", buf); + fprintf(fff, "C:%d:%s\n", mode, key); + } + + /* Start dumping */ + fprintf(fff, "\n\n\n"); + + + /* Close */ + my_fclose(fff); + + /* Success */ + return (0); +} + + + +/* + * Interact with "macros" + * + * Note that the macro "action" must be defined before the trigger. + * + * Could use some helpful instructions on this page. XXX XXX XXX + */ +void do_cmd_macros(void) +{ + int i; + + char tmp[1024]; + + char buf[1024]; + + int mode; + + + /* Keymap mode */ + mode = get_keymap_mode(); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save screen */ + Term_save(); + + + /* Process requests until done */ + while (1) + { + /* Clear screen */ + Term_clear(); + + /* Describe */ + prt("Interact with Macros", 2, 0); + + + /* Describe that action */ + prt("Current action (if any) shown below:", 20, 0); + + /* Analyze the current action */ + ascii_to_text(buf, macro__buf); + + /* Display the current action */ + prt(buf, 22, 0); + + + /* Selections */ + prt("(1) Load a user pref file", 4, 5); + prt("(2) Append macros to a file", 5, 5); + prt("(3) Query a macro", 6, 5); + prt("(4) Create a macro", 7, 5); + prt("(5) Remove a macro", 8, 5); + prt("(6) Append keymaps to a file", 9, 5); + prt("(7) Query a keymap", 10, 5); + prt("(8) Create a keymap", 11, 5); + prt("(9) Remove a keymap", 12, 5); + prt("(0) Enter a new action", 13, 5); + + /* Prompt */ + prt("Command: ", 16, 0); + + /* Get a command */ + i = inkey(); + + /* Leave */ + if (i == ESCAPE) break; + + /* Load a 'macro' file */ + else if (i == '1') + { + /* Prompt */ + prt("Command: Load a user pref file", 16, 0); + + /* Prompt */ + prt("File: ", 18, 0); + + /* Default filename */ + strnfmt(tmp, 1024, "%s.prf", player_name); + + /* Ask for a file */ + if (!askfor_aux(tmp, 80)) continue; + + /* Process the given filename */ + if (0 != process_pref_file(tmp)) + { + /* Prompt */ + msg_print("Could not load file!"); + } + } + + /* Save macros */ + else if (i == '2') + { + /* Prompt */ + prt("Command: Append macros to a file", 16, 0); + + /* Prompt */ + prt("File: ", 18, 0); + + /* Default filename */ + strnfmt(tmp, 1024, "%s.prf", player_name); + + /* Ask for a file */ + if (!askfor_aux(tmp, 80)) continue; + + /* Dump the macros */ + (void)macro_dump(tmp); + + /* Prompt */ + msg_print("Appended macros."); + } + + /* Query a macro */ + else if (i == '3') + { + int k; + + /* Prompt */ + prt("Command: Query a macro", 16, 0); + + /* Prompt */ + prt("Trigger: ", 18, 0); + + /* Get a macro trigger */ + do_cmd_macro_aux(buf, TRUE); + + /* Acquire action */ + k = macro_find_exact(buf); + + /* Nothing found */ + if (k < 0) + { + /* Prompt */ + msg_print("Found no macro."); + } + + /* Found one */ + else + { + /* Obtain the action */ + strcpy(macro__buf, macro__act[k]); + + /* Analyze the current action */ + ascii_to_text(buf, macro__buf); + + /* Display the current action */ + prt(buf, 22, 0); + + /* Prompt */ + msg_print("Found a macro."); + } + } + + /* Create a macro */ + else if (i == '4') + { + /* Prompt */ + prt("Command: Create a macro", 16, 0); + + /* Prompt */ + prt("Trigger: ", 18, 0); + + /* Get a macro trigger */ + do_cmd_macro_aux(buf, TRUE); + + /* Clear */ + clear_from(20); + + /* Prompt */ + prt("Action: ", 20, 0); + + /* Convert to text */ + ascii_to_text(tmp, macro__buf); + + /* Get an encoded action */ + if (askfor_aux(tmp, 80)) + { + /* Convert to ascii */ + text_to_ascii(macro__buf, tmp); + + /* Link the macro */ + macro_add(buf, macro__buf); + + /* Prompt */ + msg_print("Added a macro."); + } + } + + /* Remove a macro */ + else if (i == '5') + { + /* Prompt */ + prt("Command: Remove a macro", 16, 0); + + /* Prompt */ + prt("Trigger: ", 18, 0); + + /* Get a macro trigger */ + do_cmd_macro_aux(buf, TRUE); + + /* Link the macro */ + macro_add(buf, buf); + + /* Prompt */ + msg_print("Removed a macro."); + } + + /* Save keymaps */ + else if (i == '6') + { + /* Prompt */ + prt("Command: Append keymaps to a file", 16, 0); + + /* Prompt */ + prt("File: ", 18, 0); + + /* Default filename */ + strnfmt(tmp, 1024, "%s.prf", player_name); + + /* Ask for a file */ + if (!askfor_aux(tmp, 80)) continue; + + /* Dump the macros */ + (void)keymap_dump(tmp); + + /* Prompt */ + msg_print("Appended keymaps."); + } + + /* Query a keymap */ + else if (i == '7') + { + cptr act; + + /* Prompt */ + prt("Command: Query a keymap", 16, 0); + + /* Prompt */ + prt("Keypress: ", 18, 0); + + /* Get a keymap trigger */ + do_cmd_macro_aux_keymap(buf); + + /* Look up the keymap */ + act = keymap_act[mode][(byte)(buf[0])]; + + /* Nothing found */ + if (!act) + { + /* Prompt */ + msg_print("Found no keymap."); + } + + /* Found one */ + else + { + /* Obtain the action */ + strcpy(macro__buf, act); + + /* Analyze the current action */ + ascii_to_text(buf, macro__buf); + + /* Display the current action */ + prt(buf, 22, 0); + + /* Prompt */ + msg_print("Found a keymap."); + } + } + + /* Create a keymap */ + else if (i == '8') + { + /* Prompt */ + prt("Command: Create a keymap", 16, 0); + + /* Prompt */ + prt("Keypress: ", 18, 0); + + /* Get a keymap trigger */ + do_cmd_macro_aux_keymap(buf); + + /* Clear */ + clear_from(20); + + /* Prompt */ + prt("Action: ", 20, 0); + + /* Convert to text */ + ascii_to_text(tmp, macro__buf); + + /* Get an encoded action */ + if (askfor_aux(tmp, 80)) + { + /* Convert to ascii */ + text_to_ascii(macro__buf, tmp); + + /* Free old keymap */ + string_free(keymap_act[mode][(byte)(buf[0])]); + + /* Make new keymap */ + keymap_act[mode][(byte)(buf[0])] = string_make(macro__buf); + + /* Prompt */ + msg_print("Added a keymap."); + } + } + + /* Remove a keymap */ + else if (i == '9') + { + /* Prompt */ + prt("Command: Remove a keymap", 16, 0); + + /* Prompt */ + prt("Keypress: ", 18, 0); + + /* Get a keymap trigger */ + do_cmd_macro_aux_keymap(buf); + + /* Free old keymap */ + string_free(keymap_act[mode][(byte)(buf[0])]); + + /* Make new keymap */ + keymap_act[mode][(byte)(buf[0])] = NULL; + + /* Prompt */ + msg_print("Removed a keymap."); + } + + /* Enter a new action */ + else if (i == '0') + { + /* Prompt */ + prt("Command: Enter a new action", 16, 0); + + /* Go to the correct location */ + Term_gotoxy(0, 22); + + /* Hack -- limit the value */ + tmp[80] = '\0'; + + /* Get an encoded action */ + if (!askfor_aux(buf, 80)) continue; + + /* Extract an action */ + text_to_ascii(macro__buf, buf); + } + + /* Oops */ + else + { + /* Oops */ + bell(); + } + + /* Flush messages */ + msg_print(NULL); + } + + /* Load screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; +} + + +/* + * Interact with "visuals" + */ +void do_cmd_visuals(void) +{ + int i; + + FILE *fff; + + char tmp[160]; + + char buf[1024]; + + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + + /* Interact until done */ + while (1) + { + /* Clear screen */ + Term_clear(); + + /* Ask for a choice */ + prt("Interact with Visuals", 2, 0); + + /* Give some choices */ + prt("(1) Load a user pref file", 4, 5); + prt("(2) Dump monster attr/chars", 5, 5); + prt("(3) Dump object attr/chars", 6, 5); + prt("(4) Dump feature attr/chars", 7, 5); + prt("(5) (unused)", 8, 5); + prt("(6) Change monster attr/chars", 9, 5); + prt("(7) Change object attr/chars", 10, 5); + prt("(8) Change feature attr/chars", 11, 5); + prt("(9) (unused)", 12, 5); + prt("(0) Reset visuals", 13, 5); + + /* Prompt */ + prt("Command: ", 15, 0); + + /* Prompt */ + i = inkey(); + + /* Done */ + if (i == ESCAPE) break; + + /* Load a 'pref' file */ + else if (i == '1') + { + /* Prompt */ + prt("Command: Load a user pref file", 15, 0); + + /* Prompt */ + prt("File: ", 17, 0); + + /* Default filename */ + strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); + + /* Query */ + if (!askfor_aux(tmp, 70)) continue; + + /* Process the given filename */ + (void)process_pref_file(tmp); + } + + /* Dump monster attr/chars */ + else if (i == '2') + { + /* Prompt */ + prt("Command: Dump monster attr/chars", 15, 0); + + /* Prompt */ + prt("File: ", 17, 0); + + /* Default filename */ + strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); + + /* Get a filename */ + if (!askfor_aux(tmp, 70)) continue; + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, tmp); + + /* Append to the file */ + fff = my_fopen(buf, "a"); + + /* Failure */ + if (!fff) continue; + + /* Start dumping */ + fprintf(fff, "\n\n"); + fprintf(fff, "# Monster attr/char definitions\n\n"); + + /* Dump monsters */ + for (i = 0; i < max_r_idx; i++) + { + monster_race *r_ptr = &r_info[i]; + + /* Skip non-entries */ + if (!r_ptr->name) continue; + + /* Dump a comment */ + fprintf(fff, "# %s\n", (r_name + r_ptr->name)); + + /* Dump the monster attr/char info */ + fprintf(fff, "R:%d:0x%02X:0x%02X\n\n", i, + static_cast(r_ptr->x_attr), + static_cast(r_ptr->x_char)); + } + + /* All done */ + fprintf(fff, "\n\n\n\n"); + + /* Close */ + my_fclose(fff); + + /* Message */ + msg_print("Dumped monster attr/chars."); + } + + /* Dump object attr/chars */ + else if (i == '3') + { + /* Prompt */ + prt("Command: Dump object attr/chars", 15, 0); + + /* Prompt */ + prt("File: ", 17, 0); + + /* Default filename */ + strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); + + /* Get a filename */ + if (!askfor_aux(tmp, 70)) continue; + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, tmp); + + /* Append to the file */ + fff = my_fopen(buf, "a"); + + /* Failure */ + if (!fff) continue; + + /* Start dumping */ + fprintf(fff, "\n\n"); + fprintf(fff, "# Object attr/char definitions\n\n"); + + /* Dump objects */ + for (i = 0; i < max_k_idx; i++) + { + object_kind *k_ptr = &k_info[i]; + + /* Skip non-entries */ + if (!k_ptr->name) continue; + + /* Dump a comment */ + fprintf(fff, "# %s\n", (k_name + k_ptr->name)); + + /* Dump the object attr/char info */ + fprintf(fff, "K:%d:0x%02X:0x%02X\n\n", i, + (byte)(k_ptr->x_attr), (byte)(k_ptr->x_char)); + } + + /* All done */ + fprintf(fff, "\n\n\n\n"); + + /* Close */ + my_fclose(fff); + + /* Message */ + msg_print("Dumped object attr/chars."); + } + + /* Dump feature attr/chars */ + else if (i == '4') + { + /* Prompt */ + prt("Command: Dump feature attr/chars", 15, 0); + + /* Prompt */ + prt("File: ", 17, 0); + + /* Default filename */ + strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); + + /* Get a filename */ + if (!askfor_aux(tmp, 70)) continue; + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, tmp); + + /* Append to the file */ + fff = my_fopen(buf, "a"); + + /* Failure */ + if (!fff) continue; + + /* Start dumping */ + fprintf(fff, "\n\n"); + fprintf(fff, "# Feature attr/char definitions\n\n"); + + /* Dump features */ + for (i = 0; i < max_f_idx; i++) + { + feature_type *f_ptr = &f_info[i]; + + /* Skip non-entries */ + if (!f_ptr->name) continue; + + /* Dump a comment */ + fprintf(fff, "# %s\n", (f_name + f_ptr->name)); + + /* Dump the feature attr/char info */ + fprintf(fff, "F:%d:0x%02X:0x%02X\n\n", i, + (byte)(f_ptr->x_attr), (byte)(f_ptr->x_char)); + } + + /* All done */ + fprintf(fff, "\n\n\n\n"); + + /* Close */ + my_fclose(fff); + + /* Message */ + msg_print("Dumped feature attr/chars."); + } + + /* Modify monster attr/chars */ + else if (i == '6') + { + static int r = 0; + + /* Prompt */ + prt("Command: Change monster attr/chars", 15, 0); + + /* Hack -- query until done */ + while (1) + { + monster_race *r_ptr = &r_info[r]; + + byte da = (r_ptr->d_attr); + char dc = (r_ptr->d_char); + byte ca = (r_ptr->x_attr); + char cc = (r_ptr->x_char); + + /* Label the object */ + Term_putstr(5, 17, -1, TERM_WHITE, + format("Monster = %d, Name = %-40.40s", + r, (r_name + r_ptr->name))); + + /* Label the Default values */ + Term_putstr(10, 19, -1, TERM_WHITE, + format("Default attr/char = %3u / %3u", da, (dc & 0xFF))); + Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>"); + Term_putch(43, 19, da, dc); + if (use_bigtile) + { + if (da & 0x80) + Term_putch(44, 19, 255, 255); + else + Term_putch(44, 19, 0, ' '); + } + + /* Label the Current values */ + Term_putstr(10, 20, -1, TERM_WHITE, + format("Current attr/char = %3u / %3u", ca, (cc & 0xFF))); + Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>"); + Term_putch(43, 20, ca, cc); + if (use_bigtile) + { + if (ca & 0x80) + Term_putch(44, 20, 255, 255); + else + Term_putch(44, 20, 0, ' '); + } + + /* Prompt */ + Term_putstr(0, 22, -1, TERM_WHITE, + "Command (n/N/a/A/c/C): "); + + /* Get a command */ + i = inkey(); + + /* All done */ + if (i == ESCAPE) break; + + /* Analyze */ + if (i == 'n') r = (r + max_r_idx + 1) % max_r_idx; + if (i == 'N') r = (r + max_r_idx - 1) % max_r_idx; + if (i == 'a') r_ptr->x_attr = (ca + 1); + if (i == 'A') r_ptr->x_attr = (ca - 1); + if (i == 'c') r_ptr->x_char = (cc + 1); + if (i == 'C') r_ptr->x_char = (cc - 1); + } + } + + /* Modify object attr/chars */ + else if (i == '7') + { + static int k = 0; + + /* Prompt */ + prt("Command: Change object attr/chars", 15, 0); + + /* Hack -- query until done */ + while (1) + { + object_kind *k_ptr = &k_info[k]; + + byte da = k_ptr->d_attr; + char dc = k_ptr->d_char; + byte ca = k_ptr->x_attr; + char cc = k_ptr->x_char; + + /* Label the object */ + Term_putstr(5, 17, -1, TERM_WHITE, + format("Object = %d, Name = %-40.40s", + k, (k_name + k_ptr->name))); + + /* Label the Default values */ + Term_putstr(10, 19, -1, TERM_WHITE, + format("Default attr/char = %3u / %3u", da, (dc & 0xFF))); + Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>"); + Term_putch(43, 19, da, dc); + if (use_bigtile) + { + if (da & 0x80) + Term_putch(44, 19, 255, 255); + else + Term_putch(44, 19, 0, ' '); + } + + /* Label the Current values */ + Term_putstr(10, 20, -1, TERM_WHITE, + format("Current attr/char = %3u / %3u", ca, (cc & 0xFF))); + Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>"); + Term_putch(43, 20, ca, cc); + if (use_bigtile) + { + if (ca & 0x80) + Term_putch(44, 20, 255, 255); + else + Term_putch(44, 20, 0, ' '); + } + + /* Prompt */ + Term_putstr(0, 22, -1, TERM_WHITE, + "Command (n/N/a/A/c/C): "); + + /* Get a command */ + i = inkey(); + + /* All done */ + if (i == ESCAPE) break; + + /* Analyze */ + if (i == 'n') k = (k + max_k_idx + 1) % max_k_idx; + if (i == 'N') k = (k + max_k_idx - 1) % max_k_idx; + if (i == 'a') k_info[k].x_attr = (ca + 1); + if (i == 'A') k_info[k].x_attr = (ca - 1); + if (i == 'c') k_info[k].x_char = (cc + 1); + if (i == 'C') k_info[k].x_char = (cc - 1); + } + } + + /* Modify feature attr/chars */ + else if (i == '8') + { + static int f = 0; + + /* Prompt */ + prt("Command: Change feature attr/chars", 15, 0); + + /* Hack -- query until done */ + while (1) + { + feature_type *f_ptr = &f_info[f]; + + byte da = f_ptr->d_attr; + char dc = f_ptr->d_char; + byte ca = f_ptr->x_attr; + char cc = f_ptr->x_char; + + /* Label the object */ + Term_putstr(5, 17, -1, TERM_WHITE, + format("Terrain = %d, Name = %-40.40s", + f, (f_name + f_ptr->name))); + + /* Label the Default values */ + Term_putstr(10, 19, -1, TERM_WHITE, + format("Default attr/char = %3u / %3u", da, (dc & 0xFF))); + Term_putstr(40, 19, -1, TERM_WHITE, "<< ? >>"); + Term_putch(43, 19, da, dc); + if (use_bigtile) + { + if (da & 0x80) + Term_putch(44, 19, 255, 255); + else + Term_putch(44, 19, 0, ' '); + } + + /* Label the Current values */ + Term_putstr(10, 20, -1, TERM_WHITE, + format("Current attr/char = %3u / %3u", ca, (cc & 0xFF))); + Term_putstr(40, 20, -1, TERM_WHITE, "<< ? >>"); + Term_putch(43, 20, ca, cc); + if (use_bigtile) + { + if (ca & 0x80) + Term_putch(44, 20, 255, 255); + else + Term_putch(44, 20, 0, ' '); + } + + /* Prompt */ + Term_putstr(0, 22, -1, TERM_WHITE, + "Command (n/N/a/A/c/C/d): "); + + /* Get a command */ + i = inkey(); + + /* All done */ + if (i == ESCAPE) break; + + /* Analyze */ + if (i == 'n') f = (f + max_f_idx + 1) % max_f_idx; + if (i == 'N') f = (f + max_f_idx - 1) % max_f_idx; + if (i == 'a') f_info[f].x_attr = (ca + 1); + if (i == 'A') f_info[f].x_attr = (ca - 1); + if (i == 'c') f_info[f].x_char = (cc + 1); + if (i == 'C') f_info[f].x_char = (cc - 1); + if (i == 'd') + { + f_info[f].x_char = f_ptr->d_char; + f_info[f].x_attr = f_ptr->d_attr; + } + } + } + + /* Reset visuals */ + else if (i == '0') + { + /* Reset */ + reset_visuals(); + + /* Message */ + msg_print("Visual attr/char tables reset."); + } + + /* Unknown option */ + else + { + bell(); + } + + /* Flush messages */ + msg_print(NULL); + } + + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; +} + + +/* + * Interact with "colors" + */ +void do_cmd_colors(void) +{ + int i; + + FILE *fff; + + char tmp[160]; + + char buf[1024]; + + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + + /* Interact until done */ + while (1) + { + /* Clear screen */ + Term_clear(); + + /* Ask for a choice */ + prt("Interact with Colors", 2, 0); + + /* Give some choices */ + prt("(1) Load a user pref file", 4, 5); + prt("(2) Dump colors", 5, 5); + prt("(3) Modify colors", 6, 5); + + /* Prompt */ + prt("Command: ", 8, 0); + + /* Prompt */ + i = inkey(); + + /* Done */ + if (i == ESCAPE) break; + + /* Load a 'pref' file */ + if (i == '1') + { + /* Prompt */ + prt("Command: Load a user pref file", 8, 0); + + /* Prompt */ + prt("File: ", 10, 0); + + /* Default file */ + strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); + + /* Query */ + if (!askfor_aux(tmp, 70)) continue; + + /* Process the given filename */ + (void)process_pref_file(tmp); + + /* Mega-Hack -- react to changes */ + Term_xtra(TERM_XTRA_REACT, 0); + + /* Mega-Hack -- redraw */ + Term_redraw(); + } + + /* Dump colors */ + else if (i == '2') + { + /* Prompt */ + prt("Command: Dump colors", 8, 0); + + /* Prompt */ + prt("File: ", 10, 0); + + /* Default filename */ + strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS); + + /* Get a filename */ + if (!askfor_aux(tmp, 70)) continue; + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, tmp); + + /* Append to the file */ + fff = my_fopen(buf, "a"); + + /* Failure */ + if (!fff) continue; + + /* Start dumping */ + fprintf(fff, "\n\n"); + fprintf(fff, "# Color redefinitions\n\n"); + + /* Dump colors */ + for (i = 0; i < 256; i++) + { + int kv = angband_color_table[i][0]; + int rv = angband_color_table[i][1]; + int gv = angband_color_table[i][2]; + int bv = angband_color_table[i][3]; + + cptr name = "unknown"; + + /* Skip non-entries */ + if (!kv && !rv && !gv && !bv) continue; + + /* Extract the color name */ + if (i < 16) name = color_names[i]; + + /* Dump a comment */ + fprintf(fff, "# Color '%s'\n", name); + + /* Dump the monster attr/char info */ + fprintf(fff, "V:%d:0x%02X:0x%02X:0x%02X:0x%02X\n\n", + i, kv, rv, gv, bv); + } + + /* All done */ + fprintf(fff, "\n\n\n\n"); + + /* Close */ + my_fclose(fff); + + /* Message */ + msg_print("Dumped color redefinitions."); + } + + /* Edit colors */ + else if (i == '3') + { + static byte a = 0; + + /* Prompt */ + prt("Command: Modify colors", 8, 0); + + /* Hack -- query until done */ + while (1) + { + cptr name; + + /* Clear */ + clear_from(10); + + /* Exhibit the normal colors */ + for (i = 0; i < 16; i++) + { + /* Exhibit this color */ + Term_putstr(i*4, 20, -1, a, "###"); + + /* Exhibit all colors */ + Term_putstr(i*4, 22, -1, i, format("%3d", i)); + } + + /* Describe the color */ + name = ((a < 16) ? color_names[a] : "undefined"); + + /* Describe the color */ + Term_putstr(5, 10, -1, TERM_WHITE, + format("Color = %d, Name = %s", a, name)); + + /* Label the Current values */ + Term_putstr(5, 12, -1, TERM_WHITE, + format("K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x", + angband_color_table[a][0], + angband_color_table[a][1], + angband_color_table[a][2], + angband_color_table[a][3])); + + /* Prompt */ + Term_putstr(0, 14, -1, TERM_WHITE, + "Command (n/N/k/K/r/R/g/G/b/B): "); + + /* Get a command */ + i = inkey(); + + /* All done */ + if (i == ESCAPE) break; + + /* Analyze */ + if (i == 'n') a = (a + 1); + if (i == 'N') a = (a - 1); + if (i == 'k') angband_color_table[a][0] = (angband_color_table[a][0] + 1); + if (i == 'K') angband_color_table[a][0] = (angband_color_table[a][0] - 1); + if (i == 'r') angband_color_table[a][1] = (angband_color_table[a][1] + 1); + if (i == 'R') angband_color_table[a][1] = (angband_color_table[a][1] - 1); + if (i == 'g') angband_color_table[a][2] = (angband_color_table[a][2] + 1); + if (i == 'G') angband_color_table[a][2] = (angband_color_table[a][2] - 1); + if (i == 'b') angband_color_table[a][3] = (angband_color_table[a][3] + 1); + if (i == 'B') angband_color_table[a][3] = (angband_color_table[a][3] - 1); + + /* Hack -- react to changes */ + Term_xtra(TERM_XTRA_REACT, 0); + + /* Hack -- redraw */ + Term_redraw(); + } + } + + /* Unknown option */ + else + { + bell(); + } + + /* Flush messages */ + msg_print(NULL); + } + + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; +} + + +/* + * Take notes. There are two ways this can happen, either in the message + * recall or a file. + */ +void do_cmd_note(void) +{ + char buf[80]; + + + /* Default */ + strcpy(buf, ""); + + if (!get_string("Note: ", buf, 60)) return; + + /* Ignore empty notes */ + if (!buf[0] || (buf[0] == ' ')) return; + + /* Add note to file */ + add_note(buf, ' '); +} + + +/* + * Mention the current version + */ +void do_cmd_version(void) +{ + /* Silly message */ + msg_format("You are playing %s made by %s (%s).", + get_version_string(), + modules[game_module_idx].meta.author.name, + modules[game_module_idx].meta.author.email); +} + + + +/* + * Array of feeling strings + */ +static cptr do_cmd_feeling_text[11] = +{ + "Looks like any other level.", + "You feel there is something special about this level.", + "You have a superb feeling about this level.", + "You have an excellent feeling...", + "You have a very good feeling...", + "You have a good feeling...", + "You feel strangely lucky...", + "You feel your luck is turning...", + "You like the look of this place...", + "This level can't be all bad...", + "What a boring place..." +}; + + +/* + * Note that "feeling" is set to zero unless some time has passed. + * Note that this is done when the level is GENERATED, not entered. + */ +void do_cmd_feeling(void) +{ + /* Verify the feeling */ + if (feeling < 0) feeling = 0; + if (feeling > 10) feeling = 10; + + /* Feeling of the fate */ + if (fate_flag && !(dungeon_flags2 & DF2_SPECIAL) && !p_ptr->inside_quest) + { + msg_print("You feel that you will meet your fate here."); + } + + /* Hooked feelings ? */ + if (process_hooks(HOOK_FEELING, "(d)", is_quest(dun_level))) + { + return; + } + + /* No useful feeling in special levels */ + if (dungeon_flags2 & DF2_DESC) + { + char buf[1024]; + + if ((get_dungeon_save(buf)) || (generate_special_feeling) || (dungeon_flags2 & DF2_DESC_ALWAYS)) + { + if (!get_level_desc(buf)) msg_print("Someone forgot to describe this level!"); + else msg_print(buf); + return; + } + } + + /* No useful feeling in quests */ + if (p_ptr->inside_quest) + { + msg_print("Looks like a typical quest level."); + return; + } + + /* Display feelings in the dungeon, nothing on the surface */ + if (dun_level) + { + /* This could be simplified with a correct p_ptr->town_num */ + int i, town_level = 0; + dungeon_info_type *d_ptr = &d_info[dungeon_type]; + + /* Is it a town level ? */ + for (i = 0; i < TOWN_DUNGEON; i++) + { + if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i]; + } + + if (town_level) + msg_print("You hear the sound of a market."); + else + msg_print(do_cmd_feeling_text[feeling]); + } + return; +} + + + +/* + * Encode the screen colors + */ +static char hack[17] = "dwsorgbuDWvyRGBU"; + + +/* + * Hack -- load a screen dump from a file + */ +void do_cmd_load_screen(void) +{ + int i, y, x; + + int wid, hgt; + int len; + + byte a = 0; + char c = ' '; + + bool_ okay = TRUE; + + FILE *fff; + + char buf[1024]; + + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, "dump.txt"); + + /* Append to the file */ + fff = my_fopen(buf, "r"); + + /* Oops */ + if (!fff) return; + + + /* Retrieve the current screen size */ + Term_get_size(&wid, &hgt); + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + /* Clear the screen */ + Term_clear(); + + + /* Load the screen */ + for (y = 0; okay; y++) + { + /* Get a line of data */ + if (my_fgets(fff, buf, 1024)) okay = FALSE; + + /* Stop on blank line */ + if (!buf[0]) break; + + /* Ignore off screen lines */ + if (y >= hgt) continue; + + /* Get width */ + len = strlen(buf); + + /* Truncate if it's longer than current screen width */ + if (len > wid) len = wid; + + /* Show each row */ + for (x = 0; x < len; x++) + { + /* Put the attr/char */ + Term_draw(x, y, TERM_WHITE, buf[x]); + } + } + + /* Dump the screen */ + for (y = 0; okay; y++) + { + /* Get a line of data */ + if (my_fgets(fff, buf, 1024)) okay = FALSE; + + /* Stop on blank line */ + if (!buf[0]) break; + + /* Ignore off screen lines */ + if (y >= hgt) continue; + + /* Get width */ + len = strlen(buf); + + /* Truncate if it's longer than current screen width */ + if (len > wid) len = wid; + + /* Dump each row */ + for (x = 0; x < len; x++) + { + /* Get the attr/char */ + (void)(Term_what(x, y, &a, &c)); + + /* Look up the attr */ + for (i = 0; i < 16; i++) + { + /* Use attr matches */ + if (hack[i] == buf[x]) a = i; + } + + /* Put the attr/char */ + Term_draw(x, y, a, c); + } + } + + + /* Close it */ + my_fclose(fff); + + + /* Message */ + msg_print("Screen dump loaded."); + msg_print(NULL); + + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; +} + + + +/* + * Redefinable "save_screen" action + */ +void (*screendump_aux)(void) = NULL; + + + + + + +/* + * Hack -- save a screen dump to a file + */ +void do_cmd_save_screen(void) +{ + /* Do we use a special screendump function ? */ + if (screendump_aux) + { + /* Dump the screen to a graphics file */ + (*screendump_aux)(); + } + + /* Dump the screen as text */ + else + { + int y, x; + int wid, hgt; + + byte a = 0; + char c = ' '; + + FILE *fff; + + char buf[1024]; + + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_USER, "dump.txt"); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Append to the file */ + fff = my_fopen(buf, "w"); + + /* Oops */ + if (!fff) return; + + + /* Retrieve the current screen size */ + Term_get_size(&wid, &hgt); + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + + /* Dump the screen */ + for (y = 0; y < hgt; y++) + { + /* Dump each row */ + for (x = 0; x < wid; x++) + { + /* Get the attr/char */ + (void)(Term_what(x, y, &a, &c)); + + /* Dump it */ + buf[x] = c; + } + + /* Terminate */ + buf[x] = '\0'; + + /* End the row */ + fprintf(fff, "%s\n", buf); + } + + /* Skip a line */ + fprintf(fff, "\n"); + + + /* Dump the screen */ + for (y = 0; y < hgt; y++) + { + /* Dump each row */ + for (x = 0; x < wid; x++) + { + /* Get the attr/char */ + (void)(Term_what(x, y, &a, &c)); + + /* Dump it */ + buf[x] = hack[a & 0x0F]; + } + + /* Terminate */ + buf[x] = '\0'; + + /* End the row */ + fprintf(fff, "%s\n", buf); + } + + /* Skip a line */ + fprintf(fff, "\n"); + + + /* Close it */ + my_fclose(fff); + + + /* Message */ + msg_print("Screen dump saved."); + msg_print(NULL); + + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; + } +} + + +/* + * Check the status of "artifacts" + */ +void do_cmd_knowledge_artifacts(void) +{ + int i, k, z, x, y; + + FILE *fff; + + char file_name[1024]; + + char base_name[80]; + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + /* Scan the artifacts */ + std::unique_ptr okay(new bool_[max_a_idx]); + for (k = 0; k < max_a_idx; k++) + { + artifact_type *a_ptr = &a_info[k]; + + /* Default */ + okay[k] = FALSE; + + /* Skip "empty" artifacts */ + if (!a_ptr->name) continue; + + /* Skip "uncreated" artifacts */ + if (!a_ptr->cur_num) continue; + + /* Assume okay */ + okay[k] = TRUE; + } + + std::unique_ptr okayk(new bool_[max_k_idx]); + for (k = 0; k < max_k_idx; k++) + { + object_kind *k_ptr = &k_info[k]; + + /* Default */ + okayk[k] = FALSE; + + /* Skip "empty" artifacts */ + if (!(k_ptr->flags3 & TR3_NORM_ART)) continue; + + /* Skip "uncreated" artifacts */ + if (!k_ptr->artifact) continue; + + /* Assume okay */ + okayk[k] = TRUE; + } + + /* Check the dungeon */ + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + cave_type *c_ptr = &cave[y][x]; + + s16b this_o_idx, next_o_idx = 0; + + /* Scan all objects in the grid */ + for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Ignore random artifacts */ + if (o_ptr->tval == TV_RANDART) continue; + + /* Ignore non-artifacts */ + if (!artifact_p(o_ptr)) continue; + + /* Ignore known items */ + if (object_known_p(o_ptr)) continue; + + /* Note the artifact */ + if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART) + { + okayk[o_ptr->k_idx] = FALSE; + } + else + { + okay[o_ptr->name1] = FALSE; + } + } + } + } + + /* Check monsters in the dungeon */ + for (i = 0; i < m_max; i++) + { + monster_type *m_ptr = &m_list[i]; + + s16b this_o_idx, next_o_idx = 0; + + /* Scan all objects the monster carries */ + for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) + { + object_type * o_ptr; + + /* Acquire object */ + o_ptr = &o_list[this_o_idx]; + + /* Acquire next object */ + next_o_idx = o_ptr->next_o_idx; + + /* Ignore random artifacts */ + if (o_ptr->tval == TV_RANDART) continue; + + /* Ignore non-artifacts */ + if (!artifact_p(o_ptr)) continue; + + /* Ignore known items */ + if (object_known_p(o_ptr)) continue; + + /* Note the artifact */ + if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART) + { + okayk[o_ptr->k_idx] = FALSE; + } + else + { + okay[o_ptr->name1] = FALSE; + } + } + } + + /* Check the p_ptr->inventory and equipment */ + for (i = 0; i < INVEN_TOTAL; i++) + { + object_type *o_ptr = &p_ptr->inventory[i]; + + /* Ignore non-objects */ + if (!o_ptr->k_idx) continue; + + /* Ignore random artifacts */ + if (o_ptr->tval == TV_RANDART) continue; + + /* Ignore non-artifacts */ + if (!artifact_p(o_ptr)) continue; + + /* Ignore known items */ + if (object_known_p(o_ptr)) continue; + + /* Note the artifact */ + if (k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART) + { + okayk[o_ptr->k_idx] = FALSE; + } + else + { + okay[o_ptr->name1] = FALSE; + } + } + + /* Scan the artifacts */ + for (k = 0; k < max_a_idx; k++) + { + artifact_type *a_ptr = &a_info[k]; + + /* List "dead" ones */ + if (!okay[k]) continue; + + /* Paranoia */ + strcpy(base_name, "Unknown Artifact"); + + /* Obtain the base object type */ + z = lookup_kind(a_ptr->tval, a_ptr->sval); + + /* Real object */ + if (z) + { + object_type forge; + object_type *q_ptr; + u32b f1, f2, f3, f4, f5, esp; + + /* Get local object */ + q_ptr = &forge; + + /* Create fake object */ + object_prep(q_ptr, z); + + /* Make it an artifact */ + q_ptr->name1 = k; + + /* Spell in it ? no ! */ + object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + if (f5 & TR5_SPELL_CONTAIN) + q_ptr->pval2 = -1; + + /* Describe the artifact */ + object_desc_store(base_name, q_ptr, FALSE, 0); + } + + /* Hack -- Build the artifact name */ + fprintf(fff, " The %s\n", base_name); + } + + for (k = 0; k < max_k_idx; k++) + { + /* List "dead" ones */ + if (!okayk[k]) continue; + + /* Paranoia */ + strcpy(base_name, "Unknown Artifact"); + + /* Real object */ + if (k) + { + object_type forge; + object_type *q_ptr; + + /* Get local object */ + q_ptr = &forge; + + /* Create fake object */ + object_prep(q_ptr, k); + + /* Describe the artifact */ + object_desc_store(base_name, q_ptr, FALSE, 0); + } + + /* Hack -- Build the artifact name */ + fprintf(fff, " The %s\n", base_name); + } + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Artifacts Seen", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * Check the status of traps + */ +void do_cmd_knowledge_traps(void) +{ + int k; + + FILE *fff; + + trap_type *t_ptr; + + char file_name[1024]; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + /* Scan the traps */ + for (k = 0; k < max_t_idx; k++) + { + /* Get the trap */ + t_ptr = &t_info[k]; + + /* Skip "empty" traps */ + if (!t_ptr->name) continue; + + /* Skip unidentified traps */ + if (!t_ptr->ident) continue; + + /* Hack -- Build the trap name */ + fprintf(fff, " %s\n", t_name + t_ptr->name); + } + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Traps known", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +static int monster_get_race_level(int r_idx) { + /* Hack -- Morgoth is always last */ + if (r_idx == 862) { + return 20000; + } + /* Otherwise, we'll use the real level. */ + return r_info[r_idx].level; +} + +static bool compare_monster_level(int r_idx1, int r_idx2) { + return monster_get_race_level(r_idx1) < monster_get_race_level(r_idx2); +} + +/* + * Display known uniques + * + * Note that the player ghosts are ignored. XXX XXX XXX + */ +static void do_cmd_knowledge_uniques(void) +{ + int k; + + FILE *fff; + + char file_name[1024]; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + // Extract the unique race indexes. + std::vector unique_r_idxs; + for (k = 1; k < max_r_idx; k++) + { + monster_race *r_ptr = &r_info[k]; + + /* Only print Uniques */ + if (r_ptr->flags1 & (RF1_UNIQUE) && + !(r_ptr->flags7 & RF7_PET) && + !(r_ptr->flags7 & RF7_NEUTRAL)) + { + unique_r_idxs.push_back(k); + } + } + + // Sort races by level. + std::sort(std::begin(unique_r_idxs), + std::end(unique_r_idxs), + compare_monster_level); + + /* Scan the monster races */ + for (int r_idx : unique_r_idxs) + { + monster_race *r_ptr = &r_info[r_idx]; + + /* Only print Uniques */ + if (r_ptr->flags1 & (RF1_UNIQUE)) + { + bool_ dead = (r_ptr->max_num == 0); + + /* Only display "known" uniques */ + if (dead || cheat_know || r_ptr->r_sights) + { + /* Print a message */ + if (dead) + { + /* Don't print the unique's ASCII symbol + * if use_graphics is on. */ + if (use_graphics) + { + fprintf(fff, "[[[[[R%-70s is dead]\n", + (r_name + r_ptr->name)); + } + else + { + fprintf(fff, "[[[[[%c%c] [[[[[R%-68s is dead]\n", + conv_color[r_ptr->d_attr], + r_ptr->d_char, + (r_name + r_ptr->name)); + } + } + else + { + /* Don't print the unique's ASCII symbol + * if use_graphics is on. */ + if (use_graphics) + { + fprintf(fff, "[[[[[w%-70s is alive]\n", + (r_name + r_ptr->name)); + } + else + { + fprintf(fff, "[[[[[%c%c] [[[[[w%-68s is alive]\n", + conv_color[r_ptr->d_attr], + r_ptr->d_char, + (r_name + r_ptr->name)); + } + } + } + } + } + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Known Uniques", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +void plural_aux(char *name) +{ + int name_len = strlen(name); + + /* Hack -- Precedent must be pluralised for this one */ + if (strstr(name, "Disembodied hand")) + { + strcpy(name, "Disembodied hands that strangled people"); + } + + /* "someone of something" */ + else if (strstr(name, " of ")) + { + cptr aider = strstr(name, " of "); + char dummy[80]; + int i = 0; + cptr ctr = name; + + while (ctr < aider) + { + dummy[i] = *ctr; + ctr++; + i++; + } + + if (dummy[i - 1] == 's') + { + strcpy(&dummy[i], "es"); + i++; + } + else + { + strcpy(&dummy[i], "s"); + } + + strcpy(&dummy[i + 1], aider); + strcpy(name, dummy); + } + + /* Creeping coins */ + else if (strstr(name, "coins")) + { + char dummy[80]; + strcpy(dummy, "piles of "); + strcat(dummy, name); + strcpy(name, dummy); + return; + } + + /* Manes stay manes */ + else if (strstr(name, "Manes")) + { + return; + } + + /* Broken plurals are, well, broken */ + else if (name[name_len - 1] == 'y') + { + strcpy(&name[name_len - 1], "ies"); + } + else if (streq(&name[name_len - 4], "ouse")) + { + strcpy(&name[name_len - 4], "ice"); + } + else if (streq(&name[name_len - 6], "kelman")) + { + strcpy(&name[name_len - 6], "kelmen"); + } + else if (streq(&name[name_len - 2], "ex")) + { + strcpy(&name[name_len - 2], "ices"); + } + else if (streq(&name[name_len - 3], "olf")) + { + strcpy(&name[name_len - 3], "olves"); + } + + /* Now begins sane cases */ + else if ((streq(&name[name_len - 2], "ch")) || (name[name_len - 1] == 's')) + { + strcpy(&name[name_len], "es"); + } + else + { + strcpy(&name[name_len], "s"); + } +} + + +/* + * Display current pets + */ +static void do_cmd_knowledge_pets(void) +{ + int i; + + FILE *fff; + + monster_type *m_ptr; + + int t_friends = 0; + + int t_levels = 0; + + int show_upkeep = 0; + + int upkeep_divider = 20; + + char file_name[1024]; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + if (has_ability(AB_PERFECT_CASTING)) upkeep_divider = 15; + + /* Process the monsters (backwards) */ + for (i = m_max - 1; i >= 1; i--) + { + /* Access the monster */ + m_ptr = &m_list[i]; + + /* Ignore "dead" monsters */ + if (!m_ptr->r_idx) continue; + + /* Calculate "upkeep" for friendly monsters */ + if (m_ptr->status >= MSTATUS_PET) + { + char pet_name[80]; + monster_race *r_ptr = race_inf(m_ptr); + + t_friends++; + t_levels += m_ptr->level; + monster_desc(pet_name, m_ptr, 0x88); + fprintf(fff, "%s%s (%s)\n", + (r_ptr->flags1 & RF1_UNIQUE) ? "#####G" : "", + pet_name, + (m_ptr->status < MSTATUS_COMPANION) ? "pet" : "companion"); + } + } + + if (t_friends > 1 + (p_ptr->lev / (upkeep_divider))) + { + show_upkeep = (t_levels); + + if (show_upkeep > 100) show_upkeep = 100; + else if (show_upkeep < 10) show_upkeep = 10; + } + + + fprintf(fff, "----------------------------------------------\n"); + fprintf(fff, " Total: %d pet%s.\n", t_friends, (t_friends == 1 ? "" : "s")); + fprintf(fff, " Upkeep: %d%% mana.\n", show_upkeep); + + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Current Pets", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + + +/* + * Total kill count + * + * Note that the player ghosts are ignored. XXX XXX XXX + */ +static void do_cmd_knowledge_kill_count(void) +{ + int k; + + FILE *fff; + + char file_name[1024]; + + s32b Total = 0; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + { + /* Monsters slain */ + int kk; + + /* For all monsters */ + for (kk = 1; kk < max_r_idx; kk++) + { + monster_race *r_ptr = &r_info[kk]; + + if (r_ptr->flags1 & (RF1_UNIQUE)) + { + bool_ dead = (r_ptr->max_num == 0); + + if (dead) + { + Total++; + } + } + else + { + s16b This = r_ptr->r_pkills; + + if (This > 0) + { + Total += This; + } + } + } + + if (Total < 1) + { + fprintf(fff, "You have defeated no enemies yet.\n\n"); + } + else if (Total == 1) + { + fprintf(fff, "You have defeated one enemy.\n\n"); + } + else + { + fprintf(fff, "You have defeated " FMTs32b " enemies.\n\n", Total); + } + } + + Total = 0; + + /* Scan the monster races */ + for (k = 0; k < max_r_idx; k++) + { + monster_race *r_ptr = &r_info[k]; + + if (r_ptr->flags1 & (RF1_UNIQUE)) + { + bool_ dead = (r_ptr->max_num == 0); + + if (dead) + { + /* Print a message */ + fprintf(fff, " %s\n", + (r_name + r_ptr->name)); + Total++; + } + } + else + { + s16b This = r_ptr->r_pkills; + + if (This > 0) + { + if (This < 2) + { + if (strstr(r_name + r_ptr->name, "coins")) + { + fprintf(fff, " 1 pile of %s\n", (r_name + r_ptr->name)); + } + else + { + fprintf(fff, " 1 %s\n", (r_name + r_ptr->name)); + } + } + else + { + char to_plural[80]; + strcpy(to_plural, (r_name + r_ptr->name)); + plural_aux(to_plural); + fprintf(fff, " %d %s\n", This, to_plural); + } + + Total += This; + } + } + } + + fprintf(fff, "----------------------------------------------\n"); + fprintf(fff, " Total: " FMTs32b " creature%s killed.\n", Total, (Total == 1 ? "" : "s")); + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Kill Count", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * Display known objects + */ +static void do_cmd_knowledge_objects(void) +{ + int k; + + FILE *fff; + + char o_name[80]; + + char file_name[1024]; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + /* Scan the object kinds */ + for (k = 1; k < max_k_idx; k++) + { + object_kind *k_ptr = &k_info[k]; + + /* Hack -- skip artifacts */ + if (k_ptr->flags3 & (TR3_INSTA_ART)) continue; + + /* List known flavored objects */ + if (k_ptr->flavor && k_ptr->aware) + { + object_type *i_ptr; + object_type object_type_body; + + /* Get local object */ + i_ptr = &object_type_body; + + /* Create fake object */ + object_prep(i_ptr, k); + + /* Describe the object */ + object_desc_store(o_name, i_ptr, FALSE, 0); + + /* Print a message */ + fprintf(fff, " %s\n", o_name); + } + } + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Known Objects", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * List recall depths + */ +static void do_cmd_knowledge_dungeons(void) +{ + int y; + char file_name[1024]; + FILE *fff; + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + /* Oops */ + if (fff == NULL) return; + + /* Scan all dungeons */ + for (y = 1; y < max_d_idx; y++) + { + /* The dungeon has a valid recall depth set */ + if (max_dlv[y]) + { + /* Describe the recall depth */ + fprintf(fff, " %c%s: Level %d (%d')\n", + (p_ptr->recall_dungeon == y) ? '*' : ' ', + d_name + d_info[y].name, + max_dlv[y], 50 * (max_dlv[y])); + } + } + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Recall Depths", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * List known towns + */ +void do_cmd_knowledge_towns(void) +{ + int i, j; + char file_name[1024]; + FILE *fff; + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + /* Oops */ + if (fff == NULL) return; + + /* Scan all dungeons */ + for (i = 0; i < max_d_idx; i++) + { + dungeon_info_type *d_ptr = &d_info[i]; + + /* Scan all dungeon town slots */ + for (j = 0; j < TOWN_DUNGEON; j++) + { + int town_idx = d_ptr->t_idx[j]; + + /* Ignore non-existent towns */ + if (!(town_info[town_idx].flags & (TOWN_REAL))) continue; + + /* Ignore unknown towns */ + if (!(town_info[town_idx].flags & (TOWN_KNOWN))) continue; + + /* Describe the dungeon town */ + fprintf(fff, " %s: Level %d (%d')\n", + d_name + d_ptr->name, + d_ptr->t_level[j], + 50 * d_ptr->t_level[j]); + } + } + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Dungeon Towns", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * List corruptions + */ +void do_cmd_knowledge_corruptions(void) +{ + FILE *fff; + + char file_name[1024]; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + /* Dump the corruptions to file */ + if (fff) + { + dump_corruptions(fff, TRUE, FALSE); + } + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Corruptions", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * Helper function for do_cmd_knowledge_quests + */ +static void insert_sort_quest(int *order, int *num, int q_idx) +{ + int i, j; + + quest_type *q_ptr = &quest[q_idx]; + + int level = q_ptr->level; + + + /* Find the place */ + for (i = 0; i < *num; i++) + { + quest_type *q2_ptr = &quest[order[i]]; + int level2 = q2_ptr->level; + + if (level < level2) break; + } + + /* Move the remaining items */ + for (j = *num - 1; j >= i; j--) + { + order[j + 1] = order[j]; + } + + /* Insert it */ + order[i] = q_idx; + (*num)++; +} + + +/* + * Print quest status of all active quests + */ +static void do_cmd_knowledge_quests(void) +{ + FILE *fff; + + char file_name[1024]; + + int order[MAX_Q_IDX] = { }; + + int num = 0; + + int i, j, z; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + for (i = 0; i < MAX_Q_IDX; i++) + { + insert_sort_quest(order, &num, i); + } + + for (z = 0; z < MAX_Q_IDX; z++) + { + i = order[z]; + + /* Dynamic descriptions */ + if (quest[i].gen_desc != NULL) + { + if (!quest[i].gen_desc(fff)) + { + continue; + } + } + + /* Fixed quests (only known ones) */ + else if (!quest[i].silent) + { + if (quest[i].status == QUEST_STATUS_TAKEN) + { + /* Print the quest info */ + fprintf(fff, "#####y%s (Danger level: %d)\n", + quest[i].name, quest[i].level); + + j = 0; + while ((j < 10) && (quest[i].desc[j][0] != '\0')) + { + fprintf(fff, "%s\n", quest[i].desc[j++]); + } + fprintf(fff, "\n"); + } + else if (quest[i].status == QUEST_STATUS_COMPLETED) + { + fprintf(fff , "#####G%s Completed - Unrewarded\n", quest[i].name); + fprintf(fff, "\n"); + } + } + } + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Quest status", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * Print fate status + */ +static void do_cmd_knowledge_fates(void) +{ + FILE *fff; + + char file_name[1024]; + + + /* Temporary file */ + if (path_temp(file_name, 1024)) return; + + /* Open a new file */ + fff = my_fopen(file_name, "w"); + + dump_fates(fff); + + /* Close the file */ + my_fclose(fff); + + /* Display the file contents */ + show_file(file_name, "Fate status", 0, 0); + + /* Remove the file */ + fd_kill(file_name); +} + + +/* + * Print the note file + */ +void do_cmd_knowledge_notes(void) +{ + /* Spawn */ + show_notes_file(); + + /* Done */ + return; +} + + +/* + * Interact with "knowledge" + */ +void do_cmd_knowledge(void) +{ + int i; + + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + /* Interact until done */ + while (1) + { + /* Clear screen */ + Term_clear(); + + /* Ask for a choice */ + prt("Display current knowledge", 2, 0); + + /* Give some choices */ + prt("(1) Display known artifacts", 4, 5); + prt("(2) Display known uniques", 5, 5); + prt("(3) Display known objects", 6, 5); + prt("(4) Display kill count", 7, 5); + prt("(5) Display recall depths", 8, 5); + prt("(6) Display corruptions", 9, 5); + prt("(7) Display current pets", 10, 5); + prt("(8) Display current quests", 11, 5); + prt("(9) Display current fates", 12, 5); + prt("(0) Display known traps", 13, 5); + prt("(A) Display known dungeon towns", 14, 5); + prt("(B) Display notes", 15, 5); + + /* Prompt */ + prt("Command: ", 17, 0); + + /* Prompt */ + i = inkey(); + + /* Done */ + if (i == ESCAPE) break; + + switch (i) + { + /* Artifacts */ + case '1': + { + do_cmd_knowledge_artifacts(); + + break; + } + + /* Uniques */ + case '2': + { + do_cmd_knowledge_uniques(); + + break; + } + + /* Objects */ + case '3': + { + do_cmd_knowledge_objects(); + + break; + } + + /* Kill count */ + case '4': + { + do_cmd_knowledge_kill_count(); + + break; + } + + /* Recall depths */ + case '5': + { + do_cmd_knowledge_dungeons(); + + break; + } + + /* corruptions */ + case '6': + { + do_cmd_knowledge_corruptions(); + + break; + } + + /* Pets */ + case '7': + { + do_cmd_knowledge_pets(); + + break; + } + + /* Quests */ + case '8': + { + do_cmd_knowledge_quests(); + + break; + } + + /* Fates */ + case '9': + { + do_cmd_knowledge_fates(); + + break; + } + + /* Traps */ + case '0': + { + do_cmd_knowledge_traps(); + + break; + } + + /* Dungeon towns */ + case 'A': + case 'a': + { + do_cmd_knowledge_towns(); + + break; + } + + /* Notes */ + case 'B': + case 'b': + { + do_cmd_knowledge_notes(); + + break; + } + + /* Unknown option */ + default: + { + bell(); + + break; + } + } + + /* Flush messages */ + msg_print(NULL); + } + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; +} + + +/* + * Check on the status of an active quest -KMW- + * TODO: Spill out status when not a simple kill # monster. + */ +void do_cmd_checkquest(void) +{ + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + /* Quest info */ + do_cmd_knowledge_quests(); + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; +} + + +/* + * Change player's "tactic" setting + */ +void do_cmd_change_tactic(int i) +{ + p_ptr->tactic += i; + if (p_ptr->tactic > 8) p_ptr->tactic = 0; + if (p_ptr->tactic < 0) p_ptr->tactic = 8; + + p_ptr->update |= (PU_BONUS); + update_stuff(); + prt("", 0, 0); +} + + +/* + * Change player's "movement" setting + */ +void do_cmd_change_movement(int i) +{ + p_ptr->movement += i; + if (p_ptr->movement > 8) p_ptr->movement = 0; + if (p_ptr->movement < 0) p_ptr->movement = 8; + + p_ptr->update |= (PU_BONUS); + update_stuff(); + prt("", 0, 0); +} + + +/* + * Display the time and date + */ +void do_cmd_time() +{ + int day = bst(DAY, turn); + + int hour = bst(HOUR, turn); + + int min = bst(MINUTE, turn); + + int full = hour * 100 + min; + + char buf2[20]; + + int start = 9999; + + int end = -9999; + + int num = 0; + + char desc[1024]; + + char buf[1024]; + + FILE *fff; + + + /* Note */ + strcpy(desc, "It is a strange time."); + + /* Format time of the day */ + strnfmt(buf2, 20, get_day(bst(YEAR, turn) + START_YEAR)); + + /* Display current date in the Elvish calendar */ + msg_format("This is %s of the %s year of the third age.", + get_month_name(day, wizard, FALSE), buf2); + + /* Message */ + msg_format("The time is %d:%02d %s.", + (hour % 12 == 0) ? 12 : (hour % 12), + min, (hour < 12) ? "AM" : "PM"); + + /* Find the path */ + if (!rand_int(10) || p_ptr->image) + { + path_build(buf, 1024, ANGBAND_DIR_FILE, "timefun.txt"); + } + else + { + path_build(buf, 1024, ANGBAND_DIR_FILE, "timenorm.txt"); + } + + /* Open this file */ + fff = my_fopen(buf, "rt"); + + /* Oops */ + if (!fff) return; + + /* Find this time */ + while (!my_fgets(fff, buf, 1024)) + { + /* Ignore comments */ + if (!buf[0] || (buf[0] == '#')) continue; + + /* Ignore invalid lines */ + if (buf[1] != ':') continue; + + /* Process 'Start' */ + if (buf[0] == 'S') + { + /* Extract the starting time */ + start = atoi(buf + 2); + + /* Assume valid for an hour */ + end = start + 59; + + /* Next... */ + continue; + } + + /* Process 'End' */ + if (buf[0] == 'E') + { + /* Extract the ending time */ + end = atoi(buf + 2); + + /* Next... */ + continue; + } + + /* Ignore incorrect range */ + if ((start > full) || (full > end)) continue; + + /* Process 'Description' */ + if (buf[0] == 'D') + { + num++; + + /* Apply the randomizer */ + if (!rand_int(num)) strcpy(desc, buf + 2); + + /* Next... */ + continue; + } + } + + /* Message */ + msg_print(desc); + + /* Close the file */ + my_fclose(fff); +} + +/* + * Macro recorder! + * It records all keypresses and then put them in a macro + * Not as powerful as the macro screen, but much easier for newbies + */ + +std::string *macro_recorder_current = nullptr; + +void macro_recorder_start() +{ + msg_print("Starting macro recording, press this key again to stop. Note that if the action you want to record accepts the @ key, use it; it will remove your the need to inscribe stuff."); + assert (macro_recorder_current == nullptr); + macro_recorder_current = new std::string(); +} + +void macro_recorder_add(char c) +{ + // Gets called unconditionally for all input, so ignore unless + // we're actual recording. + if (macro_recorder_current) { + macro_recorder_current->push_back(c); + } +} + +void macro_recorder_stop() +{ + assert(macro_recorder_current != nullptr); + + // Remove the last key, because it is the key to stop recording + macro_recorder_current->pop_back(); + + // Copy out current macro text. + std::string macro(*macro_recorder_current); + + // Stop recording. + delete macro_recorder_current; + macro_recorder_current = nullptr; + + /* Add it */ + if (get_check("Are you satisfied and want to create the macro? ")) + { + char buf[1024]; + + prt("Trigger: ", 0, 0); + + /* Get a macro trigger */ + do_cmd_macro_aux(buf, FALSE); + + /* Link the macro */ + macro_add(buf, macro.c_str()); + + /* Prompt */ + std::unique_ptr str(new char[(macro.length() + 1) * 3]); + str[0] = '\0'; + ascii_to_text(str.get(), macro.c_str()); + msg_format("Added a macro '%s'. If you want it to stay permanently, press @ now and dump macros to a file.", str.get()); + } +} + +void do_cmd_macro_recorder() +{ + if (macro_recorder_current == NULL) + macro_recorder_start(); + else + macro_recorder_stop(); +} diff --git a/src/cmd5.c b/src/cmd5.c deleted file mode 100644 index 95ae0346..00000000 --- a/src/cmd5.c +++ /dev/null @@ -1,2471 +0,0 @@ -/* File: cmd5.c */ - -/* Purpose: Class commands */ - -/* - * 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 "angband.h" - -#include - -#include "spell_type.h" -#include "quark.h" - -/* Maximum number of tries for teleporting */ -#define MAX_TRIES 300 - -bool_ is_school_book(object_type *o_ptr) -{ - if (o_ptr->tval == TV_BOOK) - { - return TRUE; - } - else if (o_ptr->tval == TV_DAEMON_BOOK) - { - return TRUE; - } - else if (o_ptr->tval == TV_INSTRUMENT) - { - return TRUE; - } - else - { - return FALSE; - } -} - -/* Does it contains a schooled spell ? */ -static bool_ hook_school_spellable(object_type *o_ptr) -{ - if (is_school_book(o_ptr)) - return TRUE; - else - { - u32b f1, f2, f3, f4, f5, esp; - - /* Extract object flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 != -1)) - return TRUE; - } - return FALSE; -} - -/* Is it a book */ -bool_ item_tester_hook_browsable(object_type *o_ptr) -{ - if (hook_school_spellable(o_ptr)) return TRUE; - if (o_ptr->tval >= TV_BOOK) return TRUE; - return FALSE; -} - -/* - * Are we using a mage staff - */ -bool_ is_magestaff() -{ - int i; - - - i = 0; - - while (p_ptr->body_parts[i] == INVEN_WIELD) - { - object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD + i]; - - /* Wielding a mage staff */ - if ((o_ptr->k_idx) && (o_ptr->tval == TV_MSTAFF)) return (TRUE); - - /* Next slot */ - i++; - - /* Paranoia */ - if (i >= (INVEN_TOTAL - INVEN_WIELD)) break; - } - - /* Not wielding a mage staff */ - return (FALSE); -} - -/* - * Peruse the spells/prayers in a book - * - * Note that *all* spells in the book are listed - * - * Note that browsing is allowed while confused or blind, - * and in the dark, primarily to allow browsing in stores. - */ - -extern void do_cmd_browse_aux(object_type *o_ptr) -{ - u32b f1, f2, f3, f4, f5, esp; - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if (is_school_book(o_ptr)) - browse_school_spell(o_ptr->sval, o_ptr->pval, o_ptr); - else if (f5 & TR5_SPELL_CONTAIN && o_ptr->pval2 != -1) - browse_school_spell(255, o_ptr->pval2, o_ptr); -} - -void do_cmd_browse(void) -{ - int item; - - cptr q, s; - - object_type *o_ptr; - - /* Restrict choices to "useful" books */ - item_tester_hook = item_tester_hook_browsable; - - /* Get an item */ - q = "Browse which book? "; - s = "You have no books that you can read."; - if (!get_item(&item, q, s, (USE_INVEN | USE_EQUIP | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - do_cmd_browse_aux(o_ptr); -} - -void do_poly_wounds(void) -{ - /* Changed to always provide at least _some_ healing */ - s16b wounds = p_ptr->cut; - - s16b hit_p = (p_ptr->mhp - p_ptr->chp); - - s16b change = damroll(p_ptr->lev, 5); - - bool_ Nasty_effect = (randint(5) == 1); - - - if (!(wounds || hit_p || Nasty_effect)) return; - - msg_print("Your wounds are polymorphed into less serious ones."); - hp_player(change); - if (Nasty_effect) - { - msg_print("A new wound was created!"); - take_hit(change / 2, "a polymorphed wound"); - set_cut(change); - } - else - { - set_cut((p_ptr->cut) - (change / 2)); - } -} - -void do_poly_self(void) -{ - int power = p_ptr->lev; - int poly_power; - - msg_print("You feel a change coming over you..."); - - if ((power > rand_int(20)) && (rand_int(3) == 0)) - { - char effect_msg[80] = ""; - int new_race, expfact, goalexpfact; - - /* Some form of racial polymorph... */ - power -= 10; - - if ((power > rand_int(5)) && (rand_int(4) == 0)) - { - /* sex change */ - power -= 2; - - if (p_ptr->psex == SEX_MALE) - { - p_ptr->psex = SEX_FEMALE; - sp_ptr = &sex_info[p_ptr->psex]; - strcpy(effect_msg, "female"); - } - else - { - p_ptr->psex = SEX_MALE; - sp_ptr = &sex_info[p_ptr->psex]; - strcpy(effect_msg, "male"); - } - } - - if ((power > rand_int(30)) && (rand_int(5) == 0)) - { - int tmp = 0; - - /* Harmful deformity */ - power -= 15; - - while (tmp < 6) - { - if ( rand_int(2) == 0) - { - (void)dec_stat(tmp, randint(6) + 6, (rand_int(3) == 0)); - power -= 1; - } - tmp++; - } - - /* Deformities are discriminated against! */ - (void)dec_stat(A_CHR, randint(6), TRUE); - - if (effect_msg[0]) - { - char tmp_msg[10]; - strnfmt(tmp_msg, 10, "%s", effect_msg); - strnfmt(effect_msg, 80, "deformed %s", tmp_msg); - } - else - { - strcpy(effect_msg, "deformed"); - } - } - - while ((power > rand_int(20)) && (rand_int(10) == 0)) - { - /* Polymorph into a less corrupted form */ - power -= 10; - - lose_corruption(); - } - - /* - * I'm not sure 'power' is always positive, with *so* many minuses. - * Also, passing zero / negative numbers to randint/rand_int can - * cause a zero divide exception, IIRC, not to speak of its absurdity - * -- pelpel - */ - poly_power = (power > 1) ? power : 1; - - /* - * Restrict the race choices by exp penalty so weak polymorph - * always means weak race - */ - goalexpfact = 100 + 3 * rand_int(poly_power); - - /* Roll until an appropriate selection is made */ - while (1) - { - new_race = rand_int(max_rp_idx); - expfact = race_info[new_race].r_exp; - - if ((new_race != p_ptr->prace) && (expfact <= goalexpfact)) break; - } - - if (effect_msg[0]) - { - msg_format("You turn into a%s %s!", - ((is_a_vowel(rp_name[race_info[new_race].title])) ? "n" : ""), - race_info[new_race].title + rp_name); - } - else - { - msg_format("You turn into a %s %s!", effect_msg, - race_info[new_race].title); - } - - p_ptr->prace = new_race; - rp_ptr = &race_info[p_ptr->prace]; - - /* Experience factor */ - p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp; - - /* Calculate the height/weight */ - get_height_weight(); - - - check_experience(); - p_ptr->max_plv = p_ptr->lev; - - p_ptr->redraw |= (PR_BASIC); - - p_ptr->update |= (PU_BONUS); - - handle_stuff(); - lite_spot(p_ptr->py, p_ptr->px); - } - - if ((power > rand_int(30)) && (rand_int(6) == 0)) - { - int tmp = 0; - - /* Abomination! */ - power -= 20; - - msg_print("Your internal organs are rearranged!"); - while (tmp < 6) - { - (void)dec_stat(tmp, randint(6) + 6, (rand_int(3) == 0)); - tmp++; - } - if (rand_int(6) == 0) - { - msg_print("You find living difficult in your present form!"); - take_hit(damroll(randint(10), p_ptr->lev), "a lethal corruption"); - power -= 10; - } - } - - if ((power > rand_int(20)) && (rand_int(4) == 0)) - { - power -= 10; - - do_cmd_rerate(); - } - - while ((power > rand_int(15)) && (rand_int(3) == 0)) - { - power -= 7; - gain_random_corruption(); - } - - if (power > rand_int(5)) - { - power -= 5; - do_poly_wounds(); - } - - /* Note: earlier deductions may have left power < 0 already. */ - while (power > 0) - { - corrupt_player(); - power--; - } -} - - -/* - * Brand the current weapon - */ -void brand_weapon(int brand_type) -{ - object_type *o_ptr; - - cptr act = NULL; - - char o_name[80]; - - - o_ptr = &p_ptr->inventory[INVEN_WIELD]; - - /* - * You can never modify artifacts / ego-items - * You can never modify cursed items - * - * TY: You _can_ modify broken items (if you're silly enough) - */ - if (!o_ptr->k_idx || artifact_p(o_ptr) || ego_item_p(o_ptr) || - o_ptr->art_name || cursed_p(o_ptr)) - { - if (flush_failure) flush(); - - msg_print("The Branding failed."); - - return; - } - - - /* Save the old name */ - object_desc(o_name, o_ptr, FALSE, 0); - - switch (brand_type) - { - case 6: - { - act = "glows with godly power."; - o_ptr->name2 = EGO_BLESS_BLADE; - o_ptr->pval = randint(4); - - break; - } - case 5: - { - act = "seems very powerful."; - o_ptr->name2 = EGO_EARTHQUAKES; - o_ptr->pval = randint(3); - - break; - } - case 4: - { - act = "seems very unstable now."; - o_ptr->name2 = EGO_DRAGON; - o_ptr->pval = randint(2); - - break; - } - case 3: - { - act = "thirsts for blood!"; - o_ptr->name2 = EGO_VAMPIRIC; - - break; - } - case 2: - { - act = "is coated with poison."; - o_ptr->name2 = EGO_BRAND_POIS; - - break; - } - case 1: - { - act = "is engulfed in raw chaos!"; - o_ptr->name2 = EGO_CHAOTIC; - - break; - } - default: - { - if (rand_int(100) < 25) - { - act = "is covered in a fiery shield!"; - o_ptr->name2 = EGO_BRAND_FIRE; - } - else - { - act = "glows deep, icy blue!"; - o_ptr->name2 = EGO_BRAND_COLD; - } - } - } - - /* Apply the ego */ - apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE); - o_ptr->discount = 100; - - msg_format("Your %s %s", o_name, act); - - enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM); -} - -/* - * Fetch an item (teleport it right underneath the caster) - */ -void fetch(int dir, int wgt, bool_ require_los) -{ - int ty, tx, i; - - cave_type *c_ptr; - - object_type *o_ptr; - - char o_name[80]; - - - /* Check to see if an object is already there */ - if (cave[p_ptr->py][p_ptr->px].o_idx) - { - msg_print("You can't fetch when you're already standing on something."); - return; - } - - /* Use a target */ - if ((dir == 5) && target_okay()) - { - tx = target_col; - ty = target_row; - - if (distance(p_ptr->py, p_ptr->px, ty, tx) > MAX_RANGE) - { - msg_print("You can't fetch something that far away!"); - return; - } - - c_ptr = &cave[ty][tx]; - - if (!c_ptr->o_idx) - { - msg_print("There is no object at this place."); - return; - } - - if (require_los && (!player_has_los_bold(ty, tx))) - { - msg_print("You have no direct line of sight to that location."); - return; - } - } - else - { - /* Use a direction */ - ty = p_ptr->py; /* Where to drop the item */ - tx = p_ptr->px; - - while (1) - { - ty += ddy[dir]; - tx += ddx[dir]; - c_ptr = &cave[ty][tx]; - - if ((distance(p_ptr->py, p_ptr->px, ty, tx) > MAX_RANGE) || - !cave_floor_bold(ty, tx)) return; - - if (c_ptr->o_idx) break; - } - } - - o_ptr = &o_list[c_ptr->o_idx]; - - if (o_ptr->weight > wgt) - { - /* Too heavy to 'fetch' */ - msg_print("The object is too heavy."); - return; - } - - i = c_ptr->o_idx; - c_ptr->o_idx = o_ptr->next_o_idx; - cave[p_ptr->py][p_ptr->px].o_idx = i; /* 'move' it */ - o_ptr->next_o_idx = 0; - o_ptr->iy = p_ptr->py; - o_ptr->ix = p_ptr->px; - - object_desc(o_name, o_ptr, TRUE, 0); - msg_format("%^s flies through the air to your feet.", o_name); - - note_spot(p_ptr->py, p_ptr->px); - p_ptr->redraw |= PR_MAP; -} - - -/* - * Handle random effects of player shrieking - */ -void shriek_effect() -{ - switch (randint(9)) - { - case 1: - case 5: - case 8: - case 9: - { - msg_print("You make a high-pitched shriek!"); - aggravate_monsters(1); - - break; - } - case 2: - case 6: - { - msg_print("Oops! You call a monster."); - summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0); - - break; - } - case 3: - case 7: - { - msg_print("The dungeon collapses!"); - earthquake(p_ptr->py, p_ptr->px, 5); - - break; - } - case 4: - { - msg_print("Your shriek is so horrible that you damage your health!"); - take_hit(damroll(p_ptr->lev / 5, 8), "inner hemorrhaging"); - - break; - } - } -} - - -/* - * Like all the random effect codes, this is *ugly*, - * and there is not a single line of comment, so I can't tell - * some fall throughs are really intended. Well, I know it's - * intended to be bizarre :) -- pelpel - */ -void wild_magic(int spell) -{ - int counter = 0; - int type = SUMMON_BIZARRE1 - 1 + randint(6); - - if (type < SUMMON_BIZARRE1) type = SUMMON_BIZARRE1; - else if (type > SUMMON_BIZARRE6) type = SUMMON_BIZARRE6; - - switch (randint(spell) + randint(8) + 1) - { - case 1: - case 2: - case 3: - { - teleport_player(10); - - break; - } - - case 4: - case 5: - case 6: - { - teleport_player(100); - - break; - } - - case 7: - case 8: - { - teleport_player(200); - - break; - } - - case 9: - case 10: - case 11: - { - unlite_area(10, 3); - - break; - } - - case 12: - case 13: - case 14: - { - lite_area(damroll(2, 3), 2); - - break; - } - - case 15: - { - destroy_doors_touch(); - - break; - } - - case 16: - case 17: - { - wall_breaker(); - - /* I don't think this is a fall through -- pelpel */ - break; - } - - case 18: - { - sleep_monsters_touch(); - - break; - } - - case 19: - case 20: - { - trap_creation(); - - break; - } - - case 21: - case 22: - { - door_creation(); - - break; - } - - case 23: - case 24: - case 25: - { - aggravate_monsters(1); - - break; - } - - case 26: - { - /* Prevent destruction of quest levels and town */ - if (!is_quest(dun_level) && dun_level) - earthquake(p_ptr->py, p_ptr->px, 5); - - break; - } - - case 27: - case 28: - { - break; - } - - case 29: - case 30: - { - apply_disenchant(0); - - break; - } - - case 31: - { - lose_all_info(); - - break; - } - - case 32: - { - fire_ball(GF_CHAOS, 0, spell + 5, 1 + (spell / 10)); - - break; - } - - case 33: - { - wall_stone(p_ptr->py, p_ptr->px); - - break; - } - - case 34: - case 35: - { - while (counter++ < 8) - { - (void) summon_specific(p_ptr->py, p_ptr->px, (dun_level * 3) / 2, type); - } - - break; - } - - case 36: - case 37: - { - activate_hi_summon(); - - break; - } - - case 38: - { - summon_cyber(); - - /* I don't think this is a fall through -- pelpel */ - break; - } - - default: - { - activate_ty_curse(); - } - } - - return; -} - - -/* - * Hack -- Determine if the player is wearing an artefact ring - * specified by art_type, that should be an index into a_info - */ -bool_ check_ring(int art_type) -{ - int i; - - - /* We are only interested in ring slots */ - i = INVEN_RING; - - /* Scan the list of rings until we reach the end */ - while (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_RING) - { - /* Found the ring we were looking for */ - if (p_ptr->inventory[i].k_idx && (p_ptr->inventory[i].name1 == art_type)) - { - return (TRUE); - } - - /* Next item */ - i++; - } - - /* Found nothing */ - return (FALSE); -} - -/* - * Return the symbiote's name or description. - */ -cptr symbiote_name(bool_ capitalize) -{ - object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY]; - static char buf[80]; - - /* Make sure there actually is a symbiote there... */ - if (!o_ptr->k_idx) - { - strcpy(buf, "A non-existent symbiote"); - } - else - { - monster_race *r_ptr = &r_info[o_ptr->pval]; - cptr s = NULL; - - if (r_ptr->flags1 & RF1_UNIQUE) - { - /* Unique monster; no preceding "your", and ignore our name. */ - strncpy(buf, r_name + r_ptr->name, sizeof(buf)); - } - else if (o_ptr->note && - (s = strstr(quark_str(o_ptr->note), "#named ")) != NULL) - { - /* We've named it. */ - strncpy(buf, s + 7, sizeof(buf)); - } - else - { - /* No special cases, just return "Your ". */ - strcpy(buf, "your "); - strncpy(buf + 5, r_name + r_ptr->name, sizeof(buf) - 5); - } - } - - /* Just in case... */ - buf[sizeof(buf) - 1] = '\0'; - if (capitalize) buf[0] = toupper(buf[0]); - return buf; -} - -/* - * Use a power of the monster in symbiosis - */ -int use_symbiotic_power(int r_idx, bool_ great, bool_ only_number, bool_ no_cost) -{ - int power = -1; - - int num = 0, dir = 0 , i; - - int powers[96]; - - bool_ flag; - - int ask, plev = p_ptr->lev; - - char choice; - - char out_val[160]; - - monster_race *r_ptr = &r_info[r_idx]; - - int rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); - - int x = p_ptr->px, y = p_ptr->py, k; - - int rad; - - int label; - - - /* List the monster powers -- RF4_* */ - for (i = 0; i < 32; i++) - { - if (r_ptr->flags4 & BIT(i)) - { - if (monster_powers[i].great && (!great)) continue; - if (!monster_powers[i].power) continue; - powers[num++] = i; - } - } - - /* List the monster powers -- RF5_* */ - for (i = 0; i < 32; i++) - { - if (r_ptr->flags5 & BIT(i)) - { - if (monster_powers[i + 32].great && (!great)) continue; - if (!monster_powers[i + 32].power) continue; - powers[num++] = i + 32; - } - } - - /* List the monster powers -- RF6_* */ - for (i = 0; i < 32; i++) - { - if (r_ptr->flags6 & BIT(i)) - { - if (monster_powers[i + 64].great && (!great)) continue; - if (!monster_powers[i + 64].power) continue; - powers[num++] = i + 64; - } - } - - if (!num) - { - msg_print("You have no powers you can use."); - return (0); - } - - if (only_number) return (num); - - /* Nothing chosen yet */ - flag = FALSE; - - /* Get the last label */ - label = (num <= 26) ? I2A(num - 1) : I2D(num - 1 - 26); - - /* Build a prompt (accept all spells) */ - /* Mega Hack -- if no_cost is false, we're actually a Possessor -dsb */ - strnfmt(out_val, 78, - "(Powers a-%c, ESC=exit) Use which power of your %s? ", - label, (no_cost ? "symbiote" : "body")); - - /* Save the screen */ - character_icky = TRUE; - Term_save(); - - /* Get a spell from the user */ - while (!flag) - { - /* Show the list */ - { - byte y = 1, x = 0; - int ctr = 0; - char dummy[80]; - - strcpy(dummy, ""); - - prt ("", y++, x); - - while (ctr < num) - { - monster_power *mp_ptr = &monster_powers[powers[ctr]]; - int mana = mp_ptr->mana / 10; - - if (mana > p_ptr->msp) mana = p_ptr->msp; - - if (!mana) mana = 1; - - label = (ctr < 26) ? I2A(ctr) : I2D(ctr - 26); - - if (!no_cost) - { - strnfmt(dummy, 80, " %c) %2d %s", - label, mana, mp_ptr->name); - } - else - { - strnfmt(dummy, 80, " %c) %s", - label, mp_ptr->name); - } - - if (ctr < 17) - { - prt(dummy, y + ctr, x); - } - else - { - prt(dummy, y + ctr - 17, x + 40); - } - - ctr++; - } - - if (ctr < 17) - { - prt ("", y + ctr, x); - } - else - { - prt ("", y + 17, x); - } - } - - if (!get_com(out_val, &choice)) - { - flag = FALSE; - break; - } - - if (choice == '\r' && num == 1) - { - choice = 'a'; - } - - if (isalpha(choice)) - { - /* Note verify */ - ask = (isupper(choice)); - - /* Lowercase */ - if (ask) choice = tolower(choice); - - /* Extract request */ - i = (islower(choice) ? A2I(choice) : -1); - } - else - { - /* Can't uppercase digits XXX XXX XXX */ - ask = FALSE; - - i = choice - '0' + 26; - } - - /* Totally Illegal */ - if ((i < 0) || (i >= num)) - { - bell(); - continue; - } - - /* Save the spell index */ - power = powers[i]; - - /* Verify it */ - if (ask) - { - char tmp_val[160]; - - /* Prompt */ - strnfmt(tmp_val, 78, "Use %s? ", monster_powers[power].name); - - /* Belay that order */ - if (!get_check(tmp_val)) continue; - } - - /* Stop the loop */ - flag = TRUE; - } - - /* Restore the screen */ - Term_load(); - character_icky = FALSE; - - /* Abort if needed */ - if (!flag) - { - energy_use = 0; - return -1; - } - - /* 'Powerful' monsters have wider radii */ - if (r_ptr->flags2 & RF2_POWERFUL) - { - rad = 1 + (p_ptr->lev / 15); - } - else - { - rad = 1 + (p_ptr->lev / 20); - } - - - /* Analyse power */ - switch (power) - { - /**** RF4 (bit position) ****/ - - /* SHRIEK */ - case 0: - { - aggravate_monsters( -1); - - break; - } - - /* MULTIPLY */ - case 1: - { - do_cmd_wiz_named_friendly(p_ptr->body_monster, FALSE); - - break; - } - - /* S_ANIMAL */ - case 2: - { - summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE); - - break; - } - - /* ROCKET */ - case 3: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_ROCKET, dir, p_ptr->lev * 12, 1 + (p_ptr->lev / 20)); - - break; - } - - /* ARROW_1 */ - case 4: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_ARROW, dir, damroll(1, 6)); - - break; - } - - /* ARROW_2 */ - case 5: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_ARROW, dir, damroll(3, 6)); - - break; - } - - /* ARROW_3 */ - case 6: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_ARROW, dir, damroll(5, 6)); - - break; - } - - /* ARROW_4 */ - case 7: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_ARROW, dir, damroll(7, 6)); - - break; - } - - /* BR_ACID */ - case 8: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_ACID, dir, p_ptr->lev * 5, rad); - - break; - } - - /* BR_ELEC */ - case 9: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_ELEC, dir, p_ptr->lev * 5, rad); - - break; - } - - /* BR_FIRE */ - case 10: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_FIRE, dir, p_ptr->lev * 5, rad); - - break; - } - - /* BR_COLD */ - case 11: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_COLD, dir, p_ptr->lev * 5, rad); - - break; - } - - /* BR_POIS */ - case 12: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_POIS, dir, p_ptr->lev * 5, rad); - - break; - } - - /* BR_NETH */ - case 13: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_NETHER, dir, p_ptr->lev * 5, rad); - - break; - } - - /* BR_LITE */ - case 14: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_LITE, dir, p_ptr->lev * 8, rad); - - break; - } - - /* BR_DARK */ - case 15: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_DARK, dir, p_ptr->lev * 8, rad); - - break; - } - - /* BR_CONF */ - case 16: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_CONFUSION, dir, p_ptr->lev * 8, rad); - - break; - } - - /* BR_SOUN */ - case 17: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_SOUND, dir, p_ptr->lev * 8, rad); - - break; - } - - /* BR_CHAO */ - case 18: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_CHAOS, dir, p_ptr->lev * 7, rad); - - break; - } - - /* BR_DISE */ - case 19: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_DISENCHANT, dir, p_ptr->lev * 7, rad); - - break; - } - - /* BR_NEXU */ - case 20: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_NEXUS, dir, p_ptr->lev * 5, rad); - - break; - } - - /* BR_TIME */ - case 21: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_TIME, dir, p_ptr->lev * 3, rad); - - break; - } - - /* BR_INER */ - case 22: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_INERTIA, dir, p_ptr->lev * 4, rad); - - break; - } - - /* BR_GRAV */ - case 23: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_GRAVITY, dir, p_ptr->lev * 4, rad); - - break; - } - - /* BR_SHAR */ - case 24: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_SHARDS, dir, p_ptr->lev * 8, rad); - - break; - } - - /* BR_PLAS */ - case 25: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_PLASMA, dir, p_ptr->lev * 3, rad); - - break; - } - - /* BR_WALL */ - case 26: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_FORCE, dir, p_ptr->lev * 4, rad); - - break; - } - - /* BR_MANA */ - case 27: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_MANA, dir, p_ptr->lev * 5, rad); - - break; - } - - /* BA_NUKE */ - case 28: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_NUKE, dir, p_ptr->lev * 8, 1 + (p_ptr->lev / 20)); - - break; - } - - /* BR_NUKE */ - case 29: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_NUKE, dir, p_ptr->lev * 8, 1 + (p_ptr->lev / 20)); - - break; - } - - /* BA_CHAO */ - case 30: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_CHAOS, dir, p_ptr->lev * 4, 2); - - break; - } - - /* BR_DISI */ - case 31: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_DISINTEGRATE, dir, p_ptr->lev * 5, 1 + (p_ptr->lev / 20)); - - break; - } - - - /**** RF5 (bit position + 32) ****/ - - /* BA_ACID */ - case 32: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_ACID, dir, randint(p_ptr->lev * 6) + 20, 2); - - break; - } - - /* BA_ELEC */ - case 33: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_ELEC, dir, randint(p_ptr->lev * 3) + 20, 2); - - break; - } - - /* BA_FIRE */ - case 34: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_FIRE, dir, randint(p_ptr->lev * 7) + 20, 2); - - break; - } - - /* BA_COLD */ - case 35: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_COLD, dir, randint(p_ptr->lev * 3) + 20, 2); - - break; - } - - /* BA_POIS */ - case 36: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_POIS, dir, damroll(12, 2), 2); - - break; - } - - /* BA_NETH */ - case 37: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_NETHER, dir, randint(p_ptr->lev * 4) + 20, 2); - - break; - } - - /* BA_WATE */ - case 38: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_WATER, dir, randint(p_ptr->lev * 4) + 20, 2); - - break; - } - - /* BA_MANA */ - case 39: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_MANA, dir, randint(p_ptr->lev * 3) + 20, 2); - - break; - } - - /* BA_DARK */ - case 40: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_DARK, dir, randint(p_ptr->lev * 3) + 20, 2); - - break; - } - - /* 41 DRAIN_MANA -- Not available */ - - /* 42 MIND_BLAST -- Not available */ - - /* 43 BRAIN_SMASH -- Not available */ - - /* CAUSE_1 */ - case 44: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_MANA, dir, damroll(3, 8)); - - break; - } - - /* CAUSE_2 */ - case 45: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_MANA, dir, damroll(8, 8)); - - break; - } - - /* CAUSE_3 */ - case 46: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_MANA, dir, damroll(10, 15)); - - break; - } - - /* CAUSE_4 */ - case 47: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_MANA, dir, damroll(15, 15)); - - break; - } - - /* BO_ACID */ - case 48: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_ACID, dir, damroll(7, 8) + (p_ptr->lev / 3)); - - break; - } - - /* BO_ELEC */ - case 49: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_ELEC, dir, damroll(4, 8) + (p_ptr->lev / 3)); - - break; - } - - /* BO_FIRE */ - case 50: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_FIRE, dir, damroll(9, 8) + (p_ptr->lev / 3)); - - break; - } - - /* BO_COLD */ - case 51: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_COLD, dir, damroll(6, 8) + (p_ptr->lev / 3)); - - break; - } - - /* BO_POIS */ - case 52: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_POIS, dir, damroll(7, 8) + (p_ptr->lev / 3)); - - break; - } - - /* BO_NETH */ - case 53: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_NETHER, dir, damroll(5, 5) + (p_ptr->lev / 3)); - - break; - } - - /* BO_WATE */ - case 54: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_WATER, dir, damroll(10, 10) + (p_ptr->lev / 3)); - - break; - } - - /* BO_MANA */ - case 55: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_MANA, dir, damroll(3, 8) + (p_ptr->lev / 3)); - - break; - } - - /* BO_PLAS */ - case 56: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_PLASMA, dir, damroll(8, 8) + (p_ptr->lev / 3)); - - break; - } - - /* BO_ICEE */ - case 57: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_ICE, dir, damroll(6, 6) + (p_ptr->lev / 3)); - - break; - } - - /* MISSILE */ - case 58: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_MISSILE, dir, damroll(2, 6) + (p_ptr->lev / 3)); - - break; - } - - /* SCARE */ - case 59: - { - if (!get_aim_dir(&dir)) break; - - fear_monster(dir, plev); - - break; - } - - /* BLIND */ - case 60: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_CONFUSION, dir, damroll(1, 8) + (p_ptr->lev / 3)); - - break; - } - - /* CONF */ - case 61: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_CONFUSION, dir, damroll(7, 8) + (p_ptr->lev / 3)); - - break; - } - - /* SLOW */ - case 62: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_OLD_SLOW, dir, damroll(6, 8) + (p_ptr->lev / 3)); - - break; - } - - /* HOLD */ - case 63: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_OLD_SLEEP, dir, damroll(5, 8) + (p_ptr->lev / 3)); - - break; - } - - - /**** RF6 (bit position + 64) ****/ - - /* HASTE */ - case 64: - { - if (!p_ptr->fast) - { - (void)set_fast(randint(20 + (plev) ) + plev, 10); - } - else - { - (void)set_fast(p_ptr->fast + randint(5), 10); - } - - break; - } - - /* HAND_DOOM */ - case 65: - { - if (!get_aim_dir(&dir)) break; - - fire_bolt(GF_MANA, dir, damroll(10, 8) + (p_ptr->lev)); - - break; - } - - /* HEAL */ - case 66: - { - hp_player(damroll(8, 5)); - - break; - } - - /* S_ANIMALS */ - case 67: - { - for (k = 0; k < 4; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE); - } - - break; - } - - /* BLINK */ - case 68: - { - if (dungeon_flags2 & DF2_NO_TELEPORT) - { - msg_print("No teleport on special levels..."); - break; - } - - teleport_player(10); - - break; - } - - /* TPORT */ - case 69: - { - if (dungeon_flags2 & DF2_NO_TELEPORT) - { - msg_print("No teleport on special levels..."); - break; - } - - teleport_player(plev * 5); - - break; - } - - /* TELE_TO */ - case 70: - { - int ii, ij; - - if (dungeon_flags2 & DF2_NO_TELEPORT) - { - msg_print("No teleport on special levels..."); - break; - } - - msg_print("You go between."); - - if (!tgt_pt(&ii, &ij)) break; - - p_ptr->energy -= 60 - plev; - - if (!cave_empty_bold(ij, ii) || - (cave[ij][ii].info & CAVE_ICKY) || - (distance(ij, ii, p_ptr->py, p_ptr->px) > plev * 20 + 2)) - { - msg_print("You fail to show the destination correctly!"); - p_ptr->energy -= 100; - teleport_player(10); - } - else teleport_player_to(ij, ii); - - break; - } - - /* TELE_AWAY */ - case 71: - { - if (dungeon_flags2 & DF2_NO_TELEPORT) - { - msg_print("No teleport on special levels..."); - break; - } - - if (!get_aim_dir(&dir)) break; - - (void)fire_beam(GF_AWAY_ALL, dir, plev); - - break; - } - - /* TELE_LEVEL */ - case 72: - { - if (dungeon_flags2 & DF2_NO_TELEPORT) - { - msg_print("No teleport on special levels..."); - break; - } - - teleport_player_level(); - - break; - } - - /* DARKNESS */ - case 73: - { - (void)project( -1, 3, p_ptr->py, p_ptr->px, 0, GF_DARK_WEAK, - PROJECT_GRID | PROJECT_KILL); - - /* Unlite the room */ - unlite_room(p_ptr->py, p_ptr->px); - - break; - } - - /* TRAPS */ - case 74: - { - trap_creation(); - - break; - } - - /* 75 FORGET -- Not available */ - - /* ANIM_DEAD -- Use the same code as the nether spell */ - case 76: - { - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_RAISE, dir, 1, 0); - - break; - } - - /* 77 S_BUG -- Not available, well we do that anyway ;) */ - - /* 78 S_RNG -- Not available, who dares? */ - - /* S_THUNDERLORD */ - case 79: - { - for (k = 0; k < 1; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_THUNDERLORD, TRUE); - } - - break; - } - - /* S_KIN -- Summon Kin, because we code bugs :) */ - case 80: - { - /* Big hack */ - summon_kin_type = r_ptr->d_char; - - for (k = 0; k < 6; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_KIN, TRUE); - } - - break; - } - - /* S_HI_DEMON */ - case 81: - { - for (k = 0; k < 1; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_HI_DEMON, TRUE); - } - - break; - } - - /* S_MONSTER */ - case 82: - { - for (k = 0; k < 1; k++) - { - summon_specific_friendly(y, x, rlev, 0, TRUE); - } - - break; - } - - /* S_MONSTERS */ - case 83: - { - for (k = 0; k < 6; k++) - { - summon_specific_friendly(y, x, rlev, 0, TRUE); - } - - break; - } - - /* S_ANT */ - case 84: - { - for (k = 0; k < 6; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_ANT, TRUE); - } - - break; - } - - /* S_SPIDER */ - case 85: - { - for (k = 0; k < 6; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_SPIDER, TRUE); - } - - break; - } - - /* S_HOUND */ - case 86: - { - for (k = 0; k < 6; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_HOUND, TRUE); - } - - break; - } - - /* S_HYDRA */ - case 87: - { - for (k = 0; k < 6; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_HYDRA, TRUE); - } - - break; - } - - /* S_ANGEL */ - case 88: - { - for (k = 0; k < 1; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_ANGEL, TRUE); - } - - break; - } - - /* S_DEMON */ - case 89: - { - for (k = 0; k < 1; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_DEMON, TRUE); - } - - break; - } - - /* S_UNDEAD */ - case 90: - { - for (k = 0; k < 1; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_UNDEAD, TRUE); - } - - break; - } - - /* S_DRAGON */ - case 91: - { - for (k = 0; k < 1; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_DRAGON, TRUE); - } - - break; - } - - /* S_HI_UNDEAD */ - case 92: - { - for (k = 0; k < 8; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_HI_UNDEAD_NO_UNIQUES, TRUE); - } - - break; - } - - /* S_HI_DRAGON */ - case 93: - { - for (k = 0; k < 8; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_HI_DRAGON_NO_UNIQUES, TRUE); - } - - break; - } - - /* S_WRAITH */ - case 94: - { - for (k = 0; k < 8; k++) - { - summon_specific_friendly(y, x, rlev, SUMMON_WRAITH, TRUE); - } - - break; - } - - /* 95 S_UNIQUE -- Not available */ - } - - /* Take some SP */ - if (!no_cost) - { - int chance, pchance; - - chance = (monster_powers[power].mana + r_ptr->level); - pchance = adj_str_wgt[p_ptr->stat_ind[A_WIS]] / 2 + get_skill(SKILL_POSSESSION); - - if (rand_int(chance) >= pchance) - { - int m = monster_powers[power].mana / 10; - - if (m > p_ptr->msp) m = p_ptr->msp; - if (!m) m = 1; - - p_ptr->csp -= m; - } - } - - /* Redraw mana */ - p_ptr->redraw |= (PR_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - - return (num); -} - -/* - * Schooled magic - */ - -/* - * Find a spell in any books/objects - */ -static int hack_force_spell = -1; -static object_type *hack_force_spell_obj = NULL; -bool_ get_item_hook_find_spell(int *item) -{ - int i, spell; - char buf[80]; - - strcpy(buf, "Manathrust"); - if (!get_string("Spell name? ", buf, 79)) - return FALSE; - - spell = find_spell(buf); - if (spell == -1) return FALSE; - - for (i = 0; i < INVEN_TOTAL; i++) - { - object_type *o_ptr = &p_ptr->inventory[i]; - u32b f1, f2, f3, f4, f5, esp; - - /* Must we wield it ? */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - if ((wield_slot(o_ptr) != -1) && (i < INVEN_WIELD) && (f5 & TR5_WIELD_CAST)) continue; - - /* Is it a non-book? */ - if (!is_school_book(o_ptr)) - { - u32b f1, f2, f3, f4, f5, esp; - - /* Extract object flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == spell)) - { - *item = i; - hack_force_spell = spell; - hack_force_spell_obj = o_ptr; - return TRUE; - } - } - /* A random book ? */ - else if (school_book_contains_spell(o_ptr->sval, spell)) - { - *item = i; - hack_force_spell = spell; - hack_force_spell_obj = o_ptr; - return TRUE; - } - } - return FALSE; -} - -/* - * Is the spell castable? - */ -bool_ is_ok_spell(s32b spell_idx, object_type *o_ptr) -{ - spell_type *spell = spell_at(spell_idx); - assert(o_ptr != NULL); - - if (get_level(spell_idx, 50, 0) == 0) - { - return FALSE; - } - - if (o_ptr->pval < spell_type_minimum_pval(spell)) - { - return FALSE; - } - - return TRUE; -} - - -/* - * Get a spell from a book - */ -s32b get_school_spell(cptr do_what, s16b force_book) -{ - int i, item; - s32b spell = -1; - int num = 0; - s32b where = 1; - int ask; - bool_ flag; - char out_val[160]; - char buf2[40]; - char buf3[40]; - object_type *o_ptr, forge; - int tmp; - int sval, pval; - u32b f1, f2, f3, f4, f5, esp; - - hack_force_spell = -1; - hack_force_spell_obj = NULL; - - /* Ok do we need to ask for a book ? */ - if (!force_book) - { - get_item_extra_hook = get_item_hook_find_spell; - item_tester_hook = hook_school_spellable; - sprintf(buf2, "You have no book to %s from", do_what); - sprintf(buf3, "%s from which book?", do_what); - if (!get_item(&item, buf3, buf2, USE_INVEN | USE_EQUIP | USE_EXTRA )) return -1; - - /* Get the item */ - o_ptr = get_object(item); - - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* If it can be wielded, it must */ - if ((wield_slot(o_ptr) != -1) && (item < INVEN_WIELD) && (f5 & TR5_WIELD_CAST)) - { - msg_format("You cannot %s from that object; it must be wielded first.", do_what); - return -1; - } - } - else - { - o_ptr = &forge; - o_ptr->tval = TV_BOOK; - o_ptr->sval = force_book; - o_ptr->pval = 0; - } - - if (repeat_pull(&tmp)) - { - return tmp; - } - - /* Nothing chosen yet */ - flag = FALSE; - - /* Show choices */ - window_stuff(); - - /* No spell to cast by default */ - spell = -1; - - /* Is it a random book, or something else ? */ - if (is_school_book(o_ptr)) - { - sval = o_ptr->sval; - pval = o_ptr->pval; - } - else - { - sval = 255; - pval = o_ptr->pval2; - } - - /* Save the screen */ - character_icky = TRUE; - Term_save(); - - /* Go */ - if (hack_force_spell == -1) - { - num = school_book_length(sval); - - /* Build a prompt (accept all spells) */ - strnfmt(out_val, 78, "(Spells %c-%c, Descs %c-%c, ESC=exit) %^s which spell? ", - I2A(0), I2A(num - 1), I2A(0) - 'a' + 'A', I2A(num - 1) - 'a' + 'A', do_what); - - /* Get a spell from the user */ - while (!flag) - { - char choice; - - /* Restore and save screen; this prevents - subprompt from leaving garbage when going - around the loop multiple times. */ - Term_load(); - Term_save(); - - /* Display a list of spells */ - where = print_book(sval, pval, o_ptr); - - /* Input */ - if (!get_com(out_val, &choice)) - { - flag = FALSE; - break; - } - - /* Note verify */ - ask = (isupper(choice)); - - /* Lowercase */ - if (ask) choice = tolower(choice); - - /* Extract request */ - i = (islower(choice) ? A2I(choice) : -1); - - /* Totally Illegal */ - if ((i < 0) || (i >= num)) - { - bell(); - continue; - } - - /* Verify it */ - if (ask) - { - /* Display a list of spells */ - where = print_book(sval, pval, o_ptr); - print_spell_desc(spell_x(sval, pval, i), where); - } - else - { - bool_ ok; - - /* Save the spell index */ - spell = spell_x(sval, pval, i); - - /* Do we need to do some pre test */ - ok = is_ok_spell(spell, o_ptr); - - /* Require "okay" spells */ - if (!ok) - { - bell(); - msg_format("You may not %s that spell.", do_what); - spell = -1; - continue; - } - - /* Stop the loop */ - flag = TRUE; - } - } - } - else - { - bool_ ok; - - /* Require "okay" spells */ - ok = is_ok_spell(hack_force_spell, hack_force_spell_obj); - if (ok) - { - flag = TRUE; - spell = hack_force_spell; - } - else - { - bell(); - msg_format("You may not %s that spell.", do_what); - spell = -1; - } - } - - - /* Restore the screen */ - Term_load(); - character_icky = FALSE; - - - /* Show choices */ - window_stuff(); - - - /* Abort if needed */ - if (!flag) return -1; - - tmp = spell; - repeat_push(tmp); - return spell; -} - -void cast_school_spell() -{ - int spell; - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - spell = get_school_spell("cast", 0); - - /* Actualy cast the choice */ - if (spell != -1) - { - lua_cast_school_spell(spell, FALSE); - } -} - -void browse_school_spell(int book, int pval, object_type *o_ptr) -{ - int i; - int num = 0, where = 1; - int ask; - char choice; - char out_val[160]; - - /* Show choices */ - window_stuff(); - - num = school_book_length(book); - - /* Build a prompt (accept all spells) */ - strnfmt(out_val, 78, "(Spells %c-%c, ESC=exit) cast which spell? ", - I2A(0), I2A(num - 1)); - - /* Save the screen */ - character_icky = TRUE; - Term_save(); - - /* Display a list of spells */ - where = print_book(book, pval, o_ptr); - - /* Get a spell from the user */ - while (get_com(out_val, &choice)) - { - /* Display a list of spells */ - where = print_book(book, pval, o_ptr); - - /* Note verify */ - ask = (isupper(choice)); - - /* Lowercase */ - if (ask) choice = tolower(choice); - - /* Extract request */ - i = (islower(choice) ? A2I(choice) : -1); - - /* Totally Illegal */ - if ((i < 0) || (i >= num)) - { - bell(); - continue; - } - - /* Restore the screen */ - Term_load(); - - /* Display a list of spells */ - where = print_book(book, pval, o_ptr); - print_spell_desc(spell_x(book, pval, i), where); - } - - - /* Restore the screen */ - Term_load(); - character_icky = FALSE; - - /* Show choices */ - window_stuff(); -} - -/* Can it contains a schooled spell ? */ -static bool_ hook_school_can_spellable(object_type *o_ptr) -{ - u32b f1, f2, f3, f4, f5, esp; - - /* Extract object flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == -1)) - return TRUE; - return FALSE; -} - -/* - * Copy a spell from a bok to an object - */ -void do_cmd_copy_spell() -{ - int spell = get_school_spell("copy", 0); - int item; - object_type *o_ptr; - - if (spell == -1) return; - - /* Spells that cannot be randomly created cannot be copied */ - if (spell_type_random_type(spell_at(spell)) <= 0) - { - msg_print("This spell cannot be copied."); - return; - } - - item_tester_hook = hook_school_can_spellable; - if (!get_item(&item, "Copy to which object? ", "You have no object to copy to.", (USE_INVEN | USE_EQUIP))) return; - o_ptr = get_object(item); - - msg_print("You copy the spell!"); - o_ptr->pval2 = spell; - inven_item_describe(item); -} diff --git a/src/cmd5.cc b/src/cmd5.cc new file mode 100644 index 00000000..95ae0346 --- /dev/null +++ b/src/cmd5.cc @@ -0,0 +1,2471 @@ +/* File: cmd5.c */ + +/* Purpose: Class commands */ + +/* + * 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 "angband.h" + +#include + +#include "spell_type.h" +#include "quark.h" + +/* Maximum number of tries for teleporting */ +#define MAX_TRIES 300 + +bool_ is_school_book(object_type *o_ptr) +{ + if (o_ptr->tval == TV_BOOK) + { + return TRUE; + } + else if (o_ptr->tval == TV_DAEMON_BOOK) + { + return TRUE; + } + else if (o_ptr->tval == TV_INSTRUMENT) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/* Does it contains a schooled spell ? */ +static bool_ hook_school_spellable(object_type *o_ptr) +{ + if (is_school_book(o_ptr)) + return TRUE; + else + { + u32b f1, f2, f3, f4, f5, esp; + + /* Extract object flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 != -1)) + return TRUE; + } + return FALSE; +} + +/* Is it a book */ +bool_ item_tester_hook_browsable(object_type *o_ptr) +{ + if (hook_school_spellable(o_ptr)) return TRUE; + if (o_ptr->tval >= TV_BOOK) return TRUE; + return FALSE; +} + +/* + * Are we using a mage staff + */ +bool_ is_magestaff() +{ + int i; + + + i = 0; + + while (p_ptr->body_parts[i] == INVEN_WIELD) + { + object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD + i]; + + /* Wielding a mage staff */ + if ((o_ptr->k_idx) && (o_ptr->tval == TV_MSTAFF)) return (TRUE); + + /* Next slot */ + i++; + + /* Paranoia */ + if (i >= (INVEN_TOTAL - INVEN_WIELD)) break; + } + + /* Not wielding a mage staff */ + return (FALSE); +} + +/* + * Peruse the spells/prayers in a book + * + * Note that *all* spells in the book are listed + * + * Note that browsing is allowed while confused or blind, + * and in the dark, primarily to allow browsing in stores. + */ + +extern void do_cmd_browse_aux(object_type *o_ptr) +{ + u32b f1, f2, f3, f4, f5, esp; + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if (is_school_book(o_ptr)) + browse_school_spell(o_ptr->sval, o_ptr->pval, o_ptr); + else if (f5 & TR5_SPELL_CONTAIN && o_ptr->pval2 != -1) + browse_school_spell(255, o_ptr->pval2, o_ptr); +} + +void do_cmd_browse(void) +{ + int item; + + cptr q, s; + + object_type *o_ptr; + + /* Restrict choices to "useful" books */ + item_tester_hook = item_tester_hook_browsable; + + /* Get an item */ + q = "Browse which book? "; + s = "You have no books that you can read."; + if (!get_item(&item, q, s, (USE_INVEN | USE_EQUIP | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + do_cmd_browse_aux(o_ptr); +} + +void do_poly_wounds(void) +{ + /* Changed to always provide at least _some_ healing */ + s16b wounds = p_ptr->cut; + + s16b hit_p = (p_ptr->mhp - p_ptr->chp); + + s16b change = damroll(p_ptr->lev, 5); + + bool_ Nasty_effect = (randint(5) == 1); + + + if (!(wounds || hit_p || Nasty_effect)) return; + + msg_print("Your wounds are polymorphed into less serious ones."); + hp_player(change); + if (Nasty_effect) + { + msg_print("A new wound was created!"); + take_hit(change / 2, "a polymorphed wound"); + set_cut(change); + } + else + { + set_cut((p_ptr->cut) - (change / 2)); + } +} + +void do_poly_self(void) +{ + int power = p_ptr->lev; + int poly_power; + + msg_print("You feel a change coming over you..."); + + if ((power > rand_int(20)) && (rand_int(3) == 0)) + { + char effect_msg[80] = ""; + int new_race, expfact, goalexpfact; + + /* Some form of racial polymorph... */ + power -= 10; + + if ((power > rand_int(5)) && (rand_int(4) == 0)) + { + /* sex change */ + power -= 2; + + if (p_ptr->psex == SEX_MALE) + { + p_ptr->psex = SEX_FEMALE; + sp_ptr = &sex_info[p_ptr->psex]; + strcpy(effect_msg, "female"); + } + else + { + p_ptr->psex = SEX_MALE; + sp_ptr = &sex_info[p_ptr->psex]; + strcpy(effect_msg, "male"); + } + } + + if ((power > rand_int(30)) && (rand_int(5) == 0)) + { + int tmp = 0; + + /* Harmful deformity */ + power -= 15; + + while (tmp < 6) + { + if ( rand_int(2) == 0) + { + (void)dec_stat(tmp, randint(6) + 6, (rand_int(3) == 0)); + power -= 1; + } + tmp++; + } + + /* Deformities are discriminated against! */ + (void)dec_stat(A_CHR, randint(6), TRUE); + + if (effect_msg[0]) + { + char tmp_msg[10]; + strnfmt(tmp_msg, 10, "%s", effect_msg); + strnfmt(effect_msg, 80, "deformed %s", tmp_msg); + } + else + { + strcpy(effect_msg, "deformed"); + } + } + + while ((power > rand_int(20)) && (rand_int(10) == 0)) + { + /* Polymorph into a less corrupted form */ + power -= 10; + + lose_corruption(); + } + + /* + * I'm not sure 'power' is always positive, with *so* many minuses. + * Also, passing zero / negative numbers to randint/rand_int can + * cause a zero divide exception, IIRC, not to speak of its absurdity + * -- pelpel + */ + poly_power = (power > 1) ? power : 1; + + /* + * Restrict the race choices by exp penalty so weak polymorph + * always means weak race + */ + goalexpfact = 100 + 3 * rand_int(poly_power); + + /* Roll until an appropriate selection is made */ + while (1) + { + new_race = rand_int(max_rp_idx); + expfact = race_info[new_race].r_exp; + + if ((new_race != p_ptr->prace) && (expfact <= goalexpfact)) break; + } + + if (effect_msg[0]) + { + msg_format("You turn into a%s %s!", + ((is_a_vowel(rp_name[race_info[new_race].title])) ? "n" : ""), + race_info[new_race].title + rp_name); + } + else + { + msg_format("You turn into a %s %s!", effect_msg, + race_info[new_race].title); + } + + p_ptr->prace = new_race; + rp_ptr = &race_info[p_ptr->prace]; + + /* Experience factor */ + p_ptr->expfact = rp_ptr->r_exp + rmp_ptr->r_exp + cp_ptr->c_exp; + + /* Calculate the height/weight */ + get_height_weight(); + + + check_experience(); + p_ptr->max_plv = p_ptr->lev; + + p_ptr->redraw |= (PR_BASIC); + + p_ptr->update |= (PU_BONUS); + + handle_stuff(); + lite_spot(p_ptr->py, p_ptr->px); + } + + if ((power > rand_int(30)) && (rand_int(6) == 0)) + { + int tmp = 0; + + /* Abomination! */ + power -= 20; + + msg_print("Your internal organs are rearranged!"); + while (tmp < 6) + { + (void)dec_stat(tmp, randint(6) + 6, (rand_int(3) == 0)); + tmp++; + } + if (rand_int(6) == 0) + { + msg_print("You find living difficult in your present form!"); + take_hit(damroll(randint(10), p_ptr->lev), "a lethal corruption"); + power -= 10; + } + } + + if ((power > rand_int(20)) && (rand_int(4) == 0)) + { + power -= 10; + + do_cmd_rerate(); + } + + while ((power > rand_int(15)) && (rand_int(3) == 0)) + { + power -= 7; + gain_random_corruption(); + } + + if (power > rand_int(5)) + { + power -= 5; + do_poly_wounds(); + } + + /* Note: earlier deductions may have left power < 0 already. */ + while (power > 0) + { + corrupt_player(); + power--; + } +} + + +/* + * Brand the current weapon + */ +void brand_weapon(int brand_type) +{ + object_type *o_ptr; + + cptr act = NULL; + + char o_name[80]; + + + o_ptr = &p_ptr->inventory[INVEN_WIELD]; + + /* + * You can never modify artifacts / ego-items + * You can never modify cursed items + * + * TY: You _can_ modify broken items (if you're silly enough) + */ + if (!o_ptr->k_idx || artifact_p(o_ptr) || ego_item_p(o_ptr) || + o_ptr->art_name || cursed_p(o_ptr)) + { + if (flush_failure) flush(); + + msg_print("The Branding failed."); + + return; + } + + + /* Save the old name */ + object_desc(o_name, o_ptr, FALSE, 0); + + switch (brand_type) + { + case 6: + { + act = "glows with godly power."; + o_ptr->name2 = EGO_BLESS_BLADE; + o_ptr->pval = randint(4); + + break; + } + case 5: + { + act = "seems very powerful."; + o_ptr->name2 = EGO_EARTHQUAKES; + o_ptr->pval = randint(3); + + break; + } + case 4: + { + act = "seems very unstable now."; + o_ptr->name2 = EGO_DRAGON; + o_ptr->pval = randint(2); + + break; + } + case 3: + { + act = "thirsts for blood!"; + o_ptr->name2 = EGO_VAMPIRIC; + + break; + } + case 2: + { + act = "is coated with poison."; + o_ptr->name2 = EGO_BRAND_POIS; + + break; + } + case 1: + { + act = "is engulfed in raw chaos!"; + o_ptr->name2 = EGO_CHAOTIC; + + break; + } + default: + { + if (rand_int(100) < 25) + { + act = "is covered in a fiery shield!"; + o_ptr->name2 = EGO_BRAND_FIRE; + } + else + { + act = "glows deep, icy blue!"; + o_ptr->name2 = EGO_BRAND_COLD; + } + } + } + + /* Apply the ego */ + apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE); + o_ptr->discount = 100; + + msg_format("Your %s %s", o_name, act); + + enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM); +} + +/* + * Fetch an item (teleport it right underneath the caster) + */ +void fetch(int dir, int wgt, bool_ require_los) +{ + int ty, tx, i; + + cave_type *c_ptr; + + object_type *o_ptr; + + char o_name[80]; + + + /* Check to see if an object is already there */ + if (cave[p_ptr->py][p_ptr->px].o_idx) + { + msg_print("You can't fetch when you're already standing on something."); + return; + } + + /* Use a target */ + if ((dir == 5) && target_okay()) + { + tx = target_col; + ty = target_row; + + if (distance(p_ptr->py, p_ptr->px, ty, tx) > MAX_RANGE) + { + msg_print("You can't fetch something that far away!"); + return; + } + + c_ptr = &cave[ty][tx]; + + if (!c_ptr->o_idx) + { + msg_print("There is no object at this place."); + return; + } + + if (require_los && (!player_has_los_bold(ty, tx))) + { + msg_print("You have no direct line of sight to that location."); + return; + } + } + else + { + /* Use a direction */ + ty = p_ptr->py; /* Where to drop the item */ + tx = p_ptr->px; + + while (1) + { + ty += ddy[dir]; + tx += ddx[dir]; + c_ptr = &cave[ty][tx]; + + if ((distance(p_ptr->py, p_ptr->px, ty, tx) > MAX_RANGE) || + !cave_floor_bold(ty, tx)) return; + + if (c_ptr->o_idx) break; + } + } + + o_ptr = &o_list[c_ptr->o_idx]; + + if (o_ptr->weight > wgt) + { + /* Too heavy to 'fetch' */ + msg_print("The object is too heavy."); + return; + } + + i = c_ptr->o_idx; + c_ptr->o_idx = o_ptr->next_o_idx; + cave[p_ptr->py][p_ptr->px].o_idx = i; /* 'move' it */ + o_ptr->next_o_idx = 0; + o_ptr->iy = p_ptr->py; + o_ptr->ix = p_ptr->px; + + object_desc(o_name, o_ptr, TRUE, 0); + msg_format("%^s flies through the air to your feet.", o_name); + + note_spot(p_ptr->py, p_ptr->px); + p_ptr->redraw |= PR_MAP; +} + + +/* + * Handle random effects of player shrieking + */ +void shriek_effect() +{ + switch (randint(9)) + { + case 1: + case 5: + case 8: + case 9: + { + msg_print("You make a high-pitched shriek!"); + aggravate_monsters(1); + + break; + } + case 2: + case 6: + { + msg_print("Oops! You call a monster."); + summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0); + + break; + } + case 3: + case 7: + { + msg_print("The dungeon collapses!"); + earthquake(p_ptr->py, p_ptr->px, 5); + + break; + } + case 4: + { + msg_print("Your shriek is so horrible that you damage your health!"); + take_hit(damroll(p_ptr->lev / 5, 8), "inner hemorrhaging"); + + break; + } + } +} + + +/* + * Like all the random effect codes, this is *ugly*, + * and there is not a single line of comment, so I can't tell + * some fall throughs are really intended. Well, I know it's + * intended to be bizarre :) -- pelpel + */ +void wild_magic(int spell) +{ + int counter = 0; + int type = SUMMON_BIZARRE1 - 1 + randint(6); + + if (type < SUMMON_BIZARRE1) type = SUMMON_BIZARRE1; + else if (type > SUMMON_BIZARRE6) type = SUMMON_BIZARRE6; + + switch (randint(spell) + randint(8) + 1) + { + case 1: + case 2: + case 3: + { + teleport_player(10); + + break; + } + + case 4: + case 5: + case 6: + { + teleport_player(100); + + break; + } + + case 7: + case 8: + { + teleport_player(200); + + break; + } + + case 9: + case 10: + case 11: + { + unlite_area(10, 3); + + break; + } + + case 12: + case 13: + case 14: + { + lite_area(damroll(2, 3), 2); + + break; + } + + case 15: + { + destroy_doors_touch(); + + break; + } + + case 16: + case 17: + { + wall_breaker(); + + /* I don't think this is a fall through -- pelpel */ + break; + } + + case 18: + { + sleep_monsters_touch(); + + break; + } + + case 19: + case 20: + { + trap_creation(); + + break; + } + + case 21: + case 22: + { + door_creation(); + + break; + } + + case 23: + case 24: + case 25: + { + aggravate_monsters(1); + + break; + } + + case 26: + { + /* Prevent destruction of quest levels and town */ + if (!is_quest(dun_level) && dun_level) + earthquake(p_ptr->py, p_ptr->px, 5); + + break; + } + + case 27: + case 28: + { + break; + } + + case 29: + case 30: + { + apply_disenchant(0); + + break; + } + + case 31: + { + lose_all_info(); + + break; + } + + case 32: + { + fire_ball(GF_CHAOS, 0, spell + 5, 1 + (spell / 10)); + + break; + } + + case 33: + { + wall_stone(p_ptr->py, p_ptr->px); + + break; + } + + case 34: + case 35: + { + while (counter++ < 8) + { + (void) summon_specific(p_ptr->py, p_ptr->px, (dun_level * 3) / 2, type); + } + + break; + } + + case 36: + case 37: + { + activate_hi_summon(); + + break; + } + + case 38: + { + summon_cyber(); + + /* I don't think this is a fall through -- pelpel */ + break; + } + + default: + { + activate_ty_curse(); + } + } + + return; +} + + +/* + * Hack -- Determine if the player is wearing an artefact ring + * specified by art_type, that should be an index into a_info + */ +bool_ check_ring(int art_type) +{ + int i; + + + /* We are only interested in ring slots */ + i = INVEN_RING; + + /* Scan the list of rings until we reach the end */ + while (p_ptr->body_parts[i - INVEN_WIELD] == INVEN_RING) + { + /* Found the ring we were looking for */ + if (p_ptr->inventory[i].k_idx && (p_ptr->inventory[i].name1 == art_type)) + { + return (TRUE); + } + + /* Next item */ + i++; + } + + /* Found nothing */ + return (FALSE); +} + +/* + * Return the symbiote's name or description. + */ +cptr symbiote_name(bool_ capitalize) +{ + object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY]; + static char buf[80]; + + /* Make sure there actually is a symbiote there... */ + if (!o_ptr->k_idx) + { + strcpy(buf, "A non-existent symbiote"); + } + else + { + monster_race *r_ptr = &r_info[o_ptr->pval]; + cptr s = NULL; + + if (r_ptr->flags1 & RF1_UNIQUE) + { + /* Unique monster; no preceding "your", and ignore our name. */ + strncpy(buf, r_name + r_ptr->name, sizeof(buf)); + } + else if (o_ptr->note && + (s = strstr(quark_str(o_ptr->note), "#named ")) != NULL) + { + /* We've named it. */ + strncpy(buf, s + 7, sizeof(buf)); + } + else + { + /* No special cases, just return "Your ". */ + strcpy(buf, "your "); + strncpy(buf + 5, r_name + r_ptr->name, sizeof(buf) - 5); + } + } + + /* Just in case... */ + buf[sizeof(buf) - 1] = '\0'; + if (capitalize) buf[0] = toupper(buf[0]); + return buf; +} + +/* + * Use a power of the monster in symbiosis + */ +int use_symbiotic_power(int r_idx, bool_ great, bool_ only_number, bool_ no_cost) +{ + int power = -1; + + int num = 0, dir = 0 , i; + + int powers[96]; + + bool_ flag; + + int ask, plev = p_ptr->lev; + + char choice; + + char out_val[160]; + + monster_race *r_ptr = &r_info[r_idx]; + + int rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1); + + int x = p_ptr->px, y = p_ptr->py, k; + + int rad; + + int label; + + + /* List the monster powers -- RF4_* */ + for (i = 0; i < 32; i++) + { + if (r_ptr->flags4 & BIT(i)) + { + if (monster_powers[i].great && (!great)) continue; + if (!monster_powers[i].power) continue; + powers[num++] = i; + } + } + + /* List the monster powers -- RF5_* */ + for (i = 0; i < 32; i++) + { + if (r_ptr->flags5 & BIT(i)) + { + if (monster_powers[i + 32].great && (!great)) continue; + if (!monster_powers[i + 32].power) continue; + powers[num++] = i + 32; + } + } + + /* List the monster powers -- RF6_* */ + for (i = 0; i < 32; i++) + { + if (r_ptr->flags6 & BIT(i)) + { + if (monster_powers[i + 64].great && (!great)) continue; + if (!monster_powers[i + 64].power) continue; + powers[num++] = i + 64; + } + } + + if (!num) + { + msg_print("You have no powers you can use."); + return (0); + } + + if (only_number) return (num); + + /* Nothing chosen yet */ + flag = FALSE; + + /* Get the last label */ + label = (num <= 26) ? I2A(num - 1) : I2D(num - 1 - 26); + + /* Build a prompt (accept all spells) */ + /* Mega Hack -- if no_cost is false, we're actually a Possessor -dsb */ + strnfmt(out_val, 78, + "(Powers a-%c, ESC=exit) Use which power of your %s? ", + label, (no_cost ? "symbiote" : "body")); + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Get a spell from the user */ + while (!flag) + { + /* Show the list */ + { + byte y = 1, x = 0; + int ctr = 0; + char dummy[80]; + + strcpy(dummy, ""); + + prt ("", y++, x); + + while (ctr < num) + { + monster_power *mp_ptr = &monster_powers[powers[ctr]]; + int mana = mp_ptr->mana / 10; + + if (mana > p_ptr->msp) mana = p_ptr->msp; + + if (!mana) mana = 1; + + label = (ctr < 26) ? I2A(ctr) : I2D(ctr - 26); + + if (!no_cost) + { + strnfmt(dummy, 80, " %c) %2d %s", + label, mana, mp_ptr->name); + } + else + { + strnfmt(dummy, 80, " %c) %s", + label, mp_ptr->name); + } + + if (ctr < 17) + { + prt(dummy, y + ctr, x); + } + else + { + prt(dummy, y + ctr - 17, x + 40); + } + + ctr++; + } + + if (ctr < 17) + { + prt ("", y + ctr, x); + } + else + { + prt ("", y + 17, x); + } + } + + if (!get_com(out_val, &choice)) + { + flag = FALSE; + break; + } + + if (choice == '\r' && num == 1) + { + choice = 'a'; + } + + if (isalpha(choice)) + { + /* Note verify */ + ask = (isupper(choice)); + + /* Lowercase */ + if (ask) choice = tolower(choice); + + /* Extract request */ + i = (islower(choice) ? A2I(choice) : -1); + } + else + { + /* Can't uppercase digits XXX XXX XXX */ + ask = FALSE; + + i = choice - '0' + 26; + } + + /* Totally Illegal */ + if ((i < 0) || (i >= num)) + { + bell(); + continue; + } + + /* Save the spell index */ + power = powers[i]; + + /* Verify it */ + if (ask) + { + char tmp_val[160]; + + /* Prompt */ + strnfmt(tmp_val, 78, "Use %s? ", monster_powers[power].name); + + /* Belay that order */ + if (!get_check(tmp_val)) continue; + } + + /* Stop the loop */ + flag = TRUE; + } + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + + /* Abort if needed */ + if (!flag) + { + energy_use = 0; + return -1; + } + + /* 'Powerful' monsters have wider radii */ + if (r_ptr->flags2 & RF2_POWERFUL) + { + rad = 1 + (p_ptr->lev / 15); + } + else + { + rad = 1 + (p_ptr->lev / 20); + } + + + /* Analyse power */ + switch (power) + { + /**** RF4 (bit position) ****/ + + /* SHRIEK */ + case 0: + { + aggravate_monsters( -1); + + break; + } + + /* MULTIPLY */ + case 1: + { + do_cmd_wiz_named_friendly(p_ptr->body_monster, FALSE); + + break; + } + + /* S_ANIMAL */ + case 2: + { + summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE); + + break; + } + + /* ROCKET */ + case 3: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_ROCKET, dir, p_ptr->lev * 12, 1 + (p_ptr->lev / 20)); + + break; + } + + /* ARROW_1 */ + case 4: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_ARROW, dir, damroll(1, 6)); + + break; + } + + /* ARROW_2 */ + case 5: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_ARROW, dir, damroll(3, 6)); + + break; + } + + /* ARROW_3 */ + case 6: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_ARROW, dir, damroll(5, 6)); + + break; + } + + /* ARROW_4 */ + case 7: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_ARROW, dir, damroll(7, 6)); + + break; + } + + /* BR_ACID */ + case 8: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_ACID, dir, p_ptr->lev * 5, rad); + + break; + } + + /* BR_ELEC */ + case 9: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_ELEC, dir, p_ptr->lev * 5, rad); + + break; + } + + /* BR_FIRE */ + case 10: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_FIRE, dir, p_ptr->lev * 5, rad); + + break; + } + + /* BR_COLD */ + case 11: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_COLD, dir, p_ptr->lev * 5, rad); + + break; + } + + /* BR_POIS */ + case 12: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_POIS, dir, p_ptr->lev * 5, rad); + + break; + } + + /* BR_NETH */ + case 13: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_NETHER, dir, p_ptr->lev * 5, rad); + + break; + } + + /* BR_LITE */ + case 14: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_LITE, dir, p_ptr->lev * 8, rad); + + break; + } + + /* BR_DARK */ + case 15: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_DARK, dir, p_ptr->lev * 8, rad); + + break; + } + + /* BR_CONF */ + case 16: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_CONFUSION, dir, p_ptr->lev * 8, rad); + + break; + } + + /* BR_SOUN */ + case 17: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_SOUND, dir, p_ptr->lev * 8, rad); + + break; + } + + /* BR_CHAO */ + case 18: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_CHAOS, dir, p_ptr->lev * 7, rad); + + break; + } + + /* BR_DISE */ + case 19: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_DISENCHANT, dir, p_ptr->lev * 7, rad); + + break; + } + + /* BR_NEXU */ + case 20: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_NEXUS, dir, p_ptr->lev * 5, rad); + + break; + } + + /* BR_TIME */ + case 21: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_TIME, dir, p_ptr->lev * 3, rad); + + break; + } + + /* BR_INER */ + case 22: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_INERTIA, dir, p_ptr->lev * 4, rad); + + break; + } + + /* BR_GRAV */ + case 23: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_GRAVITY, dir, p_ptr->lev * 4, rad); + + break; + } + + /* BR_SHAR */ + case 24: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_SHARDS, dir, p_ptr->lev * 8, rad); + + break; + } + + /* BR_PLAS */ + case 25: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_PLASMA, dir, p_ptr->lev * 3, rad); + + break; + } + + /* BR_WALL */ + case 26: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_FORCE, dir, p_ptr->lev * 4, rad); + + break; + } + + /* BR_MANA */ + case 27: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_MANA, dir, p_ptr->lev * 5, rad); + + break; + } + + /* BA_NUKE */ + case 28: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_NUKE, dir, p_ptr->lev * 8, 1 + (p_ptr->lev / 20)); + + break; + } + + /* BR_NUKE */ + case 29: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_NUKE, dir, p_ptr->lev * 8, 1 + (p_ptr->lev / 20)); + + break; + } + + /* BA_CHAO */ + case 30: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_CHAOS, dir, p_ptr->lev * 4, 2); + + break; + } + + /* BR_DISI */ + case 31: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_DISINTEGRATE, dir, p_ptr->lev * 5, 1 + (p_ptr->lev / 20)); + + break; + } + + + /**** RF5 (bit position + 32) ****/ + + /* BA_ACID */ + case 32: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_ACID, dir, randint(p_ptr->lev * 6) + 20, 2); + + break; + } + + /* BA_ELEC */ + case 33: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_ELEC, dir, randint(p_ptr->lev * 3) + 20, 2); + + break; + } + + /* BA_FIRE */ + case 34: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_FIRE, dir, randint(p_ptr->lev * 7) + 20, 2); + + break; + } + + /* BA_COLD */ + case 35: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_COLD, dir, randint(p_ptr->lev * 3) + 20, 2); + + break; + } + + /* BA_POIS */ + case 36: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_POIS, dir, damroll(12, 2), 2); + + break; + } + + /* BA_NETH */ + case 37: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_NETHER, dir, randint(p_ptr->lev * 4) + 20, 2); + + break; + } + + /* BA_WATE */ + case 38: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_WATER, dir, randint(p_ptr->lev * 4) + 20, 2); + + break; + } + + /* BA_MANA */ + case 39: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_MANA, dir, randint(p_ptr->lev * 3) + 20, 2); + + break; + } + + /* BA_DARK */ + case 40: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_DARK, dir, randint(p_ptr->lev * 3) + 20, 2); + + break; + } + + /* 41 DRAIN_MANA -- Not available */ + + /* 42 MIND_BLAST -- Not available */ + + /* 43 BRAIN_SMASH -- Not available */ + + /* CAUSE_1 */ + case 44: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_MANA, dir, damroll(3, 8)); + + break; + } + + /* CAUSE_2 */ + case 45: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_MANA, dir, damroll(8, 8)); + + break; + } + + /* CAUSE_3 */ + case 46: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_MANA, dir, damroll(10, 15)); + + break; + } + + /* CAUSE_4 */ + case 47: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_MANA, dir, damroll(15, 15)); + + break; + } + + /* BO_ACID */ + case 48: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_ACID, dir, damroll(7, 8) + (p_ptr->lev / 3)); + + break; + } + + /* BO_ELEC */ + case 49: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_ELEC, dir, damroll(4, 8) + (p_ptr->lev / 3)); + + break; + } + + /* BO_FIRE */ + case 50: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_FIRE, dir, damroll(9, 8) + (p_ptr->lev / 3)); + + break; + } + + /* BO_COLD */ + case 51: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_COLD, dir, damroll(6, 8) + (p_ptr->lev / 3)); + + break; + } + + /* BO_POIS */ + case 52: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_POIS, dir, damroll(7, 8) + (p_ptr->lev / 3)); + + break; + } + + /* BO_NETH */ + case 53: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_NETHER, dir, damroll(5, 5) + (p_ptr->lev / 3)); + + break; + } + + /* BO_WATE */ + case 54: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_WATER, dir, damroll(10, 10) + (p_ptr->lev / 3)); + + break; + } + + /* BO_MANA */ + case 55: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_MANA, dir, damroll(3, 8) + (p_ptr->lev / 3)); + + break; + } + + /* BO_PLAS */ + case 56: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_PLASMA, dir, damroll(8, 8) + (p_ptr->lev / 3)); + + break; + } + + /* BO_ICEE */ + case 57: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_ICE, dir, damroll(6, 6) + (p_ptr->lev / 3)); + + break; + } + + /* MISSILE */ + case 58: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_MISSILE, dir, damroll(2, 6) + (p_ptr->lev / 3)); + + break; + } + + /* SCARE */ + case 59: + { + if (!get_aim_dir(&dir)) break; + + fear_monster(dir, plev); + + break; + } + + /* BLIND */ + case 60: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_CONFUSION, dir, damroll(1, 8) + (p_ptr->lev / 3)); + + break; + } + + /* CONF */ + case 61: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_CONFUSION, dir, damroll(7, 8) + (p_ptr->lev / 3)); + + break; + } + + /* SLOW */ + case 62: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_OLD_SLOW, dir, damroll(6, 8) + (p_ptr->lev / 3)); + + break; + } + + /* HOLD */ + case 63: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_OLD_SLEEP, dir, damroll(5, 8) + (p_ptr->lev / 3)); + + break; + } + + + /**** RF6 (bit position + 64) ****/ + + /* HASTE */ + case 64: + { + if (!p_ptr->fast) + { + (void)set_fast(randint(20 + (plev) ) + plev, 10); + } + else + { + (void)set_fast(p_ptr->fast + randint(5), 10); + } + + break; + } + + /* HAND_DOOM */ + case 65: + { + if (!get_aim_dir(&dir)) break; + + fire_bolt(GF_MANA, dir, damroll(10, 8) + (p_ptr->lev)); + + break; + } + + /* HEAL */ + case 66: + { + hp_player(damroll(8, 5)); + + break; + } + + /* S_ANIMALS */ + case 67: + { + for (k = 0; k < 4; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_ANIMAL, TRUE); + } + + break; + } + + /* BLINK */ + case 68: + { + if (dungeon_flags2 & DF2_NO_TELEPORT) + { + msg_print("No teleport on special levels..."); + break; + } + + teleport_player(10); + + break; + } + + /* TPORT */ + case 69: + { + if (dungeon_flags2 & DF2_NO_TELEPORT) + { + msg_print("No teleport on special levels..."); + break; + } + + teleport_player(plev * 5); + + break; + } + + /* TELE_TO */ + case 70: + { + int ii, ij; + + if (dungeon_flags2 & DF2_NO_TELEPORT) + { + msg_print("No teleport on special levels..."); + break; + } + + msg_print("You go between."); + + if (!tgt_pt(&ii, &ij)) break; + + p_ptr->energy -= 60 - plev; + + if (!cave_empty_bold(ij, ii) || + (cave[ij][ii].info & CAVE_ICKY) || + (distance(ij, ii, p_ptr->py, p_ptr->px) > plev * 20 + 2)) + { + msg_print("You fail to show the destination correctly!"); + p_ptr->energy -= 100; + teleport_player(10); + } + else teleport_player_to(ij, ii); + + break; + } + + /* TELE_AWAY */ + case 71: + { + if (dungeon_flags2 & DF2_NO_TELEPORT) + { + msg_print("No teleport on special levels..."); + break; + } + + if (!get_aim_dir(&dir)) break; + + (void)fire_beam(GF_AWAY_ALL, dir, plev); + + break; + } + + /* TELE_LEVEL */ + case 72: + { + if (dungeon_flags2 & DF2_NO_TELEPORT) + { + msg_print("No teleport on special levels..."); + break; + } + + teleport_player_level(); + + break; + } + + /* DARKNESS */ + case 73: + { + (void)project( -1, 3, p_ptr->py, p_ptr->px, 0, GF_DARK_WEAK, + PROJECT_GRID | PROJECT_KILL); + + /* Unlite the room */ + unlite_room(p_ptr->py, p_ptr->px); + + break; + } + + /* TRAPS */ + case 74: + { + trap_creation(); + + break; + } + + /* 75 FORGET -- Not available */ + + /* ANIM_DEAD -- Use the same code as the nether spell */ + case 76: + { + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_RAISE, dir, 1, 0); + + break; + } + + /* 77 S_BUG -- Not available, well we do that anyway ;) */ + + /* 78 S_RNG -- Not available, who dares? */ + + /* S_THUNDERLORD */ + case 79: + { + for (k = 0; k < 1; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_THUNDERLORD, TRUE); + } + + break; + } + + /* S_KIN -- Summon Kin, because we code bugs :) */ + case 80: + { + /* Big hack */ + summon_kin_type = r_ptr->d_char; + + for (k = 0; k < 6; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_KIN, TRUE); + } + + break; + } + + /* S_HI_DEMON */ + case 81: + { + for (k = 0; k < 1; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_HI_DEMON, TRUE); + } + + break; + } + + /* S_MONSTER */ + case 82: + { + for (k = 0; k < 1; k++) + { + summon_specific_friendly(y, x, rlev, 0, TRUE); + } + + break; + } + + /* S_MONSTERS */ + case 83: + { + for (k = 0; k < 6; k++) + { + summon_specific_friendly(y, x, rlev, 0, TRUE); + } + + break; + } + + /* S_ANT */ + case 84: + { + for (k = 0; k < 6; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_ANT, TRUE); + } + + break; + } + + /* S_SPIDER */ + case 85: + { + for (k = 0; k < 6; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_SPIDER, TRUE); + } + + break; + } + + /* S_HOUND */ + case 86: + { + for (k = 0; k < 6; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_HOUND, TRUE); + } + + break; + } + + /* S_HYDRA */ + case 87: + { + for (k = 0; k < 6; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_HYDRA, TRUE); + } + + break; + } + + /* S_ANGEL */ + case 88: + { + for (k = 0; k < 1; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_ANGEL, TRUE); + } + + break; + } + + /* S_DEMON */ + case 89: + { + for (k = 0; k < 1; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_DEMON, TRUE); + } + + break; + } + + /* S_UNDEAD */ + case 90: + { + for (k = 0; k < 1; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_UNDEAD, TRUE); + } + + break; + } + + /* S_DRAGON */ + case 91: + { + for (k = 0; k < 1; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_DRAGON, TRUE); + } + + break; + } + + /* S_HI_UNDEAD */ + case 92: + { + for (k = 0; k < 8; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_HI_UNDEAD_NO_UNIQUES, TRUE); + } + + break; + } + + /* S_HI_DRAGON */ + case 93: + { + for (k = 0; k < 8; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_HI_DRAGON_NO_UNIQUES, TRUE); + } + + break; + } + + /* S_WRAITH */ + case 94: + { + for (k = 0; k < 8; k++) + { + summon_specific_friendly(y, x, rlev, SUMMON_WRAITH, TRUE); + } + + break; + } + + /* 95 S_UNIQUE -- Not available */ + } + + /* Take some SP */ + if (!no_cost) + { + int chance, pchance; + + chance = (monster_powers[power].mana + r_ptr->level); + pchance = adj_str_wgt[p_ptr->stat_ind[A_WIS]] / 2 + get_skill(SKILL_POSSESSION); + + if (rand_int(chance) >= pchance) + { + int m = monster_powers[power].mana / 10; + + if (m > p_ptr->msp) m = p_ptr->msp; + if (!m) m = 1; + + p_ptr->csp -= m; + } + } + + /* Redraw mana */ + p_ptr->redraw |= (PR_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + + return (num); +} + +/* + * Schooled magic + */ + +/* + * Find a spell in any books/objects + */ +static int hack_force_spell = -1; +static object_type *hack_force_spell_obj = NULL; +bool_ get_item_hook_find_spell(int *item) +{ + int i, spell; + char buf[80]; + + strcpy(buf, "Manathrust"); + if (!get_string("Spell name? ", buf, 79)) + return FALSE; + + spell = find_spell(buf); + if (spell == -1) return FALSE; + + for (i = 0; i < INVEN_TOTAL; i++) + { + object_type *o_ptr = &p_ptr->inventory[i]; + u32b f1, f2, f3, f4, f5, esp; + + /* Must we wield it ? */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + if ((wield_slot(o_ptr) != -1) && (i < INVEN_WIELD) && (f5 & TR5_WIELD_CAST)) continue; + + /* Is it a non-book? */ + if (!is_school_book(o_ptr)) + { + u32b f1, f2, f3, f4, f5, esp; + + /* Extract object flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == spell)) + { + *item = i; + hack_force_spell = spell; + hack_force_spell_obj = o_ptr; + return TRUE; + } + } + /* A random book ? */ + else if (school_book_contains_spell(o_ptr->sval, spell)) + { + *item = i; + hack_force_spell = spell; + hack_force_spell_obj = o_ptr; + return TRUE; + } + } + return FALSE; +} + +/* + * Is the spell castable? + */ +bool_ is_ok_spell(s32b spell_idx, object_type *o_ptr) +{ + spell_type *spell = spell_at(spell_idx); + assert(o_ptr != NULL); + + if (get_level(spell_idx, 50, 0) == 0) + { + return FALSE; + } + + if (o_ptr->pval < spell_type_minimum_pval(spell)) + { + return FALSE; + } + + return TRUE; +} + + +/* + * Get a spell from a book + */ +s32b get_school_spell(cptr do_what, s16b force_book) +{ + int i, item; + s32b spell = -1; + int num = 0; + s32b where = 1; + int ask; + bool_ flag; + char out_val[160]; + char buf2[40]; + char buf3[40]; + object_type *o_ptr, forge; + int tmp; + int sval, pval; + u32b f1, f2, f3, f4, f5, esp; + + hack_force_spell = -1; + hack_force_spell_obj = NULL; + + /* Ok do we need to ask for a book ? */ + if (!force_book) + { + get_item_extra_hook = get_item_hook_find_spell; + item_tester_hook = hook_school_spellable; + sprintf(buf2, "You have no book to %s from", do_what); + sprintf(buf3, "%s from which book?", do_what); + if (!get_item(&item, buf3, buf2, USE_INVEN | USE_EQUIP | USE_EXTRA )) return -1; + + /* Get the item */ + o_ptr = get_object(item); + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* If it can be wielded, it must */ + if ((wield_slot(o_ptr) != -1) && (item < INVEN_WIELD) && (f5 & TR5_WIELD_CAST)) + { + msg_format("You cannot %s from that object; it must be wielded first.", do_what); + return -1; + } + } + else + { + o_ptr = &forge; + o_ptr->tval = TV_BOOK; + o_ptr->sval = force_book; + o_ptr->pval = 0; + } + + if (repeat_pull(&tmp)) + { + return tmp; + } + + /* Nothing chosen yet */ + flag = FALSE; + + /* Show choices */ + window_stuff(); + + /* No spell to cast by default */ + spell = -1; + + /* Is it a random book, or something else ? */ + if (is_school_book(o_ptr)) + { + sval = o_ptr->sval; + pval = o_ptr->pval; + } + else + { + sval = 255; + pval = o_ptr->pval2; + } + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Go */ + if (hack_force_spell == -1) + { + num = school_book_length(sval); + + /* Build a prompt (accept all spells) */ + strnfmt(out_val, 78, "(Spells %c-%c, Descs %c-%c, ESC=exit) %^s which spell? ", + I2A(0), I2A(num - 1), I2A(0) - 'a' + 'A', I2A(num - 1) - 'a' + 'A', do_what); + + /* Get a spell from the user */ + while (!flag) + { + char choice; + + /* Restore and save screen; this prevents + subprompt from leaving garbage when going + around the loop multiple times. */ + Term_load(); + Term_save(); + + /* Display a list of spells */ + where = print_book(sval, pval, o_ptr); + + /* Input */ + if (!get_com(out_val, &choice)) + { + flag = FALSE; + break; + } + + /* Note verify */ + ask = (isupper(choice)); + + /* Lowercase */ + if (ask) choice = tolower(choice); + + /* Extract request */ + i = (islower(choice) ? A2I(choice) : -1); + + /* Totally Illegal */ + if ((i < 0) || (i >= num)) + { + bell(); + continue; + } + + /* Verify it */ + if (ask) + { + /* Display a list of spells */ + where = print_book(sval, pval, o_ptr); + print_spell_desc(spell_x(sval, pval, i), where); + } + else + { + bool_ ok; + + /* Save the spell index */ + spell = spell_x(sval, pval, i); + + /* Do we need to do some pre test */ + ok = is_ok_spell(spell, o_ptr); + + /* Require "okay" spells */ + if (!ok) + { + bell(); + msg_format("You may not %s that spell.", do_what); + spell = -1; + continue; + } + + /* Stop the loop */ + flag = TRUE; + } + } + } + else + { + bool_ ok; + + /* Require "okay" spells */ + ok = is_ok_spell(hack_force_spell, hack_force_spell_obj); + if (ok) + { + flag = TRUE; + spell = hack_force_spell; + } + else + { + bell(); + msg_format("You may not %s that spell.", do_what); + spell = -1; + } + } + + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + + + /* Show choices */ + window_stuff(); + + + /* Abort if needed */ + if (!flag) return -1; + + tmp = spell; + repeat_push(tmp); + return spell; +} + +void cast_school_spell() +{ + int spell; + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + spell = get_school_spell("cast", 0); + + /* Actualy cast the choice */ + if (spell != -1) + { + lua_cast_school_spell(spell, FALSE); + } +} + +void browse_school_spell(int book, int pval, object_type *o_ptr) +{ + int i; + int num = 0, where = 1; + int ask; + char choice; + char out_val[160]; + + /* Show choices */ + window_stuff(); + + num = school_book_length(book); + + /* Build a prompt (accept all spells) */ + strnfmt(out_val, 78, "(Spells %c-%c, ESC=exit) cast which spell? ", + I2A(0), I2A(num - 1)); + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Display a list of spells */ + where = print_book(book, pval, o_ptr); + + /* Get a spell from the user */ + while (get_com(out_val, &choice)) + { + /* Display a list of spells */ + where = print_book(book, pval, o_ptr); + + /* Note verify */ + ask = (isupper(choice)); + + /* Lowercase */ + if (ask) choice = tolower(choice); + + /* Extract request */ + i = (islower(choice) ? A2I(choice) : -1); + + /* Totally Illegal */ + if ((i < 0) || (i >= num)) + { + bell(); + continue; + } + + /* Restore the screen */ + Term_load(); + + /* Display a list of spells */ + where = print_book(book, pval, o_ptr); + print_spell_desc(spell_x(book, pval, i), where); + } + + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + + /* Show choices */ + window_stuff(); +} + +/* Can it contains a schooled spell ? */ +static bool_ hook_school_can_spellable(object_type *o_ptr) +{ + u32b f1, f2, f3, f4, f5, esp; + + /* Extract object flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == -1)) + return TRUE; + return FALSE; +} + +/* + * Copy a spell from a bok to an object + */ +void do_cmd_copy_spell() +{ + int spell = get_school_spell("copy", 0); + int item; + object_type *o_ptr; + + if (spell == -1) return; + + /* Spells that cannot be randomly created cannot be copied */ + if (spell_type_random_type(spell_at(spell)) <= 0) + { + msg_print("This spell cannot be copied."); + return; + } + + item_tester_hook = hook_school_can_spellable; + if (!get_item(&item, "Copy to which object? ", "You have no object to copy to.", (USE_INVEN | USE_EQUIP))) return; + o_ptr = get_object(item); + + msg_print("You copy the spell!"); + o_ptr->pval2 = spell; + inven_item_describe(item); +} diff --git a/src/cmd6.c b/src/cmd6.c deleted file mode 100644 index b8a9f1df..00000000 --- a/src/cmd6.c +++ /dev/null @@ -1,7792 +0,0 @@ -/* File: cmd6.c */ - -/* Purpose: Object commands */ - -/* - * 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 "angband.h" - -#include "spell_type.h" -#include "hooks.h" - -/* - * Forward declare - */ -static bool_ activate_spell(object_type * o_ptr, byte choice); - - -/* - * General function to find an item by its name - */ -cptr get_item_hook_find_obj_what; -bool_ get_item_hook_find_obj(int *item) -{ - int i; - char buf[80]; - char buf2[100]; - - strcpy(buf, ""); - if (!get_string(get_item_hook_find_obj_what, buf, 79)) - return FALSE; - - for (i = 0; i < INVEN_TOTAL; i++) - { - object_type *o_ptr = &p_ptr->inventory[i]; - - if (!item_tester_okay(o_ptr)) continue; - - object_desc(buf2, o_ptr, -1, 0); - if (!strcmp(buf, buf2)) - { - *item = i; - return TRUE; - } - } - return FALSE; -} - - -/* - * This file includes code for eating food, drinking potions, - * reading scrolls, aiming wands, using staffs, zapping rods, - * and activating artifacts. - * - * In all cases, if the player becomes "aware" of the item's use - * by testing it, mark it as "aware" and reward some experience - * based on the object's level, always rounding up. If the player - * remains "unaware", mark that object "kind" as "tried". - * - * This code now correctly handles the unstacking of wands, staffs, - * and rods. Note the overly paranoid warning about potential pack - * overflow, which allows the player to use and drop a stacked item. - * - * In all "unstacking" scenarios, the "used" object is "carried" as if - * the player had just picked it up. In particular, this means that if - * the use of an item induces pack overflow, that item will be dropped. - * - * For simplicity, these routines induce a full "pack reorganization" - * which not only combines similar items, but also reorganizes various - * items to obey the current "sorting" method. This may require about - * 400 item comparisons, but only occasionally. - * - * There may be a BIG problem with any "effect" that can cause "changes" - * to the p_ptr->inventory. For example, a "scroll of recharging" can cause - * a wand/staff to "disappear", moving the p_ptr->inventory up. Luckily, the - * scrolls all appear BEFORE the staffs/wands, so this is not a problem. - * But, for example, a "staff of recharging" could cause MAJOR problems. - * In such a case, it will be best to either (1) "postpone" the effect - * until the end of the function, or (2) "change" the effect, say, into - * giving a staff "negative" charges, or "turning a staff into a stick". - * It seems as though a "rod of recharging" might in fact cause problems. - * The basic problem is that the act of recharging (and destroying) an - * item causes the inducer of that action to "move", causing "o_ptr" to - * no longer point at the correct item, with horrifying results. - * - * Note that food/potions/scrolls no longer use bit-flags for effects, - * but instead use the "sval" (which is also used to sort the objects). - */ - - -/* - * Determine the effects of eating a corpse. A corpse can be - * eaten whole or cut into pieces for later. - */ -static void corpse_effect(object_type *o_ptr, bool_ cutting) -{ - monster_race *r_ptr = &r_info[o_ptr->pval2]; - - /* Assume no bad effects */ - bool_ harmful = FALSE; - - byte method, effect, d_dice, d_side; - - int i, dam, idam = 0, mdam, brpow, brdam = 0; - - - /* How much of the monster's breath attack remains */ - if (o_ptr->pval <= r_ptr->weight) - { - brpow = 0; - } - else - { - brpow = (o_ptr->pval - r_ptr->weight) / 5; - if (brpow > (r_ptr->weight / 5)) brpow = r_ptr->weight / 5; - } - - if (o_ptr->weight <= 0) o_ptr->weight = 1; - if (o_ptr->pval <= 0) o_ptr->pval = 1; - - /* - * The breath is only discharged by accident or by slicing off pieces - * of meat, and only by corpses. - */ - if ((o_ptr->sval != SV_CORPSE_CORPSE) || - (rand_int(o_ptr->weight / 5) && !cutting)) brpow = 0; - - /* Immediate effects - poison, acid, fire, etc. */ - if (!cutting) - { - for (i = 0; i < 4; i++) - { - /* skip empty blow slot */ - if (!r_ptr->blow[i].method) continue; - - method = r_ptr->blow[i].method; - effect = r_ptr->blow[i].effect; - d_dice = r_ptr->blow[i].d_dice; - d_side = r_ptr->blow[i].d_side; - dam = damroll(d_dice, d_side) * o_ptr->pval / o_ptr->weight / 2; - idam = damroll(d_dice, d_side) * - ((o_ptr->weight / o_ptr->pval > 2) ? - o_ptr->weight / o_ptr->pval : 2); - mdam = maxroll(d_dice, d_side) * 2; - - /* Analyse method */ - switch (method) - { - /* Methods that are meaningless after death */ - case RBM_BITE: - case RBM_STING: - case RBM_ENGULF: - case RBM_DROOL: - case RBM_SPIT: - case RBM_GAZE: - case RBM_WAIL: - case RBM_BEG: - case RBM_INSULT: - case RBM_MOAN: - { - continue; - } - } - - /* Analyse effect */ - switch (effect) - { - /* Effects that are meaningless after death */ - case RBE_HURT: - case RBE_UN_BONUS: - case RBE_UN_POWER: - case RBE_EAT_GOLD: - case RBE_EAT_ITEM: - case RBE_EAT_FOOD: - case RBE_EAT_LITE: - case RBE_ELEC: - case RBE_COLD: - case RBE_SHATTER: - { - break; - } - - case RBE_POISON: - { - if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) - { - set_poisoned(p_ptr->poisoned + dam + idam + 10); - harmful = TRUE; - } - - break; - } - - case RBE_ACID: - { - /* Total Immunity */ - if (!(p_ptr->immune_acid || (dam <= 0))) - { - /* Resist the damage */ - if (p_ptr->resist_acid) dam = (dam + 2) / 3; - if (p_ptr->oppose_acid) dam = (dam + 2) / 3; - - /* Take damage */ - take_hit(dam, "acidic food"); - harmful = TRUE; - } - else - { - set_oppose_acid(p_ptr->oppose_acid + idam); - } - - break; - } - - case RBE_FIRE: - { - /* Totally immune */ - if (p_ptr->immune_fire || (dam <= 0)) - { - /* Resist the damage */ - if (p_ptr->resist_fire) dam = (dam + 2) / 3; - if (p_ptr->oppose_fire) dam = (dam + 2) / 3; - - /* Take damage */ - take_hit(dam, "a fiery meal"); - harmful = TRUE; - } - else - { - set_oppose_fire(p_ptr->oppose_fire + idam); - } - - break; - } - - case RBE_BLIND: - { - if (!p_ptr->resist_blind) - { - set_blind(p_ptr->blind + dam * 2 + idam * 2 + 20); - } - - break; - } - - case RBE_CONFUSE: - { - if (!p_ptr->resist_conf) - { - set_confused(p_ptr->confused + dam + idam + 10); - } - if (!p_ptr->resist_chaos && rand_int(mdam - dam)) - { - set_image(p_ptr->image + dam * 10 + idam * 10 + 100); - } - - break; - } - - case RBE_HALLU: - { - if (!p_ptr->resist_chaos && rand_int(mdam - dam)) - { - set_image(p_ptr->image + dam * 10 + idam * 10 + 50); - } - - break; - } - - case RBE_TERRIFY: - { - if (!p_ptr->resist_fear) - { - set_afraid(p_ptr->afraid + dam + idam + 10); - } - - break; - } - - case RBE_PARALYZE: - { - if (!p_ptr->free_act) - { - set_paralyzed(dam + idam + 10); - } - - break; - } - - case RBE_LOSE_STR: - { - do_dec_stat(A_STR, STAT_DEC_NORMAL); - - break; - } - - case RBE_LOSE_INT: - { - do_dec_stat(A_INT, STAT_DEC_NORMAL); - - break; - } - - case RBE_LOSE_WIS: - { - do_dec_stat(A_WIS, STAT_DEC_NORMAL); - - break; - } - - case RBE_LOSE_DEX: - { - do_dec_stat(A_DEX, STAT_DEC_NORMAL); - - break; - } - - case RBE_LOSE_CON: - { - do_dec_stat(A_CON, STAT_DEC_NORMAL); - - break; - } - - case RBE_LOSE_CHR: - { - do_dec_stat(A_CHR, STAT_DEC_NORMAL); - - break; - } - - /* Don't eat Morgoth's corpse :) */ - case RBE_LOSE_ALL: - { - do_dec_stat(A_STR, STAT_DEC_NORMAL); - do_dec_stat(A_INT, STAT_DEC_NORMAL); - do_dec_stat(A_WIS, STAT_DEC_NORMAL); - do_dec_stat(A_DEX, STAT_DEC_NORMAL); - do_dec_stat(A_CON, STAT_DEC_NORMAL); - do_dec_stat(A_CHR, STAT_DEC_NORMAL); - o_ptr->pval = 1; - - break; - } - - case RBE_SANITY: - { - msg_print("You feel your sanity slipping away!"); - take_sanity_hit(dam, "eating an insane monster"); - - break; - } - - /* Unlife is bad to eat */ - case RBE_EXP_10: - { - msg_print("A black aura surrounds the corpse!"); - - if (p_ptr->hold_life && (rand_int(100) < 50)) - { - msg_print("You keep hold of your life force!"); - } - else - { - s32b d = damroll(10, 6) + - (p_ptr->exp / 100) * MON_DRAIN_LIFE; - - if (p_ptr->hold_life) - { - msg_print("You feel your life slipping away!"); - lose_exp(d / 10); - } - else - { - msg_print("You feel your life draining away!"); - lose_exp(d); - } - } - - o_ptr->pval = 1; - - break; - } - - case RBE_EXP_20: - { - msg_print("A black aura surrounds the corpse!"); - - if (p_ptr->hold_life && (rand_int(100) < 50)) - { - msg_print("You keep hold of your life force!"); - } - else - { - s32b d = damroll(20, 6) + - (p_ptr->exp / 100) * MON_DRAIN_LIFE; - - if (p_ptr->hold_life) - { - msg_print("You feel your life slipping away!"); - lose_exp(d / 10); - } - else - { - msg_print("You feel your life draining away!"); - lose_exp(d); - } - } - - o_ptr->pval = 1; - - break; - } - - case RBE_EXP_40: - { - msg_print("A black aura surrounds the corpse!"); - - if (p_ptr->hold_life && (rand_int(100) < 50)) - { - msg_print("You keep hold of your life force!"); - } - else - { - s32b d = damroll(40, 6) + - (p_ptr->exp / 100) * MON_DRAIN_LIFE; - - if (p_ptr->hold_life) - { - msg_print("You feel your life slipping away!"); - lose_exp(d / 10); - } - else - { - msg_print("You feel your life draining away!"); - lose_exp(d); - } - } - - o_ptr->pval = 1; - - break; - } - - case RBE_EXP_80: - { - msg_print("A black aura surrounds the corpse!"); - - if (p_ptr->hold_life && (rand_int(100) < 50)) - { - msg_print("You keep hold of your life force!"); - } - else - { - s32b d = damroll(80, 6) + - (p_ptr->exp / 100) * MON_DRAIN_LIFE; - - if (p_ptr->hold_life) - { - msg_print("You feel your life slipping away!"); - lose_exp(d / 10); - } - else - { - msg_print("You feel your life draining away!"); - lose_exp(d); - } - } - - o_ptr->pval = 1; - - break; - } - } - } - } /* if (!cutting) */ - - - /* - * The organ that supplies breath attacks is not - * immediately emptied upon death, although some types - * of breath have no effect. - * AMHD's make rather risky meals, and deadly snacks. - */ - - /* Acid */ - if (r_ptr->flags4 & RF4_BR_ACID && brpow > 0) - { - brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3)); - - msg_print("You are hit by a gush of acid!"); - - /* Total Immunity */ - if (!(p_ptr->immune_acid || (brdam <= 0))) - { - /* Take damage */ - acid_dam(brdam, "a gush of acid"); - harmful = TRUE; - } - o_ptr->pval = 1; - } - else if (r_ptr->flags4 & RF4_BR_ACID) - { - set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10); - } - - /* Electricity */ - if (r_ptr->flags4 & RF4_BR_ELEC && brpow > 0) - { - brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3)); - - msg_print("You receive a heavy shock!"); - - /* Total Immunity */ - if (!(p_ptr->immune_elec || (brdam <= 0))) - { - /* Take damage */ - elec_dam(brdam, "an electric shock"); - harmful = TRUE; - } - o_ptr->weight = o_ptr->weight - brpow; - o_ptr->pval = o_ptr->weight; - } - else if (r_ptr->flags4 & RF4_BR_ELEC) - { - set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10); - } - - /* Fire */ - if (r_ptr->flags4 & RF4_BR_FIRE && brpow > 0) - { - brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3)); - - msg_print("Roaring flames engulf you!"); - - /* Total Immunity */ - if (!(p_ptr->immune_fire || (brdam <= 0))) - { - /* Take damage */ - fire_dam(brdam, "an explosion"); - harmful = TRUE; - } - o_ptr->pval = 1; - } - else if (r_ptr->flags4 & RF4_BR_FIRE) - { - set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10); - } - - /* Cold */ - if (r_ptr->flags4 & RF4_BR_COLD && brpow > 0) - { - brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3)); - - msg_print("You are caught in a freezing liquid!"); - - /* Total Immunity */ - if (!(p_ptr->immune_cold || (brdam <= 0))) - { - /* Take damage */ - cold_dam(brdam, "a chilling blast"); - harmful = TRUE; - } - o_ptr->weight = o_ptr->weight - brpow; - o_ptr->pval = o_ptr->weight; - } - else if (r_ptr->flags4 & RF4_BR_COLD) - { - set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10); - } - - /* Poison */ - if (r_ptr->flags4 & RF4_BR_POIS && brpow > 0) - { - brdam = ((brpow / 3) > 800 ? 800 : (brpow / 3)); - - msg_print("You are surrounded by toxic gases!"); - - /* Resist the damage */ - if (p_ptr->resist_pois) brdam = (brdam + 2) / 3; - if (p_ptr->oppose_pois) brdam = (brdam + 2) / 3; - - if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) - { - (void)set_poisoned(p_ptr->poisoned + rand_int(brdam) + 10); - } - - /* Take damage */ - take_hit(brdam, "toxic gases"); - o_ptr->weight = o_ptr->weight - brpow; - o_ptr->pval = o_ptr->weight; - harmful = TRUE; - } - - /* Nether */ - if (r_ptr->flags4 & RF4_BR_NETH && brpow > 0) - { - brdam = ((brpow / 6) > 550 ? 550 : (brpow / 6)); - - msg_print("A black aura surrounds the corpse!"); - - if (p_ptr->resist_neth) - { - brdam *= 6; - brdam /= (randint(6) + 6); - } - else - { - if (p_ptr->hold_life && (rand_int(100) < 75)) - { - msg_print("You keep hold of your life force!"); - } - else if (p_ptr->hold_life) - { - msg_print("You feel your life slipping away!"); - lose_exp(200 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE); - } - else - { - msg_print("You feel your life draining away!"); - lose_exp(200 + (p_ptr->exp / 100) * MON_DRAIN_LIFE); - } - } - - /* Take damage */ - take_hit(brdam, "an unholy blast"); - harmful = TRUE; - o_ptr->weight = o_ptr->weight - brpow; - o_ptr->pval = o_ptr->weight; - } - - /* Confusion */ - if (r_ptr->flags4 & RF4_BR_CONF && brpow > 0) - { - msg_print("A strange liquid splashes on you!"); - - if (!p_ptr->resist_conf) - { - set_confused(p_ptr->confused + brdam + idam + 10); - } - o_ptr->weight = o_ptr->weight - brpow; - o_ptr->pval = o_ptr->weight; - } - - /* Chaos */ - if (r_ptr->flags4 & RF4_BR_CHAO && brpow > 0) - { - brdam = ((brpow / 6) > 600 ? 600 : (brpow / 6)); - - msg_print("A swirling cloud surrounds you!"); - - if (p_ptr->resist_chaos) - { - brdam *= 6; - brdam /= (randint(6) + 6); - } - - if (!p_ptr->resist_conf) - { - (void)set_confused(p_ptr->confused + rand_int(20) + 10); - } - - if (!p_ptr->resist_chaos) - { - (void)set_image(p_ptr->image + randint(10)); - } - - if (!p_ptr->resist_neth && !p_ptr->resist_chaos) - { - if (p_ptr->hold_life && (rand_int(100) < 75)) - { - msg_print("You keep hold of your life force!"); - } - else if (p_ptr->hold_life) - { - msg_print("You feel your life slipping away!"); - lose_exp(500 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE); - } - else - { - msg_print("You feel your life draining away!"); - lose_exp(5000 + (p_ptr->exp / 100) * MON_DRAIN_LIFE); - } - } - - /* Take damage */ - take_hit(brdam, "chaotic forces"); - o_ptr->pval = 1; - } - - /* Disenchantment */ - if (r_ptr->flags4 & RF4_BR_DISE && brpow > 0) - { - brdam = ((brpow / 6) > 500 ? 500 : (brpow / 6)); - - msg_print("You are blasted by raw mana!"); - - if (p_ptr->resist_disen) - { - brdam *= 6; - brdam /= (randint(6) + 6); - } - else - { - (void)apply_disenchant(0); - } - - /* Take damage */ - take_hit(brdam, "raw mana"); - o_ptr->pval = 1; - } - - /* Plasma */ - if (r_ptr->flags4 & RF4_BR_PLAS && brpow > 0) - { - brdam = ((brpow / 6) > 150 ? 150 : (brpow / 6)); - - msg_print("Searing flames engulf the corpse!"); - - /* Resist the damage */ - if (p_ptr->resist_fire || p_ptr->oppose_fire) brdam = (brdam + 2) / 3; - - if (!p_ptr->resist_sound) - { - int k = (randint((brdam > 40) ? 35 : (brdam * 3 / 4 + 5))); - (void)set_stun(p_ptr->stun + k); - } - - /* Take damage */ - take_hit(brdam, "an explosion"); - harmful = TRUE; - o_ptr->pval = 1; - } - - /* Hack -- Jellies are immune to acid only if they are already acidic */ - if (strchr("j", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_ACID)) - { - dam = damroll(8, 8); - - /* Total Immunity */ - if (!(p_ptr->immune_acid || (dam <= 0))) - { - /* Resist the damage */ - if (p_ptr->resist_acid) dam = (dam + 2) / 3; - if (p_ptr->oppose_acid) dam = (dam + 2) / 3; - - /* Take damage */ - take_hit(dam, "acidic food"); - } - harmful = TRUE; - } - - /* - * Hack -- Jellies, kobolds, spiders, icky things, molds, and mushrooms - * are immune to poison because their body already contains - * poisonous chemicals. - */ - if (strchr("ijkmS,", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_POIS)) - { - if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) - { - set_poisoned(p_ptr->poisoned + rand_int(15) + 10); - } - harmful = TRUE; - } - - /* - * Bad effects override good effects - * and hacked-up corpses lose intrinsics. - */ - if (!harmful && !cutting && (o_ptr->sval != SV_CORPSE_MEAT)) - { - if (r_ptr->flags3 & RF3_IM_ACID) - { - set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10); - } - if (r_ptr->flags3 & RF3_IM_ELEC) - { - set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10); - } - if (r_ptr->flags3 & RF3_IM_FIRE) - { - set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10); - } - if (r_ptr->flags3 & RF3_IM_COLD) - { - set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10); - } - if (r_ptr->flags3 & RF3_IM_POIS) - { - set_oppose_pois(p_ptr->oppose_pois + rand_int(10) + 10); - } - if (r_ptr->flags3 & RF3_RES_NETH) - { - set_protevil(p_ptr->protevil + rand_int(25) + 3 * r_ptr->level); - } - if (r_ptr->flags3 & RF3_RES_PLAS) - { - set_oppose_fire(p_ptr->oppose_fire + rand_int(20) + 20); - } - if (r_ptr->flags2 & RF2_SHAPECHANGER) - { - /* DGDGDG (void)set_mimic(20 , rand_int(MIMIC_VALAR)); */ - } - - if (r_ptr->flags3 & RF3_DEMON) - { - /* DGDGDG (void)set_mimic(30 , MIMIC_DEMON); */ - } - - if (r_ptr->flags3 & RF3_UNDEAD) - { - /* DGDGDG (void)set_mimic(30 , MIMIC_VAMPIRE); */ - } - - if (r_ptr->flags3 & RF3_NO_FEAR) - { - (void)set_afraid(0); - } - if (r_ptr->flags3 & RF3_NO_STUN) - { - (void)set_stun(0); - } - if (r_ptr->flags3 & RF3_NO_CONF) - { - (void)set_confused(0); - } - if (r_ptr->flags6 & RF6_S_THUNDERLORD) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_THUNDERLORD, FALSE); - } - if (r_ptr->flags6 & RF6_S_DEMON) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DEMON, FALSE); - } - if (r_ptr->flags6 & RF6_S_KIN) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_KIN, FALSE); - } - if (r_ptr->flags6 & RF6_S_HI_DEMON) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DEMON, FALSE); - } - if (r_ptr->flags6 & RF6_S_MONSTER) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE); - } - if (r_ptr->flags6 & RF6_S_MONSTERS) - { - int k; - for (k = 0; k < 8; k++) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE); - } - } - if (r_ptr->flags6 & RF6_S_UNDEAD) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD, FALSE); - } - if (r_ptr->flags6 & RF6_S_DRAGON) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DRAGON, FALSE); - } - if (r_ptr->flags6 & RF6_S_ANT) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANT, FALSE); - } - if (r_ptr->flags6 & RF6_S_SPIDER) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_SPIDER, FALSE); - } - if (r_ptr->flags6 & RF6_S_HOUND) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HOUND, FALSE); - } - if (r_ptr->flags6 & RF6_S_HYDRA) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HYDRA, FALSE); - } - if (r_ptr->flags6 & RF6_S_ANGEL) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANGEL, FALSE); - } - if (r_ptr->flags6 & RF6_S_HI_DRAGON) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DRAGON, FALSE); - } - if (r_ptr->flags6 & RF6_S_HI_UNDEAD) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_UNDEAD, FALSE); - } - if (r_ptr->flags6 & RF6_S_WRAITH) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_WRAITH, FALSE); - } - if (r_ptr->flags6 & RF6_S_UNIQUE) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNIQUE, FALSE); - } - } -} - - -/* - * Hook to determine if an object is eatable - */ -static bool_ item_tester_hook_eatable(object_type *o_ptr) -{ - /* Foods and, well, corpses are edible */ - if ((o_ptr->tval == TV_FOOD) || (o_ptr->tval == TV_CORPSE)) return (TRUE); - - /* Assume not */ - return (FALSE); -} - - -/* - * Eat some food (from the pack or floor) - */ -void do_cmd_eat_food(void) -{ - int item, ident, lev, fval = 0; - - object_type *o_ptr; - object_type *q_ptr, forge; - - monster_race *r_ptr; - - cptr q, s; - - bool_ destroy = TRUE; - - - /* Restrict choices to food */ - item_tester_hook = item_tester_hook_eatable; - - /* Set up the extra finder */ - get_item_hook_find_obj_what = "Food full name? "; - get_item_extra_hook = get_item_hook_find_obj; - - /* Get an item */ - q = "Eat which item? "; - s = "You have nothing to eat."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Sound */ - sound(SOUND_EAT); - - - /* Take a turn */ - energy_use = 100; - - /* Identity not known yet */ - ident = FALSE; - - /* Object level */ - lev = k_info[o_ptr->k_idx].level; - - /* Scripted foods */ - hook_eat_in in = { o_ptr }; - hook_eat_out out = { FALSE }; - - if (process_hooks_ret(HOOK_EAT, "d", "(O)", o_ptr)) - { - ident = process_hooks_return[0].num; - } - else if (process_hooks_new(HOOK_EAT, &in, &out)) - { - ident = out.ident; - } - /* (not quite) Normal foods */ - else if (o_ptr->tval == TV_FOOD) - { - /* Analyze the food */ - switch (o_ptr->sval) - { - case SV_FOOD_GREAT_HEALTH: - { - p_ptr->hp_mod += 70; - msg_print("As you eat it you begin to feel your life flow getting stronger."); - ident = TRUE; - p_ptr->update |= (PU_HP); - - break; - } - - case SV_FOOD_POISON: - { - if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) - { - if (set_poisoned(p_ptr->poisoned + rand_int(10) + 10)) - { - ident = TRUE; - } - } - - break; - } - - case SV_FOOD_BLINDNESS: - { - if (!p_ptr->resist_blind) - { - if (set_blind(p_ptr->blind + rand_int(200) + 200)) - { - ident = TRUE; - } - } - - break; - } - - case SV_FOOD_PARANOIA: - { - if (!p_ptr->resist_fear) - { - if (set_afraid(p_ptr->afraid + rand_int(10) + 10)) - { - ident = TRUE; - } - } - - break; - } - - case SV_FOOD_CONFUSION: - { - if (!p_ptr->resist_conf) - { - if (set_confused(p_ptr->confused + rand_int(10) + 10)) - { - ident = TRUE; - } - } - - break; - } - - case SV_FOOD_HALLUCINATION: - { - if (!p_ptr->resist_chaos) - { - if (set_image(p_ptr->image + rand_int(250) + 250)) - { - ident = TRUE; - } - } - - break; - } - - case SV_FOOD_PARALYSIS: - { - if (!p_ptr->free_act) - { - if (set_paralyzed(rand_int(10) + 10)) - { - ident = TRUE; - } - } - - break; - } - - case SV_FOOD_WEAKNESS: - { - take_hit(damroll(6, 6), "poisonous food"); - (void)do_dec_stat(A_STR, STAT_DEC_NORMAL); - - ident = TRUE; - - break; - } - - case SV_FOOD_SICKNESS: - { - take_hit(damroll(6, 6), "poisonous food"); - (void)do_dec_stat(A_CON, STAT_DEC_NORMAL); - - ident = TRUE; - - break; - } - - case SV_FOOD_STUPIDITY: - { - take_hit(damroll(8, 8), "poisonous food"); - (void)do_dec_stat(A_INT, STAT_DEC_NORMAL); - - ident = TRUE; - - break; - } - - case SV_FOOD_NAIVETY: - { - take_hit(damroll(8, 8), "poisonous food"); - (void)do_dec_stat(A_WIS, STAT_DEC_NORMAL); - - ident = TRUE; - - break; - } - - case SV_FOOD_UNHEALTH: - { - take_hit(damroll(10, 10), "poisonous food"); - (void)do_dec_stat(A_CON, STAT_DEC_NORMAL); - - ident = TRUE; - - break; - } - - case SV_FOOD_DISEASE: - { - take_hit(damroll(10, 10), "poisonous food"); - (void)do_dec_stat(A_STR, STAT_DEC_NORMAL); - - ident = TRUE; - - break; - } - - case SV_FOOD_CURE_POISON: - { - if (set_poisoned(0)) ident = TRUE; - - break; - } - - case SV_FOOD_CURE_BLINDNESS: - { - if (set_blind(0)) ident = TRUE; - - break; - } - - case SV_FOOD_CURE_PARANOIA: - { - if (set_afraid(0)) ident = TRUE; - - break; - } - - case SV_FOOD_CURE_CONFUSION: - { - if (set_confused(0)) ident = TRUE; - - break; - } - - case SV_FOOD_CURE_SERIOUS: - { - if (hp_player(damroll(4, 8))) ident = TRUE; - - break; - } - - case SV_FOOD_RESTORE_STR: - { - if (do_res_stat(A_STR, TRUE)) ident = TRUE; - - break; - } - - case SV_FOOD_RESTORE_CON: - { - if (do_res_stat(A_CON, TRUE)) ident = TRUE; - - break; - } - - case SV_FOOD_RESTORING: - { - if (do_res_stat(A_STR, TRUE)) ident = TRUE; - if (do_res_stat(A_INT, TRUE)) ident = TRUE; - if (do_res_stat(A_WIS, TRUE)) ident = TRUE; - if (do_res_stat(A_DEX, TRUE)) ident = TRUE; - if (do_res_stat(A_CON, TRUE)) ident = TRUE; - if (do_res_stat(A_CHR, TRUE)) ident = TRUE; - - break; - } - - case SV_FOOD_FORTUNE_COOKIE: - { - char rumour[80]; - - msg_print("That tastes good."); - msg_print("There is message in the cookie. It says:"); - msg_print(NULL); - - switch (randint(20)) - { - case 1: - { - get_rnd_line("chainswd.txt", rumour); - break; - } - - case 2: - { - get_rnd_line("error.txt", rumour); - break; - } - - case 3: - case 4: - case 5: - { - get_rnd_line("death.txt", rumour); - break; - } - - default: - { - get_rnd_line("rumors.txt", rumour); - break; - } - } - - msg_format("%s", rumour); - msg_print(NULL); - - ident = TRUE; - - break; - } - - - case SV_FOOD_RATION: - case SV_FOOD_BISCUIT: - case SV_FOOD_JERKY: - { - msg_print("That tastes good."); - - ident = TRUE; - - break; - } - - case SV_FOOD_SLIME_MOLD: - { - msg_print("That tastes good."); - - /* 2% chance of getting the mold power */ - if (magik(2)) - { - ADD_POWER(p_ptr->powers_mod, PWR_GROW_MOLD); - p_ptr->update |= PU_POWERS; - } - - ident = TRUE; - - break; - } - - case SV_FOOD_WAYBREAD: - { - msg_print("That tastes very good."); - (void)set_poisoned(0); - (void)hp_player(damroll(4, 8)); - set_food(PY_FOOD_MAX - 1); - - ident = TRUE; - - break; - } - - case SV_FOOD_PINT_OF_ALE: - case SV_FOOD_PINT_OF_WINE: - { - msg_print("That tastes good."); - - ident = TRUE; - - q_ptr = &forge; - object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1)); - q_ptr->number = 1; - object_aware(q_ptr); - object_known(q_ptr); - q_ptr->ident |= IDENT_STOREB; - (void)inven_carry(q_ptr, FALSE); - - break; - } - - case SV_FOOD_ATHELAS: - { - msg_print("A fresh, clean essence rises, driving away wounds and poison."); - - (void)set_poisoned(0); - (void)set_stun(0); - (void)set_cut(0); - if (p_ptr->black_breath) - { - msg_print("The hold of the Black Breath on you is broken!"); - p_ptr->black_breath = FALSE; - } - - ident = TRUE; - - break; - } - } - } - - /* Corpses... */ - else - { - r_ptr = &r_info[o_ptr->pval2]; - - /* Analyse the corpse */ - switch (o_ptr->sval) - { - case SV_CORPSE_CORPSE: - { - bool_ no_meat = FALSE; - - /* Not all is edible. Apologies if messy. */ - - /* Check weight -- they have to have some meat left */ - if (r_ptr->flags9 & RF9_DROP_SKELETON) - { - if (o_ptr->weight <= (r_ptr->weight * 3) / 5) - { - no_meat = TRUE; - } - } - - /* Non-skeletons are naturally have more allowances */ - else - { - if (o_ptr->weight <= (r_ptr->weight * 7) / 20) - { - no_meat = TRUE; - } - } - - /* Nothing left to eat */ - if (no_meat) - { - msg_print("There is not enough meat."); - return; - } - - - /* Check freshness */ - if (!o_ptr->timeout) msg_print("Ugh! Raw meat!"); - else msg_print("That tastes good."); - - - /* A pound of raw meat */ - o_ptr->pval -= 10; - o_ptr->weight -= 10; - - /* Corpses still have meat on them */ - destroy = FALSE; - - ident = TRUE; - - break; - } - - case SV_CORPSE_HEAD: - { - msg_print("You feel rather sick."); - - /* A pound of raw meat */ - o_ptr->pval -= 10; - o_ptr->weight -= 10; - - /* Corpses still have meat on them */ - destroy = FALSE; - - ident = TRUE; - - break; - } - - case SV_CORPSE_MEAT: - { - /* Just meat */ - if (!o_ptr->timeout) msg_print("You quickly swallow the meat."); - else msg_print("That tastes good."); - - ident = TRUE; - - /* Those darn microorganisms */ - if (!o_ptr->timeout && (o_ptr->weight > o_ptr->pval) && - !(p_ptr->resist_pois || p_ptr->oppose_pois)) - { - set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) + - (o_ptr->weight - o_ptr->pval)); - } - - break; - } - } - - corpse_effect(o_ptr, FALSE); - - /* Less nutritious than food rations, but much more of it. */ - fval = (o_ptr->timeout) ? 2000 : 2500; - - /* Those darn microorganisms */ - if (!o_ptr->timeout && (o_ptr->weight - o_ptr->pval > 10) && - !(p_ptr->resist_pois || p_ptr->oppose_pois)) - { - set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) + - (o_ptr->weight - o_ptr->pval)); - } - - /* Partially cured */ - if (o_ptr->weight > o_ptr->timeout) - { - /* Adjust the "timeout" without overflowing */ - o_ptr->timeout = (o_ptr->timeout * ((100 * o_ptr->timeout) / o_ptr->weight)) / 100; - } - } - - - /* Combine / Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* We have tried it */ - object_tried(o_ptr); - - /* The player is now aware of the object */ - if (ident && !object_aware_p(o_ptr)) - { - object_aware(o_ptr); - gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev); - } - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - if (!fval) fval = o_ptr->pval; - - /* Food can feed the player, in a different ways */ - - /* Vampires */ - if ((PRACE_FLAG(PR1_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire"))) - { - /* Reduced nutritional benefit */ - /* (void)set_food(p_ptr->food + (fval / 10)); -- No more */ - msg_print("Mere victuals hold scant sustenance for a being such as yourself."); - - /* Hungry */ - if (p_ptr->food < PY_FOOD_ALERT) - { - msg_print("Your hunger can only be satisfied with fresh blood!"); - } - } - - else if (PRACE_FLAG(PR1_NO_FOOD)) - { - if (PRACE_FLAG(PR1_UNDEAD)) - { - msg_print("The food of mortals is poor sustenance for you."); - } - else - { - msg_print("Food is poor sustenance for you."); - } - set_food(p_ptr->food + ((fval) / 40)); - } - - /* Those living in fresh */ - else - { - (void)set_food(p_ptr->food + fval); - } - - - /* Destroy food? */ - if (destroy) - { - inc_stack_size(item, -1); - } -} - - -/* - * Cut a corpse up for convenient storage - */ -void do_cmd_cut_corpse(void) -{ - int item, meat = 0, not_meat = 0; - - object_type *o_ptr; - - object_type *i_ptr; - - object_type object_type_body; - - monster_race *r_ptr; - - cptr q, s; - - - /* Restrict choices to corpses */ - item_tester_tval = TV_CORPSE; - - /* Get an item */ - q = "Hack up which corpse? "; - s = "You have no corpses."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - r_ptr = &r_info[o_ptr->pval2]; - - if ((o_ptr->sval != SV_CORPSE_CORPSE) && (o_ptr->sval != SV_CORPSE_HEAD)) - { - msg_print ("You cannot split that."); - return; - } - - switch (o_ptr->sval) - { - case SV_CORPSE_CORPSE: - { - if (r_ptr->flags9 & RF9_DROP_SKELETON) - { - not_meat = (r_ptr->weight * 3) / 5; - } - else - { - not_meat = (r_ptr->weight * 7) / 20; - } - meat = r_ptr->weight + r_ptr->weight / 10 - not_meat; - - break; - } - - case SV_CORPSE_HEAD: - { - not_meat = r_ptr->weight / 150; - meat = r_ptr->weight / 30 + r_ptr->weight / 300 - not_meat; - - break; - } - } - - if ((o_ptr->weight <= not_meat) || (meat < 10)) - { - msg_print("There is not enough meat."); - return; - } - - /* Hacking 10 pounds off */ - if (meat > 100) meat = 100; - - /* Take a turn */ - energy_use = 100; - - o_ptr->pval -= meat; - o_ptr->weight -= meat; - - msg_print("You hack some meat off the corpse."); - - corpse_effect(o_ptr, TRUE); - - /* Get local object */ - i_ptr = &object_type_body; - - /* Make some meat */ - object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_MEAT)); - - i_ptr->number = meat / 10; - i_ptr->pval2 = o_ptr->pval2; - - /* Length of time before decay */ - i_ptr->pval = 1000 + rand_int(1000); - - if (inven_carry_okay(i_ptr)) - { - inven_carry(i_ptr, TRUE); - } - else - { - drop_near(i_ptr, 0, p_ptr->py, p_ptr->px); - } -} - - -/* - * Use a potion to cure some meat - * - * Salt water works well. - */ -void do_cmd_cure_meat(void) -{ - int item, num, cure; - - object_type *o_ptr; - - object_type *i_ptr; - - cptr q, s; - - - /* Restrict choices to corpses */ - item_tester_tval = TV_CORPSE; - item_tester_hook = item_tester_hook_eatable; - - /* Get some meat */ - q = "Cure which meat? "; - s = "You have no meat to cure."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Restrict choices to potions */ - item_tester_tval = TV_POTION; - - /* Get a potion */ - q = "Use which potion? "; - s = "You have no potions to use."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - i_ptr = get_object(item); - - if (i_ptr->number > 1) - { - /* Get a number */ - get_count(1, i_ptr->number); - - /* Save it */ - num = command_arg; - } - else - { - num = 1; - } - - if (num == 0) return; - - /* Take a turn */ - energy_use = 100; - - q = "You soak the meat."; - s = "You soak the meat."; - - switch (i_ptr->sval) - { - case SV_POTION_SALT_WATER: - { - q = "You salt the meat."; - cure = 200 * num; - - break; - } - - case SV_POTION_POISON: - { - q = "You poison the meat."; - cure = 0; - o_ptr->pval /= 2; - if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight; - - break; - } - - case SV_POTION_CONFUSION: - { - cure = 80 * num; - - break; - } - - case SV_POTION_SLOW_POISON: - { - cure = 20 * num; - - break; - } - - case SV_POTION_CURE_POISON: - { - cure = 45 * num; - - break; - } - - case SV_POTION_DEATH: - { - q = "You ruin the meat."; - cure = 0; - o_ptr->pval /= 10; - if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight / 2; - - break; - } - - default: - { - cure = 0; - - break; - } - } - - /* Message */ - if (object_known_p(i_ptr)) msg_print(q); - else msg_print(s); - - /* The meat is already spoiling */ - if (((o_ptr->sval == SV_CORPSE_MEAT) && (o_ptr->weight > o_ptr->pval)) || - (o_ptr->weight - o_ptr->pval > 10)) - { - cure = (cure * o_ptr->pval) / (o_ptr->weight * 20); - } - - /* Cure the meat */ - o_ptr->timeout += cure / o_ptr->number; - - if (o_ptr->timeout > o_ptr->pval) o_ptr->timeout = o_ptr->pval; - - /* Use up the potions */ - inc_stack_size(item, -num); -} - - -/* - * Hook to determine if an object is quaffable - */ -static bool_ item_tester_hook_quaffable(object_type *o_ptr) -{ - if ((o_ptr->tval == TV_POTION) || (o_ptr->tval == TV_POTION2)) return (TRUE); - - /* Assume not */ - return (FALSE); -} - - -static bool_ quaff_potion(int tval, int sval, int pval, int pval2) -{ - int ident = FALSE; - - - /* "Traditional" potions */ - if (tval == TV_POTION) - { - switch (sval) - { - case SV_POTION_WATER: - case SV_POTION_APPLE_JUICE: - case SV_POTION_SLIME_MOLD: - { - msg_print("You feel less thirsty."); - ident = TRUE; - - break; - } - - case SV_POTION_SLOWNESS: - { - if (set_slow(p_ptr->slow + randint(25) + 15)) ident = TRUE; - - break; - } - - case SV_POTION_SALT_WATER: - { - msg_print("The potion makes you vomit!"); - (void)set_food(PY_FOOD_STARVE - 1); - (void)set_poisoned(0); - (void)set_paralyzed(4); - ident = TRUE; - - break; - } - - case SV_POTION_POISON: - { - if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) - { - if (set_poisoned(p_ptr->poisoned + rand_int(15) + 10)) - { - ident = TRUE; - } - } - - break; - } - - case SV_POTION_BLINDNESS: - { - if (!p_ptr->resist_blind) - { - if (set_blind(p_ptr->blind + rand_int(100) + 100)) - { - ident = TRUE; - } - } - - break; - } - - /* Booze */ - case SV_POTION_CONFUSION: - { - if (!((p_ptr->resist_conf) || (p_ptr->resist_chaos))) - { - if (set_confused(p_ptr->confused + rand_int(20) + 15)) - { - ident = TRUE; - } - if (randint(2) == 1) - { - if (set_image(p_ptr->image + rand_int(150) + 150)) - { - ident = TRUE; - } - } - if (randint(13) == 1) - { - ident = TRUE; - if (randint(3) == 1) lose_all_info(); - else wiz_dark(); - teleport_player(100); - wiz_dark(); - msg_print("You wake up elsewhere with a sore head..."); - msg_print("You can't remember a thing, or how you got here!"); - } - } - - break; - } - - case SV_POTION_SLEEP: - { - if (!p_ptr->free_act) - { - if (set_paralyzed(rand_int(4) + 4)) - { - ident = TRUE; - } - } - - break; - } - - case SV_POTION_LOSE_MEMORIES: - { - if (!p_ptr->hold_life && (p_ptr->exp > 0)) - { - msg_print("You feel your memories fade."); - lose_exp(p_ptr->exp / 4); - ident = TRUE; - } - - break; - } - - case SV_POTION_RUINATION: - { - msg_print("Your nerves and muscles feel weak and lifeless!"); - take_hit(damroll(10, 10), "a potion of Ruination"); - (void)dec_stat(A_DEX, 25, TRUE); - (void)dec_stat(A_WIS, 25, TRUE); - (void)dec_stat(A_CON, 25, TRUE); - (void)dec_stat(A_STR, 25, TRUE); - (void)dec_stat(A_CHR, 25, TRUE); - (void)dec_stat(A_INT, 25, TRUE); - ident = TRUE; - - break; - } - - case SV_POTION_DEC_STR: - { - if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) ident = TRUE; - - break; - } - - case SV_POTION_DEC_INT: - { - if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) ident = TRUE; - - break; - } - - case SV_POTION_DEC_WIS: - { - if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) ident = TRUE; - - break; - } - - case SV_POTION_DEC_DEX: - { - if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) ident = TRUE; - - break; - } - - case SV_POTION_DEC_CON: - { - if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) ident = TRUE; - - break; - } - - case SV_POTION_DEC_CHR: - { - if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) ident = TRUE; - - break; - } - - case SV_POTION_DETONATIONS: - { - msg_print("Massive explosions rupture your body!"); - take_hit(damroll(50, 20), "a potion of Detonation"); - (void)set_stun(p_ptr->stun + 75); - (void)set_cut(p_ptr->cut + 5000); - ident = TRUE; - - break; - } - - case SV_POTION_DEATH: - { - msg_print("A feeling of Death flows through your body."); - take_hit(5000, "a potion of Death"); - ident = TRUE; - - break; - } - - case SV_POTION_INFRAVISION: - { - if (set_tim_infra(p_ptr->tim_infra + 100 + randint(100))) - { - ident = TRUE; - } - - break; - } - - case SV_POTION_DETECT_INVIS: - { - if (set_tim_invis(p_ptr->tim_invis + 12 + randint(12))) - { - ident = TRUE; - } - - break; - } - - case SV_POTION_SLOW_POISON: - { - if (set_poisoned(p_ptr->poisoned / 2)) ident = TRUE; - - break; - } - - case SV_POTION_CURE_POISON: - { - if (set_poisoned(0)) ident = TRUE; - - break; - } - - case SV_POTION_BOLDNESS: - { - if (set_afraid(0)) ident = TRUE; - - break; - } - - case SV_POTION_SPEED: - { - if (!p_ptr->fast) - { - if (set_fast(randint(25) + 15, 10)) ident = TRUE; - } - else - { - (void)set_fast(p_ptr->fast + 5, 10); - } - - break; - } - - case SV_POTION_RESIST_HEAT: - { - if (set_oppose_fire(p_ptr->oppose_fire + randint(10) + 10)) - { - ident = TRUE; - } - - break; - } - - case SV_POTION_RESIST_COLD: - { - if (set_oppose_cold(p_ptr->oppose_cold + randint(10) + 10)) - { - ident = TRUE; - } - - break; - } - - case SV_POTION_HEROISM: - { - if (set_afraid(0)) ident = TRUE; - if (set_hero(p_ptr->hero + randint(25) + 25)) ident = TRUE; - if (hp_player(10)) ident = TRUE; - - break; - } - - case SV_POTION_BESERK_STRENGTH: - { - if (set_afraid(0)) ident = TRUE; - if (set_shero(p_ptr->shero + randint(25) + 25)) ident = TRUE; - if (hp_player(30)) ident = TRUE; - - break; - } - - case SV_POTION_CURE_LIGHT: - { - if (hp_player(damroll(2, 8))) ident = TRUE; - if (set_blind(0)) ident = TRUE; - if (set_cut(p_ptr->cut - 10)) ident = TRUE; - - break; - } - - case SV_POTION_CURE_SERIOUS: - { - if (hp_player(damroll(4, 8))) ident = TRUE; - if (set_blind(0)) ident = TRUE; - if (set_confused(0)) ident = TRUE; - if (set_cut((p_ptr->cut / 2) - 50)) ident = TRUE; - - break; - } - - case SV_POTION_CURE_CRITICAL: - { - if (hp_player(damroll(6, 8))) ident = TRUE; - if (set_blind(0)) ident = TRUE; - if (set_confused(0)) ident = TRUE; - if (set_poisoned(0)) ident = TRUE; - if (set_stun(0)) ident = TRUE; - if (set_cut(0)) ident = TRUE; - - break; - } - - case SV_POTION_HEALING: - { - if (hp_player(300)) ident = TRUE; - if (set_blind(0)) ident = TRUE; - if (set_confused(0)) ident = TRUE; - if (set_poisoned(0)) ident = TRUE; - if (set_stun(0)) ident = TRUE; - if (set_cut(0)) ident = TRUE; - - break; - } - - case SV_POTION_STAR_HEALING: - { - if (hp_player(1200)) ident = TRUE; - if (set_blind(0)) ident = TRUE; - if (set_confused(0)) ident = TRUE; - if (set_poisoned(0)) ident = TRUE; - if (set_stun(0)) ident = TRUE; - if (set_cut(0)) ident = TRUE; - - break; - } - - case SV_POTION_LIFE: - { - msg_print("You feel life flow through your body!"); - restore_level(); - hp_player(5000); - (void)set_poisoned(0); - (void)set_blind(0); - (void)set_confused(0); - (void)set_image(0); - (void)set_stun(0); - (void)set_cut(0); - (void)do_res_stat(A_STR, TRUE); - (void)do_res_stat(A_CON, TRUE); - (void)do_res_stat(A_DEX, TRUE); - (void)do_res_stat(A_WIS, TRUE); - (void)do_res_stat(A_INT, TRUE); - (void)do_res_stat(A_CHR, TRUE); - if (p_ptr->black_breath) - { - msg_print("The hold of the Black Breath on you is broken!"); - } - p_ptr->black_breath = FALSE; - ident = TRUE; - - break; - } - - case SV_POTION_RESTORE_MANA: - { - if (p_ptr->csp < p_ptr->msp) - { - p_ptr->csp = p_ptr->msp; - p_ptr->csp_frac = 0; - msg_print("Your feel your head clear."); - p_ptr->redraw |= (PR_MANA); - p_ptr->window |= (PW_PLAYER); - ident = TRUE; - } - - break; - } - - case SV_POTION_RESTORE_EXP: - { - if (restore_level()) ident = TRUE; - - break; - } - - case SV_POTION_RES_STR: - { - if (do_res_stat(A_STR, TRUE)) ident = TRUE; - - break; - } - - case SV_POTION_RES_INT: - { - if (do_res_stat(A_INT, TRUE)) ident = TRUE; - - break; - } - - case SV_POTION_RES_WIS: - { - if (do_res_stat(A_WIS, TRUE)) ident = TRUE; - - break; - } - - case SV_POTION_RES_DEX: - { - if (do_res_stat(A_DEX, TRUE)) ident = TRUE; - - break; - } - - case SV_POTION_RES_CON: - { - if (do_res_stat(A_CON, TRUE)) ident = TRUE; - - break; - } - - case SV_POTION_RES_CHR: - { - if (do_res_stat(A_CHR, TRUE)) ident = TRUE; - - break; - } - - case SV_POTION_INC_STR: - { - if (do_inc_stat(A_STR)) ident = TRUE; - - break; - } - - case SV_POTION_INC_INT: - { - if (do_inc_stat(A_INT)) ident = TRUE; - - break; - } - - case SV_POTION_INC_WIS: - { - if (do_inc_stat(A_WIS)) ident = TRUE; - - break; - } - - case SV_POTION_INC_DEX: - { - if (do_inc_stat(A_DEX)) ident = TRUE; - - break; - } - - case SV_POTION_INC_CON: - { - if (do_inc_stat(A_CON)) ident = TRUE; - - break; - } - - case SV_POTION_INC_CHR: - { - if (do_inc_stat(A_CHR)) ident = TRUE; - - break; - } - - case SV_POTION_AUGMENTATION: - { - if (do_inc_stat(A_STR)) ident = TRUE; - if (do_inc_stat(A_INT)) ident = TRUE; - if (do_inc_stat(A_WIS)) ident = TRUE; - if (do_inc_stat(A_DEX)) ident = TRUE; - if (do_inc_stat(A_CON)) ident = TRUE; - if (do_inc_stat(A_CHR)) ident = TRUE; - - break; - } - - case SV_POTION_ENLIGHTENMENT: - { - msg_print("An image of your surroundings forms in your mind..."); - wiz_lite(); - ident = TRUE; - - break; - } - - case SV_POTION_STAR_ENLIGHTENMENT: - { - msg_print("You begin to feel more enlightened..."); - msg_print(NULL); - wiz_lite_extra(); - (void)do_inc_stat(A_INT); - (void)do_inc_stat(A_WIS); - (void)detect_traps(DEFAULT_RADIUS); - (void)detect_doors(DEFAULT_RADIUS); - (void)detect_stairs(DEFAULT_RADIUS); - (void)detect_treasure(DEFAULT_RADIUS); - (void)detect_objects_gold(DEFAULT_RADIUS); - (void)detect_objects_normal(DEFAULT_RADIUS); - identify_pack(); - self_knowledge(NULL); - ident = TRUE; - - break; - } - - case SV_POTION_SELF_KNOWLEDGE: - { - msg_print("You begin to know yourself a little better..."); - msg_print(NULL); - self_knowledge(NULL); - ident = TRUE; - - break; - } - - case SV_POTION_EXPERIENCE: - { - if (p_ptr->exp < PY_MAX_EXP) - { - msg_print("You feel more experienced."); - gain_exp(100000L); - ident = TRUE; - } - - break; - } - - case SV_POTION_RESISTANCE: - { - (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20); - (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20); - (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20); - (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20); - (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20); - ident = TRUE; - - break; - } - - case SV_POTION_CURING: - { - if (hp_player(50)) ident = TRUE; - if (set_blind(0)) ident = TRUE; - if (set_poisoned(0)) ident = TRUE; - if (set_confused(0)) ident = TRUE; - if (set_stun(0)) ident = TRUE; - if (set_cut(0)) ident = TRUE; - if (set_image(0)) ident = TRUE; - if (heal_insanity(50)) ident = TRUE; - - break; - } - - case SV_POTION_INVULNERABILITY: - { - (void)set_invuln(p_ptr->invuln + randint(7) + 7); - ident = TRUE; - - break; - } - - case SV_POTION_NEW_LIFE: - { - do_cmd_rerate(); - ident = TRUE; - - break; - } - - case SV_POTION_BLOOD: - { - msg_print("You feel the blood of life running through your veins!"); - ident = TRUE; - p_ptr->allow_one_death++; - - break; - } - - case SV_POTION_MUTATION: - { - /* In Theme, Melkor likes players who quaff - potions of corruption. */ - if (game_module_idx == MODULE_THEME) - { - GOD(GOD_MELKOR) - { - msg_print("Your quaffing of this potion pleases Melkor!"); - set_grace(p_ptr->grace + 2); - } - } - - msg_print("You feel the dark corruptions of Morgoth coming over you!"); - gain_random_corruption(); - ident = TRUE; - break; - } - - case SV_POTION_INVIS: - { - int t = 30 + randint(30); - - if (set_invis(p_ptr->tim_invis + t, 35)) - { - ident = TRUE; - } - set_tim_invis(p_ptr->tim_invis + t); - - break; - } - - case SV_POTION_LEARNING: - { - p_ptr->skill_points += rand_range(4, 10 + luck( -4, 4)); - cmsg_format(TERM_L_GREEN, "You can increase %d more skills.", p_ptr->skill_points); - - break; - } - - default: - { - break; - } - } - } - - /* "Duplicate" potions */ - else - { - switch (sval) - { - case SV_POTION2_MIMIC: - { - if (!p_ptr->mimic_form) - { - s32b time = get_mimic_random_duration(pval2); - - set_mimic(time, pval2, (p_ptr->lev * 2) / 3); - - /* Redraw title */ - p_ptr->redraw |= (PR_TITLE); - - /* Recalculate bonuses */ - p_ptr->update |= (PU_BONUS); - - ident = TRUE; - } - - break; - } - - case SV_POTION2_CURE_LIGHT_SANITY: - { - if (heal_insanity(damroll(4, 8))) ident = TRUE; - - break; - } - - case SV_POTION2_CURE_SERIOUS_SANITY: - { - if (heal_insanity(damroll(8, 8))) ident = TRUE; - - break; - } - - case SV_POTION2_CURE_CRITICAL_SANITY: - { - if (heal_insanity(damroll(12, 8))) ident = TRUE; - - break; - } - - case SV_POTION2_CURE_SANITY: - { - if (heal_insanity(damroll(10, 100))) ident = TRUE; - - break; - } - - default: - { - break; - } - } - } - - return (ident); -} - - -/* - * Quaff a potion (from the pack or the floor) - */ -void do_cmd_quaff_potion(void) -{ - int item, ident, lev; - - object_type *o_ptr; - - object_type *q_ptr, forge; - - cptr q, s; - - - /* Restrict choices to potions */ - item_tester_hook = item_tester_hook_quaffable; - - /* Set up the extra finder */ - get_item_hook_find_obj_what = "Potion full name? "; - get_item_extra_hook = get_item_hook_find_obj; - - /* Get an item */ - q = "Quaff which potion? "; - s = "You have no potions to quaff."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; - - /* Get the item */ - o_ptr = get_object(item); - - - /* Sound */ - sound(SOUND_QUAFF); - - - /* Take a turn */ - energy_use = 100; - - /* Not identified yet */ - ident = FALSE; - - /* Object level */ - lev = k_info[o_ptr->k_idx].level; - - /* Demon Breath corruption can spoil potions. */ - if (player_has_corruption(CORRUPT_DEMON_BREATH) && magik(9)) - { - msg_print("Your demon breath spoils the potion!"); - ident = FALSE; - } - else - { - /* Normal potion handling */ - ident = quaff_potion(o_ptr->tval, o_ptr->sval, o_ptr->pval, o_ptr->pval2); - } - - /* Combine / Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* The item has been tried */ - object_tried(o_ptr); - - /* An identification was made */ - if (ident && !object_aware_p(o_ptr)) - { - object_aware(o_ptr); - gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev); - } - - if (get_skill(SKILL_ALCHEMY)) - { - if (item >= 0) - { - q_ptr = &forge; - object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1)); - q_ptr->number = 1; - object_aware(q_ptr); - object_known(q_ptr); - - q_ptr->ident |= IDENT_STOREB; - - (void)inven_carry(q_ptr, FALSE); - } - } - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - - /* Potions can feed the player */ - (void)set_food(p_ptr->food + o_ptr->pval); - - - /* Destroy potion */ - inc_stack_size(item, -1); -} - - -/* - * Drink from a fountain - */ -void do_cmd_drink_fountain(void) -{ - cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; - - bool_ ident; - - int tval, sval, pval = 0; - - int i; - - char ch; - - - /* Is the fountain empty? */ - if (c_ptr->special2 <= 0) - { - msg_print("The fountain is dried out."); - return; - } - - /* We quaff or we fill ? */ - if (!get_com("Do you want to [Q]uaff or [F]ill from the fountain? ", &ch)) - { - return; - } - - if ((ch == 'F') || (ch == 'f')) - { - do_cmd_fill_bottle(); - - return; - } - - else if ((ch == 'Q') || (ch == 'q')) - { - if (c_ptr->special <= SV_POTION_LAST) - { - tval = TV_POTION; - sval = c_ptr->special; - } - else - { - tval = TV_POTION2; - sval = c_ptr->special - SV_POTION_LAST; - } - - for (i = 0; i < max_k_idx; i++) - { - object_kind *k_ptr = &k_info[i]; - - if (k_ptr->tval != tval) continue; - if (k_ptr->sval != sval) continue; - - pval = k_ptr->pval; - - break; - } - - ident = quaff_potion(tval, sval, pval, 0); - - c_ptr->special2--; - - if (c_ptr->special2 <= 0) - { - cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN); - } - - if (ident) c_ptr->info |= CAVE_IDNT; - } -} - - -/* - * Fill an empty bottle - */ -void do_cmd_fill_bottle(void) -{ - cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; - - int tval, sval, item, amt = 1; - - object_type *q_ptr, *o_ptr, forge; - - cptr q, s; - - /* Is the fountain empty? */ - /* - * This check is redundant as it is done in do_cmd_drink_fountain() - * but I keep this because someone might want to call this directly. - * -- Kusunose - */ - if (c_ptr->special2 <= 0) - { - msg_print("The fountain has dried up."); - return; - } - - /* Determine the tval/sval of the potion */ - if (c_ptr->special <= SV_POTION_LAST) - { - tval = TV_POTION; - sval = c_ptr->special; - } - else - { - tval = TV_POTION2; - sval = c_ptr->special - SV_POTION_LAST; - } - - /* Restrict choices to bottles */ - item_tester_tval = TV_BOTTLE; - - /* Get an item */ - q = "Fill which bottle? "; - s = "You have no bottles to fill."; - if (!get_item(&item, q, s, (USE_INVEN))) return; - o_ptr = &p_ptr->inventory[item]; - - /* Find out how many the player wants */ - if (o_ptr->number > 1) - { - /* Get a quantity */ - amt = get_quantity(NULL, o_ptr->number); - - /* Allow user abort */ - if (amt <= 0) return; - } - - if (amt > c_ptr->special2) amt = c_ptr->special2; - - /* Destroy bottles */ - inc_stack_size(item, -amt); - - /* Create the potion */ - q_ptr = &forge; - object_prep(q_ptr, lookup_kind(tval, sval)); - q_ptr->number = amt; - - if (c_ptr->info & CAVE_IDNT) - { - object_aware(q_ptr); - object_known(q_ptr); - } - - inven_carry(q_ptr, TRUE); - - c_ptr->special2 -= amt; - - if (c_ptr->special2 <= 0) - { - cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN); - } - - return; -} - - -/* - * Curse the players armor - */ -bool_ curse_armor(void) -{ - object_type *o_ptr; - - char o_name[80]; - - - /* Curse the body armor */ - o_ptr = &p_ptr->inventory[INVEN_BODY]; - - /* Nothing to curse */ - if (!o_ptr->k_idx) return (FALSE); - - - /* Describe */ - object_desc(o_name, o_ptr, FALSE, 3); - - /* Attempt a saving throw for artifacts */ - if (((o_ptr->art_name) || artifact_p(o_ptr)) && (rand_int(100) < 50)) - { - /* Cool */ - msg_format("A terrible black aura tries to surround your armour, " - "but your %s resists the effects!", o_name); - } - - /* not artifact or failed save... */ - else - { - /* Oops */ - msg_format("A terrible black aura blasts your %s!", o_name); - - /* Blast the armor */ - o_ptr->name1 = 0; - o_ptr->name2 = EGO_BLASTED; - o_ptr->to_a = 0 - randint(5) - randint(5); - o_ptr->to_h = 0; - o_ptr->to_d = 0; - o_ptr->ac = 0; - o_ptr->dd = 0; - o_ptr->ds = 0; - o_ptr->art_flags1 = 0; - o_ptr->art_flags2 = 0; - o_ptr->art_flags3 = 0; - o_ptr->art_flags4 = 0; - - /* Curse it */ - o_ptr->ident |= (IDENT_CURSED); - - /* Recalculate bonuses */ - p_ptr->update |= (PU_BONUS); - - /* Recalculate mana */ - p_ptr->update |= (PU_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - } - - return (TRUE); -} - - -/* - * Curse the players weapon - */ -bool_ curse_weapon(void) -{ - object_type *o_ptr; - - char o_name[80]; - - - /* Curse the weapon */ - o_ptr = &p_ptr->inventory[INVEN_WIELD]; - - /* Nothing to curse */ - if (!o_ptr->k_idx) return (FALSE); - - - /* Describe */ - object_desc(o_name, o_ptr, FALSE, 3); - - /* Attempt a saving throw */ - if ((artifact_p(o_ptr) || o_ptr->art_name) && (rand_int(100) < 50)) - { - /* Cool */ - msg_format("A terrible black aura tries to surround your weapon, " - "but your %s resists the effects!", o_name); - } - - /* not artifact or failed save... */ - else - { - /* Oops */ - msg_format("A terrible black aura blasts your %s!", o_name); - - /* Shatter the weapon */ - o_ptr->name1 = 0; - o_ptr->name2 = EGO_SHATTERED; - o_ptr->to_h = 0 - randint(5) - randint(5); - o_ptr->to_d = 0 - randint(5) - randint(5); - o_ptr->to_a = 0; - o_ptr->ac = 0; - o_ptr->dd = 0; - o_ptr->ds = 0; - o_ptr->art_flags1 = 0; - o_ptr->art_flags2 = 0; - o_ptr->art_flags3 = 0; - o_ptr->art_flags4 = 0; - - - /* Curse it */ - o_ptr->ident |= (IDENT_CURSED); - - /* Recalculate bonuses */ - p_ptr->update |= (PU_BONUS); - - /* Recalculate mana */ - p_ptr->update |= (PU_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - } - - /* Notice */ - return (TRUE); -} - - -/* - * Hook to determine if an object is readable - */ -static bool_ item_tester_hook_readable(object_type *o_ptr) -{ - if ((o_ptr->tval == TV_SCROLL) || (o_ptr->tval == TV_PARCHMENT)) return (TRUE); - - /* Assume not */ - return (FALSE); -} - - -/* - * Read a scroll (from the pack or floor). - * - * Certain scrolls can be "aborted" without losing the scroll. These - * include scrolls with no effects but recharge or identify, which are - * cancelled before use. XXX Reading them still takes a turn, though. - */ -void do_cmd_read_scroll(void) -{ - int item, k, used_up, ident, lev; - - object_type *o_ptr; - - object_type *q_ptr, forge; - - cptr q, s; - - - /* Check some conditions */ - if (p_ptr->blind) - { - msg_print("You can't see anything."); - return; - } - - if (no_lite()) - { - msg_print("You have no light by which to read."); - return; - } - - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - - /* Restrict choices to scrolls */ - item_tester_hook = item_tester_hook_readable; - - /* Set up the extra finder */ - get_item_hook_find_obj_what = "Scroll full name? "; - get_item_extra_hook = get_item_hook_find_obj; - - /* Get an item */ - q = "Read which scroll? "; - s = "You have no scrolls to read."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Take a turn */ - energy_use = 100; - - /* Not identified yet */ - ident = FALSE; - - /* Object level */ - lev = k_info[o_ptr->k_idx].level; - - /* Assume the scroll will get used up */ - used_up = TRUE; - - /* Corruption */ - if (player_has_corruption(CORRUPT_BALROG_AURA) && magik(5)) - { - msg_print("Your demon aura burns the scroll before you read it!"); - used_up = TRUE; - ident = FALSE; - } - - /* Scrolls */ - else if (o_ptr->tval == TV_SCROLL) - { - /* Analyze the scroll */ - switch (o_ptr->sval) - { - case SV_SCROLL_MASS_RESURECTION: - { - int k; - - ident = TRUE; - msg_print("You feel the souls of the dead coming back " - "from the Halls of Mandos."); - - for (k = 0; k < max_r_idx; k++) - { - monster_race *r_ptr = &r_info[k]; - - if (r_ptr->flags1 & RF1_UNIQUE && - !(r_ptr->flags9 & RF9_SPECIAL_GENE)) - { - r_ptr->max_num = 1; - } - } - - break; - } - - case SV_SCROLL_DEINCARNATION: - { - if (!get_check("Do you really want to leave your body? " - "(beware, it'll be destroyed!) ")) - { - used_up = FALSE; - break; - } - - do_cmd_leave_body(FALSE); - - ident = TRUE; - used_up = TRUE; - - break; - } - - /* original didn't set used_up flag ??? -- pelpel */ - case SV_SCROLL_RESET_RECALL: - { - if (!reset_recall(TRUE)) - { - used_up = FALSE; - break; - } - - msg_format("Recall reset to %s at level %d.", - d_info[p_ptr->recall_dungeon].name + d_name, - max_dlv[p_ptr->recall_dungeon]); - - ident = TRUE; - used_up = TRUE; - - break; - } - - case SV_SCROLL_DIVINATION: - { - int i, count = 0; - char buf[120]; - - while (count < 1000) - { - count++; - i = rand_int(MAX_FATES); - if (!fates[i].fate) continue; - if (fates[i].know) continue; - - msg_print("A message appears on the scroll. It says:"); - msg_print(NULL); - - fate_desc(buf, i); - msg_format("%s", buf); - - msg_print(NULL); - msg_print("The scroll disappears in a puff of smoke!"); - - fates[i].know = TRUE; - ident = TRUE; - - break; - } - - break; - } - - case SV_SCROLL_DARKNESS: - { - if (!(p_ptr->resist_blind) && !(p_ptr->resist_dark)) - { - (void)set_blind(p_ptr->blind + 3 + randint(5)); - } - if (unlite_area(10, 3)) ident = TRUE; - - break; - } - - case SV_SCROLL_AGGRAVATE_MONSTER: - { - msg_print("There is a high-pitched humming noise."); - aggravate_monsters(1); - - ident = TRUE; - - break; - } - - case SV_SCROLL_CURSE_ARMOR: - { - if (curse_armor()) ident = TRUE; - - break; - } - - case SV_SCROLL_CURSE_WEAPON: - { - if (curse_weapon()) ident = TRUE; - - break; - } - - case SV_SCROLL_SUMMON_MONSTER: - { - for (k = 0; k < randint(3); k++) - { - if (summon_specific(p_ptr->py, p_ptr->px, dun_level, 0)) - { - ident = TRUE; - } - } - - break; - } - - case SV_SCROLL_SUMMON_MINE: - { - if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE)) - { - ident = TRUE; - } - - break; - } - - case SV_SCROLL_SUMMON_UNDEAD: - { - for (k = 0; k < randint(3); k++) - { - if (summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD)) - { - ident = TRUE; - } - } - - break; - } - - case SV_SCROLL_TRAP_CREATION: - { - if (trap_creation()) ident = TRUE; - - break; - } - - case SV_SCROLL_PHASE_DOOR: - { - teleport_player(10); - - ident = TRUE; - - break; - } - - case SV_SCROLL_TELEPORT: - { - teleport_player(100); - - ident = TRUE; - - break; - } - - case SV_SCROLL_TELEPORT_LEVEL: - { - (void)teleport_player_level(); - - ident = TRUE; - - break; - } - - case SV_SCROLL_WORD_OF_RECALL: - { - if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")) - { - used_up = FALSE; - } - else - { - recall_player(21, 15); - - ident = TRUE; - } - - break; - } - - case SV_SCROLL_IDENTIFY: - { - ident = TRUE; - - if (!ident_spell()) used_up = FALSE; - - break; - } - - case SV_SCROLL_STAR_IDENTIFY: - { - ident = TRUE; - - if (!identify_fully()) used_up = FALSE; - - break; - } - - case SV_SCROLL_REMOVE_CURSE: - { - if (remove_curse()) - { - msg_print("You feel as if someone is watching over you."); - ident = TRUE; - } - - break; - } - - case SV_SCROLL_STAR_REMOVE_CURSE: - { - remove_all_curse(); - - ident = TRUE; - - break; - } - - case SV_SCROLL_ENCHANT_ARMOR: - { - ident = TRUE; - - if (!enchant_spell(0, 0, 1, 0)) used_up = FALSE; - - break; - } - - case SV_SCROLL_ENCHANT_WEAPON_TO_HIT: - { - if (!enchant_spell(1, 0, 0, 0)) used_up = FALSE; - - ident = TRUE; - - break; - } - - case SV_SCROLL_ENCHANT_WEAPON_TO_DAM: - { - if (!enchant_spell(0, 1, 0, 0)) used_up = FALSE; - - ident = TRUE; - - break; - } - - case SV_SCROLL_ENCHANT_WEAPON_PVAL: - { - if (!enchant_spell(0, 0, 0, 1)) used_up = FALSE; - - ident = TRUE; - - break; - } - - case SV_SCROLL_STAR_ENCHANT_ARMOR: - { - if (!enchant_spell(0, 0, randint(3) + 2, 0)) used_up = FALSE; - - ident = TRUE; - - break; - } - - case SV_SCROLL_STAR_ENCHANT_WEAPON: - { - if (!enchant_spell(randint(3), randint(3), 0, 0)) used_up = FALSE; - - ident = TRUE; - - break; - } - - case SV_SCROLL_RECHARGING: - { - if (!recharge(60)) used_up = FALSE; - - ident = TRUE; - - break; - } - - case SV_SCROLL_LIGHT: - { - if (lite_area(damroll(2, 8), 2)) ident = TRUE; - - break; - } - - case SV_SCROLL_MAPPING: - { - map_area(); - - ident = TRUE; - - break; - } - - case SV_SCROLL_DETECT_GOLD: - { - if (detect_treasure(DEFAULT_RADIUS)) ident = TRUE; - if (detect_objects_gold(DEFAULT_RADIUS)) ident = TRUE; - - break; - } - - case SV_SCROLL_DETECT_ITEM: - { - if (detect_objects_normal(DEFAULT_RADIUS)) ident = TRUE; - - break; - } - - case SV_SCROLL_DETECT_TRAP: - { - if (detect_traps(DEFAULT_RADIUS)) ident = TRUE; - - break; - } - - case SV_SCROLL_DETECT_DOOR: - { - if (detect_doors(DEFAULT_RADIUS)) ident = TRUE; - if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE; - - break; - } - - case SV_SCROLL_DETECT_INVIS: - { - if (detect_monsters_invis(DEFAULT_RADIUS)) ident = TRUE; - - break; - } - - case SV_SCROLL_SATISFY_HUNGER: - { - if (set_food(PY_FOOD_MAX - 1)) ident = TRUE; - - break; - } - - case SV_SCROLL_BLESSING: - { - if (set_blessed(p_ptr->blessed + randint(12) + 6)) ident = TRUE; - - break; - } - - case SV_SCROLL_HOLY_CHANT: - { - if (set_blessed(p_ptr->blessed + randint(24) + 12)) ident = TRUE; - - break; - } - - case SV_SCROLL_HOLY_PRAYER: - { - if (set_blessed(p_ptr->blessed + randint(48) + 24)) ident = TRUE; - - break; - } - - case SV_SCROLL_MONSTER_CONFUSION: - { - if (p_ptr->confusing == 0) - { - msg_print("Your hands begin to glow."); - p_ptr->confusing = TRUE; - ident = TRUE; - } - - break; - } - - case SV_SCROLL_PROTECTION_FROM_EVIL: - { - k = 3 * p_ptr->lev; - if (set_protevil(p_ptr->protevil + randint(25) + k)) ident = TRUE; - - break; - } - - case SV_SCROLL_RUNE_OF_PROTECTION: - { - warding_glyph(); - - ident = TRUE; - - break; - } - - case SV_SCROLL_TRAP_DOOR_DESTRUCTION: - { - if (destroy_doors_touch()) ident = TRUE; - - break; - } - - case SV_SCROLL_STAR_DESTRUCTION: - { - /* Prevent destruction of quest levels and town */ - if (!is_quest(dun_level) && dun_level) - { - destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE); - } - else - { - msg_print("The dungeon trembles..."); - } - - ident = TRUE; - - break; - } - - case SV_SCROLL_DISPEL_UNDEAD: - { - if (dispel_undead(60)) ident = TRUE; - - break; - } - - case SV_SCROLL_GENOCIDE: - { - (void)genocide(TRUE); - - ident = TRUE; - - break; - } - - case SV_SCROLL_MASS_GENOCIDE: - { - (void)mass_genocide(TRUE); - - ident = TRUE; - - break; - } - - case SV_SCROLL_ACQUIREMENT: - { - acquirement(p_ptr->py, p_ptr->px, 1, TRUE, FALSE); - - ident = TRUE; - - break; - } - - case SV_SCROLL_STAR_ACQUIREMENT: - { - acquirement(p_ptr->py, p_ptr->px, randint(2) + 1, TRUE, FALSE); - - ident = TRUE; - - break; - } - - /* ZAngband scrolls */ - case SV_SCROLL_FIRE: - { - fire_ball(GF_FIRE, 0, 150, 4); - - /* - * Note: "Double" damage since it is centered on - * the player ... - */ - if (!p_ptr->oppose_fire && !p_ptr->resist_fire && - !p_ptr->immune_fire) - { - take_hit(50 + randint(50) + (p_ptr->sensible_fire) ? 20 : 0, - "a Scroll of Fire"); - } - - ident = TRUE; - - break; - } - - - case SV_SCROLL_ICE: - { - fire_ball(GF_ICE, 0, 175, 4); - - if (!p_ptr->oppose_cold && !p_ptr->resist_cold && - !p_ptr->immune_cold) - { - take_hit(100 + randint(100), "a Scroll of Ice"); - } - - ident = TRUE; - - break; - } - - case SV_SCROLL_CHAOS: - { - fire_ball(GF_CHAOS, 0, 222, 4); - - if (!p_ptr->resist_chaos) - { - take_hit(111 + randint(111), "a Scroll of Chaos"); - } - - ident = TRUE; - - break; - } - - case SV_SCROLL_RUMOR: - { - char rumour[80]; - - msg_print("There is message on the scroll. It says:"); - msg_print(NULL); - - /* Pick random text */ - switch (randint(20)) - { - case 1: - { - get_rnd_line("chainswd.txt", rumour); - - break; - } - - case 2: - { - get_rnd_line("error.txt", rumour); - - break; - } - - case 3: - case 4: - case 5: - { - get_rnd_line("death.txt", rumour); - - break; - } - - default: - { - get_rnd_line("rumors.txt", rumour); - - break; - } - } - - msg_format("%s", rumour); - msg_print(NULL); - - msg_print("The scroll disappears in a puff of smoke!"); - - ident = TRUE; - - break; - } - - case SV_SCROLL_ARTIFACT: - { - ident = TRUE; - - if (!artifact_scroll()) used_up = FALSE; - - break; - } - - case SV_SCROLL_STERILIZATION: - { - msg_print("A neutralising wave radiates from you!"); - set_no_breeders(randint(100) + 100); - - break; - } - - default: - { - break; - } - } - } - - /* Other readable items */ - else - { - /* Maps */ - if (o_ptr->sval >= 200) - { - int i, n; - char buf[80], fil[20]; - - strnfmt(fil, 20, "book-%d.txt", o_ptr->sval); - - n = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, -1)); - - /* Parse all the fields */ - for (i = 0; i < n; i += 4) - { - /* Grab the fields */ - int x = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 0)); - int y = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 1)); - int w = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 2)); - int h = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 3)); - - reveal_wilderness_around_player(y, x, h, w); - } - } - - /* Normal parchements */ - else - { - /* Save screen */ - screen_save(); - - /* Get the filename */ - q = format("book-%d.txt", o_ptr->sval); - - /* Peruse the help file */ - (void)show_file(q, NULL, 0, 0); - - /* Load screen */ - screen_load(); - - if (o_ptr->sval >= 100) - { - inscription_info[o_ptr->sval - 100].know = TRUE; - } - - used_up = FALSE; - } - } - - - /* Combine / Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* The item was tried */ - object_tried(o_ptr); - - /* An identification was made */ - if (ident && !object_aware_p(o_ptr)) - { - object_aware(o_ptr); - gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev); - } - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - - /* Hack -- allow certain scrolls to be "preserved" */ - if (!used_up) return; - - sound(SOUND_SCROLL); - - /* Destroy scroll */ - inc_stack_size(item, -1); - - if (get_skill(SKILL_ALCHEMY)) - { - if (item >= 0) - { - q_ptr = &forge; - object_prep(q_ptr, lookup_kind(TV_SCROLL, SV_SCROLL_NOTHING)); - object_aware(q_ptr); - object_known(q_ptr); - - q_ptr->ident |= IDENT_STOREB; - - (void)inven_carry(q_ptr, FALSE); - } - } -} - - - -/* Set the 'stick mode' on */ -void set_stick_mode(object_type *o_ptr) -{ - s32b bonus = o_ptr->pval3 & 0xFFFF; - s32b max = o_ptr->pval3 >> 16; - - get_level_use_stick = bonus; - get_level_max_stick = max; -} - -/* Remove 'stick mode' */ -void unset_stick_mode() -{ - get_level_use_stick = -1; - get_level_max_stick = -1; -} - - -/* - * Activate a device - */ -static void activate_stick(s16b s, bool_ *obvious, bool_ *use_charge) -{ - spell_type *spell = spell_at(s); - casting_result ret; - - assert(obvious != NULL); - assert(use_charge != NULL); - - ret = spell_type_produce_effect(spell, -1); - - switch (ret) - { - case NO_CAST: - *use_charge = FALSE; - *obvious = FALSE; - break; - case CAST_HIDDEN: - *use_charge = TRUE; - *obvious = FALSE; - break; - case CAST_OBVIOUS: - *use_charge = TRUE; - *obvious = TRUE; - break; - default: - assert(FALSE); - } -} - - -/* - * Use a staff. -RAK- - * - * One charge of one staff disappears. - * - * Hack -- staffs of identify can be "cancelled". - */ -void do_cmd_use_staff(void) -{ - int item, ident, chance; - - bool_ obvious, use_charge; - - object_type *o_ptr; - - u32b f1, f2, f3, f4, f5, esp; - - cptr q, s; - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* Restrict choices to wands */ - item_tester_tval = TV_STAFF; - - /* Set up the extra finder */ - get_item_hook_find_obj_what = "Staff full name? "; - get_item_extra_hook = get_item_hook_find_obj; - - /* Get an item */ - q = "Use which staff? "; - s = "You have no staff to use."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Mega-Hack -- refuse to use a pile from the ground */ - if ((item < 0) && (o_ptr->number > 1)) - { - msg_print("You must first pick up the staffs."); - return; - } - - /* Enter device mode */ - set_stick_mode(o_ptr); - - /* Take a turn */ - energy_use = 100; - - /* Not identified yet */ - ident = FALSE; - - /* get the chance */ - chance = spell_chance(o_ptr->pval2); - - /* Extract object flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Is it simple to use ? */ - if (f4 & TR4_EASY_USE) - { - chance /= 3; - } - - /* Give everyone a (slight) chance */ - if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0)) - { - chance = USE_DEVICE; - } - - /* Roll for usage */ - if (magik(chance)) - { - if (flush_failure) flush(); - msg_print("You failed to use the staff properly."); - sound(SOUND_FAIL); - - /* Leave device mode */ - unset_stick_mode(); - return; - } - - /* Notice empty staffs */ - if (o_ptr->pval <= 0) - { - if (flush_failure) flush(); - msg_print("The staff has no charges left."); - o_ptr->ident |= (IDENT_EMPTY); - - /* Leave device mode */ - unset_stick_mode(); - return; - } - - - /* Sound */ - sound(SOUND_ZAP); - - - /* Analyze the staff */ - activate_stick(o_ptr->pval2, &obvious, &use_charge); - - /* Combine / Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* Tried the item */ - object_tried(o_ptr); - - /* An identification was made */ - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - - /* Hack -- some uses are "free" */ - if (!use_charge) - { - /* Leave device mode */ - unset_stick_mode(); - - return; - } - - /* An identification was made */ - if (obvious) - { - object_aware(o_ptr); - } - - /* Use a single charge */ - o_ptr->pval--; - - /* XXX Hack -- unstack if necessary */ - if ((item >= 0) && (o_ptr->number > 1)) - { - object_type forge; - object_type *q_ptr; - - /* Get local object */ - q_ptr = &forge; - - /* Obtain a local object */ - object_copy(q_ptr, o_ptr); - - /* Modify quantity */ - q_ptr->number = 1; - - /* Restore the charges */ - o_ptr->pval++; - - /* Unstack the used item */ - o_ptr->number--; - item = inven_carry(q_ptr, FALSE); - - /* Message */ - msg_print("You unstack your staff."); - } - - /* Describe charges in the pack */ - if (item >= 0) - { - inven_item_charges(item); - } - - /* Describe charges on the floor */ - else - { - floor_item_charges(0 - item); - } - - /* Leave device mode */ - unset_stick_mode(); -} - - -/* - * Aim a wand (from the pack or floor). - * - * Use a single charge from a single item. - * Handle "unstacking" in a logical manner. - * - * For simplicity, you cannot use a stack of items from the - * ground. This would require too much nasty code. - * - * There are no wands which can "destroy" themselves, in the p_ptr->inventory - * or on the ground, so we can ignore this possibility. Note that this - * required giving "wand of wonder" the ability to ignore destruction - * by electric balls. - * - * All wands can be "cancelled" at the "Direction?" prompt for free. - * - * Note that the basic "bolt" wands do slightly less damage than the - * basic "bolt" rods, but the basic "ball" wands do the same damage - * as the basic "ball" rods. - */ -void do_cmd_aim_wand(void) -{ - bool_ obvious, use_charge; - - int item, ident, chance; - - object_type *o_ptr; - - cptr q, s; - - u32b f1, f2, f3, f4, f5, esp; - - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* Restrict choices to wands */ - item_tester_tval = TV_WAND; - - /* Set up the extra finder */ - get_item_hook_find_obj_what = "Wand full name? "; - get_item_extra_hook = get_item_hook_find_obj; - - /* Get an item */ - q = "Aim which wand? "; - s = "You have no wand to aim."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; - - /* Get the item */ - o_ptr = get_object(item); - - - /* Mega-Hack -- refuse to aim a pile from the ground */ - if ((item < 0) && (o_ptr->number > 1)) - { - msg_print("You must first pick up the wands."); - return; - } - - /* Take a turn */ - energy_use = 100; - - /* Not identified yet */ - ident = FALSE; - - /* Enter device mode */ - set_stick_mode(o_ptr); - - /* get the chance */ - chance = spell_chance(o_ptr->pval2); - - /* Extract object flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Is it simple to use ? */ - if (f4 & TR4_EASY_USE) - { - chance /= 3; - } - - /* Roll for usage */ - if (magik(chance)) - { - if (flush_failure) flush(); - msg_print("You failed to use the wand properly."); - sound(SOUND_FAIL); - - /* Leave device mode */ - unset_stick_mode(); - return; - } - - /* The wand is already empty! */ - if (o_ptr->pval <= 0) - { - if (flush_failure) flush(); - msg_print("The wand has no charges left."); - o_ptr->ident |= (IDENT_EMPTY); - - /* Leave device mode */ - unset_stick_mode(); - return; - } - - - /* Sound */ - sound(SOUND_ZAP); - - - /* Analyze the wand */ - activate_stick(o_ptr->pval2, &obvious, &use_charge); - - /* Combine / Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* Mark it as tried */ - object_tried(o_ptr); - - /* Hack -- some uses are "free" */ - if (!use_charge) - { - /* Leave device mode */ - unset_stick_mode(); - - return; - } - - /* An identification was made */ - if (obvious) - { - object_aware(o_ptr); - } - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - - /* Use a single charge */ - o_ptr->pval--; - - /* Describe the charges in the pack */ - if (item >= 0) - { - inven_item_charges(item); - } - - /* Describe the charges on the floor */ - else - { - floor_item_charges(0 - item); - } - - /* Leave device mode */ - unset_stick_mode(); -} - - - - - - -/* - * Activate (zap) a Rod - * - * Unstack fully charged rods as needed. - * - * Hack -- rods of perception/genocide can be "cancelled" - * All rods can be cancelled at the "Direction?" prompt - */ - - -/* - * Hook to determine if an object is zapable - */ -static bool_ item_tester_hook_zapable(object_type *o_ptr) -{ - if ((o_ptr->tval == TV_ROD) || (o_ptr->tval == TV_ROD_MAIN)) return (TRUE); - - /* Assume not */ - return (FALSE); -} - - -/* - * Hook to determine if an object is attachable - */ -static bool_ item_tester_hook_attachable(object_type *o_ptr) -{ - if ((o_ptr->tval == TV_ROD_MAIN) && - (o_ptr->pval == SV_ROD_NOTHING)) return (TRUE); - - /* Assume not */ - return (FALSE); -} - - -/* - * Combine a rod and a rod tip - */ -void zap_combine_rod_tip(object_type *q_ptr, int tip_item) -{ - int item; - - object_type *o_ptr; - - cptr q, s; - - u32b f1, f2, f3, f4, f5, esp; - s32b cost; - - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* Restrict choices to rods */ - item_tester_hook = item_tester_hook_attachable; - - /* Get an item */ - q = "Attach the rod tip with which rod? "; - s = "You have no rod to attach to."; - if (!get_item(&item, q, s, (USE_INVEN))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Examine the rod */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Calculate rod tip's mana cost */ - cost = q_ptr->pval; - - if (f4 & TR4_CHEAPNESS) - { - cost /= 2; - } - - /* - * The rod must have at least the same mana capacity as the - * rod tip spell needs - */ - if (o_ptr->pval2 < cost) - { - msg_print("This rod doesn't have enough mana for the rod tip."); - return; - } - - /* Attach the tip to the rod */ - o_ptr->pval = q_ptr->sval; - - /* Destroy rod tip */ - inc_stack_size(tip_item, -1); -} - - -/* - * Zap a rod, or attack a rod tip to a rod - */ -void do_cmd_zap_rod(void) -{ - int item, ident, chance, dir, lev; - - int cost; - - bool_ require_dir; - - object_type *o_ptr; - - object_kind *tip_ptr; - - u32b f1, f2, f3, f4, f5, esp; - - cptr q, s; - - /* Hack -- let perception get aborted */ - bool_ use_charge = TRUE; - - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - - /* Restrict choices to rods */ - item_tester_hook = item_tester_hook_zapable; - - /* Set up the extra finder */ - get_item_hook_find_obj_what = "Rod full name? "; - get_item_extra_hook = get_item_hook_find_obj; - - /* Get an item */ - q = "Zap which rod? "; - s = "You have no rod to zap."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; - - /* Get the item */ - o_ptr = get_object(item); - - - /* "Zapping" a Rod Tip on rod of nothing will attach it */ - if (o_ptr->tval == TV_ROD) - { - if (item >= 0) - { - zap_combine_rod_tip(o_ptr, item); - return; - } - else - { - msg_print("You can't zap a rod tip that's on the floor."); - return; - } - } - - - /* Non-directed rods */ - if (o_ptr->pval < SV_ROD_MIN_DIRECTION) - { - require_dir = FALSE; - } - - /* Some rods always require direction */ - else - { - switch (o_ptr->pval) - { - case SV_ROD_DETECT_TRAP: - case SV_ROD_HAVOC: - case SV_ROD_HOME: - { - require_dir = FALSE; - break; - } - - default: - { - require_dir = TRUE; - break; - } - } - } - - /* Get a direction (unless KNOWN not to need it) */ - if (!object_aware_p(o_ptr) || require_dir) - { - /* Get a direction, allow cancel */ - if (!get_aim_dir(&dir)) return; - } - - /* Take a turn */ - energy_use = 100; - - /* Examine the rod */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if (f4 & TR4_FAST_CAST) energy_use /= 2; - - /* Not identified yet */ - ident = FALSE; - - /* Extract the item level */ - tip_ptr = &k_info[lookup_kind(TV_ROD, o_ptr->pval)]; - lev = k_info[lookup_kind(TV_ROD, o_ptr->pval)].level; - - /* Base chance of success */ - chance = p_ptr->skill_dev; - - /* Confusion hurts skill */ - if (p_ptr->confused) chance = chance / 2; - - /* High level objects are harder */ - chance = chance - ((lev > 50) ? 50 : lev); - - if (chance <= 0) - { - chance = 1; - } - - /* Is it simple to use ? */ - if (f4 & TR4_EASY_USE) - { - chance *= 10; - } - - /* Give everyone a (slight) chance */ - if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0)) - { - chance = USE_DEVICE; - } - - /* Roll for usage */ - if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE)) - { - /* Flush input if necessary */ - if (flush_failure) flush(); - - /* Message */ - msg_print("You failed to use the rod properly."); - - sound(SOUND_FAIL); - - return; - } - - /* Extract mana cost */ - cost = tip_ptr->pval; - - /* "Cheapness" ego halven the cost */ - if (f4 & TR4_CHEAPNESS) cost = cost / 2; - - /* A single rod is still charging */ - if (o_ptr->timeout < cost) - { - /* Flush input if necessary */ - if (flush_failure) flush(); - - /* Message */ - msg_print("The rod does not have enough mana yet."); - - return; - } - - /* Increase the timeout by the rod kind's pval. */ - o_ptr->timeout -= cost; - - /* Sound */ - sound(SOUND_ZAP); - - /* Analyze the rod */ - switch (o_ptr->pval) - { - case SV_ROD_HOME: - { - ident = TRUE; - - do_cmd_home_trump(); - - break; - } - - case SV_ROD_DETECT_TRAP: - { - if (detect_traps(DEFAULT_RADIUS)) ident = TRUE; - - break; - } - - case SV_ROD_DETECT_DOOR: - { - if (detect_doors(DEFAULT_RADIUS)) ident = TRUE; - if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE; - - break; - } - - case SV_ROD_IDENTIFY: - { - ident = TRUE; - - if (!ident_spell()) use_charge = FALSE; - - break; - } - - case SV_ROD_RECALL: - { - if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")) - { - use_charge = FALSE; - } - else - { - recall_player(21, 15); - - ident = TRUE; - } - - break; - } - - case SV_ROD_ILLUMINATION: - { - if (lite_area(damroll(2, 8), 2)) ident = TRUE; - - break; - } - - case SV_ROD_MAPPING: - { - map_area(); - - ident = TRUE; - - break; - } - - case SV_ROD_DETECTION: - { - detect_all(DEFAULT_RADIUS); - - ident = TRUE; - - break; - } - - case SV_ROD_PROBING: - { - probing(); - - ident = TRUE; - - break; - } - - case SV_ROD_CURING: - { - if (set_blind(0)) ident = TRUE; - if (set_poisoned(0)) ident = TRUE; - if (set_confused(0)) ident = TRUE; - if (set_stun(0)) ident = TRUE; - if (set_cut(0)) ident = TRUE; - if (set_image(0)) ident = TRUE; - - break; - } - - case SV_ROD_HEALING: - { - if (hp_player(500)) ident = TRUE; - if (set_stun(0)) ident = TRUE; - if (set_cut(0)) ident = TRUE; - - break; - } - - case SV_ROD_RESTORATION: - { - if (restore_level()) ident = TRUE; - if (do_res_stat(A_STR, TRUE)) ident = TRUE; - if (do_res_stat(A_INT, TRUE)) ident = TRUE; - if (do_res_stat(A_WIS, TRUE)) ident = TRUE; - if (do_res_stat(A_DEX, TRUE)) ident = TRUE; - if (do_res_stat(A_CON, TRUE)) ident = TRUE; - if (do_res_stat(A_CHR, TRUE)) ident = TRUE; - - break; - } - - case SV_ROD_SPEED: - { - if (!p_ptr->fast) - { - if (set_fast(randint(30) + 15, 10)) ident = TRUE; - } - else - { - (void)set_fast(p_ptr->fast + 5, 10); - } - - break; - } - - case SV_ROD_TELEPORT_AWAY: - { - if (teleport_monster(dir)) ident = TRUE; - - break; - } - - case SV_ROD_DISARMING: - { - if (disarm_trap(dir)) ident = TRUE; - - break; - } - - case SV_ROD_LITE: - { - msg_print("A line of blue shimmering light appears."); - lite_line(dir); - - ident = TRUE; - - break; - } - - case SV_ROD_SLEEP_MONSTER: - { - if (sleep_monster(dir)) ident = TRUE; - - break; - } - - case SV_ROD_SLOW_MONSTER: - { - if (slow_monster(dir)) ident = TRUE; - - break; - } - - case SV_ROD_DRAIN_LIFE: - { - if (drain_life(dir, 75)) ident = TRUE; - - break; - } - - case SV_ROD_POLYMORPH: - { - if (poly_monster(dir)) ident = TRUE; - - break; - } - - case SV_ROD_ACID_BOLT: - { - fire_bolt_or_beam(10, GF_ACID, dir, damroll(6, 8)); - - ident = TRUE; - - break; - } - - case SV_ROD_ELEC_BOLT: - { - fire_bolt_or_beam(10, GF_ELEC, dir, damroll(3, 8)); - - ident = TRUE; - - break; - } - - case SV_ROD_FIRE_BOLT: - { - fire_bolt_or_beam(10, GF_FIRE, dir, damroll(8, 8)); - - ident = TRUE; - - break; - } - - case SV_ROD_COLD_BOLT: - { - fire_bolt_or_beam(10, GF_COLD, dir, damroll(5, 8)); - - ident = TRUE; - - break; - } - - case SV_ROD_ACID_BALL: - { - fire_ball(GF_ACID, dir, 60, 2); - - ident = TRUE; - - break; - } - - case SV_ROD_ELEC_BALL: - { - fire_ball(GF_ELEC, dir, 32, 2); - - ident = TRUE; - - break; - } - - case SV_ROD_FIRE_BALL: - { - fire_ball(GF_FIRE, dir, 72, 2); - - ident = TRUE; - - break; - } - - case SV_ROD_COLD_BALL: - { - fire_ball(GF_COLD, dir, 48, 2); - - ident = TRUE; - - break; - } - - case SV_ROD_HAVOC: - { - call_chaos(); - - ident = TRUE; - - break; - } - - default: - { - process_hooks(HOOK_ZAP, "(d,d)", o_ptr->tval, o_ptr->sval); - - break; - } - } - - - /* Combine / Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* Tried the object */ - object_tried(o_ptr); - - /* Successfully determined the object function */ - if (ident && !object_aware_p(o_ptr)) - { - object_aware(o_ptr); - gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev); - } - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - /* Hack -- deal with cancelled zap */ - if (!use_charge) - { - o_ptr->timeout += cost; - - return; - } -} - - - - -/* - * Hook to determine if an object is activable - */ -static bool_ item_tester_hook_activate(object_type *o_ptr) -{ - u32b f1, f2, f3, f4, f5, esp; - - - /* Not known */ - if (!object_known_p(o_ptr)) return (FALSE); - - /* Extract the flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Check activation flag */ - if (f3 & (TR3_ACTIVATE)) return (TRUE); - - /* Assume not */ - return (FALSE); -} - - - -/* - * Hack -- activate the ring of power - */ -int ring_of_power() -{ - char ch = 0, p = 0; - - int plev = p_ptr->lev; - - int timeout = 0; - - - /* Select power to use */ - while (TRUE) - { - if (!get_com("[S]ummon a wraith, [R]ule the world or " - "[C]ast a powerful attack? ", &ch)) - { - return (0); - } - - if (ch == 'S' || ch == 's') - { - p = 1; - break; - } - if (ch == 'R' || ch == 'r') - { - p = 2; - break; - } - if (ch == 'C' || ch == 'c') - { - p = 3; - break; - } - } - - /* Summon a Wraith */ - if (p == 1) - { - /* Rewrite this -- pelpel */ - if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), - (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD), - (((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE))) - { - msg_print("Cold winds begin to blow around you, " - "carrying with them the stench of decay..."); - msg_print("Ancient, long-dead forms arise from the ground " - "to serve you!"); - } - timeout = 200 + rand_int(200); - } - - /* Rule the World -- only if we can really do so */ - else if (p == 2) - { - msg_print("The power of the ring destroys the world!"); - msg_print("The world changes!"); - - autosave_checkpoint(); - - /* Leaving */ - p_ptr->leaving = TRUE; - timeout = 250 + rand_int(250); - } - - /* Cast a powerful spell */ - else if (p == 3) - { - int dir; - - if (!get_aim_dir(&dir)) return (0); - - if (rand_int(3) == 0) - { - msg_print("You call the fire of Mount Doom!"); - fire_ball(GF_METEOR, dir, 600, 4); - } - else - { - msg_print("Your ring tries to take possession of your enemy's mind!"); - fire_bolt(GF_CHARM, dir, 600); - } - timeout = 300 + rand_int(300); - } - - return (timeout); -} - - - - -/* - * Enchant some bolts - */ -bool_ brand_bolts(void) -{ - int i; - - - /* Use the first acceptable bolts */ - for (i = 0; i < INVEN_PACK; i++) - { - object_type *o_ptr = &p_ptr->inventory[i]; - - /* Skip non-bolts */ - if (o_ptr->tval != TV_BOLT) continue; - - /* Skip artifacts and ego-items */ - if (o_ptr->art_name || artifact_p(o_ptr) || ego_item_p(o_ptr)) continue; - - /* Skip cursed/broken items */ - if (cursed_p(o_ptr)) continue; - - /* Randomize */ - if (rand_int(100) < 75) continue; - - /* Message */ - msg_print("Your bolts are covered in a fiery aura!"); - - /* Ego-item */ - o_ptr->name2 = EGO_FLAME; - - /* Apply the ego */ - apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE); - - /* Enchant */ - enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM); - - /* Notice */ - return (TRUE); - } - - /* Flush */ - if (flush_failure) flush(); - - /* Fail */ - msg_print("The fiery enchantment failed."); - - /* Notice */ - return (TRUE); -} - - -/* - * Objects in the p_ptr->inventory can now be activated, and - * SOME of those may be able to stack (ego wands or something) - * in any case, we can't know that it's impossible. *BUT* we'll - * ignore it for now, and the timeout will be set on the entire stack - * of objects. Reduces their utility, but oh well. - * - * Note that it always takes a turn to activate an object, even if - * the user hits "escape" at the "direction" prompt. - */ -void do_cmd_activate(void) -{ - int item, lev, chance; - - char ch, spell_choice; - - object_type *o_ptr; - - u32b f1, f2, f3, f4, f5, esp; - - cptr q, s; - - - /* Prepare the hook */ - item_tester_hook = item_tester_hook_activate; - - /* Get an item */ - command_wrk = USE_EQUIP; - q = "Activate which item? "; - s = "You have nothing to activate."; - if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Extract object flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Wearable items have to be worn */ - if (!(f5 & TR5_ACTIVATE_NO_WIELD)) - { - if (item < INVEN_WIELD) - { - msg_print("You must wear it to activate it."); - return; - } - } - - /* Take a turn */ - energy_use = 100; - - /* Extract the item level */ - lev = k_info[o_ptr->k_idx].level; - - /* Hack -- Use artifact level instead */ - if (artifact_p(o_ptr)) - { - if (o_ptr->tval == TV_RANDART) - { - lev = random_artifacts[o_ptr->sval].level; - } - else - { - lev = a_info[o_ptr->name1].level; - } - } - - /* Base chance of success */ - chance = p_ptr->skill_dev; - - /* Confusion hurts skill */ - if (p_ptr->confused) chance = chance / 2; - - /* Hight level objects are harder */ - chance = chance - ((lev > 50) ? 50 : lev); - - if (chance <= 0) - { - chance = 1; - } - - /* Is it simple to use ? */ - if (f4 & TR4_EASY_USE) - { - chance *= 10; - } - - /* Give everyone a (slight) chance */ - if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0)) - { - chance = USE_DEVICE; - } - - /* Roll for usage */ - if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE)) - { - if (flush_failure) flush(); - msg_print("You failed to activate it properly."); - sound(SOUND_FAIL); - return; - } - - /* Check the recharge */ - if (o_ptr->timeout) - { - /* Mage Staff of Spells -- Have another timeout in xtra2 */ - if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && o_ptr->xtra2) - { - msg_print("It whines, glows and fades..."); - return; - } - - /* Monster eggs */ - else if (o_ptr->tval == TV_EGG) - { - msg_print("You resume the development of the egg."); - o_ptr->timeout = 0; - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - /* Success */ - return; - } - - /* Normal activatable items */ - else - { - msg_print("It whines, glows and fades..."); - return; - } - } - - - /* Activate the item */ - msg_print("You activate it..."); - - /* Sound */ - sound(SOUND_ZAP); - - /* Lua hook ? -- go first to allow lua to override */ - if (process_hooks(HOOK_ACTIVATE, "(d)", item)) - { - return; - } - - /* New mostly unified activation code - This has to be early to allow artifacts to override normal items -- neil */ - - if ( activation_aux(o_ptr, TRUE, item) == NULL ) - { - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - /* Success */ - return; - } - - /* Mage Staff of Spells */ - if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL)) - { - while (TRUE) - { - if (!get_com("Use Spell [1] or [2]?", &ch)) - { - return; - } - - if (ch == '1') - { - spell_choice = 1; - break; - } - - if (ch == '2') - { - spell_choice = 2; - break; - } - } - - if (spell_choice == 1) - { - /* Still need to check timeouts because there is another counter */ - if (o_ptr->timeout) - { - msg_print("The first spell is still charging!"); - return; - } - - /* Cast spell 1 */ - activate_spell(o_ptr, spell_choice); - } - else if (spell_choice == 2) - { - /* Still need to check timeouts because there is another counter */ - if (o_ptr->xtra2) - { - msg_print("The second spell is still charging!"); - return; - } - - /* Cast spell 2 */ - activate_spell(o_ptr, spell_choice); - } - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - /* Success */ - return; - } - - /* Monster eggs */ - if (o_ptr->tval == TV_EGG) - { - msg_print("You stop the development of the egg."); - o_ptr->timeout = -1; - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - /* Success */ - return; - } - - /* Musical instruments */ - if (o_ptr->tval == TV_INSTRUMENT) - { - /* Horns */ - if (o_ptr->sval == SV_HORN) - { - msg_format("Your instrument emits a loud sound!"); - - aggravate_monsters(1); - - o_ptr->timeout = 100; - } - - /* Success */ - return; - } - - /* Mistake */ - msg_print("Oops. That object cannot be activated."); -} - -const char *activation_aux(object_type * o_ptr, bool_ doit, int item) -{ - static char buf[256]; - int plev = get_skill(SKILL_DEVICE); - - int i = 0, ii = 0, ij = 0, k, dir, dummy = 0; - int chance; - bool_ is_junkart = (o_ptr->tval == TV_RANDART); - - int spell = 0; - - /* Junkarts */ - if (is_junkart) - spell = activation_info[o_ptr->pval2].spell; - - /* True Actifacts */ - if (!spell && o_ptr->name1) - spell = a_info[o_ptr->name1].activate; - - /* Random and Alchemist Artifacts */ - if (!spell && o_ptr->art_name) - spell = o_ptr->xtra2; - - /* Ego Items */ - if (!spell && o_ptr->name2) - spell = e_info[o_ptr->name2].activate; - - /* Dual egos with the second ego having the activation */ - if (!spell && o_ptr->name2b) - spell = e_info[o_ptr->name2b].activate; - - /* Intrinsic to item type (rings of Ice, etc) */ - if (!spell) - spell = k_info[o_ptr->k_idx].activate; - - /* Complain about mis-configured .txt files? */ - if (!spell) - return "Unknown!"; - - /* Negative means a unified spell index */ - if (spell < 0) - { - struct spell_type *spell_ptr = spell_at(-spell); - if (doit) - { - spell_type_produce_effect(spell_ptr, item); - o_ptr->timeout = spell_type_activation_roll_timeout(spell_ptr); - } - else - { - spell_type_activation_description(spell_ptr, buf); - return buf; - } - } - else - { - /* Activate for attack */ - switch (spell) - { - case ACT_GILGALAD: - { - if (!doit) return "starlight (75) every 75+d75 turns"; - for (k = 1; k < 10; k++) - { - if (k - 5) fire_beam(GF_LITE, k, 75); - } - - o_ptr->timeout = rand_int(75) + 75; - - break; - } - - case ACT_CELEBRIMBOR: - { - if (!doit) return "temporary ESP (dur 20+d20) every 20+d50 turns"; - set_tim_esp(p_ptr->tim_esp + randint(20) + 20); - - o_ptr->timeout = rand_int(50) + 20; - - break; - } - - case ACT_SKULLCLEAVER: - { - if (!doit) return "destruction every 200+d200 turns"; - destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE); - - o_ptr->timeout = rand_int(200) + 200; - - break; - } - - case ACT_HARADRIM: - { - if (!doit) return "berserk strength every 50+d50 turns"; - set_afraid(0); - set_shero(p_ptr->shero + randint(25) + 25); - hp_player(30); - - o_ptr->timeout = rand_int(50) + 50; - - break; - } - - case ACT_FUNDIN: - { - if (!doit) return "dispel evil (x4) every 100+d100 turns"; - dispel_evil(p_ptr->lev * 4); - - o_ptr->timeout = rand_int(100) + 100; - - break; - } - - case ACT_EOL: - { - if (!doit) return "mana bolt (9d8) 7+d7 turns"; - if (!get_aim_dir(&dir)) break; - fire_bolt(GF_MANA, dir, damroll(9, 8)); - - o_ptr->timeout = rand_int(7) + 7; - - break; - } - - case ACT_UMBAR: - { - if (!doit) return "magic arrow (10d10) every 20+d20 turns"; - if (!get_aim_dir(&dir)) break; - fire_bolt(GF_MISSILE, dir, damroll(10, 10)); - - o_ptr->timeout = rand_int(20) + 20; - - break; - } - - case ACT_NUMENOR: - { - /* Give full knowledge */ - /* Hack -- Maximal info */ - monster_race *r_ptr; - cave_type *c_ptr; - int x, y, m; - - if (!doit) return "analyze monster every 500+d200 turns"; - - if (!tgt_pt(&x, &y)) break; - - c_ptr = &cave[y][x]; - if (!c_ptr->m_idx) break; - - r_ptr = &r_info[c_ptr->m_idx]; - - /* Observe "maximal" attacks */ - for (m = 0; m < 4; m++) - { - /* Examine "actual" blows */ - if (r_ptr->blow[m].effect || r_ptr->blow[m].method) - { - /* Hack -- maximal observations */ - r_ptr->r_blows[m] = MAX_UCHAR; - } - } - - /* Hack -- maximal drops */ - r_ptr->r_drop_gold = r_ptr->r_drop_item = - (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) + - ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) + - ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) + - ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) + - ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) + - ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0)); - - /* Hack -- but only "valid" drops */ - if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0; - if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0; - - /* Hack -- observe many spells */ - r_ptr->r_cast_inate = MAX_UCHAR; - r_ptr->r_cast_spell = MAX_UCHAR; - - /* Hack -- know all the flags */ - r_ptr->r_flags1 = r_ptr->flags1; - r_ptr->r_flags2 = r_ptr->flags2; - r_ptr->r_flags3 = r_ptr->flags3; - r_ptr->r_flags4 = r_ptr->flags4; - r_ptr->r_flags5 = r_ptr->flags5; - r_ptr->r_flags6 = r_ptr->flags6; - r_ptr->r_flags7 = r_ptr->flags7; - r_ptr->r_flags8 = r_ptr->flags8; - r_ptr->r_flags9 = r_ptr->flags9; - - o_ptr->timeout = rand_int(200) + 500; - - break; - } - - case ACT_KNOWLEDGE: - { - if (!doit) return "whispers from beyond(sanity drain) 100+d200 turns"; - identify_fully(); - take_sanity_hit(damroll(10, 7), "the sounds of the dead"); - - o_ptr->timeout = rand_int(200) + 100; - - break; - } - - case ACT_UNDEATH: - { - if (!doit) return "ruination every 10+d10 turns"; - msg_print("The phial wells with dark light..."); - unlite_area(damroll(2, 15), 3); - take_hit(damroll(10, 10), "activating The Phial of Undeath"); - (void)dec_stat(A_DEX, 25, STAT_DEC_PERMANENT); - (void)dec_stat(A_WIS, 25, STAT_DEC_PERMANENT); - (void)dec_stat(A_CON, 25, STAT_DEC_PERMANENT); - (void)dec_stat(A_STR, 25, STAT_DEC_PERMANENT); - (void)dec_stat(A_CHR, 25, STAT_DEC_PERMANENT); - (void)dec_stat(A_INT, 25, STAT_DEC_PERMANENT); - - o_ptr->timeout = rand_int(10) + 10; - - break; - } - - case ACT_THRAIN: - { - if (!doit) return "detection every 30+d30 turns"; - msg_print("The stone glows a deep green..."); - detect_all(DEFAULT_RADIUS); - - o_ptr->timeout = rand_int(30) + 30; - - break; - } - - case ACT_BARAHIR: - { - if (!doit) return "dispel small life every 55+d55 turns"; - msg_print("You exterminate small life."); - (void)dispel_monsters(4); - - o_ptr->timeout = rand_int(55) + 55; - - break; - } - - case ACT_TULKAS: - { - if (!doit) return "haste self (75+d75 turns) every 150+d150 turns"; - msg_print("The ring glows brightly..."); - if (!p_ptr->fast) - { - (void)set_fast(randint(75) + 75, 10); - } - else - { - (void)set_fast(p_ptr->fast + 5, 10); - } - - o_ptr->timeout = rand_int(150) + 150; - - break; - } - - case ACT_NARYA: - { - if (!doit) return "healing (500) every 200+d100 turns"; - msg_print("The ring glows deep red..."); - hp_player(500); - set_blind(0); - set_confused(0); - set_poisoned(0); - set_stun(0); - set_cut(0); - - o_ptr->timeout = rand_int(100) + 200; - - break; - } - - case ACT_NENYA: - { - if (!doit) return "healing (800) every 100+d200 turns"; - msg_print("The ring glows bright white..."); - hp_player(800); - set_blind(0); - set_confused(0); - set_poisoned(0); - set_stun(0); - set_cut(0); - - o_ptr->timeout = rand_int(200) + 100; - - break; - } - - case ACT_VILYA: - { - if (!doit) return "greater healing (900) every 200+d200 turns"; - msg_print("The ring glows deep blue..."); - hp_player(900); - set_blind(0); - set_confused(0); - set_poisoned(0); - set_stun(0); - set_cut(0); - if (p_ptr->black_breath) - { - p_ptr->black_breath = FALSE; - msg_print("The hold of the Black Breath on you is broken!"); - } - - o_ptr->timeout = rand_int(200) + 200; - - break; - } - - case ACT_POWER: - { - if (!doit) return "powerful things"; - msg_print("The ring glows intensely black..."); - - o_ptr->timeout = ring_of_power(); - - break; - } - - - /* The Stone of Lore is perilous, for the sake of game balance. */ - case ACT_STONE_LORE: - { - if (!doit) return "perilous identify every turn"; - msg_print("The stone reveals hidden mysteries..."); - if (!ident_spell()) break; - - if (has_ability(AB_PERFECT_CASTING)) - { - /* Sufficient mana */ - if (20 <= p_ptr->csp) - { - /* Use some mana */ - p_ptr->csp -= 20; - } - - /* Over-exert the player */ - else - { - int oops = 20 - p_ptr->csp; - - /* No mana left */ - p_ptr->csp = 0; - p_ptr->csp_frac = 0; - - /* Message */ - msg_print("You are too weak to control the stone!"); - - /* Hack -- Bypass free action */ - (void)set_paralyzed(randint(5 * oops + 1)); - - /* Confusing. */ - (void)set_confused(p_ptr->confused + - randint(5 * oops + 1)); - } - - /* Redraw mana */ - p_ptr->redraw |= (PR_MANA); - } - - take_hit(damroll(1, 12), "perilous secrets"); - - /* Confusing. */ - if (rand_int(5) == 0) - { - (void)set_confused(p_ptr->confused + randint(10)); - } - - /* Exercise a little care... */ - if (rand_int(20) == 0) - { - take_hit(damroll(4, 10), "perilous secrets"); - } - - o_ptr->timeout = 1; - - break; - } - - case ACT_RAZORBACK: - { - if (!doit) return "star ball (150) every 1000 turns"; - msg_print("Your armor is surrounded by lightning..."); - for (i = 0; i < 8; i++) fire_ball(GF_ELEC, ddd[i], 150, 3); - - o_ptr->timeout = 1000; - - break; - } - - case ACT_BLADETURNER: - { - if (!doit) return "invulnerability (4+d8) every 800 turns"; - set_invuln(p_ptr->invuln + randint(8) + 4); - - o_ptr->timeout = 800; - - break; - } - - case ACT_MEDIATOR: - { - if (!doit) return "breathe elements (300), berserk rage, bless, and resistance every 400 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe the elements."); - fire_ball(GF_MISSILE, dir, 300, 4); - msg_print("Your armor glows many colours..."); - (void)set_afraid(0); - (void)set_shero(p_ptr->shero + randint(50) + 50); - (void)hp_player(30); - (void)set_blessed(p_ptr->blessed + randint(50) + 50); - (void)set_oppose_acid(p_ptr->oppose_acid + randint(50) + 50); - (void)set_oppose_elec(p_ptr->oppose_elec + randint(50) + 50); - (void)set_oppose_fire(p_ptr->oppose_fire + randint(50) + 50); - (void)set_oppose_cold(p_ptr->oppose_cold + randint(50) + 50); - (void)set_oppose_pois(p_ptr->oppose_pois + randint(50) + 50); - - o_ptr->timeout = 400; - - break; - } - - case ACT_BELEGENNON: - { - if (!doit) return ("heal (777), curing and heroism every 300 turns"); - msg_print("A heavenly choir sings..."); - (void)set_poisoned(0); - (void)set_cut(0); - (void)set_stun(0); - (void)set_confused(0); - (void)set_blind(0); - (void)set_hero(p_ptr->hero + randint(25) + 25); - (void)hp_player(777); - - o_ptr->timeout = 300; - - break; - } - - case ACT_GORLIM: - { - if (!doit) return "rays of fear in every direction"; - turn_monsters(40 + p_ptr->lev); - - o_ptr->timeout = 3 * (p_ptr->lev + 10); - - break; - } - - case ACT_COLLUIN: - { - if (!doit) return "resistance (20+d20 turns) every 111 turns"; - msg_print("Your cloak glows many colours..."); - (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20); - (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20); - (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20); - (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20); - (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20); - - o_ptr->timeout = 111; - - break; - } - - - case ACT_BELANGIL: - { - if (!doit) return "frost ball (48) every 5+d5 turns"; - msg_print("Your dagger is covered in frost..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_COLD, dir, 48, 2); - - o_ptr->timeout = rand_int(5) + 5; - - break; - } - - case ACT_ANGUIREL: - { - if (!doit) return "a getaway every 35 turns"; - switch (randint(13)) - { - case 1: - case 2: - case 3: - case 4: - case 5: - { - teleport_player(10); - - break; - } - - case 6: - case 7: - case 8: - case 9: - case 10: - { - teleport_player(222); - - break; - } - - case 11: - case 12: - { - (void)stair_creation(); - - break; - } - - default: - { - if (get_check("Leave this level? ")) - { - autosave_checkpoint(); - - /* Leaving */ - p_ptr->leaving = TRUE; - } - - break; - } - } - - o_ptr->timeout = 35; - - break; - } - - case ACT_ERU: - { - if (!doit) return "healing(7000), curing every 500 turns"; - msg_print("Your sword glows an intense white..."); - hp_player(7000); - heal_insanity(50); - set_blind(0); - set_poisoned(0); - set_confused(0); - set_stun(0); - set_cut(0); - set_image(0); - - o_ptr->timeout = 500; - - break; - } - - case ACT_DAWN: - { - if (!doit) return "summon the Legion of the Dawn every 500+d500 turns"; - msg_print("You summon the Legion of the Dawn."); - (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DAWN, TRUE); - - o_ptr->timeout = 500 + randint(500); - - break; - } - - case ACT_FIRESTAR: - { - if (!doit) return "large fire ball (72) every 100 turns"; - msg_print("Your morning star rages in fire..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_FIRE, dir, 72, 3); - - o_ptr->timeout = 100; - - break; - } - - case ACT_TURMIL: - { - if (!doit) return "drain life (90) every 70 turns"; - msg_print("Your hammer glows white..."); - if (!get_aim_dir(&dir)) break; - drain_life(dir, 90); - - o_ptr->timeout = 70; - - break; - } - - case ACT_CUBRAGOL: - { - if (!doit) return "fire branding of bolts every 999 turns"; - msg_print("Your crossbow glows deep red..."); - (void)brand_bolts(); - - o_ptr->timeout = 999; - - break; - } - - case ACT_ELESSAR: - { - if (!doit) return "heal and cure black breath every 200 turns"; - if (p_ptr->black_breath) - { - msg_print("The hold of the Black Breath on you is broken!"); - } - p_ptr->black_breath = FALSE; - hp_player(100); - - o_ptr->timeout = 200; - - break; - } - - case ACT_GANDALF: - { - if (!doit) return "restore mana every 666 turns"; - msg_print("Your mage staff glows deep blue..."); - if (p_ptr->csp < p_ptr->msp) - { - p_ptr->csp = p_ptr->msp; - p_ptr->csp_frac = 0; - msg_print("Your feel your head clear."); - p_ptr->redraw |= (PR_MANA); - p_ptr->window |= (PW_PLAYER); - } - - o_ptr->timeout = 666; - - break; - } - - case ACT_MARDA: - { - if (!doit) return "summon a thunderlord every 1000 turns"; - if (randint(3) == 1) - { - if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_THUNDERLORD)) - { - msg_print("A Thunderlord comes from thin air!"); - msg_print("'I will burn you!'"); - } - } - else - { - if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), - SUMMON_THUNDERLORD, (plev == 50 ? TRUE : FALSE))) - { - msg_print("A Thunderlord comes from thin air!"); - msg_print("'I will help you in your difficult task.'"); - } - } - - o_ptr->timeout = 1000; - - break; - } - - case ACT_PALANTIR: - { - if (!doit) return "clairvoyance every 100+d100 turns"; - msg_print("The stone glows a deep green..."); - wiz_lite_extra(); - (void)detect_traps(DEFAULT_RADIUS); - (void)detect_doors(DEFAULT_RADIUS); - (void)detect_stairs(DEFAULT_RADIUS); - - o_ptr->timeout = rand_int(100) + 100; - - break; - } - - case ACT_EREBOR: - { - if (!doit) return "open a secret passage every 75 turns"; - msg_print("Your pick twists in your hands."); - - if (!get_aim_dir(&dir)) break; - if (passwall(dir, TRUE)) - { - msg_print("A passage opens, and you step through."); - } - else - { - msg_print("There is no wall there!"); - } - - o_ptr->timeout = 75; - - break; - } - - case ACT_DRUEDAIN: - { - if (!doit) return "detection every 99 turns"; - msg_print("Your drum shows you the world."); - detect_all(DEFAULT_RADIUS); - - o_ptr->timeout = 99; - - break; - } - - case ACT_ROHAN: - { - if (!doit) return "heroism, berserker, and haste every 250 turns"; - msg_print("Your horn glows deep red."); - set_afraid(0); - set_shero(p_ptr->shero + damroll(5, 10) + 30); - set_afraid(0); - set_hero(p_ptr->hero + damroll(5, 10) + 30); - set_fast(p_ptr->fast + damroll(5, 10) + 30, 10); - hp_player(30); - - o_ptr->timeout = 250; - - break; - } - - case ACT_HELM: - { - if (!doit) return "sound ball (300) every 300 turns"; - msg_print("Your horn emits a loud sound."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_SOUND, dir, 300, 6); - - o_ptr->timeout = 300; - - break; - } - - case ACT_BOROMIR: - { - if (!doit) return "mass human summoning every 1000 turns"; - msg_print("Your horn calls for help."); - for (i = 0; i < 15; i++) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_HUMAN, TRUE); - } - - o_ptr->timeout = 1000; - - break; - } - - case ACT_HURIN: - { - if (!doit) return "berserker and +10 to speed (50) every 100+d200 turns"; - if (!p_ptr->fast) - { - (void)set_fast(randint(50) + 50, 10); - } - else - { - (void)set_fast(p_ptr->fast + 5, 10); - } - hp_player(30); - set_afraid(0); - set_shero(p_ptr->shero + randint(50) + 50); - - o_ptr->timeout = rand_int(200) + 100; - - break; - } - - case ACT_AXE_GOTHMOG: - { - if (!doit) return "fire ball (300) every 200+d200 turns"; - msg_print("Your lochaber axe erupts in fire..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_FIRE, dir, 300, 4); - - o_ptr->timeout = 200 + rand_int(200); - - break; - } - - case ACT_MELKOR: - { - if (!doit) return "darkness ball (150) every 100 turns"; - msg_print("Your spear is covered of darkness..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_DARK, dir, 150, 3); - - o_ptr->timeout = 100; - - break; - } - - case ACT_GROND: - { - if (!doit) return "alter reality every 100 turns"; - msg_print("Your hammer hits the floor..."); - alter_reality(); - - o_ptr->timeout = 100; - - break; - } - - case ACT_NATUREBANE: - { - if (!doit) return "dispel monsters (300) every 200+d200 turns"; - msg_print("Your axe glows blood red..."); - dispel_monsters(300); - - o_ptr->timeout = 200 + randint(200); - - break; - } - - case ACT_NIGHT: - { - if (!doit) return "vampiric drain (3*100) every 250 turns"; - msg_print("Your axe emits a black aura..."); - if (!get_aim_dir(&dir)) break; - for (i = 0; i < 3; i++) - { - if (drain_life(dir, 100)) hp_player(100); - } - - o_ptr->timeout = 250; - - break; - } - - case ACT_ORCHAST: - { - if (!doit) return "detect orcs every 10 turns"; - msg_print("Your weapon glows brightly..."); - (void)detect_monsters_xxx(RF3_ORC, DEFAULT_RADIUS); - - o_ptr->timeout = 10; - - break; - } - case ACT_SUNLIGHT: - { - if (!doit) return "beam of sunlight every 10 turns"; - - if (!get_aim_dir(&dir)) break; - msg_print("A line of sunlight appears."); - lite_line(dir); - - o_ptr->timeout = 10; - - break; - } - - case ACT_BO_MISS_1: - { - if (!doit) return "magic missile (2d6) every 2 turns"; - msg_print("It glows extremely brightly..."); - if (!get_aim_dir(&dir)) break; - fire_bolt(GF_MISSILE, dir, damroll(2, 6)); - - o_ptr->timeout = 2; - - break; - } - - case ACT_BA_POIS_1: - { - if (!doit) return "stinking cloud (12), rad. 3, every 4+d4 turns"; - msg_print("It throbs deep green..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_POIS, dir, 12, 3); - - o_ptr->timeout = rand_int(4) + 4; - - break; - } - - case ACT_BO_ELEC_1: - { - if (!doit) return "lightning bolt (4d8) every 6+d6 turns"; - msg_print("It is covered in sparks..."); - if (!get_aim_dir(&dir)) break; - fire_bolt(GF_ELEC, dir, damroll(4, 8)); - - o_ptr->timeout = rand_int(6) + 6; - - break; - } - - case ACT_BO_ACID_1: - { - if (!doit) return "acid bolt (5d8) every 5+d5 turns"; - msg_print("It is covered in acid..."); - if (!get_aim_dir(&dir)) break; - fire_bolt(GF_ACID, dir, damroll(5, 8)); - - o_ptr->timeout = rand_int(5) + 5; - - break; - } - - case ACT_BO_COLD_1: - { - if (!doit) return "frost bolt (6d8) every 7+d7 turns"; - msg_print("It is covered in frost..."); - if (!get_aim_dir(&dir)) break; - fire_bolt(GF_COLD, dir, damroll(6, 8)); - - o_ptr->timeout = rand_int(7) + 7; - - break; - } - - case ACT_BO_FIRE_1: - { - if (!doit) return "fire bolt (9d8) every 8+d8 turns"; - msg_print("It is covered in fire..."); - if (!get_aim_dir(&dir)) break; - fire_bolt(GF_FIRE, dir, damroll(9, 8)); - - o_ptr->timeout = rand_int(8) + 8; - - break; - } - - case ACT_BA_COLD_1: - { - if (!doit) return "ball of cold (48) every 400 turns"; - msg_print("It is covered in frost..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_COLD, dir, 48, 2); - - o_ptr->timeout = 400; - - break; - } - - case ACT_BA_FIRE_1: - { - if (!doit) return "ball of fire (72) every 400 turns"; - msg_print("It glows an intense red..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_FIRE, dir, 72, 2); - - o_ptr->timeout = 400; - - break; - } - - case ACT_DRAIN_1: - { - if (!doit) return "drain life (100) every 100+d100 turns"; - msg_print("It glows black..."); - if (!get_aim_dir(&dir)) break; - if (drain_life(dir, 100)) - - o_ptr->timeout = rand_int(100) + 100; - - break; - } - - case ACT_BA_COLD_2: - { - if (!doit) return "ball of cold (100) every 300 turns"; - msg_print("It glows an intense blue..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_COLD, dir, 100, 2); - - o_ptr->timeout = 300; - - break; - } - - case ACT_BA_ELEC_2: - { - if (!doit) return "ball of lightning (100) every 500 turns"; - msg_print("It crackles with electricity..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_ELEC, dir, 100, 3); - - o_ptr->timeout = 500; - - break; - } - - case ACT_DRAIN_2: - { - if (!doit) return "drain life (120) every 400 turns"; - msg_print("It glows black..."); - if (!get_aim_dir(&dir)) break; - drain_life(dir, 120); - - o_ptr->timeout = 400; - - break; - } - - case ACT_VAMPIRE_1: - { - if (!doit) return "vampiric drain (3*50) every 400 turns"; - if (!get_aim_dir(&dir)) break; - for (dummy = 0; dummy < 3; dummy++) - { - if (drain_life(dir, 50)) - hp_player(50); - } - - o_ptr->timeout = 400; - - break; - } - - case ACT_BO_MISS_2: - { - if (!doit) return "arrows (150) every 90+d90 turns"; - msg_print("It grows magical spikes..."); - if (!get_aim_dir(&dir)) break; - fire_bolt(GF_ARROW, dir, 150); - - o_ptr->timeout = rand_int(90) + 90; - - break; - } - - case ACT_BA_FIRE_2: - { - if (!doit) return "fire ball (120) every 225+d225 turns"; - msg_print("It glows deep red..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_FIRE, dir, 120, 3); - - o_ptr->timeout = rand_int(225) + 225; - - break; - } - - case ACT_BA_COLD_3: - { - if (!doit) return "ball of cold (200) every 325+d325 turns"; - msg_print("It glows bright white..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_COLD, dir, 200, 3); - - o_ptr->timeout = rand_int(325) + 325; - - break; - } - - case ACT_BA_ELEC_3: - { - if (!doit) return "Lightning Ball (250) every 425+d425 turns"; - msg_print("It glows deep blue..."); - if (!get_aim_dir(&dir)) break; - fire_ball(GF_ELEC, dir, 250, 3); - - o_ptr->timeout = rand_int(425) + 425; - - break; - } - - case ACT_WHIRLWIND: - { - int y = 0, x = 0; - cave_type *c_ptr; - monster_type *m_ptr; - if (!doit) return "whirlwind attack every 250 turns"; - - for (dir = 0; dir <= 9; dir++) - { - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - c_ptr = &cave[y][x]; - - /* Get the monster */ - m_ptr = &m_list[c_ptr->m_idx]; - - /* Hack -- attack monsters */ - if (c_ptr->m_idx && (m_ptr->ml || cave_floor_bold(y, x))) - { - py_attack(y, x, -1); - } - } - - o_ptr->timeout = 250; - - break; - } - - case ACT_VAMPIRE_2: - { - if (!doit) return "vampiric drain (3*100) every 400 turns"; - if (!get_aim_dir(&dir)) break; - for (dummy = 0; dummy < 3; dummy++) - { - if (drain_life(dir, 100)) - hp_player(100); - } - - o_ptr->timeout = 400; - - break; - } - - - case ACT_CALL_CHAOS: - { - if (!doit) return "call chaos every 350 turns"; - msg_print("It glows in scintillating colours..."); - call_chaos(); - - o_ptr->timeout = 350; - - break; - } - - case ACT_ROCKET: - { - if (!doit) return "launch rocket (120+level) every 400 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You launch a rocket!"); - fire_ball(GF_ROCKET, dir, 120 + (plev), 2); - - o_ptr->timeout = 400; - - break; - } - - case ACT_DISP_EVIL: - { - if (!doit) return "dispel evil (level*5) every 300+d300 turns"; - msg_print("It floods the area with goodness..."); - dispel_evil(p_ptr->lev * 5); - - o_ptr->timeout = rand_int(300) + 300; - - break; - } - - case ACT_DISP_GOOD: - { - if (!doit) return "dispel good (level*5) every 300+d300 turns"; - msg_print("It floods the area with evil..."); - dispel_good(p_ptr->lev * 5); - - o_ptr->timeout = rand_int(300) + 300; - - break; - } - - case ACT_BA_MISS_3: - { - if (!doit) return "elemental breath (300) every 500 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe the elements."); - fire_ball(GF_MISSILE, dir, 300, 4); - - o_ptr->timeout = 500; - - break; - } - - /* Activate for other offensive action */ - - case ACT_CONFUSE: - { - if (!doit) return "confuse monster every 15 turns"; - msg_print("It glows in scintillating colours..."); - if (!get_aim_dir(&dir)) break; - confuse_monster(dir, 20); - - o_ptr->timeout = 15; - - break; - } - - case ACT_SLEEP: - { - if (!doit) return "sleep nearby monsters every 55 turns"; - msg_print("It glows deep blue..."); - sleep_monsters_touch(); - - o_ptr->timeout = 55; - - break; - } - - case ACT_QUAKE: - { - if (!doit) return "earthquake (rad 10) every 50 turns"; - /* Prevent destruction of quest levels and town */ - if (!is_quest(dun_level) && dun_level) - { - earthquake(p_ptr->py, p_ptr->px, 10); - o_ptr->timeout = 50; - } - - break; - } - - case ACT_TERROR: - { - if (!doit) return "terror every 3 * (level+10) turns"; - turn_monsters(40 + p_ptr->lev); - - o_ptr->timeout = 3 * (p_ptr->lev + 10); - - break; - } - - case ACT_TELE_AWAY: - { - if (!doit) return "teleport away every 200 turns"; - if (!get_aim_dir(&dir)) break; - (void)fire_beam(GF_AWAY_ALL, dir, plev); - - o_ptr->timeout = 200; - - break; - } - - case ACT_BANISH_EVIL: - { - if (!doit) return "banish evil every 250+d250 turns"; - if (banish_evil(100)) - { - msg_print("The power of the artifact banishes evil!"); - } - - o_ptr->timeout = 250 + randint(250); - - break; - } - - case ACT_GENOCIDE: - { - if (!doit) return "genocide every 500 turns"; - msg_print("It glows deep blue..."); - (void)genocide(TRUE); - - o_ptr->timeout = 500; - - break; - } - - case ACT_MASS_GENO: - { - if (!doit) return "mass genocide every 1000 turns"; - msg_print("It lets out a long, shrill note..."); - (void)mass_genocide(TRUE); - - o_ptr->timeout = 1000; - - break; - } - - /* Activate for summoning / charming */ - - case ACT_CHARM_ANIMAL: - { - if (!doit) return "charm animal every 300 turns"; - if (!get_aim_dir(&dir)) break; - (void) charm_animal(dir, plev); - - o_ptr->timeout = 300; - - break; - } - - case ACT_CHARM_UNDEAD: - { - if (!doit) return "enslave undead every 333 turns"; - if (!get_aim_dir(&dir)) break; - (void)control_one_undead(dir, plev); - - o_ptr->timeout = 333; - - break; - } - - case ACT_CHARM_OTHER: - { - if (!doit) return "charm monster every 400 turns"; - if (!get_aim_dir(&dir)) break; - (void) charm_monster(dir, plev); - - o_ptr->timeout = 400; - - break; - } - - case ACT_CHARM_ANIMALS: - { - if (!doit) return "animal friendship every 500 turns"; - (void) charm_animals(plev * 2); - - o_ptr->timeout = 500; - - break; - } - - case ACT_CHARM_OTHERS: - { - if (!doit) return "mass charm every 750 turns"; - charm_monsters(plev * 2); - - o_ptr->timeout = 750; - - break; - } - - case ACT_SUMMON_ANIMAL: - { - if (!doit) return "summon animal every 200+d300 turns"; - (void)summon_specific_friendly(p_ptr->py, p_ptr->px, plev, SUMMON_ANIMAL_RANGER, TRUE); - - o_ptr->timeout = 200 + randint(300); - - break; - } - - case ACT_SUMMON_PHANTOM: - { - if (!doit) return "summon phantasmal servant every 200+d200 turns"; - msg_print("You summon a phantasmal servant."); - (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_PHANTOM, TRUE); - - o_ptr->timeout = 200 + randint(200); - - break; - } - - case ACT_SUMMON_ELEMENTAL: - { - if (!doit) return "summon elemental every 750 turns"; - if (randint(3) == 1) - { - if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_ELEMENTAL)) - { - msg_print("An elemental materialises..."); - msg_print("You fail to control it!"); - } - } - else - { - if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), - SUMMON_ELEMENTAL, (plev == 50 ? TRUE : FALSE))) - { - msg_print("An elemental materialises..."); - msg_print("It seems obedient to you."); - } - } - - o_ptr->timeout = 750; - - break; - } - - case ACT_SUMMON_DEMON: - { - if (!doit) return "summon demon every 666+d333 turns"; - if (randint(3) == 1) - { - if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_DEMON)) - { - msg_print("The area fills with a stench of sulphur and brimstone."); - msg_print("'NON SERVIAM! Wretch! I shall feast on thy mortal soul!'"); - } - } - else - { - if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), - SUMMON_DEMON, (plev == 50 ? TRUE : FALSE))) - { - msg_print("The area fills with a stench of sulphur and brimstone."); - msg_print("'What is thy bidding... Master?'"); - } - } - - o_ptr->timeout = 666 + randint(333); - - break; - } - - case ACT_SUMMON_UNDEAD: - { - if (!doit) return "summon undead every 666+d333 turns"; - if (randint(3) == 1) - { - if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), - (plev > 47 ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD))) - { - msg_print("Cold winds begin to blow around you, carrying with them the stench of decay..."); - msg_print("'The dead arise... to punish you for disturbing them!'"); - } - } - else - { - if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), - (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD), - (((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE))) - { - msg_print("Cold winds begin to blow around you, carrying with them the stench of decay..."); - msg_print("Ancient, long-dead forms arise from the ground to serve you!"); - } - } - - o_ptr->timeout = 666 + randint(333); - - break; - } - - /* Activate for healing */ - - case ACT_CURE_LW: - { - if (!doit) return format("cure light wounds every %d turns", (is_junkart ? 50 : 10)); - (void)set_afraid(0); - (void)hp_player(30); - - o_ptr->timeout = 10; - - break; - } - - case ACT_CURE_MW: - { - if (!doit) return format("cure serious wounds every %s turns", (is_junkart? "75" : "3+d3")); - msg_print("It radiates deep purple..."); - hp_player(damroll(4, 8)); - (void)set_cut((p_ptr->cut / 2) - 50); - - o_ptr->timeout = rand_int(3) + 3; - - break; - } - - case ACT_CURE_POISON: - { - if (!doit) return "remove fear and cure poison every 5 turns"; - msg_print("It glows deep blue..."); - (void)set_afraid(0); - (void)set_poisoned(0); - - o_ptr->timeout = 5; - - break; - } - - case ACT_REST_LIFE: - { - if (!doit) return "restore life levels every 450 turns"; - msg_print("It glows a deep red..."); - restore_level(); - - o_ptr->timeout = 450; - - break; - } - - case ACT_REST_ALL: - { - if (!doit) return format("restore stats and life levels every %d turns", (is_junkart ? 200 : 750)); - msg_print("It glows a deep green..."); - (void)do_res_stat(A_STR, TRUE); - (void)do_res_stat(A_INT, TRUE); - (void)do_res_stat(A_WIS, TRUE); - (void)do_res_stat(A_DEX, TRUE); - (void)do_res_stat(A_CON, TRUE); - (void)do_res_stat(A_CHR, TRUE); - (void)restore_level(); - - o_ptr->timeout = 750; - - break; - } - - case ACT_CURE_700: - { - if (!doit) return format("heal 700 hit points every %d turns", (is_junkart ? 100 : 250)); - msg_print("It glows deep blue..."); - msg_print("You feel a warm tingling inside..."); - (void)hp_player(700); - (void)set_cut(0); - - o_ptr->timeout = 250; - - break; - } - - case ACT_CURE_1000: - { - if (!doit) return "heal 1000 hit points every 888 turns"; - msg_print("It glows a bright white..."); - msg_print("You feel much better..."); - (void)hp_player(1000); - (void)set_cut(0); - - o_ptr->timeout = 888; - - break; - } - - case ACT_ESP: - { - if (!doit) return "temporary ESP (dur 25+d30) every 200 turns"; - (void)set_tim_esp(p_ptr->tim_esp + randint(30) + 25); - - o_ptr->timeout = 200; - - break; - } - - case ACT_BERSERK: - { - if (!doit) return "heroism and berserk (dur 50+d50) every 100+d100 turns"; - (void)set_shero(p_ptr->shero + randint(50) + 50); - (void)set_blessed(p_ptr->blessed + randint(50) + 50); - - o_ptr->timeout = 100 + randint(100); - - break; - } - - case ACT_PROT_EVIL: - { - if (!doit) return "protection from evil (dur level*3 + d25) every 225+d225 turns"; - msg_print("It lets out a shrill wail..."); - k = 3 * p_ptr->lev; - (void)set_protevil(p_ptr->protevil + randint(25) + k); - - o_ptr->timeout = rand_int(225) + 225; - - break; - } - - case ACT_RESIST_ALL: - { - if (!doit) return "resist elements (dur 40+d40) every 200 turns"; - msg_print("It glows many colours..."); - (void)set_oppose_acid(p_ptr->oppose_acid + randint(40) + 40); - (void)set_oppose_elec(p_ptr->oppose_elec + randint(40) + 40); - (void)set_oppose_fire(p_ptr->oppose_fire + randint(40) + 40); - (void)set_oppose_cold(p_ptr->oppose_cold + randint(40) + 40); - (void)set_oppose_pois(p_ptr->oppose_pois + randint(40) + 40); - - o_ptr->timeout = 200; - - break; - } - - case ACT_SPEED: - { - if (!doit) return "speed (dur 20+d20) every 250 turns"; - msg_print("It glows bright green..."); - if (!p_ptr->fast) - { - (void)set_fast(randint(20) + 20, 10); - } - else - { - (void)set_fast(p_ptr->fast + 5, 10); - } - - o_ptr->timeout = 250; - - break; - } - - case ACT_XTRA_SPEED: - { - if (!doit) return "speed (dur 75+d75) every 200+d200 turns"; - msg_print("It glows brightly..."); - if (!p_ptr->fast) - { - (void)set_fast(randint(75) + 75, 10); - } - else - { - (void)set_fast(p_ptr->fast + 5, 10); - } - - o_ptr->timeout = rand_int(200) + 200; - - break; - } - - case ACT_WRAITH: - { - if (!doit) return "wraith form (level/2 + d(level/2)) every 1000 turns"; - set_shadow(p_ptr->tim_wraith + randint(plev / 2) + (plev / 2)); - - o_ptr->timeout = 1000; - - break; - } - - case ACT_INVULN: - { - if (!doit) return "invulnerability (dur 8+d8) every 1000 turns"; - (void)set_invuln(p_ptr->invuln + randint(8) + 8); - - o_ptr->timeout = 1000; - - break; - } - - /* Activate for general purpose effect (detection etc.) */ - - case ACT_LIGHT: - { - if (!doit) return format("light area (dam 2d15) every %s turns", (is_junkart ? "100" : "10+d10")); - msg_print("It wells with clear light..."); - lite_area(damroll(2, 15), 3); - - o_ptr->timeout = rand_int(10) + 10; - - break; - } - - case ACT_MAP_LIGHT: - { - if (!doit) return "light (dam 2d15) & map area every 50+d50 turns"; - msg_print("It shines brightly..."); - map_area(); - lite_area(damroll(2, 15), 3); - - o_ptr->timeout = rand_int(50) + 50; - - break; - } - - case ACT_DETECT_ALL: - { - if (!doit) return "detection every 55+d55 turns"; - msg_print("It glows bright white..."); - msg_print("An image forms in your mind..."); - detect_all(DEFAULT_RADIUS); - - o_ptr->timeout = rand_int(55) + 55; - - break; - } - - case ACT_DETECT_XTRA: - { - if (!doit) return "detection, probing and identify true every 1000 turns"; - msg_print("It glows brightly..."); - detect_all(DEFAULT_RADIUS); - probing(); - identify_fully(); - - o_ptr->timeout = 1000; - - break; - } - - case ACT_ID_FULL: - { - if (!doit) return "identify true every 750 turns"; - msg_print("It glows yellow..."); - identify_fully(); - - o_ptr->timeout = 750; - - break; - } - - case ACT_ID_PLAIN: - { - if (!doit) return "identify spell every 10 turns"; - if (!ident_spell()) break; - - o_ptr->timeout = 10; - - break; - } - - case ACT_RUNE_EXPLO: - { - if (!doit) return "explosive rune every 200 turns"; - msg_print("It glows bright red..."); - explosive_rune(); - - o_ptr->timeout = 200; - - break; - } - - case ACT_RUNE_PROT: - { - if (!doit) return "rune of protection every 400 turns"; - msg_print("It glows light blue..."); - warding_glyph(); - - o_ptr->timeout = 400; - - break; - } - - case ACT_SATIATE: - { - if (!doit) return "satisfy hunger every 200 turns"; - (void)set_food(PY_FOOD_MAX - 1); - - o_ptr->timeout = 200; - - break; - } - - case ACT_DEST_DOOR: - { - if (!doit) return "destroy doors and traps every 10 turns"; - msg_print("It glows bright red..."); - destroy_doors_touch(); - - o_ptr->timeout = 10; - - break; - } - - case ACT_STONE_MUD: - { - if (!doit) return "stone to mud every 5 turns"; - msg_print("It pulsates..."); - if (!get_aim_dir(&dir)) break; - wall_to_mud(dir); - - o_ptr->timeout = 5; - - break; - } - - case ACT_RECHARGE: - { - if (!doit) return "recharging every 70 turns"; - recharge(60); - - o_ptr->timeout = 70; - - break; - } - - case ACT_ALCHEMY: - { - if (!doit) return "alchemy every 500 turns"; - msg_print("It glows bright yellow..."); - (void) alchemy(); - - o_ptr->timeout = 500; - - break; - } - - case ACT_DIM_DOOR: - { - if (!doit) return "dimension door every 100 turns"; - if (dungeon_flags2 & DF2_NO_TELEPORT) - { - msg_print("Not on special levels!"); - break; - } - - msg_print("You open a Void Jumpgate. Choose a destination."); - if (!tgt_pt(&ii, &ij)) break; - - p_ptr->energy -= 60 - plev; - - if (!cave_empty_bold(ij, ii) || (cave[ij][ii].info & CAVE_ICKY) || - (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2) || - (!rand_int(plev * plev / 2))) - { - msg_print("You fail to exit the void correctly!"); - p_ptr->energy -= 100; - get_pos_player(10, &ij, &ii); - } - - cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN); - cave_set_feat(ij, ii, FEAT_BETWEEN); - cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8); - cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8); - - o_ptr->timeout = 100; - - break; - } - - case ACT_TELEPORT: - { - if (!doit) return format("teleport (range 100) every %d turns", (is_junkart? 100 : 45)); - msg_print("It twists space around you..."); - teleport_player(100); - - o_ptr->timeout = 45; - - break; - } - - case ACT_RECALL: - { - if (!(dungeon_flags2 & DF2_ASK_LEAVE) || ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? "))) - { - if (!doit) return "word of recall every 200 turns"; - msg_print("It glows soft white..."); - recall_player(20,15); - - o_ptr->timeout = 200; - } - - break; - } - - case ACT_DEATH: - { - if (!doit) return "death"; - take_hit(5000, "activating a death spell"); - - /* Timeout is set before return */ - - break; - } - - case ACT_RUINATION: - { - if (!doit) return "Ruination"; - msg_print("Your nerves and muscles feel weak and lifeless!"); - - take_hit(damroll(10, 10), "activating Ruination"); - (void)dec_stat(A_DEX, 25, TRUE); - (void)dec_stat(A_WIS, 25, TRUE); - (void)dec_stat(A_CON, 25, TRUE); - (void)dec_stat(A_STR, 25, TRUE); - (void)dec_stat(A_CHR, 25, TRUE); - (void)dec_stat(A_INT, 25, TRUE); - - /* Timeout is set before return */ - - break; - } - - case ACT_DESTRUC: - { - if (!doit) return "Destruction every 100 turns"; - earthquake(p_ptr->py, p_ptr->px, 12); - - /* Timeout is set before return */ - - break; - } - - case ACT_UNINT: - { - if (!doit) return "decreasing Intelligence"; - (void)dec_stat(A_INT, 25, FALSE); - - /* Timeout is set before return */ - - break; - } - - case ACT_UNSTR: - { - if (!doit) return "decreasing Strength"; - (void)dec_stat(A_STR, 25, FALSE); - - /* Timeout is set before return */ - - break; - } - - case ACT_UNCON: - { - if (!doit) return "decreasing Constitution"; - (void)dec_stat(A_CON, 25, FALSE); - - /* Timeout is set before return */ - - break; - } - - case ACT_UNCHR: - { - if (!doit) return "decreasing Charisma"; - (void)dec_stat(A_CHR, 25, FALSE); - - /* Timeout is set before return */ - - break; - } - - case ACT_UNDEX: - { - if (!doit) return "decreasing Dexterity"; - (void)dec_stat(A_DEX, 25, FALSE); - - /* Timeout is set before return */ - - break; - } - - case ACT_UNWIS: - { - if (!doit) return "decreasing Wisdom"; - (void)dec_stat(A_WIS, 25, FALSE); - - /* Timeout is set before return */ - - break; - } - - case ACT_STATLOSS: - { - if (!doit) return "stat loss"; - (void)dec_stat(A_STR, 15, FALSE); - (void)dec_stat(A_INT, 15, FALSE); - (void)dec_stat(A_WIS, 15, FALSE); - (void)dec_stat(A_DEX, 15, FALSE); - (void)dec_stat(A_CON, 15, FALSE); - (void)dec_stat(A_CHR, 15, FALSE); - - /* Timeout is set before return */ - - break; - } - - case ACT_HISTATLOSS: - { - if (!doit) return "high stat loss"; - (void)dec_stat(A_STR, 25, FALSE); - (void)dec_stat(A_INT, 25, FALSE); - (void)dec_stat(A_WIS, 25, FALSE); - (void)dec_stat(A_DEX, 25, FALSE); - (void)dec_stat(A_CON, 25, FALSE); - (void)dec_stat(A_CHR, 25, FALSE); - - /* Timeout is set before return */ - - break; - } - - case ACT_EXPLOSS: - { - if (!doit) return "experience loss"; - lose_exp(p_ptr->exp / 20); - - /* Timeout is set before return */ - - break; - } - - case ACT_HIEXPLOSS: - { - if (!doit) return "high experience loss"; - lose_exp(p_ptr->exp / 10); - - /* Timeout is set before return */ - - break; - } - - case ACT_SUMMON_MONST: - { - if (!doit) return "summon monster"; - summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0); - - /* Timeout is set before return */ - - break; - } - - case ACT_PARALYZE: - { - if (!doit) return "paralyze"; - set_paralyzed(20 + randint(10)); - - /* Timeout is set before return */ - - break; - } - - case ACT_HALLU: - { - if (!doit) return "hallucination every 10 turns"; - set_image(p_ptr->image + 20 + randint(10)); - - /* Timeout is set before return */ - - break; - } - - case ACT_POISON: - { - if (!doit) return "poison"; - set_poisoned(p_ptr->poisoned + 20 + randint(10)); - - /* Timeout is set before return */ - - break; - } - - case ACT_HUNGER: - { - if (!doit) return "create hunger"; - (void)set_food(PY_FOOD_WEAK); - - /* Timeout is set before return */ - - break; - } - - case ACT_STUN: - { - if (!doit) return "stun"; - set_stun(p_ptr->stun + 20 + randint(10)); - - /* Timeout is set before return */ - - break; - } - - case ACT_CUTS: - { - if (!doit) return "cuts"; - set_cut(p_ptr->cut + 20 + randint(10)); - - /* Timeout is set before return */ - - break; - } - - case ACT_PARANO: - { - if (!doit) return "confusion"; - set_confused(p_ptr->confused + 30 + randint(10)); - - /* Timeout is set before return */ - - break; - } - - case ACT_CONFUSION: - { - if (!doit) return "confusion"; - set_confused(p_ptr->confused + 20 + randint(10)); - - /* Timeout is set before return */ - - break; - } - - case ACT_BLIND: - { - if (!doit) return "blindness"; - set_blind(p_ptr->blind + 20 + randint(10)); - - /* Timeout is set before return */ - - break; - } - - case ACT_PET_SUMMON: - { - if (!doit) return "summon pet every 101 turns"; - summon_specific_friendly(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0, FALSE); - - /* Timeout is set before return */ - /*FINDME*/ - - break; - } - - case ACT_CURE_PARA: - { - if (!doit) return "cure confusion every 500 turns"; - set_confused(0); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_HALLU: - { - if (!doit) return "cure hallucination every 100 turns"; - set_image(0); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_POIS: - { - if (!doit) return "cure poison every 100 turns"; - set_poisoned(0); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_HUNGER: - { - if (!doit) return "satisfy hunger every 100 turns"; - (void)set_food(PY_FOOD_MAX - 1); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_STUN: - { - if (!doit) return "cure stun every 100 turns"; - set_stun(0); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_CUTS: - { - if (!doit) return "cure cuts every 100 turns"; - set_cut(0); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_FEAR: - { - if (!doit) return "cure fear every 100 turns"; - set_afraid(0); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_CONF: - { - if (!doit) return "cure confusion every 100 turns"; - set_confused(0); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_BLIND: - { - if (!doit) return "cure blindness every 100 turns"; - set_blind(0); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURING: - { - if (!doit) return "curing every 110 turns"; - set_blind(0); - set_poisoned(0); - set_confused(0); - set_stun(0); - set_cut(0); - set_image(0); - - /* Timeout is set before return */ - - break; - } - - case ACT_DARKNESS: - { - if (!doit) return "darkness"; - unlite_area(damroll(2, 10), 10); - - /* Timeout is set before return */ - - break; - } - - case ACT_LEV_TELE: - { - if (!doit) return "teleport level every 50 turns"; - teleport_player_level(); - - /* Timeout is set before return */ - - break; - } - - case ACT_ACQUIREMENT: - { - if (!doit) return "acquirement every 3000 turns"; - acquirement(p_ptr->py, p_ptr->px, 1, FALSE, FALSE); - - /* Timeout is set before return */ - - break; - } - - case ACT_WEIRD: - { - if (!doit) return "something weird every 5 turns"; - /* It doesn't do anything */ - - /* Timeout is set before return */ - - break; - } - - case ACT_AGGRAVATE: - { - if (!doit) return "aggravate"; - aggravate_monsters(1); - - /* Timeout is set before return */ - - break; - } - - case ACT_MUT: - { - if (!doit) return "gain corruption every 10 turns"; - gain_random_corruption(); - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_INSANITY: - { - if (!doit) return "cure insanity every 200 turns"; - heal_insanity(damroll(10, 10)); - - /* Timeout is set before return */ - - break; - } - - case ACT_CURE_MUT: - { - msg_print("Ahah, you wish."); - /* Timeout is set before return */ - - break; - } - - case ACT_LIGHT_ABSORBTION: - { - int y, x, light = 0, dir; - cave_type *c_ptr; - - if (!doit) return "light absorption every 80 turns"; - - for (y = p_ptr->py - 6; y <= p_ptr->py + 6; y++) - { - for (x = p_ptr->px - 6; x <= p_ptr->px + 6; x++) - { - if (!in_bounds(y, x)) continue; - - c_ptr = &cave[y][x]; - - if (distance(y, x, p_ptr->py, p_ptr->px) > 6) continue; - - if (c_ptr->info & CAVE_GLOW) - { - light++; - - /* No longer in the array */ - c_ptr->info &= ~(CAVE_TEMP); - - /* Darken the grid */ - c_ptr->info &= ~(CAVE_GLOW); - - /* Hack -- Forget "boring" grids */ - if (cave_plain_floor_grid(c_ptr) && - !(c_ptr->info & (CAVE_TRDT))) - { - /* Forget the grid */ - c_ptr->info &= ~(CAVE_MARK); - - /* Notice */ - note_spot(y, x); - } - - /* Process affected monsters */ - if (c_ptr->m_idx) - { - /* Update the monster */ - update_mon(c_ptr->m_idx, FALSE); - } - - /* Redraw */ - lite_spot(y, x); - } - } - } - - if (!get_aim_dir(&dir)) return (FALSE); - - msg_print("The light around you is absorbed... " - "and released in a powerful bolt!"); - fire_bolt(GF_LITE, dir, damroll(light, p_ptr->lev)); - - /* Timeout is set before return */ - - break; - } - /* Horns of DragonKind (Note that these are new egos)*/ - case ACT_BA_FIRE_H: - { - if (!doit) return "large fire ball (300) every 100 turns"; - fire_ball(GF_FIRE, 5, 300, 7); - - o_ptr->timeout = 100; - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - break; - } - case ACT_BA_COLD_H: - { - if (!doit) return "large cold ball (300) every 100 turns"; - fire_ball(GF_COLD, 5, 300, 7); - - o_ptr->timeout = 100; - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - break; - } - case ACT_BA_ELEC_H: - { - if (!doit) return "large lightning ball (300) every 100 turns"; - fire_ball(GF_ELEC, 5, 300, 7); - - o_ptr->timeout = 100; - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - break; - } - case ACT_BA_ACID_H: - { - if (!doit) return "large acid ball (300) every 100 turns"; - fire_ball(GF_ACID, 5, 300, 7); - - o_ptr->timeout = 100; - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - break; - } - - case ACT_SPIN: - { - if (!doit) return "spinning around every 50+d25 turns"; - do_spin(); - - o_ptr->timeout = 50 + randint(25); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - /* Done */ - break; - } - case ACT_NOLDOR: - { - if (!doit) return "detect treasure every 10+d20 turns"; - detect_treasure(DEFAULT_RADIUS); - - o_ptr->timeout = 10 + randint(20); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - /* Done */ - break; - } - case ACT_SPECTRAL: - { - if (!doit) return "wraith-form every 50+d50 turns"; - if (!p_ptr->wraith_form) - { - set_shadow(20 + randint(20)); - } - else - { - set_shadow(p_ptr->tim_wraith + randint(20)); - } - - o_ptr->timeout = 50 + randint(50); - - /* Window stuff */ - p_ptr->window |= PW_INVEN | PW_EQUIP; - - /* Done */ - break; - } - case ACT_JUMP: - { - if (!doit) return "phasing every 10+d10 turns"; - teleport_player(10); - o_ptr->timeout = 10 + randint(10); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - - /* Done */ - break; - } - - case ACT_DEST_TELE: - { - if (!doit) return "teleportation and destruction of the ring"; - if (!item) - { - msg_print("You can't activate this when it's there!"); - } - if (get_check("This will destroy the ring. Do you wish to continue? ")) - { - msg_print("The ring explodes into a space distortion."); - teleport_player(200); - - /* It explodes, doesn't it ? */ - take_hit(damroll(2, 10), "an exploding ring"); - - inc_stack_size_ex(item, -255, OPTIMIZE, NO_DESCRIBE); - } - - break; - } - /*amulet of serpents dam 100, rad 2 timeout 40+d60 */ - case ACT_BA_POIS_4: - { - if (!doit) return "venom breathing every 40+d60 turns"; - /* Get a direction for breathing (or abort) */ - if (!get_aim_dir(&dir)) break; - - msg_print("You breathe venom..."); - fire_ball(GF_POIS, dir, 100, 2); - - o_ptr->timeout = rand_int(60) + 40; - - /* Window stuff */ - p_ptr->window |= PW_INVEN | PW_EQUIP; - - /* Done */ - break; - } - /*rings of X 50,50+d50 dur 20+d20 */ - case ACT_BA_COLD_4: - { - if (!doit) return "ball of cold and resist cold every 50+d50 turns"; - /* Get a direction for breathing (or abort) */ - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_COLD, dir, 50, 2); - (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20); - - o_ptr->timeout = rand_int(50) + 50; - - break; - } - - case ACT_BA_FIRE_4: - { - if (!doit) return "ball of fire and resist fire every 50+d50 turns"; - /* Get a direction for breathing (or abort) */ - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_FIRE, dir, 50, 2); - (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20); - - o_ptr->timeout = rand_int(50) + 50; - - break; - } - case ACT_BA_ACID_4: - { - if (!doit) return "ball of acid and resist acid every 50+d50 turns"; - /* Get a direction for breathing (or abort) */ - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_ACID, dir, 50, 2); - (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20); - - o_ptr->timeout = rand_int(50) + 50; - - break; - } - - case ACT_BA_ELEC_4: - { - if (!doit) return "ball of lightning and resist lightning every 50+d50 turns"; - /* Get a direction for breathing (or abort) */ - if (!get_aim_dir(&dir)) break; - - fire_ball(GF_ELEC, dir, 50, 2); - (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20); - - o_ptr->timeout = rand_int(50) + 50; - - break; - } - - case ACT_BR_ELEC: - { - if (!doit) return "breathe lightning (100) every 90+d90 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe lightning."); - fire_ball(GF_ELEC, dir, 100, 2); - - o_ptr->timeout = rand_int(90) + 90; - - break; - } - - case ACT_BR_COLD: - { - if (!doit) return "breathe frost (110) every 90+d90 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe frost."); - fire_ball(GF_COLD, dir, 110, 2); - - o_ptr->timeout = rand_int(90) + 90; - - break; - } - - case ACT_BR_FIRE: - { - if (!doit) return "breathe fire (200) every 90+d90 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe fire."); - fire_ball(GF_FIRE, dir, 200, 2); - - o_ptr->timeout = rand_int(90) + 90; - - break; - } - - case ACT_BR_ACID: - { - if (!doit) return "breathe acid (130) every 90+d90 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe acid."); - fire_ball(GF_ACID, dir, 130, 2); - - o_ptr->timeout = rand_int(90) + 90; - - break; - } - - case ACT_BR_POIS: - { - if (!doit) return "breathe poison gas (150) every 90+d90 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe poison gas."); - fire_ball(GF_POIS, dir, 150, 2); - - o_ptr->timeout = rand_int(90) + 90; - - break; - } - - case ACT_BR_MANY: - { - if (!doit) return "breathe multi-hued (250) every 60+d60 turns"; - if (!get_aim_dir(&dir)) break; - chance = rand_int(5); - msg_format("You breathe %s.", - ((chance == 1) ? "lightning" : - ((chance == 2) ? "frost" : - ((chance == 3) ? "acid" : - ((chance == 4) ? "poison gas" : "fire"))))); - fire_ball(((chance == 1) ? GF_ELEC : - ((chance == 2) ? GF_COLD : - ((chance == 3) ? GF_ACID : - ((chance == 4) ? GF_POIS : GF_FIRE)))), - dir, 250, 2); - - o_ptr->timeout = rand_int(60) + 60; - - break; - } - - case ACT_BR_CONF: - { - if (!doit) return "breathe confusion (120) every 90+d90 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe confusion."); - fire_ball(GF_CONFUSION, dir, 120, 2); - - o_ptr->timeout = rand_int(90) + 90; - - break; - } - - case ACT_BR_SOUND: - { - if (!doit) return "breathe sound (130) every 90+d90 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe sound."); - fire_ball(GF_SOUND, dir, 130, 2); - - o_ptr->timeout = rand_int(90) + 90; - - break; - } - - case ACT_BR_CHAOS: - { - if (!doit) return "breathe chaos/disenchant (220) every 60+d90 turns"; - if (!get_aim_dir(&dir)) break; - chance = rand_int(2); - msg_format("You breathe %s.", - ((chance == 1 ? "chaos" : "disenchantment"))); - fire_ball((chance == 1 ? GF_CHAOS : GF_DISENCHANT), - dir, 220, 2); - - o_ptr->timeout = rand_int(90) + 60; - - break; - } - - case ACT_BR_SHARD: - { - if (!doit) return "breathe sound/shards (230) every 60+d90 turns"; - if (!get_aim_dir(&dir)) break; - chance = rand_int(2); - msg_format("You breathe %s.", - ((chance == 1 ? "sound" : "shards"))); - fire_ball((chance == 1 ? GF_SOUND : GF_SHARDS), - dir, 230, 2); - - o_ptr->timeout = rand_int(90) + 60; - - break; - } - - case ACT_BR_BALANCE: - { - if (!doit) return "breathe balance (250) every 60+d90 turns"; - if (!get_aim_dir(&dir)) break; - chance = rand_int(4); - msg_format("You breathe %s.", - ((chance == 1) ? "chaos" : - ((chance == 2) ? "disenchantment" : - ((chance == 3) ? "sound" : "shards")))); - fire_ball(((chance == 1) ? GF_CHAOS : - ((chance == 2) ? GF_DISENCHANT : - ((chance == 3) ? GF_SOUND : GF_SHARDS))), - dir, 250, 2); - - o_ptr->timeout = rand_int(90) + 60; - - break; - } - - case ACT_BR_LIGHT: - { - if (!doit) return "breathe light/darkness (200) every 60+d90 turns"; - if (!get_aim_dir(&dir)) break; - chance = rand_int(2); - msg_format("You breathe %s.", - ((chance == 0 ? "light" : "darkness"))); - fire_ball((chance == 0 ? GF_LITE : GF_DARK), dir, 200, 2); - - o_ptr->timeout = rand_int(90) + 60; - - break; - } - case ACT_BR_POWER: - { - if (!doit) return "breathe the elements (300) every 60+d90 turns"; - if (!get_aim_dir(&dir)) break; - msg_print("You breathe the elements."); - fire_ball(GF_MISSILE, dir, 300, 3); - - o_ptr->timeout = rand_int(90) + 60; - - break; - } - case ACT_GROW_MOLD: - { - if (!doit) return "grow mushrooms every 50+d50 turns"; - msg_print("You twirl and spores fly everywhere!"); - for (i = 0; i < 8; i++) - summon_specific_friendly(p_ptr->py, p_ptr->px, p_ptr->lev, SUMMON_BIZARRE1, FALSE); - - o_ptr->timeout = randint(50) + 50; - - break; - } - case ACT_MUSIC: - /* - fall through to unknown, as music should be - handled by calling procedure. - */ - - default: - { - msg_format("Unknown activation effect: %d.", spell); - if ( !doit ) return "Unknown Activation"; - return NULL; - } - } - } - - /* Set timeout for junkarts - * Note that I still need to set the timeouts for other - * (non-random) artifacts above - */ - if (is_junkart && doit) - o_ptr->timeout = activation_info[o_ptr->pval2].cost / 10; - - return NULL; -} - - -static bool_ activate_spell(object_type * o_ptr, byte choice) -{ - int mana = 0, gf = 0, mod = 0; - - rune_spell s_ptr; - - - if (choice == 1) - { - gf = o_ptr->pval & 0xFFFF; - mod = o_ptr->pval3 & 0xFFFF; - mana = o_ptr->pval2 & 0xFF; - } - else if (choice == 2) - { - gf = o_ptr->pval >> 16; - mod = o_ptr->pval3 >> 16; - mana = o_ptr->pval2 >> 8; - } - - s_ptr.type = gf; - s_ptr.rune2 = 1 << mod; - s_ptr.mana = mana; - - /* Execute */ - rune_exec(&s_ptr, 0); - - if (choice == 1) o_ptr->timeout = mana * 5; - if (choice == 2) o_ptr->xtra2 = mana * 5; - - return (TRUE); -} diff --git a/src/cmd6.cc b/src/cmd6.cc new file mode 100644 index 00000000..b8a9f1df --- /dev/null +++ b/src/cmd6.cc @@ -0,0 +1,7792 @@ +/* File: cmd6.c */ + +/* Purpose: Object commands */ + +/* + * 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 "angband.h" + +#include "spell_type.h" +#include "hooks.h" + +/* + * Forward declare + */ +static bool_ activate_spell(object_type * o_ptr, byte choice); + + +/* + * General function to find an item by its name + */ +cptr get_item_hook_find_obj_what; +bool_ get_item_hook_find_obj(int *item) +{ + int i; + char buf[80]; + char buf2[100]; + + strcpy(buf, ""); + if (!get_string(get_item_hook_find_obj_what, buf, 79)) + return FALSE; + + for (i = 0; i < INVEN_TOTAL; i++) + { + object_type *o_ptr = &p_ptr->inventory[i]; + + if (!item_tester_okay(o_ptr)) continue; + + object_desc(buf2, o_ptr, -1, 0); + if (!strcmp(buf, buf2)) + { + *item = i; + return TRUE; + } + } + return FALSE; +} + + +/* + * This file includes code for eating food, drinking potions, + * reading scrolls, aiming wands, using staffs, zapping rods, + * and activating artifacts. + * + * In all cases, if the player becomes "aware" of the item's use + * by testing it, mark it as "aware" and reward some experience + * based on the object's level, always rounding up. If the player + * remains "unaware", mark that object "kind" as "tried". + * + * This code now correctly handles the unstacking of wands, staffs, + * and rods. Note the overly paranoid warning about potential pack + * overflow, which allows the player to use and drop a stacked item. + * + * In all "unstacking" scenarios, the "used" object is "carried" as if + * the player had just picked it up. In particular, this means that if + * the use of an item induces pack overflow, that item will be dropped. + * + * For simplicity, these routines induce a full "pack reorganization" + * which not only combines similar items, but also reorganizes various + * items to obey the current "sorting" method. This may require about + * 400 item comparisons, but only occasionally. + * + * There may be a BIG problem with any "effect" that can cause "changes" + * to the p_ptr->inventory. For example, a "scroll of recharging" can cause + * a wand/staff to "disappear", moving the p_ptr->inventory up. Luckily, the + * scrolls all appear BEFORE the staffs/wands, so this is not a problem. + * But, for example, a "staff of recharging" could cause MAJOR problems. + * In such a case, it will be best to either (1) "postpone" the effect + * until the end of the function, or (2) "change" the effect, say, into + * giving a staff "negative" charges, or "turning a staff into a stick". + * It seems as though a "rod of recharging" might in fact cause problems. + * The basic problem is that the act of recharging (and destroying) an + * item causes the inducer of that action to "move", causing "o_ptr" to + * no longer point at the correct item, with horrifying results. + * + * Note that food/potions/scrolls no longer use bit-flags for effects, + * but instead use the "sval" (which is also used to sort the objects). + */ + + +/* + * Determine the effects of eating a corpse. A corpse can be + * eaten whole or cut into pieces for later. + */ +static void corpse_effect(object_type *o_ptr, bool_ cutting) +{ + monster_race *r_ptr = &r_info[o_ptr->pval2]; + + /* Assume no bad effects */ + bool_ harmful = FALSE; + + byte method, effect, d_dice, d_side; + + int i, dam, idam = 0, mdam, brpow, brdam = 0; + + + /* How much of the monster's breath attack remains */ + if (o_ptr->pval <= r_ptr->weight) + { + brpow = 0; + } + else + { + brpow = (o_ptr->pval - r_ptr->weight) / 5; + if (brpow > (r_ptr->weight / 5)) brpow = r_ptr->weight / 5; + } + + if (o_ptr->weight <= 0) o_ptr->weight = 1; + if (o_ptr->pval <= 0) o_ptr->pval = 1; + + /* + * The breath is only discharged by accident or by slicing off pieces + * of meat, and only by corpses. + */ + if ((o_ptr->sval != SV_CORPSE_CORPSE) || + (rand_int(o_ptr->weight / 5) && !cutting)) brpow = 0; + + /* Immediate effects - poison, acid, fire, etc. */ + if (!cutting) + { + for (i = 0; i < 4; i++) + { + /* skip empty blow slot */ + if (!r_ptr->blow[i].method) continue; + + method = r_ptr->blow[i].method; + effect = r_ptr->blow[i].effect; + d_dice = r_ptr->blow[i].d_dice; + d_side = r_ptr->blow[i].d_side; + dam = damroll(d_dice, d_side) * o_ptr->pval / o_ptr->weight / 2; + idam = damroll(d_dice, d_side) * + ((o_ptr->weight / o_ptr->pval > 2) ? + o_ptr->weight / o_ptr->pval : 2); + mdam = maxroll(d_dice, d_side) * 2; + + /* Analyse method */ + switch (method) + { + /* Methods that are meaningless after death */ + case RBM_BITE: + case RBM_STING: + case RBM_ENGULF: + case RBM_DROOL: + case RBM_SPIT: + case RBM_GAZE: + case RBM_WAIL: + case RBM_BEG: + case RBM_INSULT: + case RBM_MOAN: + { + continue; + } + } + + /* Analyse effect */ + switch (effect) + { + /* Effects that are meaningless after death */ + case RBE_HURT: + case RBE_UN_BONUS: + case RBE_UN_POWER: + case RBE_EAT_GOLD: + case RBE_EAT_ITEM: + case RBE_EAT_FOOD: + case RBE_EAT_LITE: + case RBE_ELEC: + case RBE_COLD: + case RBE_SHATTER: + { + break; + } + + case RBE_POISON: + { + if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) + { + set_poisoned(p_ptr->poisoned + dam + idam + 10); + harmful = TRUE; + } + + break; + } + + case RBE_ACID: + { + /* Total Immunity */ + if (!(p_ptr->immune_acid || (dam <= 0))) + { + /* Resist the damage */ + if (p_ptr->resist_acid) dam = (dam + 2) / 3; + if (p_ptr->oppose_acid) dam = (dam + 2) / 3; + + /* Take damage */ + take_hit(dam, "acidic food"); + harmful = TRUE; + } + else + { + set_oppose_acid(p_ptr->oppose_acid + idam); + } + + break; + } + + case RBE_FIRE: + { + /* Totally immune */ + if (p_ptr->immune_fire || (dam <= 0)) + { + /* Resist the damage */ + if (p_ptr->resist_fire) dam = (dam + 2) / 3; + if (p_ptr->oppose_fire) dam = (dam + 2) / 3; + + /* Take damage */ + take_hit(dam, "a fiery meal"); + harmful = TRUE; + } + else + { + set_oppose_fire(p_ptr->oppose_fire + idam); + } + + break; + } + + case RBE_BLIND: + { + if (!p_ptr->resist_blind) + { + set_blind(p_ptr->blind + dam * 2 + idam * 2 + 20); + } + + break; + } + + case RBE_CONFUSE: + { + if (!p_ptr->resist_conf) + { + set_confused(p_ptr->confused + dam + idam + 10); + } + if (!p_ptr->resist_chaos && rand_int(mdam - dam)) + { + set_image(p_ptr->image + dam * 10 + idam * 10 + 100); + } + + break; + } + + case RBE_HALLU: + { + if (!p_ptr->resist_chaos && rand_int(mdam - dam)) + { + set_image(p_ptr->image + dam * 10 + idam * 10 + 50); + } + + break; + } + + case RBE_TERRIFY: + { + if (!p_ptr->resist_fear) + { + set_afraid(p_ptr->afraid + dam + idam + 10); + } + + break; + } + + case RBE_PARALYZE: + { + if (!p_ptr->free_act) + { + set_paralyzed(dam + idam + 10); + } + + break; + } + + case RBE_LOSE_STR: + { + do_dec_stat(A_STR, STAT_DEC_NORMAL); + + break; + } + + case RBE_LOSE_INT: + { + do_dec_stat(A_INT, STAT_DEC_NORMAL); + + break; + } + + case RBE_LOSE_WIS: + { + do_dec_stat(A_WIS, STAT_DEC_NORMAL); + + break; + } + + case RBE_LOSE_DEX: + { + do_dec_stat(A_DEX, STAT_DEC_NORMAL); + + break; + } + + case RBE_LOSE_CON: + { + do_dec_stat(A_CON, STAT_DEC_NORMAL); + + break; + } + + case RBE_LOSE_CHR: + { + do_dec_stat(A_CHR, STAT_DEC_NORMAL); + + break; + } + + /* Don't eat Morgoth's corpse :) */ + case RBE_LOSE_ALL: + { + do_dec_stat(A_STR, STAT_DEC_NORMAL); + do_dec_stat(A_INT, STAT_DEC_NORMAL); + do_dec_stat(A_WIS, STAT_DEC_NORMAL); + do_dec_stat(A_DEX, STAT_DEC_NORMAL); + do_dec_stat(A_CON, STAT_DEC_NORMAL); + do_dec_stat(A_CHR, STAT_DEC_NORMAL); + o_ptr->pval = 1; + + break; + } + + case RBE_SANITY: + { + msg_print("You feel your sanity slipping away!"); + take_sanity_hit(dam, "eating an insane monster"); + + break; + } + + /* Unlife is bad to eat */ + case RBE_EXP_10: + { + msg_print("A black aura surrounds the corpse!"); + + if (p_ptr->hold_life && (rand_int(100) < 50)) + { + msg_print("You keep hold of your life force!"); + } + else + { + s32b d = damroll(10, 6) + + (p_ptr->exp / 100) * MON_DRAIN_LIFE; + + if (p_ptr->hold_life) + { + msg_print("You feel your life slipping away!"); + lose_exp(d / 10); + } + else + { + msg_print("You feel your life draining away!"); + lose_exp(d); + } + } + + o_ptr->pval = 1; + + break; + } + + case RBE_EXP_20: + { + msg_print("A black aura surrounds the corpse!"); + + if (p_ptr->hold_life && (rand_int(100) < 50)) + { + msg_print("You keep hold of your life force!"); + } + else + { + s32b d = damroll(20, 6) + + (p_ptr->exp / 100) * MON_DRAIN_LIFE; + + if (p_ptr->hold_life) + { + msg_print("You feel your life slipping away!"); + lose_exp(d / 10); + } + else + { + msg_print("You feel your life draining away!"); + lose_exp(d); + } + } + + o_ptr->pval = 1; + + break; + } + + case RBE_EXP_40: + { + msg_print("A black aura surrounds the corpse!"); + + if (p_ptr->hold_life && (rand_int(100) < 50)) + { + msg_print("You keep hold of your life force!"); + } + else + { + s32b d = damroll(40, 6) + + (p_ptr->exp / 100) * MON_DRAIN_LIFE; + + if (p_ptr->hold_life) + { + msg_print("You feel your life slipping away!"); + lose_exp(d / 10); + } + else + { + msg_print("You feel your life draining away!"); + lose_exp(d); + } + } + + o_ptr->pval = 1; + + break; + } + + case RBE_EXP_80: + { + msg_print("A black aura surrounds the corpse!"); + + if (p_ptr->hold_life && (rand_int(100) < 50)) + { + msg_print("You keep hold of your life force!"); + } + else + { + s32b d = damroll(80, 6) + + (p_ptr->exp / 100) * MON_DRAIN_LIFE; + + if (p_ptr->hold_life) + { + msg_print("You feel your life slipping away!"); + lose_exp(d / 10); + } + else + { + msg_print("You feel your life draining away!"); + lose_exp(d); + } + } + + o_ptr->pval = 1; + + break; + } + } + } + } /* if (!cutting) */ + + + /* + * The organ that supplies breath attacks is not + * immediately emptied upon death, although some types + * of breath have no effect. + * AMHD's make rather risky meals, and deadly snacks. + */ + + /* Acid */ + if (r_ptr->flags4 & RF4_BR_ACID && brpow > 0) + { + brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3)); + + msg_print("You are hit by a gush of acid!"); + + /* Total Immunity */ + if (!(p_ptr->immune_acid || (brdam <= 0))) + { + /* Take damage */ + acid_dam(brdam, "a gush of acid"); + harmful = TRUE; + } + o_ptr->pval = 1; + } + else if (r_ptr->flags4 & RF4_BR_ACID) + { + set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10); + } + + /* Electricity */ + if (r_ptr->flags4 & RF4_BR_ELEC && brpow > 0) + { + brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3)); + + msg_print("You receive a heavy shock!"); + + /* Total Immunity */ + if (!(p_ptr->immune_elec || (brdam <= 0))) + { + /* Take damage */ + elec_dam(brdam, "an electric shock"); + harmful = TRUE; + } + o_ptr->weight = o_ptr->weight - brpow; + o_ptr->pval = o_ptr->weight; + } + else if (r_ptr->flags4 & RF4_BR_ELEC) + { + set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10); + } + + /* Fire */ + if (r_ptr->flags4 & RF4_BR_FIRE && brpow > 0) + { + brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3)); + + msg_print("Roaring flames engulf you!"); + + /* Total Immunity */ + if (!(p_ptr->immune_fire || (brdam <= 0))) + { + /* Take damage */ + fire_dam(brdam, "an explosion"); + harmful = TRUE; + } + o_ptr->pval = 1; + } + else if (r_ptr->flags4 & RF4_BR_FIRE) + { + set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10); + } + + /* Cold */ + if (r_ptr->flags4 & RF4_BR_COLD && brpow > 0) + { + brdam = ((brpow / 3) > 1600 ? 1600 : (brpow / 3)); + + msg_print("You are caught in a freezing liquid!"); + + /* Total Immunity */ + if (!(p_ptr->immune_cold || (brdam <= 0))) + { + /* Take damage */ + cold_dam(brdam, "a chilling blast"); + harmful = TRUE; + } + o_ptr->weight = o_ptr->weight - brpow; + o_ptr->pval = o_ptr->weight; + } + else if (r_ptr->flags4 & RF4_BR_COLD) + { + set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10); + } + + /* Poison */ + if (r_ptr->flags4 & RF4_BR_POIS && brpow > 0) + { + brdam = ((brpow / 3) > 800 ? 800 : (brpow / 3)); + + msg_print("You are surrounded by toxic gases!"); + + /* Resist the damage */ + if (p_ptr->resist_pois) brdam = (brdam + 2) / 3; + if (p_ptr->oppose_pois) brdam = (brdam + 2) / 3; + + if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) + { + (void)set_poisoned(p_ptr->poisoned + rand_int(brdam) + 10); + } + + /* Take damage */ + take_hit(brdam, "toxic gases"); + o_ptr->weight = o_ptr->weight - brpow; + o_ptr->pval = o_ptr->weight; + harmful = TRUE; + } + + /* Nether */ + if (r_ptr->flags4 & RF4_BR_NETH && brpow > 0) + { + brdam = ((brpow / 6) > 550 ? 550 : (brpow / 6)); + + msg_print("A black aura surrounds the corpse!"); + + if (p_ptr->resist_neth) + { + brdam *= 6; + brdam /= (randint(6) + 6); + } + else + { + if (p_ptr->hold_life && (rand_int(100) < 75)) + { + msg_print("You keep hold of your life force!"); + } + else if (p_ptr->hold_life) + { + msg_print("You feel your life slipping away!"); + lose_exp(200 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE); + } + else + { + msg_print("You feel your life draining away!"); + lose_exp(200 + (p_ptr->exp / 100) * MON_DRAIN_LIFE); + } + } + + /* Take damage */ + take_hit(brdam, "an unholy blast"); + harmful = TRUE; + o_ptr->weight = o_ptr->weight - brpow; + o_ptr->pval = o_ptr->weight; + } + + /* Confusion */ + if (r_ptr->flags4 & RF4_BR_CONF && brpow > 0) + { + msg_print("A strange liquid splashes on you!"); + + if (!p_ptr->resist_conf) + { + set_confused(p_ptr->confused + brdam + idam + 10); + } + o_ptr->weight = o_ptr->weight - brpow; + o_ptr->pval = o_ptr->weight; + } + + /* Chaos */ + if (r_ptr->flags4 & RF4_BR_CHAO && brpow > 0) + { + brdam = ((brpow / 6) > 600 ? 600 : (brpow / 6)); + + msg_print("A swirling cloud surrounds you!"); + + if (p_ptr->resist_chaos) + { + brdam *= 6; + brdam /= (randint(6) + 6); + } + + if (!p_ptr->resist_conf) + { + (void)set_confused(p_ptr->confused + rand_int(20) + 10); + } + + if (!p_ptr->resist_chaos) + { + (void)set_image(p_ptr->image + randint(10)); + } + + if (!p_ptr->resist_neth && !p_ptr->resist_chaos) + { + if (p_ptr->hold_life && (rand_int(100) < 75)) + { + msg_print("You keep hold of your life force!"); + } + else if (p_ptr->hold_life) + { + msg_print("You feel your life slipping away!"); + lose_exp(500 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE); + } + else + { + msg_print("You feel your life draining away!"); + lose_exp(5000 + (p_ptr->exp / 100) * MON_DRAIN_LIFE); + } + } + + /* Take damage */ + take_hit(brdam, "chaotic forces"); + o_ptr->pval = 1; + } + + /* Disenchantment */ + if (r_ptr->flags4 & RF4_BR_DISE && brpow > 0) + { + brdam = ((brpow / 6) > 500 ? 500 : (brpow / 6)); + + msg_print("You are blasted by raw mana!"); + + if (p_ptr->resist_disen) + { + brdam *= 6; + brdam /= (randint(6) + 6); + } + else + { + (void)apply_disenchant(0); + } + + /* Take damage */ + take_hit(brdam, "raw mana"); + o_ptr->pval = 1; + } + + /* Plasma */ + if (r_ptr->flags4 & RF4_BR_PLAS && brpow > 0) + { + brdam = ((brpow / 6) > 150 ? 150 : (brpow / 6)); + + msg_print("Searing flames engulf the corpse!"); + + /* Resist the damage */ + if (p_ptr->resist_fire || p_ptr->oppose_fire) brdam = (brdam + 2) / 3; + + if (!p_ptr->resist_sound) + { + int k = (randint((brdam > 40) ? 35 : (brdam * 3 / 4 + 5))); + (void)set_stun(p_ptr->stun + k); + } + + /* Take damage */ + take_hit(brdam, "an explosion"); + harmful = TRUE; + o_ptr->pval = 1; + } + + /* Hack -- Jellies are immune to acid only if they are already acidic */ + if (strchr("j", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_ACID)) + { + dam = damroll(8, 8); + + /* Total Immunity */ + if (!(p_ptr->immune_acid || (dam <= 0))) + { + /* Resist the damage */ + if (p_ptr->resist_acid) dam = (dam + 2) / 3; + if (p_ptr->oppose_acid) dam = (dam + 2) / 3; + + /* Take damage */ + take_hit(dam, "acidic food"); + } + harmful = TRUE; + } + + /* + * Hack -- Jellies, kobolds, spiders, icky things, molds, and mushrooms + * are immune to poison because their body already contains + * poisonous chemicals. + */ + if (strchr("ijkmS,", r_ptr->d_char) && (r_ptr->flags3 & RF3_IM_POIS)) + { + if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) + { + set_poisoned(p_ptr->poisoned + rand_int(15) + 10); + } + harmful = TRUE; + } + + /* + * Bad effects override good effects + * and hacked-up corpses lose intrinsics. + */ + if (!harmful && !cutting && (o_ptr->sval != SV_CORPSE_MEAT)) + { + if (r_ptr->flags3 & RF3_IM_ACID) + { + set_oppose_acid(p_ptr->oppose_acid + rand_int(10) + 10); + } + if (r_ptr->flags3 & RF3_IM_ELEC) + { + set_oppose_elec(p_ptr->oppose_elec + rand_int(10) + 10); + } + if (r_ptr->flags3 & RF3_IM_FIRE) + { + set_oppose_fire(p_ptr->oppose_fire + rand_int(10) + 10); + } + if (r_ptr->flags3 & RF3_IM_COLD) + { + set_oppose_cold(p_ptr->oppose_cold + rand_int(10) + 10); + } + if (r_ptr->flags3 & RF3_IM_POIS) + { + set_oppose_pois(p_ptr->oppose_pois + rand_int(10) + 10); + } + if (r_ptr->flags3 & RF3_RES_NETH) + { + set_protevil(p_ptr->protevil + rand_int(25) + 3 * r_ptr->level); + } + if (r_ptr->flags3 & RF3_RES_PLAS) + { + set_oppose_fire(p_ptr->oppose_fire + rand_int(20) + 20); + } + if (r_ptr->flags2 & RF2_SHAPECHANGER) + { + /* DGDGDG (void)set_mimic(20 , rand_int(MIMIC_VALAR)); */ + } + + if (r_ptr->flags3 & RF3_DEMON) + { + /* DGDGDG (void)set_mimic(30 , MIMIC_DEMON); */ + } + + if (r_ptr->flags3 & RF3_UNDEAD) + { + /* DGDGDG (void)set_mimic(30 , MIMIC_VAMPIRE); */ + } + + if (r_ptr->flags3 & RF3_NO_FEAR) + { + (void)set_afraid(0); + } + if (r_ptr->flags3 & RF3_NO_STUN) + { + (void)set_stun(0); + } + if (r_ptr->flags3 & RF3_NO_CONF) + { + (void)set_confused(0); + } + if (r_ptr->flags6 & RF6_S_THUNDERLORD) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_THUNDERLORD, FALSE); + } + if (r_ptr->flags6 & RF6_S_DEMON) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DEMON, FALSE); + } + if (r_ptr->flags6 & RF6_S_KIN) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_KIN, FALSE); + } + if (r_ptr->flags6 & RF6_S_HI_DEMON) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DEMON, FALSE); + } + if (r_ptr->flags6 & RF6_S_MONSTER) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE); + } + if (r_ptr->flags6 & RF6_S_MONSTERS) + { + int k; + for (k = 0; k < 8; k++) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, 0, FALSE); + } + } + if (r_ptr->flags6 & RF6_S_UNDEAD) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD, FALSE); + } + if (r_ptr->flags6 & RF6_S_DRAGON) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DRAGON, FALSE); + } + if (r_ptr->flags6 & RF6_S_ANT) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANT, FALSE); + } + if (r_ptr->flags6 & RF6_S_SPIDER) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_SPIDER, FALSE); + } + if (r_ptr->flags6 & RF6_S_HOUND) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HOUND, FALSE); + } + if (r_ptr->flags6 & RF6_S_HYDRA) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HYDRA, FALSE); + } + if (r_ptr->flags6 & RF6_S_ANGEL) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_ANGEL, FALSE); + } + if (r_ptr->flags6 & RF6_S_HI_DRAGON) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_DRAGON, FALSE); + } + if (r_ptr->flags6 & RF6_S_HI_UNDEAD) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_HI_UNDEAD, FALSE); + } + if (r_ptr->flags6 & RF6_S_WRAITH) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_WRAITH, FALSE); + } + if (r_ptr->flags6 & RF6_S_UNIQUE) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNIQUE, FALSE); + } + } +} + + +/* + * Hook to determine if an object is eatable + */ +static bool_ item_tester_hook_eatable(object_type *o_ptr) +{ + /* Foods and, well, corpses are edible */ + if ((o_ptr->tval == TV_FOOD) || (o_ptr->tval == TV_CORPSE)) return (TRUE); + + /* Assume not */ + return (FALSE); +} + + +/* + * Eat some food (from the pack or floor) + */ +void do_cmd_eat_food(void) +{ + int item, ident, lev, fval = 0; + + object_type *o_ptr; + object_type *q_ptr, forge; + + monster_race *r_ptr; + + cptr q, s; + + bool_ destroy = TRUE; + + + /* Restrict choices to food */ + item_tester_hook = item_tester_hook_eatable; + + /* Set up the extra finder */ + get_item_hook_find_obj_what = "Food full name? "; + get_item_extra_hook = get_item_hook_find_obj; + + /* Get an item */ + q = "Eat which item? "; + s = "You have nothing to eat."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Sound */ + sound(SOUND_EAT); + + + /* Take a turn */ + energy_use = 100; + + /* Identity not known yet */ + ident = FALSE; + + /* Object level */ + lev = k_info[o_ptr->k_idx].level; + + /* Scripted foods */ + hook_eat_in in = { o_ptr }; + hook_eat_out out = { FALSE }; + + if (process_hooks_ret(HOOK_EAT, "d", "(O)", o_ptr)) + { + ident = process_hooks_return[0].num; + } + else if (process_hooks_new(HOOK_EAT, &in, &out)) + { + ident = out.ident; + } + /* (not quite) Normal foods */ + else if (o_ptr->tval == TV_FOOD) + { + /* Analyze the food */ + switch (o_ptr->sval) + { + case SV_FOOD_GREAT_HEALTH: + { + p_ptr->hp_mod += 70; + msg_print("As you eat it you begin to feel your life flow getting stronger."); + ident = TRUE; + p_ptr->update |= (PU_HP); + + break; + } + + case SV_FOOD_POISON: + { + if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) + { + if (set_poisoned(p_ptr->poisoned + rand_int(10) + 10)) + { + ident = TRUE; + } + } + + break; + } + + case SV_FOOD_BLINDNESS: + { + if (!p_ptr->resist_blind) + { + if (set_blind(p_ptr->blind + rand_int(200) + 200)) + { + ident = TRUE; + } + } + + break; + } + + case SV_FOOD_PARANOIA: + { + if (!p_ptr->resist_fear) + { + if (set_afraid(p_ptr->afraid + rand_int(10) + 10)) + { + ident = TRUE; + } + } + + break; + } + + case SV_FOOD_CONFUSION: + { + if (!p_ptr->resist_conf) + { + if (set_confused(p_ptr->confused + rand_int(10) + 10)) + { + ident = TRUE; + } + } + + break; + } + + case SV_FOOD_HALLUCINATION: + { + if (!p_ptr->resist_chaos) + { + if (set_image(p_ptr->image + rand_int(250) + 250)) + { + ident = TRUE; + } + } + + break; + } + + case SV_FOOD_PARALYSIS: + { + if (!p_ptr->free_act) + { + if (set_paralyzed(rand_int(10) + 10)) + { + ident = TRUE; + } + } + + break; + } + + case SV_FOOD_WEAKNESS: + { + take_hit(damroll(6, 6), "poisonous food"); + (void)do_dec_stat(A_STR, STAT_DEC_NORMAL); + + ident = TRUE; + + break; + } + + case SV_FOOD_SICKNESS: + { + take_hit(damroll(6, 6), "poisonous food"); + (void)do_dec_stat(A_CON, STAT_DEC_NORMAL); + + ident = TRUE; + + break; + } + + case SV_FOOD_STUPIDITY: + { + take_hit(damroll(8, 8), "poisonous food"); + (void)do_dec_stat(A_INT, STAT_DEC_NORMAL); + + ident = TRUE; + + break; + } + + case SV_FOOD_NAIVETY: + { + take_hit(damroll(8, 8), "poisonous food"); + (void)do_dec_stat(A_WIS, STAT_DEC_NORMAL); + + ident = TRUE; + + break; + } + + case SV_FOOD_UNHEALTH: + { + take_hit(damroll(10, 10), "poisonous food"); + (void)do_dec_stat(A_CON, STAT_DEC_NORMAL); + + ident = TRUE; + + break; + } + + case SV_FOOD_DISEASE: + { + take_hit(damroll(10, 10), "poisonous food"); + (void)do_dec_stat(A_STR, STAT_DEC_NORMAL); + + ident = TRUE; + + break; + } + + case SV_FOOD_CURE_POISON: + { + if (set_poisoned(0)) ident = TRUE; + + break; + } + + case SV_FOOD_CURE_BLINDNESS: + { + if (set_blind(0)) ident = TRUE; + + break; + } + + case SV_FOOD_CURE_PARANOIA: + { + if (set_afraid(0)) ident = TRUE; + + break; + } + + case SV_FOOD_CURE_CONFUSION: + { + if (set_confused(0)) ident = TRUE; + + break; + } + + case SV_FOOD_CURE_SERIOUS: + { + if (hp_player(damroll(4, 8))) ident = TRUE; + + break; + } + + case SV_FOOD_RESTORE_STR: + { + if (do_res_stat(A_STR, TRUE)) ident = TRUE; + + break; + } + + case SV_FOOD_RESTORE_CON: + { + if (do_res_stat(A_CON, TRUE)) ident = TRUE; + + break; + } + + case SV_FOOD_RESTORING: + { + if (do_res_stat(A_STR, TRUE)) ident = TRUE; + if (do_res_stat(A_INT, TRUE)) ident = TRUE; + if (do_res_stat(A_WIS, TRUE)) ident = TRUE; + if (do_res_stat(A_DEX, TRUE)) ident = TRUE; + if (do_res_stat(A_CON, TRUE)) ident = TRUE; + if (do_res_stat(A_CHR, TRUE)) ident = TRUE; + + break; + } + + case SV_FOOD_FORTUNE_COOKIE: + { + char rumour[80]; + + msg_print("That tastes good."); + msg_print("There is message in the cookie. It says:"); + msg_print(NULL); + + switch (randint(20)) + { + case 1: + { + get_rnd_line("chainswd.txt", rumour); + break; + } + + case 2: + { + get_rnd_line("error.txt", rumour); + break; + } + + case 3: + case 4: + case 5: + { + get_rnd_line("death.txt", rumour); + break; + } + + default: + { + get_rnd_line("rumors.txt", rumour); + break; + } + } + + msg_format("%s", rumour); + msg_print(NULL); + + ident = TRUE; + + break; + } + + + case SV_FOOD_RATION: + case SV_FOOD_BISCUIT: + case SV_FOOD_JERKY: + { + msg_print("That tastes good."); + + ident = TRUE; + + break; + } + + case SV_FOOD_SLIME_MOLD: + { + msg_print("That tastes good."); + + /* 2% chance of getting the mold power */ + if (magik(2)) + { + ADD_POWER(p_ptr->powers_mod, PWR_GROW_MOLD); + p_ptr->update |= PU_POWERS; + } + + ident = TRUE; + + break; + } + + case SV_FOOD_WAYBREAD: + { + msg_print("That tastes very good."); + (void)set_poisoned(0); + (void)hp_player(damroll(4, 8)); + set_food(PY_FOOD_MAX - 1); + + ident = TRUE; + + break; + } + + case SV_FOOD_PINT_OF_ALE: + case SV_FOOD_PINT_OF_WINE: + { + msg_print("That tastes good."); + + ident = TRUE; + + q_ptr = &forge; + object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1)); + q_ptr->number = 1; + object_aware(q_ptr); + object_known(q_ptr); + q_ptr->ident |= IDENT_STOREB; + (void)inven_carry(q_ptr, FALSE); + + break; + } + + case SV_FOOD_ATHELAS: + { + msg_print("A fresh, clean essence rises, driving away wounds and poison."); + + (void)set_poisoned(0); + (void)set_stun(0); + (void)set_cut(0); + if (p_ptr->black_breath) + { + msg_print("The hold of the Black Breath on you is broken!"); + p_ptr->black_breath = FALSE; + } + + ident = TRUE; + + break; + } + } + } + + /* Corpses... */ + else + { + r_ptr = &r_info[o_ptr->pval2]; + + /* Analyse the corpse */ + switch (o_ptr->sval) + { + case SV_CORPSE_CORPSE: + { + bool_ no_meat = FALSE; + + /* Not all is edible. Apologies if messy. */ + + /* Check weight -- they have to have some meat left */ + if (r_ptr->flags9 & RF9_DROP_SKELETON) + { + if (o_ptr->weight <= (r_ptr->weight * 3) / 5) + { + no_meat = TRUE; + } + } + + /* Non-skeletons are naturally have more allowances */ + else + { + if (o_ptr->weight <= (r_ptr->weight * 7) / 20) + { + no_meat = TRUE; + } + } + + /* Nothing left to eat */ + if (no_meat) + { + msg_print("There is not enough meat."); + return; + } + + + /* Check freshness */ + if (!o_ptr->timeout) msg_print("Ugh! Raw meat!"); + else msg_print("That tastes good."); + + + /* A pound of raw meat */ + o_ptr->pval -= 10; + o_ptr->weight -= 10; + + /* Corpses still have meat on them */ + destroy = FALSE; + + ident = TRUE; + + break; + } + + case SV_CORPSE_HEAD: + { + msg_print("You feel rather sick."); + + /* A pound of raw meat */ + o_ptr->pval -= 10; + o_ptr->weight -= 10; + + /* Corpses still have meat on them */ + destroy = FALSE; + + ident = TRUE; + + break; + } + + case SV_CORPSE_MEAT: + { + /* Just meat */ + if (!o_ptr->timeout) msg_print("You quickly swallow the meat."); + else msg_print("That tastes good."); + + ident = TRUE; + + /* Those darn microorganisms */ + if (!o_ptr->timeout && (o_ptr->weight > o_ptr->pval) && + !(p_ptr->resist_pois || p_ptr->oppose_pois)) + { + set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) + + (o_ptr->weight - o_ptr->pval)); + } + + break; + } + } + + corpse_effect(o_ptr, FALSE); + + /* Less nutritious than food rations, but much more of it. */ + fval = (o_ptr->timeout) ? 2000 : 2500; + + /* Those darn microorganisms */ + if (!o_ptr->timeout && (o_ptr->weight - o_ptr->pval > 10) && + !(p_ptr->resist_pois || p_ptr->oppose_pois)) + { + set_poisoned(p_ptr->poisoned + rand_int(o_ptr->weight - o_ptr->pval) + + (o_ptr->weight - o_ptr->pval)); + } + + /* Partially cured */ + if (o_ptr->weight > o_ptr->timeout) + { + /* Adjust the "timeout" without overflowing */ + o_ptr->timeout = (o_ptr->timeout * ((100 * o_ptr->timeout) / o_ptr->weight)) / 100; + } + } + + + /* Combine / Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* We have tried it */ + object_tried(o_ptr); + + /* The player is now aware of the object */ + if (ident && !object_aware_p(o_ptr)) + { + object_aware(o_ptr); + gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev); + } + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + if (!fval) fval = o_ptr->pval; + + /* Food can feed the player, in a different ways */ + + /* Vampires */ + if ((PRACE_FLAG(PR1_VAMPIRE)) || (p_ptr->mimic_form == resolve_mimic_name("Vampire"))) + { + /* Reduced nutritional benefit */ + /* (void)set_food(p_ptr->food + (fval / 10)); -- No more */ + msg_print("Mere victuals hold scant sustenance for a being such as yourself."); + + /* Hungry */ + if (p_ptr->food < PY_FOOD_ALERT) + { + msg_print("Your hunger can only be satisfied with fresh blood!"); + } + } + + else if (PRACE_FLAG(PR1_NO_FOOD)) + { + if (PRACE_FLAG(PR1_UNDEAD)) + { + msg_print("The food of mortals is poor sustenance for you."); + } + else + { + msg_print("Food is poor sustenance for you."); + } + set_food(p_ptr->food + ((fval) / 40)); + } + + /* Those living in fresh */ + else + { + (void)set_food(p_ptr->food + fval); + } + + + /* Destroy food? */ + if (destroy) + { + inc_stack_size(item, -1); + } +} + + +/* + * Cut a corpse up for convenient storage + */ +void do_cmd_cut_corpse(void) +{ + int item, meat = 0, not_meat = 0; + + object_type *o_ptr; + + object_type *i_ptr; + + object_type object_type_body; + + monster_race *r_ptr; + + cptr q, s; + + + /* Restrict choices to corpses */ + item_tester_tval = TV_CORPSE; + + /* Get an item */ + q = "Hack up which corpse? "; + s = "You have no corpses."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + r_ptr = &r_info[o_ptr->pval2]; + + if ((o_ptr->sval != SV_CORPSE_CORPSE) && (o_ptr->sval != SV_CORPSE_HEAD)) + { + msg_print ("You cannot split that."); + return; + } + + switch (o_ptr->sval) + { + case SV_CORPSE_CORPSE: + { + if (r_ptr->flags9 & RF9_DROP_SKELETON) + { + not_meat = (r_ptr->weight * 3) / 5; + } + else + { + not_meat = (r_ptr->weight * 7) / 20; + } + meat = r_ptr->weight + r_ptr->weight / 10 - not_meat; + + break; + } + + case SV_CORPSE_HEAD: + { + not_meat = r_ptr->weight / 150; + meat = r_ptr->weight / 30 + r_ptr->weight / 300 - not_meat; + + break; + } + } + + if ((o_ptr->weight <= not_meat) || (meat < 10)) + { + msg_print("There is not enough meat."); + return; + } + + /* Hacking 10 pounds off */ + if (meat > 100) meat = 100; + + /* Take a turn */ + energy_use = 100; + + o_ptr->pval -= meat; + o_ptr->weight -= meat; + + msg_print("You hack some meat off the corpse."); + + corpse_effect(o_ptr, TRUE); + + /* Get local object */ + i_ptr = &object_type_body; + + /* Make some meat */ + object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_MEAT)); + + i_ptr->number = meat / 10; + i_ptr->pval2 = o_ptr->pval2; + + /* Length of time before decay */ + i_ptr->pval = 1000 + rand_int(1000); + + if (inven_carry_okay(i_ptr)) + { + inven_carry(i_ptr, TRUE); + } + else + { + drop_near(i_ptr, 0, p_ptr->py, p_ptr->px); + } +} + + +/* + * Use a potion to cure some meat + * + * Salt water works well. + */ +void do_cmd_cure_meat(void) +{ + int item, num, cure; + + object_type *o_ptr; + + object_type *i_ptr; + + cptr q, s; + + + /* Restrict choices to corpses */ + item_tester_tval = TV_CORPSE; + item_tester_hook = item_tester_hook_eatable; + + /* Get some meat */ + q = "Cure which meat? "; + s = "You have no meat to cure."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Restrict choices to potions */ + item_tester_tval = TV_POTION; + + /* Get a potion */ + q = "Use which potion? "; + s = "You have no potions to use."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + i_ptr = get_object(item); + + if (i_ptr->number > 1) + { + /* Get a number */ + get_count(1, i_ptr->number); + + /* Save it */ + num = command_arg; + } + else + { + num = 1; + } + + if (num == 0) return; + + /* Take a turn */ + energy_use = 100; + + q = "You soak the meat."; + s = "You soak the meat."; + + switch (i_ptr->sval) + { + case SV_POTION_SALT_WATER: + { + q = "You salt the meat."; + cure = 200 * num; + + break; + } + + case SV_POTION_POISON: + { + q = "You poison the meat."; + cure = 0; + o_ptr->pval /= 2; + if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight; + + break; + } + + case SV_POTION_CONFUSION: + { + cure = 80 * num; + + break; + } + + case SV_POTION_SLOW_POISON: + { + cure = 20 * num; + + break; + } + + case SV_POTION_CURE_POISON: + { + cure = 45 * num; + + break; + } + + case SV_POTION_DEATH: + { + q = "You ruin the meat."; + cure = 0; + o_ptr->pval /= 10; + if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight / 2; + + break; + } + + default: + { + cure = 0; + + break; + } + } + + /* Message */ + if (object_known_p(i_ptr)) msg_print(q); + else msg_print(s); + + /* The meat is already spoiling */ + if (((o_ptr->sval == SV_CORPSE_MEAT) && (o_ptr->weight > o_ptr->pval)) || + (o_ptr->weight - o_ptr->pval > 10)) + { + cure = (cure * o_ptr->pval) / (o_ptr->weight * 20); + } + + /* Cure the meat */ + o_ptr->timeout += cure / o_ptr->number; + + if (o_ptr->timeout > o_ptr->pval) o_ptr->timeout = o_ptr->pval; + + /* Use up the potions */ + inc_stack_size(item, -num); +} + + +/* + * Hook to determine if an object is quaffable + */ +static bool_ item_tester_hook_quaffable(object_type *o_ptr) +{ + if ((o_ptr->tval == TV_POTION) || (o_ptr->tval == TV_POTION2)) return (TRUE); + + /* Assume not */ + return (FALSE); +} + + +static bool_ quaff_potion(int tval, int sval, int pval, int pval2) +{ + int ident = FALSE; + + + /* "Traditional" potions */ + if (tval == TV_POTION) + { + switch (sval) + { + case SV_POTION_WATER: + case SV_POTION_APPLE_JUICE: + case SV_POTION_SLIME_MOLD: + { + msg_print("You feel less thirsty."); + ident = TRUE; + + break; + } + + case SV_POTION_SLOWNESS: + { + if (set_slow(p_ptr->slow + randint(25) + 15)) ident = TRUE; + + break; + } + + case SV_POTION_SALT_WATER: + { + msg_print("The potion makes you vomit!"); + (void)set_food(PY_FOOD_STARVE - 1); + (void)set_poisoned(0); + (void)set_paralyzed(4); + ident = TRUE; + + break; + } + + case SV_POTION_POISON: + { + if (!(p_ptr->resist_pois || p_ptr->oppose_pois)) + { + if (set_poisoned(p_ptr->poisoned + rand_int(15) + 10)) + { + ident = TRUE; + } + } + + break; + } + + case SV_POTION_BLINDNESS: + { + if (!p_ptr->resist_blind) + { + if (set_blind(p_ptr->blind + rand_int(100) + 100)) + { + ident = TRUE; + } + } + + break; + } + + /* Booze */ + case SV_POTION_CONFUSION: + { + if (!((p_ptr->resist_conf) || (p_ptr->resist_chaos))) + { + if (set_confused(p_ptr->confused + rand_int(20) + 15)) + { + ident = TRUE; + } + if (randint(2) == 1) + { + if (set_image(p_ptr->image + rand_int(150) + 150)) + { + ident = TRUE; + } + } + if (randint(13) == 1) + { + ident = TRUE; + if (randint(3) == 1) lose_all_info(); + else wiz_dark(); + teleport_player(100); + wiz_dark(); + msg_print("You wake up elsewhere with a sore head..."); + msg_print("You can't remember a thing, or how you got here!"); + } + } + + break; + } + + case SV_POTION_SLEEP: + { + if (!p_ptr->free_act) + { + if (set_paralyzed(rand_int(4) + 4)) + { + ident = TRUE; + } + } + + break; + } + + case SV_POTION_LOSE_MEMORIES: + { + if (!p_ptr->hold_life && (p_ptr->exp > 0)) + { + msg_print("You feel your memories fade."); + lose_exp(p_ptr->exp / 4); + ident = TRUE; + } + + break; + } + + case SV_POTION_RUINATION: + { + msg_print("Your nerves and muscles feel weak and lifeless!"); + take_hit(damroll(10, 10), "a potion of Ruination"); + (void)dec_stat(A_DEX, 25, TRUE); + (void)dec_stat(A_WIS, 25, TRUE); + (void)dec_stat(A_CON, 25, TRUE); + (void)dec_stat(A_STR, 25, TRUE); + (void)dec_stat(A_CHR, 25, TRUE); + (void)dec_stat(A_INT, 25, TRUE); + ident = TRUE; + + break; + } + + case SV_POTION_DEC_STR: + { + if (do_dec_stat(A_STR, STAT_DEC_NORMAL)) ident = TRUE; + + break; + } + + case SV_POTION_DEC_INT: + { + if (do_dec_stat(A_INT, STAT_DEC_NORMAL)) ident = TRUE; + + break; + } + + case SV_POTION_DEC_WIS: + { + if (do_dec_stat(A_WIS, STAT_DEC_NORMAL)) ident = TRUE; + + break; + } + + case SV_POTION_DEC_DEX: + { + if (do_dec_stat(A_DEX, STAT_DEC_NORMAL)) ident = TRUE; + + break; + } + + case SV_POTION_DEC_CON: + { + if (do_dec_stat(A_CON, STAT_DEC_NORMAL)) ident = TRUE; + + break; + } + + case SV_POTION_DEC_CHR: + { + if (do_dec_stat(A_CHR, STAT_DEC_NORMAL)) ident = TRUE; + + break; + } + + case SV_POTION_DETONATIONS: + { + msg_print("Massive explosions rupture your body!"); + take_hit(damroll(50, 20), "a potion of Detonation"); + (void)set_stun(p_ptr->stun + 75); + (void)set_cut(p_ptr->cut + 5000); + ident = TRUE; + + break; + } + + case SV_POTION_DEATH: + { + msg_print("A feeling of Death flows through your body."); + take_hit(5000, "a potion of Death"); + ident = TRUE; + + break; + } + + case SV_POTION_INFRAVISION: + { + if (set_tim_infra(p_ptr->tim_infra + 100 + randint(100))) + { + ident = TRUE; + } + + break; + } + + case SV_POTION_DETECT_INVIS: + { + if (set_tim_invis(p_ptr->tim_invis + 12 + randint(12))) + { + ident = TRUE; + } + + break; + } + + case SV_POTION_SLOW_POISON: + { + if (set_poisoned(p_ptr->poisoned / 2)) ident = TRUE; + + break; + } + + case SV_POTION_CURE_POISON: + { + if (set_poisoned(0)) ident = TRUE; + + break; + } + + case SV_POTION_BOLDNESS: + { + if (set_afraid(0)) ident = TRUE; + + break; + } + + case SV_POTION_SPEED: + { + if (!p_ptr->fast) + { + if (set_fast(randint(25) + 15, 10)) ident = TRUE; + } + else + { + (void)set_fast(p_ptr->fast + 5, 10); + } + + break; + } + + case SV_POTION_RESIST_HEAT: + { + if (set_oppose_fire(p_ptr->oppose_fire + randint(10) + 10)) + { + ident = TRUE; + } + + break; + } + + case SV_POTION_RESIST_COLD: + { + if (set_oppose_cold(p_ptr->oppose_cold + randint(10) + 10)) + { + ident = TRUE; + } + + break; + } + + case SV_POTION_HEROISM: + { + if (set_afraid(0)) ident = TRUE; + if (set_hero(p_ptr->hero + randint(25) + 25)) ident = TRUE; + if (hp_player(10)) ident = TRUE; + + break; + } + + case SV_POTION_BESERK_STRENGTH: + { + if (set_afraid(0)) ident = TRUE; + if (set_shero(p_ptr->shero + randint(25) + 25)) ident = TRUE; + if (hp_player(30)) ident = TRUE; + + break; + } + + case SV_POTION_CURE_LIGHT: + { + if (hp_player(damroll(2, 8))) ident = TRUE; + if (set_blind(0)) ident = TRUE; + if (set_cut(p_ptr->cut - 10)) ident = TRUE; + + break; + } + + case SV_POTION_CURE_SERIOUS: + { + if (hp_player(damroll(4, 8))) ident = TRUE; + if (set_blind(0)) ident = TRUE; + if (set_confused(0)) ident = TRUE; + if (set_cut((p_ptr->cut / 2) - 50)) ident = TRUE; + + break; + } + + case SV_POTION_CURE_CRITICAL: + { + if (hp_player(damroll(6, 8))) ident = TRUE; + if (set_blind(0)) ident = TRUE; + if (set_confused(0)) ident = TRUE; + if (set_poisoned(0)) ident = TRUE; + if (set_stun(0)) ident = TRUE; + if (set_cut(0)) ident = TRUE; + + break; + } + + case SV_POTION_HEALING: + { + if (hp_player(300)) ident = TRUE; + if (set_blind(0)) ident = TRUE; + if (set_confused(0)) ident = TRUE; + if (set_poisoned(0)) ident = TRUE; + if (set_stun(0)) ident = TRUE; + if (set_cut(0)) ident = TRUE; + + break; + } + + case SV_POTION_STAR_HEALING: + { + if (hp_player(1200)) ident = TRUE; + if (set_blind(0)) ident = TRUE; + if (set_confused(0)) ident = TRUE; + if (set_poisoned(0)) ident = TRUE; + if (set_stun(0)) ident = TRUE; + if (set_cut(0)) ident = TRUE; + + break; + } + + case SV_POTION_LIFE: + { + msg_print("You feel life flow through your body!"); + restore_level(); + hp_player(5000); + (void)set_poisoned(0); + (void)set_blind(0); + (void)set_confused(0); + (void)set_image(0); + (void)set_stun(0); + (void)set_cut(0); + (void)do_res_stat(A_STR, TRUE); + (void)do_res_stat(A_CON, TRUE); + (void)do_res_stat(A_DEX, TRUE); + (void)do_res_stat(A_WIS, TRUE); + (void)do_res_stat(A_INT, TRUE); + (void)do_res_stat(A_CHR, TRUE); + if (p_ptr->black_breath) + { + msg_print("The hold of the Black Breath on you is broken!"); + } + p_ptr->black_breath = FALSE; + ident = TRUE; + + break; + } + + case SV_POTION_RESTORE_MANA: + { + if (p_ptr->csp < p_ptr->msp) + { + p_ptr->csp = p_ptr->msp; + p_ptr->csp_frac = 0; + msg_print("Your feel your head clear."); + p_ptr->redraw |= (PR_MANA); + p_ptr->window |= (PW_PLAYER); + ident = TRUE; + } + + break; + } + + case SV_POTION_RESTORE_EXP: + { + if (restore_level()) ident = TRUE; + + break; + } + + case SV_POTION_RES_STR: + { + if (do_res_stat(A_STR, TRUE)) ident = TRUE; + + break; + } + + case SV_POTION_RES_INT: + { + if (do_res_stat(A_INT, TRUE)) ident = TRUE; + + break; + } + + case SV_POTION_RES_WIS: + { + if (do_res_stat(A_WIS, TRUE)) ident = TRUE; + + break; + } + + case SV_POTION_RES_DEX: + { + if (do_res_stat(A_DEX, TRUE)) ident = TRUE; + + break; + } + + case SV_POTION_RES_CON: + { + if (do_res_stat(A_CON, TRUE)) ident = TRUE; + + break; + } + + case SV_POTION_RES_CHR: + { + if (do_res_stat(A_CHR, TRUE)) ident = TRUE; + + break; + } + + case SV_POTION_INC_STR: + { + if (do_inc_stat(A_STR)) ident = TRUE; + + break; + } + + case SV_POTION_INC_INT: + { + if (do_inc_stat(A_INT)) ident = TRUE; + + break; + } + + case SV_POTION_INC_WIS: + { + if (do_inc_stat(A_WIS)) ident = TRUE; + + break; + } + + case SV_POTION_INC_DEX: + { + if (do_inc_stat(A_DEX)) ident = TRUE; + + break; + } + + case SV_POTION_INC_CON: + { + if (do_inc_stat(A_CON)) ident = TRUE; + + break; + } + + case SV_POTION_INC_CHR: + { + if (do_inc_stat(A_CHR)) ident = TRUE; + + break; + } + + case SV_POTION_AUGMENTATION: + { + if (do_inc_stat(A_STR)) ident = TRUE; + if (do_inc_stat(A_INT)) ident = TRUE; + if (do_inc_stat(A_WIS)) ident = TRUE; + if (do_inc_stat(A_DEX)) ident = TRUE; + if (do_inc_stat(A_CON)) ident = TRUE; + if (do_inc_stat(A_CHR)) ident = TRUE; + + break; + } + + case SV_POTION_ENLIGHTENMENT: + { + msg_print("An image of your surroundings forms in your mind..."); + wiz_lite(); + ident = TRUE; + + break; + } + + case SV_POTION_STAR_ENLIGHTENMENT: + { + msg_print("You begin to feel more enlightened..."); + msg_print(NULL); + wiz_lite_extra(); + (void)do_inc_stat(A_INT); + (void)do_inc_stat(A_WIS); + (void)detect_traps(DEFAULT_RADIUS); + (void)detect_doors(DEFAULT_RADIUS); + (void)detect_stairs(DEFAULT_RADIUS); + (void)detect_treasure(DEFAULT_RADIUS); + (void)detect_objects_gold(DEFAULT_RADIUS); + (void)detect_objects_normal(DEFAULT_RADIUS); + identify_pack(); + self_knowledge(NULL); + ident = TRUE; + + break; + } + + case SV_POTION_SELF_KNOWLEDGE: + { + msg_print("You begin to know yourself a little better..."); + msg_print(NULL); + self_knowledge(NULL); + ident = TRUE; + + break; + } + + case SV_POTION_EXPERIENCE: + { + if (p_ptr->exp < PY_MAX_EXP) + { + msg_print("You feel more experienced."); + gain_exp(100000L); + ident = TRUE; + } + + break; + } + + case SV_POTION_RESISTANCE: + { + (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20); + (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20); + (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20); + (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20); + (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20); + ident = TRUE; + + break; + } + + case SV_POTION_CURING: + { + if (hp_player(50)) ident = TRUE; + if (set_blind(0)) ident = TRUE; + if (set_poisoned(0)) ident = TRUE; + if (set_confused(0)) ident = TRUE; + if (set_stun(0)) ident = TRUE; + if (set_cut(0)) ident = TRUE; + if (set_image(0)) ident = TRUE; + if (heal_insanity(50)) ident = TRUE; + + break; + } + + case SV_POTION_INVULNERABILITY: + { + (void)set_invuln(p_ptr->invuln + randint(7) + 7); + ident = TRUE; + + break; + } + + case SV_POTION_NEW_LIFE: + { + do_cmd_rerate(); + ident = TRUE; + + break; + } + + case SV_POTION_BLOOD: + { + msg_print("You feel the blood of life running through your veins!"); + ident = TRUE; + p_ptr->allow_one_death++; + + break; + } + + case SV_POTION_MUTATION: + { + /* In Theme, Melkor likes players who quaff + potions of corruption. */ + if (game_module_idx == MODULE_THEME) + { + GOD(GOD_MELKOR) + { + msg_print("Your quaffing of this potion pleases Melkor!"); + set_grace(p_ptr->grace + 2); + } + } + + msg_print("You feel the dark corruptions of Morgoth coming over you!"); + gain_random_corruption(); + ident = TRUE; + break; + } + + case SV_POTION_INVIS: + { + int t = 30 + randint(30); + + if (set_invis(p_ptr->tim_invis + t, 35)) + { + ident = TRUE; + } + set_tim_invis(p_ptr->tim_invis + t); + + break; + } + + case SV_POTION_LEARNING: + { + p_ptr->skill_points += rand_range(4, 10 + luck( -4, 4)); + cmsg_format(TERM_L_GREEN, "You can increase %d more skills.", p_ptr->skill_points); + + break; + } + + default: + { + break; + } + } + } + + /* "Duplicate" potions */ + else + { + switch (sval) + { + case SV_POTION2_MIMIC: + { + if (!p_ptr->mimic_form) + { + s32b time = get_mimic_random_duration(pval2); + + set_mimic(time, pval2, (p_ptr->lev * 2) / 3); + + /* Redraw title */ + p_ptr->redraw |= (PR_TITLE); + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); + + ident = TRUE; + } + + break; + } + + case SV_POTION2_CURE_LIGHT_SANITY: + { + if (heal_insanity(damroll(4, 8))) ident = TRUE; + + break; + } + + case SV_POTION2_CURE_SERIOUS_SANITY: + { + if (heal_insanity(damroll(8, 8))) ident = TRUE; + + break; + } + + case SV_POTION2_CURE_CRITICAL_SANITY: + { + if (heal_insanity(damroll(12, 8))) ident = TRUE; + + break; + } + + case SV_POTION2_CURE_SANITY: + { + if (heal_insanity(damroll(10, 100))) ident = TRUE; + + break; + } + + default: + { + break; + } + } + } + + return (ident); +} + + +/* + * Quaff a potion (from the pack or the floor) + */ +void do_cmd_quaff_potion(void) +{ + int item, ident, lev; + + object_type *o_ptr; + + object_type *q_ptr, forge; + + cptr q, s; + + + /* Restrict choices to potions */ + item_tester_hook = item_tester_hook_quaffable; + + /* Set up the extra finder */ + get_item_hook_find_obj_what = "Potion full name? "; + get_item_extra_hook = get_item_hook_find_obj; + + /* Get an item */ + q = "Quaff which potion? "; + s = "You have no potions to quaff."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; + + /* Get the item */ + o_ptr = get_object(item); + + + /* Sound */ + sound(SOUND_QUAFF); + + + /* Take a turn */ + energy_use = 100; + + /* Not identified yet */ + ident = FALSE; + + /* Object level */ + lev = k_info[o_ptr->k_idx].level; + + /* Demon Breath corruption can spoil potions. */ + if (player_has_corruption(CORRUPT_DEMON_BREATH) && magik(9)) + { + msg_print("Your demon breath spoils the potion!"); + ident = FALSE; + } + else + { + /* Normal potion handling */ + ident = quaff_potion(o_ptr->tval, o_ptr->sval, o_ptr->pval, o_ptr->pval2); + } + + /* Combine / Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* The item has been tried */ + object_tried(o_ptr); + + /* An identification was made */ + if (ident && !object_aware_p(o_ptr)) + { + object_aware(o_ptr); + gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev); + } + + if (get_skill(SKILL_ALCHEMY)) + { + if (item >= 0) + { + q_ptr = &forge; + object_prep(q_ptr, lookup_kind(TV_BOTTLE, 1)); + q_ptr->number = 1; + object_aware(q_ptr); + object_known(q_ptr); + + q_ptr->ident |= IDENT_STOREB; + + (void)inven_carry(q_ptr, FALSE); + } + } + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + + /* Potions can feed the player */ + (void)set_food(p_ptr->food + o_ptr->pval); + + + /* Destroy potion */ + inc_stack_size(item, -1); +} + + +/* + * Drink from a fountain + */ +void do_cmd_drink_fountain(void) +{ + cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; + + bool_ ident; + + int tval, sval, pval = 0; + + int i; + + char ch; + + + /* Is the fountain empty? */ + if (c_ptr->special2 <= 0) + { + msg_print("The fountain is dried out."); + return; + } + + /* We quaff or we fill ? */ + if (!get_com("Do you want to [Q]uaff or [F]ill from the fountain? ", &ch)) + { + return; + } + + if ((ch == 'F') || (ch == 'f')) + { + do_cmd_fill_bottle(); + + return; + } + + else if ((ch == 'Q') || (ch == 'q')) + { + if (c_ptr->special <= SV_POTION_LAST) + { + tval = TV_POTION; + sval = c_ptr->special; + } + else + { + tval = TV_POTION2; + sval = c_ptr->special - SV_POTION_LAST; + } + + for (i = 0; i < max_k_idx; i++) + { + object_kind *k_ptr = &k_info[i]; + + if (k_ptr->tval != tval) continue; + if (k_ptr->sval != sval) continue; + + pval = k_ptr->pval; + + break; + } + + ident = quaff_potion(tval, sval, pval, 0); + + c_ptr->special2--; + + if (c_ptr->special2 <= 0) + { + cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN); + } + + if (ident) c_ptr->info |= CAVE_IDNT; + } +} + + +/* + * Fill an empty bottle + */ +void do_cmd_fill_bottle(void) +{ + cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; + + int tval, sval, item, amt = 1; + + object_type *q_ptr, *o_ptr, forge; + + cptr q, s; + + /* Is the fountain empty? */ + /* + * This check is redundant as it is done in do_cmd_drink_fountain() + * but I keep this because someone might want to call this directly. + * -- Kusunose + */ + if (c_ptr->special2 <= 0) + { + msg_print("The fountain has dried up."); + return; + } + + /* Determine the tval/sval of the potion */ + if (c_ptr->special <= SV_POTION_LAST) + { + tval = TV_POTION; + sval = c_ptr->special; + } + else + { + tval = TV_POTION2; + sval = c_ptr->special - SV_POTION_LAST; + } + + /* Restrict choices to bottles */ + item_tester_tval = TV_BOTTLE; + + /* Get an item */ + q = "Fill which bottle? "; + s = "You have no bottles to fill."; + if (!get_item(&item, q, s, (USE_INVEN))) return; + o_ptr = &p_ptr->inventory[item]; + + /* Find out how many the player wants */ + if (o_ptr->number > 1) + { + /* Get a quantity */ + amt = get_quantity(NULL, o_ptr->number); + + /* Allow user abort */ + if (amt <= 0) return; + } + + if (amt > c_ptr->special2) amt = c_ptr->special2; + + /* Destroy bottles */ + inc_stack_size(item, -amt); + + /* Create the potion */ + q_ptr = &forge; + object_prep(q_ptr, lookup_kind(tval, sval)); + q_ptr->number = amt; + + if (c_ptr->info & CAVE_IDNT) + { + object_aware(q_ptr); + object_known(q_ptr); + } + + inven_carry(q_ptr, TRUE); + + c_ptr->special2 -= amt; + + if (c_ptr->special2 <= 0) + { + cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN); + } + + return; +} + + +/* + * Curse the players armor + */ +bool_ curse_armor(void) +{ + object_type *o_ptr; + + char o_name[80]; + + + /* Curse the body armor */ + o_ptr = &p_ptr->inventory[INVEN_BODY]; + + /* Nothing to curse */ + if (!o_ptr->k_idx) return (FALSE); + + + /* Describe */ + object_desc(o_name, o_ptr, FALSE, 3); + + /* Attempt a saving throw for artifacts */ + if (((o_ptr->art_name) || artifact_p(o_ptr)) && (rand_int(100) < 50)) + { + /* Cool */ + msg_format("A terrible black aura tries to surround your armour, " + "but your %s resists the effects!", o_name); + } + + /* not artifact or failed save... */ + else + { + /* Oops */ + msg_format("A terrible black aura blasts your %s!", o_name); + + /* Blast the armor */ + o_ptr->name1 = 0; + o_ptr->name2 = EGO_BLASTED; + o_ptr->to_a = 0 - randint(5) - randint(5); + o_ptr->to_h = 0; + o_ptr->to_d = 0; + o_ptr->ac = 0; + o_ptr->dd = 0; + o_ptr->ds = 0; + o_ptr->art_flags1 = 0; + o_ptr->art_flags2 = 0; + o_ptr->art_flags3 = 0; + o_ptr->art_flags4 = 0; + + /* Curse it */ + o_ptr->ident |= (IDENT_CURSED); + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); + + /* Recalculate mana */ + p_ptr->update |= (PU_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + } + + return (TRUE); +} + + +/* + * Curse the players weapon + */ +bool_ curse_weapon(void) +{ + object_type *o_ptr; + + char o_name[80]; + + + /* Curse the weapon */ + o_ptr = &p_ptr->inventory[INVEN_WIELD]; + + /* Nothing to curse */ + if (!o_ptr->k_idx) return (FALSE); + + + /* Describe */ + object_desc(o_name, o_ptr, FALSE, 3); + + /* Attempt a saving throw */ + if ((artifact_p(o_ptr) || o_ptr->art_name) && (rand_int(100) < 50)) + { + /* Cool */ + msg_format("A terrible black aura tries to surround your weapon, " + "but your %s resists the effects!", o_name); + } + + /* not artifact or failed save... */ + else + { + /* Oops */ + msg_format("A terrible black aura blasts your %s!", o_name); + + /* Shatter the weapon */ + o_ptr->name1 = 0; + o_ptr->name2 = EGO_SHATTERED; + o_ptr->to_h = 0 - randint(5) - randint(5); + o_ptr->to_d = 0 - randint(5) - randint(5); + o_ptr->to_a = 0; + o_ptr->ac = 0; + o_ptr->dd = 0; + o_ptr->ds = 0; + o_ptr->art_flags1 = 0; + o_ptr->art_flags2 = 0; + o_ptr->art_flags3 = 0; + o_ptr->art_flags4 = 0; + + + /* Curse it */ + o_ptr->ident |= (IDENT_CURSED); + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); + + /* Recalculate mana */ + p_ptr->update |= (PU_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + } + + /* Notice */ + return (TRUE); +} + + +/* + * Hook to determine if an object is readable + */ +static bool_ item_tester_hook_readable(object_type *o_ptr) +{ + if ((o_ptr->tval == TV_SCROLL) || (o_ptr->tval == TV_PARCHMENT)) return (TRUE); + + /* Assume not */ + return (FALSE); +} + + +/* + * Read a scroll (from the pack or floor). + * + * Certain scrolls can be "aborted" without losing the scroll. These + * include scrolls with no effects but recharge or identify, which are + * cancelled before use. XXX Reading them still takes a turn, though. + */ +void do_cmd_read_scroll(void) +{ + int item, k, used_up, ident, lev; + + object_type *o_ptr; + + object_type *q_ptr, forge; + + cptr q, s; + + + /* Check some conditions */ + if (p_ptr->blind) + { + msg_print("You can't see anything."); + return; + } + + if (no_lite()) + { + msg_print("You have no light by which to read."); + return; + } + + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + + /* Restrict choices to scrolls */ + item_tester_hook = item_tester_hook_readable; + + /* Set up the extra finder */ + get_item_hook_find_obj_what = "Scroll full name? "; + get_item_extra_hook = get_item_hook_find_obj; + + /* Get an item */ + q = "Read which scroll? "; + s = "You have no scrolls to read."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Take a turn */ + energy_use = 100; + + /* Not identified yet */ + ident = FALSE; + + /* Object level */ + lev = k_info[o_ptr->k_idx].level; + + /* Assume the scroll will get used up */ + used_up = TRUE; + + /* Corruption */ + if (player_has_corruption(CORRUPT_BALROG_AURA) && magik(5)) + { + msg_print("Your demon aura burns the scroll before you read it!"); + used_up = TRUE; + ident = FALSE; + } + + /* Scrolls */ + else if (o_ptr->tval == TV_SCROLL) + { + /* Analyze the scroll */ + switch (o_ptr->sval) + { + case SV_SCROLL_MASS_RESURECTION: + { + int k; + + ident = TRUE; + msg_print("You feel the souls of the dead coming back " + "from the Halls of Mandos."); + + for (k = 0; k < max_r_idx; k++) + { + monster_race *r_ptr = &r_info[k]; + + if (r_ptr->flags1 & RF1_UNIQUE && + !(r_ptr->flags9 & RF9_SPECIAL_GENE)) + { + r_ptr->max_num = 1; + } + } + + break; + } + + case SV_SCROLL_DEINCARNATION: + { + if (!get_check("Do you really want to leave your body? " + "(beware, it'll be destroyed!) ")) + { + used_up = FALSE; + break; + } + + do_cmd_leave_body(FALSE); + + ident = TRUE; + used_up = TRUE; + + break; + } + + /* original didn't set used_up flag ??? -- pelpel */ + case SV_SCROLL_RESET_RECALL: + { + if (!reset_recall(TRUE)) + { + used_up = FALSE; + break; + } + + msg_format("Recall reset to %s at level %d.", + d_info[p_ptr->recall_dungeon].name + d_name, + max_dlv[p_ptr->recall_dungeon]); + + ident = TRUE; + used_up = TRUE; + + break; + } + + case SV_SCROLL_DIVINATION: + { + int i, count = 0; + char buf[120]; + + while (count < 1000) + { + count++; + i = rand_int(MAX_FATES); + if (!fates[i].fate) continue; + if (fates[i].know) continue; + + msg_print("A message appears on the scroll. It says:"); + msg_print(NULL); + + fate_desc(buf, i); + msg_format("%s", buf); + + msg_print(NULL); + msg_print("The scroll disappears in a puff of smoke!"); + + fates[i].know = TRUE; + ident = TRUE; + + break; + } + + break; + } + + case SV_SCROLL_DARKNESS: + { + if (!(p_ptr->resist_blind) && !(p_ptr->resist_dark)) + { + (void)set_blind(p_ptr->blind + 3 + randint(5)); + } + if (unlite_area(10, 3)) ident = TRUE; + + break; + } + + case SV_SCROLL_AGGRAVATE_MONSTER: + { + msg_print("There is a high-pitched humming noise."); + aggravate_monsters(1); + + ident = TRUE; + + break; + } + + case SV_SCROLL_CURSE_ARMOR: + { + if (curse_armor()) ident = TRUE; + + break; + } + + case SV_SCROLL_CURSE_WEAPON: + { + if (curse_weapon()) ident = TRUE; + + break; + } + + case SV_SCROLL_SUMMON_MONSTER: + { + for (k = 0; k < randint(3); k++) + { + if (summon_specific(p_ptr->py, p_ptr->px, dun_level, 0)) + { + ident = TRUE; + } + } + + break; + } + + case SV_SCROLL_SUMMON_MINE: + { + if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE)) + { + ident = TRUE; + } + + break; + } + + case SV_SCROLL_SUMMON_UNDEAD: + { + for (k = 0; k < randint(3); k++) + { + if (summon_specific(p_ptr->py, p_ptr->px, dun_level, SUMMON_UNDEAD)) + { + ident = TRUE; + } + } + + break; + } + + case SV_SCROLL_TRAP_CREATION: + { + if (trap_creation()) ident = TRUE; + + break; + } + + case SV_SCROLL_PHASE_DOOR: + { + teleport_player(10); + + ident = TRUE; + + break; + } + + case SV_SCROLL_TELEPORT: + { + teleport_player(100); + + ident = TRUE; + + break; + } + + case SV_SCROLL_TELEPORT_LEVEL: + { + (void)teleport_player_level(); + + ident = TRUE; + + break; + } + + case SV_SCROLL_WORD_OF_RECALL: + { + if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")) + { + used_up = FALSE; + } + else + { + recall_player(21, 15); + + ident = TRUE; + } + + break; + } + + case SV_SCROLL_IDENTIFY: + { + ident = TRUE; + + if (!ident_spell()) used_up = FALSE; + + break; + } + + case SV_SCROLL_STAR_IDENTIFY: + { + ident = TRUE; + + if (!identify_fully()) used_up = FALSE; + + break; + } + + case SV_SCROLL_REMOVE_CURSE: + { + if (remove_curse()) + { + msg_print("You feel as if someone is watching over you."); + ident = TRUE; + } + + break; + } + + case SV_SCROLL_STAR_REMOVE_CURSE: + { + remove_all_curse(); + + ident = TRUE; + + break; + } + + case SV_SCROLL_ENCHANT_ARMOR: + { + ident = TRUE; + + if (!enchant_spell(0, 0, 1, 0)) used_up = FALSE; + + break; + } + + case SV_SCROLL_ENCHANT_WEAPON_TO_HIT: + { + if (!enchant_spell(1, 0, 0, 0)) used_up = FALSE; + + ident = TRUE; + + break; + } + + case SV_SCROLL_ENCHANT_WEAPON_TO_DAM: + { + if (!enchant_spell(0, 1, 0, 0)) used_up = FALSE; + + ident = TRUE; + + break; + } + + case SV_SCROLL_ENCHANT_WEAPON_PVAL: + { + if (!enchant_spell(0, 0, 0, 1)) used_up = FALSE; + + ident = TRUE; + + break; + } + + case SV_SCROLL_STAR_ENCHANT_ARMOR: + { + if (!enchant_spell(0, 0, randint(3) + 2, 0)) used_up = FALSE; + + ident = TRUE; + + break; + } + + case SV_SCROLL_STAR_ENCHANT_WEAPON: + { + if (!enchant_spell(randint(3), randint(3), 0, 0)) used_up = FALSE; + + ident = TRUE; + + break; + } + + case SV_SCROLL_RECHARGING: + { + if (!recharge(60)) used_up = FALSE; + + ident = TRUE; + + break; + } + + case SV_SCROLL_LIGHT: + { + if (lite_area(damroll(2, 8), 2)) ident = TRUE; + + break; + } + + case SV_SCROLL_MAPPING: + { + map_area(); + + ident = TRUE; + + break; + } + + case SV_SCROLL_DETECT_GOLD: + { + if (detect_treasure(DEFAULT_RADIUS)) ident = TRUE; + if (detect_objects_gold(DEFAULT_RADIUS)) ident = TRUE; + + break; + } + + case SV_SCROLL_DETECT_ITEM: + { + if (detect_objects_normal(DEFAULT_RADIUS)) ident = TRUE; + + break; + } + + case SV_SCROLL_DETECT_TRAP: + { + if (detect_traps(DEFAULT_RADIUS)) ident = TRUE; + + break; + } + + case SV_SCROLL_DETECT_DOOR: + { + if (detect_doors(DEFAULT_RADIUS)) ident = TRUE; + if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE; + + break; + } + + case SV_SCROLL_DETECT_INVIS: + { + if (detect_monsters_invis(DEFAULT_RADIUS)) ident = TRUE; + + break; + } + + case SV_SCROLL_SATISFY_HUNGER: + { + if (set_food(PY_FOOD_MAX - 1)) ident = TRUE; + + break; + } + + case SV_SCROLL_BLESSING: + { + if (set_blessed(p_ptr->blessed + randint(12) + 6)) ident = TRUE; + + break; + } + + case SV_SCROLL_HOLY_CHANT: + { + if (set_blessed(p_ptr->blessed + randint(24) + 12)) ident = TRUE; + + break; + } + + case SV_SCROLL_HOLY_PRAYER: + { + if (set_blessed(p_ptr->blessed + randint(48) + 24)) ident = TRUE; + + break; + } + + case SV_SCROLL_MONSTER_CONFUSION: + { + if (p_ptr->confusing == 0) + { + msg_print("Your hands begin to glow."); + p_ptr->confusing = TRUE; + ident = TRUE; + } + + break; + } + + case SV_SCROLL_PROTECTION_FROM_EVIL: + { + k = 3 * p_ptr->lev; + if (set_protevil(p_ptr->protevil + randint(25) + k)) ident = TRUE; + + break; + } + + case SV_SCROLL_RUNE_OF_PROTECTION: + { + warding_glyph(); + + ident = TRUE; + + break; + } + + case SV_SCROLL_TRAP_DOOR_DESTRUCTION: + { + if (destroy_doors_touch()) ident = TRUE; + + break; + } + + case SV_SCROLL_STAR_DESTRUCTION: + { + /* Prevent destruction of quest levels and town */ + if (!is_quest(dun_level) && dun_level) + { + destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE); + } + else + { + msg_print("The dungeon trembles..."); + } + + ident = TRUE; + + break; + } + + case SV_SCROLL_DISPEL_UNDEAD: + { + if (dispel_undead(60)) ident = TRUE; + + break; + } + + case SV_SCROLL_GENOCIDE: + { + (void)genocide(TRUE); + + ident = TRUE; + + break; + } + + case SV_SCROLL_MASS_GENOCIDE: + { + (void)mass_genocide(TRUE); + + ident = TRUE; + + break; + } + + case SV_SCROLL_ACQUIREMENT: + { + acquirement(p_ptr->py, p_ptr->px, 1, TRUE, FALSE); + + ident = TRUE; + + break; + } + + case SV_SCROLL_STAR_ACQUIREMENT: + { + acquirement(p_ptr->py, p_ptr->px, randint(2) + 1, TRUE, FALSE); + + ident = TRUE; + + break; + } + + /* ZAngband scrolls */ + case SV_SCROLL_FIRE: + { + fire_ball(GF_FIRE, 0, 150, 4); + + /* + * Note: "Double" damage since it is centered on + * the player ... + */ + if (!p_ptr->oppose_fire && !p_ptr->resist_fire && + !p_ptr->immune_fire) + { + take_hit(50 + randint(50) + (p_ptr->sensible_fire) ? 20 : 0, + "a Scroll of Fire"); + } + + ident = TRUE; + + break; + } + + + case SV_SCROLL_ICE: + { + fire_ball(GF_ICE, 0, 175, 4); + + if (!p_ptr->oppose_cold && !p_ptr->resist_cold && + !p_ptr->immune_cold) + { + take_hit(100 + randint(100), "a Scroll of Ice"); + } + + ident = TRUE; + + break; + } + + case SV_SCROLL_CHAOS: + { + fire_ball(GF_CHAOS, 0, 222, 4); + + if (!p_ptr->resist_chaos) + { + take_hit(111 + randint(111), "a Scroll of Chaos"); + } + + ident = TRUE; + + break; + } + + case SV_SCROLL_RUMOR: + { + char rumour[80]; + + msg_print("There is message on the scroll. It says:"); + msg_print(NULL); + + /* Pick random text */ + switch (randint(20)) + { + case 1: + { + get_rnd_line("chainswd.txt", rumour); + + break; + } + + case 2: + { + get_rnd_line("error.txt", rumour); + + break; + } + + case 3: + case 4: + case 5: + { + get_rnd_line("death.txt", rumour); + + break; + } + + default: + { + get_rnd_line("rumors.txt", rumour); + + break; + } + } + + msg_format("%s", rumour); + msg_print(NULL); + + msg_print("The scroll disappears in a puff of smoke!"); + + ident = TRUE; + + break; + } + + case SV_SCROLL_ARTIFACT: + { + ident = TRUE; + + if (!artifact_scroll()) used_up = FALSE; + + break; + } + + case SV_SCROLL_STERILIZATION: + { + msg_print("A neutralising wave radiates from you!"); + set_no_breeders(randint(100) + 100); + + break; + } + + default: + { + break; + } + } + } + + /* Other readable items */ + else + { + /* Maps */ + if (o_ptr->sval >= 200) + { + int i, n; + char buf[80], fil[20]; + + strnfmt(fil, 20, "book-%d.txt", o_ptr->sval); + + n = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, -1)); + + /* Parse all the fields */ + for (i = 0; i < n; i += 4) + { + /* Grab the fields */ + int x = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 0)); + int y = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 1)); + int w = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 2)); + int h = atoi(get_line(fil, ANGBAND_DIR_FILE, buf, i + 3)); + + reveal_wilderness_around_player(y, x, h, w); + } + } + + /* Normal parchements */ + else + { + /* Save screen */ + screen_save(); + + /* Get the filename */ + q = format("book-%d.txt", o_ptr->sval); + + /* Peruse the help file */ + (void)show_file(q, NULL, 0, 0); + + /* Load screen */ + screen_load(); + + if (o_ptr->sval >= 100) + { + inscription_info[o_ptr->sval - 100].know = TRUE; + } + + used_up = FALSE; + } + } + + + /* Combine / Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* The item was tried */ + object_tried(o_ptr); + + /* An identification was made */ + if (ident && !object_aware_p(o_ptr)) + { + object_aware(o_ptr); + gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev); + } + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + + /* Hack -- allow certain scrolls to be "preserved" */ + if (!used_up) return; + + sound(SOUND_SCROLL); + + /* Destroy scroll */ + inc_stack_size(item, -1); + + if (get_skill(SKILL_ALCHEMY)) + { + if (item >= 0) + { + q_ptr = &forge; + object_prep(q_ptr, lookup_kind(TV_SCROLL, SV_SCROLL_NOTHING)); + object_aware(q_ptr); + object_known(q_ptr); + + q_ptr->ident |= IDENT_STOREB; + + (void)inven_carry(q_ptr, FALSE); + } + } +} + + + +/* Set the 'stick mode' on */ +void set_stick_mode(object_type *o_ptr) +{ + s32b bonus = o_ptr->pval3 & 0xFFFF; + s32b max = o_ptr->pval3 >> 16; + + get_level_use_stick = bonus; + get_level_max_stick = max; +} + +/* Remove 'stick mode' */ +void unset_stick_mode() +{ + get_level_use_stick = -1; + get_level_max_stick = -1; +} + + +/* + * Activate a device + */ +static void activate_stick(s16b s, bool_ *obvious, bool_ *use_charge) +{ + spell_type *spell = spell_at(s); + casting_result ret; + + assert(obvious != NULL); + assert(use_charge != NULL); + + ret = spell_type_produce_effect(spell, -1); + + switch (ret) + { + case NO_CAST: + *use_charge = FALSE; + *obvious = FALSE; + break; + case CAST_HIDDEN: + *use_charge = TRUE; + *obvious = FALSE; + break; + case CAST_OBVIOUS: + *use_charge = TRUE; + *obvious = TRUE; + break; + default: + assert(FALSE); + } +} + + +/* + * Use a staff. -RAK- + * + * One charge of one staff disappears. + * + * Hack -- staffs of identify can be "cancelled". + */ +void do_cmd_use_staff(void) +{ + int item, ident, chance; + + bool_ obvious, use_charge; + + object_type *o_ptr; + + u32b f1, f2, f3, f4, f5, esp; + + cptr q, s; + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* Restrict choices to wands */ + item_tester_tval = TV_STAFF; + + /* Set up the extra finder */ + get_item_hook_find_obj_what = "Staff full name? "; + get_item_extra_hook = get_item_hook_find_obj; + + /* Get an item */ + q = "Use which staff? "; + s = "You have no staff to use."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Mega-Hack -- refuse to use a pile from the ground */ + if ((item < 0) && (o_ptr->number > 1)) + { + msg_print("You must first pick up the staffs."); + return; + } + + /* Enter device mode */ + set_stick_mode(o_ptr); + + /* Take a turn */ + energy_use = 100; + + /* Not identified yet */ + ident = FALSE; + + /* get the chance */ + chance = spell_chance(o_ptr->pval2); + + /* Extract object flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Is it simple to use ? */ + if (f4 & TR4_EASY_USE) + { + chance /= 3; + } + + /* Give everyone a (slight) chance */ + if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0)) + { + chance = USE_DEVICE; + } + + /* Roll for usage */ + if (magik(chance)) + { + if (flush_failure) flush(); + msg_print("You failed to use the staff properly."); + sound(SOUND_FAIL); + + /* Leave device mode */ + unset_stick_mode(); + return; + } + + /* Notice empty staffs */ + if (o_ptr->pval <= 0) + { + if (flush_failure) flush(); + msg_print("The staff has no charges left."); + o_ptr->ident |= (IDENT_EMPTY); + + /* Leave device mode */ + unset_stick_mode(); + return; + } + + + /* Sound */ + sound(SOUND_ZAP); + + + /* Analyze the staff */ + activate_stick(o_ptr->pval2, &obvious, &use_charge); + + /* Combine / Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* Tried the item */ + object_tried(o_ptr); + + /* An identification was made */ + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + + /* Hack -- some uses are "free" */ + if (!use_charge) + { + /* Leave device mode */ + unset_stick_mode(); + + return; + } + + /* An identification was made */ + if (obvious) + { + object_aware(o_ptr); + } + + /* Use a single charge */ + o_ptr->pval--; + + /* XXX Hack -- unstack if necessary */ + if ((item >= 0) && (o_ptr->number > 1)) + { + object_type forge; + object_type *q_ptr; + + /* Get local object */ + q_ptr = &forge; + + /* Obtain a local object */ + object_copy(q_ptr, o_ptr); + + /* Modify quantity */ + q_ptr->number = 1; + + /* Restore the charges */ + o_ptr->pval++; + + /* Unstack the used item */ + o_ptr->number--; + item = inven_carry(q_ptr, FALSE); + + /* Message */ + msg_print("You unstack your staff."); + } + + /* Describe charges in the pack */ + if (item >= 0) + { + inven_item_charges(item); + } + + /* Describe charges on the floor */ + else + { + floor_item_charges(0 - item); + } + + /* Leave device mode */ + unset_stick_mode(); +} + + +/* + * Aim a wand (from the pack or floor). + * + * Use a single charge from a single item. + * Handle "unstacking" in a logical manner. + * + * For simplicity, you cannot use a stack of items from the + * ground. This would require too much nasty code. + * + * There are no wands which can "destroy" themselves, in the p_ptr->inventory + * or on the ground, so we can ignore this possibility. Note that this + * required giving "wand of wonder" the ability to ignore destruction + * by electric balls. + * + * All wands can be "cancelled" at the "Direction?" prompt for free. + * + * Note that the basic "bolt" wands do slightly less damage than the + * basic "bolt" rods, but the basic "ball" wands do the same damage + * as the basic "ball" rods. + */ +void do_cmd_aim_wand(void) +{ + bool_ obvious, use_charge; + + int item, ident, chance; + + object_type *o_ptr; + + cptr q, s; + + u32b f1, f2, f3, f4, f5, esp; + + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* Restrict choices to wands */ + item_tester_tval = TV_WAND; + + /* Set up the extra finder */ + get_item_hook_find_obj_what = "Wand full name? "; + get_item_extra_hook = get_item_hook_find_obj; + + /* Get an item */ + q = "Aim which wand? "; + s = "You have no wand to aim."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; + + /* Get the item */ + o_ptr = get_object(item); + + + /* Mega-Hack -- refuse to aim a pile from the ground */ + if ((item < 0) && (o_ptr->number > 1)) + { + msg_print("You must first pick up the wands."); + return; + } + + /* Take a turn */ + energy_use = 100; + + /* Not identified yet */ + ident = FALSE; + + /* Enter device mode */ + set_stick_mode(o_ptr); + + /* get the chance */ + chance = spell_chance(o_ptr->pval2); + + /* Extract object flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Is it simple to use ? */ + if (f4 & TR4_EASY_USE) + { + chance /= 3; + } + + /* Roll for usage */ + if (magik(chance)) + { + if (flush_failure) flush(); + msg_print("You failed to use the wand properly."); + sound(SOUND_FAIL); + + /* Leave device mode */ + unset_stick_mode(); + return; + } + + /* The wand is already empty! */ + if (o_ptr->pval <= 0) + { + if (flush_failure) flush(); + msg_print("The wand has no charges left."); + o_ptr->ident |= (IDENT_EMPTY); + + /* Leave device mode */ + unset_stick_mode(); + return; + } + + + /* Sound */ + sound(SOUND_ZAP); + + + /* Analyze the wand */ + activate_stick(o_ptr->pval2, &obvious, &use_charge); + + /* Combine / Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* Mark it as tried */ + object_tried(o_ptr); + + /* Hack -- some uses are "free" */ + if (!use_charge) + { + /* Leave device mode */ + unset_stick_mode(); + + return; + } + + /* An identification was made */ + if (obvious) + { + object_aware(o_ptr); + } + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + + /* Use a single charge */ + o_ptr->pval--; + + /* Describe the charges in the pack */ + if (item >= 0) + { + inven_item_charges(item); + } + + /* Describe the charges on the floor */ + else + { + floor_item_charges(0 - item); + } + + /* Leave device mode */ + unset_stick_mode(); +} + + + + + + +/* + * Activate (zap) a Rod + * + * Unstack fully charged rods as needed. + * + * Hack -- rods of perception/genocide can be "cancelled" + * All rods can be cancelled at the "Direction?" prompt + */ + + +/* + * Hook to determine if an object is zapable + */ +static bool_ item_tester_hook_zapable(object_type *o_ptr) +{ + if ((o_ptr->tval == TV_ROD) || (o_ptr->tval == TV_ROD_MAIN)) return (TRUE); + + /* Assume not */ + return (FALSE); +} + + +/* + * Hook to determine if an object is attachable + */ +static bool_ item_tester_hook_attachable(object_type *o_ptr) +{ + if ((o_ptr->tval == TV_ROD_MAIN) && + (o_ptr->pval == SV_ROD_NOTHING)) return (TRUE); + + /* Assume not */ + return (FALSE); +} + + +/* + * Combine a rod and a rod tip + */ +void zap_combine_rod_tip(object_type *q_ptr, int tip_item) +{ + int item; + + object_type *o_ptr; + + cptr q, s; + + u32b f1, f2, f3, f4, f5, esp; + s32b cost; + + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* Restrict choices to rods */ + item_tester_hook = item_tester_hook_attachable; + + /* Get an item */ + q = "Attach the rod tip with which rod? "; + s = "You have no rod to attach to."; + if (!get_item(&item, q, s, (USE_INVEN))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Examine the rod */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Calculate rod tip's mana cost */ + cost = q_ptr->pval; + + if (f4 & TR4_CHEAPNESS) + { + cost /= 2; + } + + /* + * The rod must have at least the same mana capacity as the + * rod tip spell needs + */ + if (o_ptr->pval2 < cost) + { + msg_print("This rod doesn't have enough mana for the rod tip."); + return; + } + + /* Attach the tip to the rod */ + o_ptr->pval = q_ptr->sval; + + /* Destroy rod tip */ + inc_stack_size(tip_item, -1); +} + + +/* + * Zap a rod, or attack a rod tip to a rod + */ +void do_cmd_zap_rod(void) +{ + int item, ident, chance, dir, lev; + + int cost; + + bool_ require_dir; + + object_type *o_ptr; + + object_kind *tip_ptr; + + u32b f1, f2, f3, f4, f5, esp; + + cptr q, s; + + /* Hack -- let perception get aborted */ + bool_ use_charge = TRUE; + + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + + /* Restrict choices to rods */ + item_tester_hook = item_tester_hook_zapable; + + /* Set up the extra finder */ + get_item_hook_find_obj_what = "Rod full name? "; + get_item_extra_hook = get_item_hook_find_obj; + + /* Get an item */ + q = "Zap which rod? "; + s = "You have no rod to zap."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR | USE_EXTRA))) return; + + /* Get the item */ + o_ptr = get_object(item); + + + /* "Zapping" a Rod Tip on rod of nothing will attach it */ + if (o_ptr->tval == TV_ROD) + { + if (item >= 0) + { + zap_combine_rod_tip(o_ptr, item); + return; + } + else + { + msg_print("You can't zap a rod tip that's on the floor."); + return; + } + } + + + /* Non-directed rods */ + if (o_ptr->pval < SV_ROD_MIN_DIRECTION) + { + require_dir = FALSE; + } + + /* Some rods always require direction */ + else + { + switch (o_ptr->pval) + { + case SV_ROD_DETECT_TRAP: + case SV_ROD_HAVOC: + case SV_ROD_HOME: + { + require_dir = FALSE; + break; + } + + default: + { + require_dir = TRUE; + break; + } + } + } + + /* Get a direction (unless KNOWN not to need it) */ + if (!object_aware_p(o_ptr) || require_dir) + { + /* Get a direction, allow cancel */ + if (!get_aim_dir(&dir)) return; + } + + /* Take a turn */ + energy_use = 100; + + /* Examine the rod */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if (f4 & TR4_FAST_CAST) energy_use /= 2; + + /* Not identified yet */ + ident = FALSE; + + /* Extract the item level */ + tip_ptr = &k_info[lookup_kind(TV_ROD, o_ptr->pval)]; + lev = k_info[lookup_kind(TV_ROD, o_ptr->pval)].level; + + /* Base chance of success */ + chance = p_ptr->skill_dev; + + /* Confusion hurts skill */ + if (p_ptr->confused) chance = chance / 2; + + /* High level objects are harder */ + chance = chance - ((lev > 50) ? 50 : lev); + + if (chance <= 0) + { + chance = 1; + } + + /* Is it simple to use ? */ + if (f4 & TR4_EASY_USE) + { + chance *= 10; + } + + /* Give everyone a (slight) chance */ + if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0)) + { + chance = USE_DEVICE; + } + + /* Roll for usage */ + if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE)) + { + /* Flush input if necessary */ + if (flush_failure) flush(); + + /* Message */ + msg_print("You failed to use the rod properly."); + + sound(SOUND_FAIL); + + return; + } + + /* Extract mana cost */ + cost = tip_ptr->pval; + + /* "Cheapness" ego halven the cost */ + if (f4 & TR4_CHEAPNESS) cost = cost / 2; + + /* A single rod is still charging */ + if (o_ptr->timeout < cost) + { + /* Flush input if necessary */ + if (flush_failure) flush(); + + /* Message */ + msg_print("The rod does not have enough mana yet."); + + return; + } + + /* Increase the timeout by the rod kind's pval. */ + o_ptr->timeout -= cost; + + /* Sound */ + sound(SOUND_ZAP); + + /* Analyze the rod */ + switch (o_ptr->pval) + { + case SV_ROD_HOME: + { + ident = TRUE; + + do_cmd_home_trump(); + + break; + } + + case SV_ROD_DETECT_TRAP: + { + if (detect_traps(DEFAULT_RADIUS)) ident = TRUE; + + break; + } + + case SV_ROD_DETECT_DOOR: + { + if (detect_doors(DEFAULT_RADIUS)) ident = TRUE; + if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE; + + break; + } + + case SV_ROD_IDENTIFY: + { + ident = TRUE; + + if (!ident_spell()) use_charge = FALSE; + + break; + } + + case SV_ROD_RECALL: + { + if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")) + { + use_charge = FALSE; + } + else + { + recall_player(21, 15); + + ident = TRUE; + } + + break; + } + + case SV_ROD_ILLUMINATION: + { + if (lite_area(damroll(2, 8), 2)) ident = TRUE; + + break; + } + + case SV_ROD_MAPPING: + { + map_area(); + + ident = TRUE; + + break; + } + + case SV_ROD_DETECTION: + { + detect_all(DEFAULT_RADIUS); + + ident = TRUE; + + break; + } + + case SV_ROD_PROBING: + { + probing(); + + ident = TRUE; + + break; + } + + case SV_ROD_CURING: + { + if (set_blind(0)) ident = TRUE; + if (set_poisoned(0)) ident = TRUE; + if (set_confused(0)) ident = TRUE; + if (set_stun(0)) ident = TRUE; + if (set_cut(0)) ident = TRUE; + if (set_image(0)) ident = TRUE; + + break; + } + + case SV_ROD_HEALING: + { + if (hp_player(500)) ident = TRUE; + if (set_stun(0)) ident = TRUE; + if (set_cut(0)) ident = TRUE; + + break; + } + + case SV_ROD_RESTORATION: + { + if (restore_level()) ident = TRUE; + if (do_res_stat(A_STR, TRUE)) ident = TRUE; + if (do_res_stat(A_INT, TRUE)) ident = TRUE; + if (do_res_stat(A_WIS, TRUE)) ident = TRUE; + if (do_res_stat(A_DEX, TRUE)) ident = TRUE; + if (do_res_stat(A_CON, TRUE)) ident = TRUE; + if (do_res_stat(A_CHR, TRUE)) ident = TRUE; + + break; + } + + case SV_ROD_SPEED: + { + if (!p_ptr->fast) + { + if (set_fast(randint(30) + 15, 10)) ident = TRUE; + } + else + { + (void)set_fast(p_ptr->fast + 5, 10); + } + + break; + } + + case SV_ROD_TELEPORT_AWAY: + { + if (teleport_monster(dir)) ident = TRUE; + + break; + } + + case SV_ROD_DISARMING: + { + if (disarm_trap(dir)) ident = TRUE; + + break; + } + + case SV_ROD_LITE: + { + msg_print("A line of blue shimmering light appears."); + lite_line(dir); + + ident = TRUE; + + break; + } + + case SV_ROD_SLEEP_MONSTER: + { + if (sleep_monster(dir)) ident = TRUE; + + break; + } + + case SV_ROD_SLOW_MONSTER: + { + if (slow_monster(dir)) ident = TRUE; + + break; + } + + case SV_ROD_DRAIN_LIFE: + { + if (drain_life(dir, 75)) ident = TRUE; + + break; + } + + case SV_ROD_POLYMORPH: + { + if (poly_monster(dir)) ident = TRUE; + + break; + } + + case SV_ROD_ACID_BOLT: + { + fire_bolt_or_beam(10, GF_ACID, dir, damroll(6, 8)); + + ident = TRUE; + + break; + } + + case SV_ROD_ELEC_BOLT: + { + fire_bolt_or_beam(10, GF_ELEC, dir, damroll(3, 8)); + + ident = TRUE; + + break; + } + + case SV_ROD_FIRE_BOLT: + { + fire_bolt_or_beam(10, GF_FIRE, dir, damroll(8, 8)); + + ident = TRUE; + + break; + } + + case SV_ROD_COLD_BOLT: + { + fire_bolt_or_beam(10, GF_COLD, dir, damroll(5, 8)); + + ident = TRUE; + + break; + } + + case SV_ROD_ACID_BALL: + { + fire_ball(GF_ACID, dir, 60, 2); + + ident = TRUE; + + break; + } + + case SV_ROD_ELEC_BALL: + { + fire_ball(GF_ELEC, dir, 32, 2); + + ident = TRUE; + + break; + } + + case SV_ROD_FIRE_BALL: + { + fire_ball(GF_FIRE, dir, 72, 2); + + ident = TRUE; + + break; + } + + case SV_ROD_COLD_BALL: + { + fire_ball(GF_COLD, dir, 48, 2); + + ident = TRUE; + + break; + } + + case SV_ROD_HAVOC: + { + call_chaos(); + + ident = TRUE; + + break; + } + + default: + { + process_hooks(HOOK_ZAP, "(d,d)", o_ptr->tval, o_ptr->sval); + + break; + } + } + + + /* Combine / Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* Tried the object */ + object_tried(o_ptr); + + /* Successfully determined the object function */ + if (ident && !object_aware_p(o_ptr)) + { + object_aware(o_ptr); + gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev); + } + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + /* Hack -- deal with cancelled zap */ + if (!use_charge) + { + o_ptr->timeout += cost; + + return; + } +} + + + + +/* + * Hook to determine if an object is activable + */ +static bool_ item_tester_hook_activate(object_type *o_ptr) +{ + u32b f1, f2, f3, f4, f5, esp; + + + /* Not known */ + if (!object_known_p(o_ptr)) return (FALSE); + + /* Extract the flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Check activation flag */ + if (f3 & (TR3_ACTIVATE)) return (TRUE); + + /* Assume not */ + return (FALSE); +} + + + +/* + * Hack -- activate the ring of power + */ +int ring_of_power() +{ + char ch = 0, p = 0; + + int plev = p_ptr->lev; + + int timeout = 0; + + + /* Select power to use */ + while (TRUE) + { + if (!get_com("[S]ummon a wraith, [R]ule the world or " + "[C]ast a powerful attack? ", &ch)) + { + return (0); + } + + if (ch == 'S' || ch == 's') + { + p = 1; + break; + } + if (ch == 'R' || ch == 'r') + { + p = 2; + break; + } + if (ch == 'C' || ch == 'c') + { + p = 3; + break; + } + } + + /* Summon a Wraith */ + if (p == 1) + { + /* Rewrite this -- pelpel */ + if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), + (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD), + (((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE))) + { + msg_print("Cold winds begin to blow around you, " + "carrying with them the stench of decay..."); + msg_print("Ancient, long-dead forms arise from the ground " + "to serve you!"); + } + timeout = 200 + rand_int(200); + } + + /* Rule the World -- only if we can really do so */ + else if (p == 2) + { + msg_print("The power of the ring destroys the world!"); + msg_print("The world changes!"); + + autosave_checkpoint(); + + /* Leaving */ + p_ptr->leaving = TRUE; + timeout = 250 + rand_int(250); + } + + /* Cast a powerful spell */ + else if (p == 3) + { + int dir; + + if (!get_aim_dir(&dir)) return (0); + + if (rand_int(3) == 0) + { + msg_print("You call the fire of Mount Doom!"); + fire_ball(GF_METEOR, dir, 600, 4); + } + else + { + msg_print("Your ring tries to take possession of your enemy's mind!"); + fire_bolt(GF_CHARM, dir, 600); + } + timeout = 300 + rand_int(300); + } + + return (timeout); +} + + + + +/* + * Enchant some bolts + */ +bool_ brand_bolts(void) +{ + int i; + + + /* Use the first acceptable bolts */ + for (i = 0; i < INVEN_PACK; i++) + { + object_type *o_ptr = &p_ptr->inventory[i]; + + /* Skip non-bolts */ + if (o_ptr->tval != TV_BOLT) continue; + + /* Skip artifacts and ego-items */ + if (o_ptr->art_name || artifact_p(o_ptr) || ego_item_p(o_ptr)) continue; + + /* Skip cursed/broken items */ + if (cursed_p(o_ptr)) continue; + + /* Randomize */ + if (rand_int(100) < 75) continue; + + /* Message */ + msg_print("Your bolts are covered in a fiery aura!"); + + /* Ego-item */ + o_ptr->name2 = EGO_FLAME; + + /* Apply the ego */ + apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE); + + /* Enchant */ + enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM); + + /* Notice */ + return (TRUE); + } + + /* Flush */ + if (flush_failure) flush(); + + /* Fail */ + msg_print("The fiery enchantment failed."); + + /* Notice */ + return (TRUE); +} + + +/* + * Objects in the p_ptr->inventory can now be activated, and + * SOME of those may be able to stack (ego wands or something) + * in any case, we can't know that it's impossible. *BUT* we'll + * ignore it for now, and the timeout will be set on the entire stack + * of objects. Reduces their utility, but oh well. + * + * Note that it always takes a turn to activate an object, even if + * the user hits "escape" at the "direction" prompt. + */ +void do_cmd_activate(void) +{ + int item, lev, chance; + + char ch, spell_choice; + + object_type *o_ptr; + + u32b f1, f2, f3, f4, f5, esp; + + cptr q, s; + + + /* Prepare the hook */ + item_tester_hook = item_tester_hook_activate; + + /* Get an item */ + command_wrk = USE_EQUIP; + q = "Activate which item? "; + s = "You have nothing to activate."; + if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Extract object flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Wearable items have to be worn */ + if (!(f5 & TR5_ACTIVATE_NO_WIELD)) + { + if (item < INVEN_WIELD) + { + msg_print("You must wear it to activate it."); + return; + } + } + + /* Take a turn */ + energy_use = 100; + + /* Extract the item level */ + lev = k_info[o_ptr->k_idx].level; + + /* Hack -- Use artifact level instead */ + if (artifact_p(o_ptr)) + { + if (o_ptr->tval == TV_RANDART) + { + lev = random_artifacts[o_ptr->sval].level; + } + else + { + lev = a_info[o_ptr->name1].level; + } + } + + /* Base chance of success */ + chance = p_ptr->skill_dev; + + /* Confusion hurts skill */ + if (p_ptr->confused) chance = chance / 2; + + /* Hight level objects are harder */ + chance = chance - ((lev > 50) ? 50 : lev); + + if (chance <= 0) + { + chance = 1; + } + + /* Is it simple to use ? */ + if (f4 & TR4_EASY_USE) + { + chance *= 10; + } + + /* Give everyone a (slight) chance */ + if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0)) + { + chance = USE_DEVICE; + } + + /* Roll for usage */ + if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE)) + { + if (flush_failure) flush(); + msg_print("You failed to activate it properly."); + sound(SOUND_FAIL); + return; + } + + /* Check the recharge */ + if (o_ptr->timeout) + { + /* Mage Staff of Spells -- Have another timeout in xtra2 */ + if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && o_ptr->xtra2) + { + msg_print("It whines, glows and fades..."); + return; + } + + /* Monster eggs */ + else if (o_ptr->tval == TV_EGG) + { + msg_print("You resume the development of the egg."); + o_ptr->timeout = 0; + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Success */ + return; + } + + /* Normal activatable items */ + else + { + msg_print("It whines, glows and fades..."); + return; + } + } + + + /* Activate the item */ + msg_print("You activate it..."); + + /* Sound */ + sound(SOUND_ZAP); + + /* Lua hook ? -- go first to allow lua to override */ + if (process_hooks(HOOK_ACTIVATE, "(d)", item)) + { + return; + } + + /* New mostly unified activation code + This has to be early to allow artifacts to override normal items -- neil */ + + if ( activation_aux(o_ptr, TRUE, item) == NULL ) + { + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Success */ + return; + } + + /* Mage Staff of Spells */ + if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL)) + { + while (TRUE) + { + if (!get_com("Use Spell [1] or [2]?", &ch)) + { + return; + } + + if (ch == '1') + { + spell_choice = 1; + break; + } + + if (ch == '2') + { + spell_choice = 2; + break; + } + } + + if (spell_choice == 1) + { + /* Still need to check timeouts because there is another counter */ + if (o_ptr->timeout) + { + msg_print("The first spell is still charging!"); + return; + } + + /* Cast spell 1 */ + activate_spell(o_ptr, spell_choice); + } + else if (spell_choice == 2) + { + /* Still need to check timeouts because there is another counter */ + if (o_ptr->xtra2) + { + msg_print("The second spell is still charging!"); + return; + } + + /* Cast spell 2 */ + activate_spell(o_ptr, spell_choice); + } + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Success */ + return; + } + + /* Monster eggs */ + if (o_ptr->tval == TV_EGG) + { + msg_print("You stop the development of the egg."); + o_ptr->timeout = -1; + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Success */ + return; + } + + /* Musical instruments */ + if (o_ptr->tval == TV_INSTRUMENT) + { + /* Horns */ + if (o_ptr->sval == SV_HORN) + { + msg_format("Your instrument emits a loud sound!"); + + aggravate_monsters(1); + + o_ptr->timeout = 100; + } + + /* Success */ + return; + } + + /* Mistake */ + msg_print("Oops. That object cannot be activated."); +} + +const char *activation_aux(object_type * o_ptr, bool_ doit, int item) +{ + static char buf[256]; + int plev = get_skill(SKILL_DEVICE); + + int i = 0, ii = 0, ij = 0, k, dir, dummy = 0; + int chance; + bool_ is_junkart = (o_ptr->tval == TV_RANDART); + + int spell = 0; + + /* Junkarts */ + if (is_junkart) + spell = activation_info[o_ptr->pval2].spell; + + /* True Actifacts */ + if (!spell && o_ptr->name1) + spell = a_info[o_ptr->name1].activate; + + /* Random and Alchemist Artifacts */ + if (!spell && o_ptr->art_name) + spell = o_ptr->xtra2; + + /* Ego Items */ + if (!spell && o_ptr->name2) + spell = e_info[o_ptr->name2].activate; + + /* Dual egos with the second ego having the activation */ + if (!spell && o_ptr->name2b) + spell = e_info[o_ptr->name2b].activate; + + /* Intrinsic to item type (rings of Ice, etc) */ + if (!spell) + spell = k_info[o_ptr->k_idx].activate; + + /* Complain about mis-configured .txt files? */ + if (!spell) + return "Unknown!"; + + /* Negative means a unified spell index */ + if (spell < 0) + { + struct spell_type *spell_ptr = spell_at(-spell); + if (doit) + { + spell_type_produce_effect(spell_ptr, item); + o_ptr->timeout = spell_type_activation_roll_timeout(spell_ptr); + } + else + { + spell_type_activation_description(spell_ptr, buf); + return buf; + } + } + else + { + /* Activate for attack */ + switch (spell) + { + case ACT_GILGALAD: + { + if (!doit) return "starlight (75) every 75+d75 turns"; + for (k = 1; k < 10; k++) + { + if (k - 5) fire_beam(GF_LITE, k, 75); + } + + o_ptr->timeout = rand_int(75) + 75; + + break; + } + + case ACT_CELEBRIMBOR: + { + if (!doit) return "temporary ESP (dur 20+d20) every 20+d50 turns"; + set_tim_esp(p_ptr->tim_esp + randint(20) + 20); + + o_ptr->timeout = rand_int(50) + 20; + + break; + } + + case ACT_SKULLCLEAVER: + { + if (!doit) return "destruction every 200+d200 turns"; + destroy_area(p_ptr->py, p_ptr->px, 15, TRUE, FALSE); + + o_ptr->timeout = rand_int(200) + 200; + + break; + } + + case ACT_HARADRIM: + { + if (!doit) return "berserk strength every 50+d50 turns"; + set_afraid(0); + set_shero(p_ptr->shero + randint(25) + 25); + hp_player(30); + + o_ptr->timeout = rand_int(50) + 50; + + break; + } + + case ACT_FUNDIN: + { + if (!doit) return "dispel evil (x4) every 100+d100 turns"; + dispel_evil(p_ptr->lev * 4); + + o_ptr->timeout = rand_int(100) + 100; + + break; + } + + case ACT_EOL: + { + if (!doit) return "mana bolt (9d8) 7+d7 turns"; + if (!get_aim_dir(&dir)) break; + fire_bolt(GF_MANA, dir, damroll(9, 8)); + + o_ptr->timeout = rand_int(7) + 7; + + break; + } + + case ACT_UMBAR: + { + if (!doit) return "magic arrow (10d10) every 20+d20 turns"; + if (!get_aim_dir(&dir)) break; + fire_bolt(GF_MISSILE, dir, damroll(10, 10)); + + o_ptr->timeout = rand_int(20) + 20; + + break; + } + + case ACT_NUMENOR: + { + /* Give full knowledge */ + /* Hack -- Maximal info */ + monster_race *r_ptr; + cave_type *c_ptr; + int x, y, m; + + if (!doit) return "analyze monster every 500+d200 turns"; + + if (!tgt_pt(&x, &y)) break; + + c_ptr = &cave[y][x]; + if (!c_ptr->m_idx) break; + + r_ptr = &r_info[c_ptr->m_idx]; + + /* Observe "maximal" attacks */ + for (m = 0; m < 4; m++) + { + /* Examine "actual" blows */ + if (r_ptr->blow[m].effect || r_ptr->blow[m].method) + { + /* Hack -- maximal observations */ + r_ptr->r_blows[m] = MAX_UCHAR; + } + } + + /* Hack -- maximal drops */ + r_ptr->r_drop_gold = r_ptr->r_drop_item = + (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) + + ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) + + ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) + + ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) + + ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) + + ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0)); + + /* Hack -- but only "valid" drops */ + if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0; + if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0; + + /* Hack -- observe many spells */ + r_ptr->r_cast_inate = MAX_UCHAR; + r_ptr->r_cast_spell = MAX_UCHAR; + + /* Hack -- know all the flags */ + r_ptr->r_flags1 = r_ptr->flags1; + r_ptr->r_flags2 = r_ptr->flags2; + r_ptr->r_flags3 = r_ptr->flags3; + r_ptr->r_flags4 = r_ptr->flags4; + r_ptr->r_flags5 = r_ptr->flags5; + r_ptr->r_flags6 = r_ptr->flags6; + r_ptr->r_flags7 = r_ptr->flags7; + r_ptr->r_flags8 = r_ptr->flags8; + r_ptr->r_flags9 = r_ptr->flags9; + + o_ptr->timeout = rand_int(200) + 500; + + break; + } + + case ACT_KNOWLEDGE: + { + if (!doit) return "whispers from beyond(sanity drain) 100+d200 turns"; + identify_fully(); + take_sanity_hit(damroll(10, 7), "the sounds of the dead"); + + o_ptr->timeout = rand_int(200) + 100; + + break; + } + + case ACT_UNDEATH: + { + if (!doit) return "ruination every 10+d10 turns"; + msg_print("The phial wells with dark light..."); + unlite_area(damroll(2, 15), 3); + take_hit(damroll(10, 10), "activating The Phial of Undeath"); + (void)dec_stat(A_DEX, 25, STAT_DEC_PERMANENT); + (void)dec_stat(A_WIS, 25, STAT_DEC_PERMANENT); + (void)dec_stat(A_CON, 25, STAT_DEC_PERMANENT); + (void)dec_stat(A_STR, 25, STAT_DEC_PERMANENT); + (void)dec_stat(A_CHR, 25, STAT_DEC_PERMANENT); + (void)dec_stat(A_INT, 25, STAT_DEC_PERMANENT); + + o_ptr->timeout = rand_int(10) + 10; + + break; + } + + case ACT_THRAIN: + { + if (!doit) return "detection every 30+d30 turns"; + msg_print("The stone glows a deep green..."); + detect_all(DEFAULT_RADIUS); + + o_ptr->timeout = rand_int(30) + 30; + + break; + } + + case ACT_BARAHIR: + { + if (!doit) return "dispel small life every 55+d55 turns"; + msg_print("You exterminate small life."); + (void)dispel_monsters(4); + + o_ptr->timeout = rand_int(55) + 55; + + break; + } + + case ACT_TULKAS: + { + if (!doit) return "haste self (75+d75 turns) every 150+d150 turns"; + msg_print("The ring glows brightly..."); + if (!p_ptr->fast) + { + (void)set_fast(randint(75) + 75, 10); + } + else + { + (void)set_fast(p_ptr->fast + 5, 10); + } + + o_ptr->timeout = rand_int(150) + 150; + + break; + } + + case ACT_NARYA: + { + if (!doit) return "healing (500) every 200+d100 turns"; + msg_print("The ring glows deep red..."); + hp_player(500); + set_blind(0); + set_confused(0); + set_poisoned(0); + set_stun(0); + set_cut(0); + + o_ptr->timeout = rand_int(100) + 200; + + break; + } + + case ACT_NENYA: + { + if (!doit) return "healing (800) every 100+d200 turns"; + msg_print("The ring glows bright white..."); + hp_player(800); + set_blind(0); + set_confused(0); + set_poisoned(0); + set_stun(0); + set_cut(0); + + o_ptr->timeout = rand_int(200) + 100; + + break; + } + + case ACT_VILYA: + { + if (!doit) return "greater healing (900) every 200+d200 turns"; + msg_print("The ring glows deep blue..."); + hp_player(900); + set_blind(0); + set_confused(0); + set_poisoned(0); + set_stun(0); + set_cut(0); + if (p_ptr->black_breath) + { + p_ptr->black_breath = FALSE; + msg_print("The hold of the Black Breath on you is broken!"); + } + + o_ptr->timeout = rand_int(200) + 200; + + break; + } + + case ACT_POWER: + { + if (!doit) return "powerful things"; + msg_print("The ring glows intensely black..."); + + o_ptr->timeout = ring_of_power(); + + break; + } + + + /* The Stone of Lore is perilous, for the sake of game balance. */ + case ACT_STONE_LORE: + { + if (!doit) return "perilous identify every turn"; + msg_print("The stone reveals hidden mysteries..."); + if (!ident_spell()) break; + + if (has_ability(AB_PERFECT_CASTING)) + { + /* Sufficient mana */ + if (20 <= p_ptr->csp) + { + /* Use some mana */ + p_ptr->csp -= 20; + } + + /* Over-exert the player */ + else + { + int oops = 20 - p_ptr->csp; + + /* No mana left */ + p_ptr->csp = 0; + p_ptr->csp_frac = 0; + + /* Message */ + msg_print("You are too weak to control the stone!"); + + /* Hack -- Bypass free action */ + (void)set_paralyzed(randint(5 * oops + 1)); + + /* Confusing. */ + (void)set_confused(p_ptr->confused + + randint(5 * oops + 1)); + } + + /* Redraw mana */ + p_ptr->redraw |= (PR_MANA); + } + + take_hit(damroll(1, 12), "perilous secrets"); + + /* Confusing. */ + if (rand_int(5) == 0) + { + (void)set_confused(p_ptr->confused + randint(10)); + } + + /* Exercise a little care... */ + if (rand_int(20) == 0) + { + take_hit(damroll(4, 10), "perilous secrets"); + } + + o_ptr->timeout = 1; + + break; + } + + case ACT_RAZORBACK: + { + if (!doit) return "star ball (150) every 1000 turns"; + msg_print("Your armor is surrounded by lightning..."); + for (i = 0; i < 8; i++) fire_ball(GF_ELEC, ddd[i], 150, 3); + + o_ptr->timeout = 1000; + + break; + } + + case ACT_BLADETURNER: + { + if (!doit) return "invulnerability (4+d8) every 800 turns"; + set_invuln(p_ptr->invuln + randint(8) + 4); + + o_ptr->timeout = 800; + + break; + } + + case ACT_MEDIATOR: + { + if (!doit) return "breathe elements (300), berserk rage, bless, and resistance every 400 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe the elements."); + fire_ball(GF_MISSILE, dir, 300, 4); + msg_print("Your armor glows many colours..."); + (void)set_afraid(0); + (void)set_shero(p_ptr->shero + randint(50) + 50); + (void)hp_player(30); + (void)set_blessed(p_ptr->blessed + randint(50) + 50); + (void)set_oppose_acid(p_ptr->oppose_acid + randint(50) + 50); + (void)set_oppose_elec(p_ptr->oppose_elec + randint(50) + 50); + (void)set_oppose_fire(p_ptr->oppose_fire + randint(50) + 50); + (void)set_oppose_cold(p_ptr->oppose_cold + randint(50) + 50); + (void)set_oppose_pois(p_ptr->oppose_pois + randint(50) + 50); + + o_ptr->timeout = 400; + + break; + } + + case ACT_BELEGENNON: + { + if (!doit) return ("heal (777), curing and heroism every 300 turns"); + msg_print("A heavenly choir sings..."); + (void)set_poisoned(0); + (void)set_cut(0); + (void)set_stun(0); + (void)set_confused(0); + (void)set_blind(0); + (void)set_hero(p_ptr->hero + randint(25) + 25); + (void)hp_player(777); + + o_ptr->timeout = 300; + + break; + } + + case ACT_GORLIM: + { + if (!doit) return "rays of fear in every direction"; + turn_monsters(40 + p_ptr->lev); + + o_ptr->timeout = 3 * (p_ptr->lev + 10); + + break; + } + + case ACT_COLLUIN: + { + if (!doit) return "resistance (20+d20 turns) every 111 turns"; + msg_print("Your cloak glows many colours..."); + (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20); + (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20); + (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20); + (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20); + (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20); + + o_ptr->timeout = 111; + + break; + } + + + case ACT_BELANGIL: + { + if (!doit) return "frost ball (48) every 5+d5 turns"; + msg_print("Your dagger is covered in frost..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_COLD, dir, 48, 2); + + o_ptr->timeout = rand_int(5) + 5; + + break; + } + + case ACT_ANGUIREL: + { + if (!doit) return "a getaway every 35 turns"; + switch (randint(13)) + { + case 1: + case 2: + case 3: + case 4: + case 5: + { + teleport_player(10); + + break; + } + + case 6: + case 7: + case 8: + case 9: + case 10: + { + teleport_player(222); + + break; + } + + case 11: + case 12: + { + (void)stair_creation(); + + break; + } + + default: + { + if (get_check("Leave this level? ")) + { + autosave_checkpoint(); + + /* Leaving */ + p_ptr->leaving = TRUE; + } + + break; + } + } + + o_ptr->timeout = 35; + + break; + } + + case ACT_ERU: + { + if (!doit) return "healing(7000), curing every 500 turns"; + msg_print("Your sword glows an intense white..."); + hp_player(7000); + heal_insanity(50); + set_blind(0); + set_poisoned(0); + set_confused(0); + set_stun(0); + set_cut(0); + set_image(0); + + o_ptr->timeout = 500; + + break; + } + + case ACT_DAWN: + { + if (!doit) return "summon the Legion of the Dawn every 500+d500 turns"; + msg_print("You summon the Legion of the Dawn."); + (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DAWN, TRUE); + + o_ptr->timeout = 500 + randint(500); + + break; + } + + case ACT_FIRESTAR: + { + if (!doit) return "large fire ball (72) every 100 turns"; + msg_print("Your morning star rages in fire..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_FIRE, dir, 72, 3); + + o_ptr->timeout = 100; + + break; + } + + case ACT_TURMIL: + { + if (!doit) return "drain life (90) every 70 turns"; + msg_print("Your hammer glows white..."); + if (!get_aim_dir(&dir)) break; + drain_life(dir, 90); + + o_ptr->timeout = 70; + + break; + } + + case ACT_CUBRAGOL: + { + if (!doit) return "fire branding of bolts every 999 turns"; + msg_print("Your crossbow glows deep red..."); + (void)brand_bolts(); + + o_ptr->timeout = 999; + + break; + } + + case ACT_ELESSAR: + { + if (!doit) return "heal and cure black breath every 200 turns"; + if (p_ptr->black_breath) + { + msg_print("The hold of the Black Breath on you is broken!"); + } + p_ptr->black_breath = FALSE; + hp_player(100); + + o_ptr->timeout = 200; + + break; + } + + case ACT_GANDALF: + { + if (!doit) return "restore mana every 666 turns"; + msg_print("Your mage staff glows deep blue..."); + if (p_ptr->csp < p_ptr->msp) + { + p_ptr->csp = p_ptr->msp; + p_ptr->csp_frac = 0; + msg_print("Your feel your head clear."); + p_ptr->redraw |= (PR_MANA); + p_ptr->window |= (PW_PLAYER); + } + + o_ptr->timeout = 666; + + break; + } + + case ACT_MARDA: + { + if (!doit) return "summon a thunderlord every 1000 turns"; + if (randint(3) == 1) + { + if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_THUNDERLORD)) + { + msg_print("A Thunderlord comes from thin air!"); + msg_print("'I will burn you!'"); + } + } + else + { + if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), + SUMMON_THUNDERLORD, (plev == 50 ? TRUE : FALSE))) + { + msg_print("A Thunderlord comes from thin air!"); + msg_print("'I will help you in your difficult task.'"); + } + } + + o_ptr->timeout = 1000; + + break; + } + + case ACT_PALANTIR: + { + if (!doit) return "clairvoyance every 100+d100 turns"; + msg_print("The stone glows a deep green..."); + wiz_lite_extra(); + (void)detect_traps(DEFAULT_RADIUS); + (void)detect_doors(DEFAULT_RADIUS); + (void)detect_stairs(DEFAULT_RADIUS); + + o_ptr->timeout = rand_int(100) + 100; + + break; + } + + case ACT_EREBOR: + { + if (!doit) return "open a secret passage every 75 turns"; + msg_print("Your pick twists in your hands."); + + if (!get_aim_dir(&dir)) break; + if (passwall(dir, TRUE)) + { + msg_print("A passage opens, and you step through."); + } + else + { + msg_print("There is no wall there!"); + } + + o_ptr->timeout = 75; + + break; + } + + case ACT_DRUEDAIN: + { + if (!doit) return "detection every 99 turns"; + msg_print("Your drum shows you the world."); + detect_all(DEFAULT_RADIUS); + + o_ptr->timeout = 99; + + break; + } + + case ACT_ROHAN: + { + if (!doit) return "heroism, berserker, and haste every 250 turns"; + msg_print("Your horn glows deep red."); + set_afraid(0); + set_shero(p_ptr->shero + damroll(5, 10) + 30); + set_afraid(0); + set_hero(p_ptr->hero + damroll(5, 10) + 30); + set_fast(p_ptr->fast + damroll(5, 10) + 30, 10); + hp_player(30); + + o_ptr->timeout = 250; + + break; + } + + case ACT_HELM: + { + if (!doit) return "sound ball (300) every 300 turns"; + msg_print("Your horn emits a loud sound."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_SOUND, dir, 300, 6); + + o_ptr->timeout = 300; + + break; + } + + case ACT_BOROMIR: + { + if (!doit) return "mass human summoning every 1000 turns"; + msg_print("Your horn calls for help."); + for (i = 0; i < 15; i++) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_HUMAN, TRUE); + } + + o_ptr->timeout = 1000; + + break; + } + + case ACT_HURIN: + { + if (!doit) return "berserker and +10 to speed (50) every 100+d200 turns"; + if (!p_ptr->fast) + { + (void)set_fast(randint(50) + 50, 10); + } + else + { + (void)set_fast(p_ptr->fast + 5, 10); + } + hp_player(30); + set_afraid(0); + set_shero(p_ptr->shero + randint(50) + 50); + + o_ptr->timeout = rand_int(200) + 100; + + break; + } + + case ACT_AXE_GOTHMOG: + { + if (!doit) return "fire ball (300) every 200+d200 turns"; + msg_print("Your lochaber axe erupts in fire..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_FIRE, dir, 300, 4); + + o_ptr->timeout = 200 + rand_int(200); + + break; + } + + case ACT_MELKOR: + { + if (!doit) return "darkness ball (150) every 100 turns"; + msg_print("Your spear is covered of darkness..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_DARK, dir, 150, 3); + + o_ptr->timeout = 100; + + break; + } + + case ACT_GROND: + { + if (!doit) return "alter reality every 100 turns"; + msg_print("Your hammer hits the floor..."); + alter_reality(); + + o_ptr->timeout = 100; + + break; + } + + case ACT_NATUREBANE: + { + if (!doit) return "dispel monsters (300) every 200+d200 turns"; + msg_print("Your axe glows blood red..."); + dispel_monsters(300); + + o_ptr->timeout = 200 + randint(200); + + break; + } + + case ACT_NIGHT: + { + if (!doit) return "vampiric drain (3*100) every 250 turns"; + msg_print("Your axe emits a black aura..."); + if (!get_aim_dir(&dir)) break; + for (i = 0; i < 3; i++) + { + if (drain_life(dir, 100)) hp_player(100); + } + + o_ptr->timeout = 250; + + break; + } + + case ACT_ORCHAST: + { + if (!doit) return "detect orcs every 10 turns"; + msg_print("Your weapon glows brightly..."); + (void)detect_monsters_xxx(RF3_ORC, DEFAULT_RADIUS); + + o_ptr->timeout = 10; + + break; + } + case ACT_SUNLIGHT: + { + if (!doit) return "beam of sunlight every 10 turns"; + + if (!get_aim_dir(&dir)) break; + msg_print("A line of sunlight appears."); + lite_line(dir); + + o_ptr->timeout = 10; + + break; + } + + case ACT_BO_MISS_1: + { + if (!doit) return "magic missile (2d6) every 2 turns"; + msg_print("It glows extremely brightly..."); + if (!get_aim_dir(&dir)) break; + fire_bolt(GF_MISSILE, dir, damroll(2, 6)); + + o_ptr->timeout = 2; + + break; + } + + case ACT_BA_POIS_1: + { + if (!doit) return "stinking cloud (12), rad. 3, every 4+d4 turns"; + msg_print("It throbs deep green..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_POIS, dir, 12, 3); + + o_ptr->timeout = rand_int(4) + 4; + + break; + } + + case ACT_BO_ELEC_1: + { + if (!doit) return "lightning bolt (4d8) every 6+d6 turns"; + msg_print("It is covered in sparks..."); + if (!get_aim_dir(&dir)) break; + fire_bolt(GF_ELEC, dir, damroll(4, 8)); + + o_ptr->timeout = rand_int(6) + 6; + + break; + } + + case ACT_BO_ACID_1: + { + if (!doit) return "acid bolt (5d8) every 5+d5 turns"; + msg_print("It is covered in acid..."); + if (!get_aim_dir(&dir)) break; + fire_bolt(GF_ACID, dir, damroll(5, 8)); + + o_ptr->timeout = rand_int(5) + 5; + + break; + } + + case ACT_BO_COLD_1: + { + if (!doit) return "frost bolt (6d8) every 7+d7 turns"; + msg_print("It is covered in frost..."); + if (!get_aim_dir(&dir)) break; + fire_bolt(GF_COLD, dir, damroll(6, 8)); + + o_ptr->timeout = rand_int(7) + 7; + + break; + } + + case ACT_BO_FIRE_1: + { + if (!doit) return "fire bolt (9d8) every 8+d8 turns"; + msg_print("It is covered in fire..."); + if (!get_aim_dir(&dir)) break; + fire_bolt(GF_FIRE, dir, damroll(9, 8)); + + o_ptr->timeout = rand_int(8) + 8; + + break; + } + + case ACT_BA_COLD_1: + { + if (!doit) return "ball of cold (48) every 400 turns"; + msg_print("It is covered in frost..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_COLD, dir, 48, 2); + + o_ptr->timeout = 400; + + break; + } + + case ACT_BA_FIRE_1: + { + if (!doit) return "ball of fire (72) every 400 turns"; + msg_print("It glows an intense red..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_FIRE, dir, 72, 2); + + o_ptr->timeout = 400; + + break; + } + + case ACT_DRAIN_1: + { + if (!doit) return "drain life (100) every 100+d100 turns"; + msg_print("It glows black..."); + if (!get_aim_dir(&dir)) break; + if (drain_life(dir, 100)) + + o_ptr->timeout = rand_int(100) + 100; + + break; + } + + case ACT_BA_COLD_2: + { + if (!doit) return "ball of cold (100) every 300 turns"; + msg_print("It glows an intense blue..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_COLD, dir, 100, 2); + + o_ptr->timeout = 300; + + break; + } + + case ACT_BA_ELEC_2: + { + if (!doit) return "ball of lightning (100) every 500 turns"; + msg_print("It crackles with electricity..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_ELEC, dir, 100, 3); + + o_ptr->timeout = 500; + + break; + } + + case ACT_DRAIN_2: + { + if (!doit) return "drain life (120) every 400 turns"; + msg_print("It glows black..."); + if (!get_aim_dir(&dir)) break; + drain_life(dir, 120); + + o_ptr->timeout = 400; + + break; + } + + case ACT_VAMPIRE_1: + { + if (!doit) return "vampiric drain (3*50) every 400 turns"; + if (!get_aim_dir(&dir)) break; + for (dummy = 0; dummy < 3; dummy++) + { + if (drain_life(dir, 50)) + hp_player(50); + } + + o_ptr->timeout = 400; + + break; + } + + case ACT_BO_MISS_2: + { + if (!doit) return "arrows (150) every 90+d90 turns"; + msg_print("It grows magical spikes..."); + if (!get_aim_dir(&dir)) break; + fire_bolt(GF_ARROW, dir, 150); + + o_ptr->timeout = rand_int(90) + 90; + + break; + } + + case ACT_BA_FIRE_2: + { + if (!doit) return "fire ball (120) every 225+d225 turns"; + msg_print("It glows deep red..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_FIRE, dir, 120, 3); + + o_ptr->timeout = rand_int(225) + 225; + + break; + } + + case ACT_BA_COLD_3: + { + if (!doit) return "ball of cold (200) every 325+d325 turns"; + msg_print("It glows bright white..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_COLD, dir, 200, 3); + + o_ptr->timeout = rand_int(325) + 325; + + break; + } + + case ACT_BA_ELEC_3: + { + if (!doit) return "Lightning Ball (250) every 425+d425 turns"; + msg_print("It glows deep blue..."); + if (!get_aim_dir(&dir)) break; + fire_ball(GF_ELEC, dir, 250, 3); + + o_ptr->timeout = rand_int(425) + 425; + + break; + } + + case ACT_WHIRLWIND: + { + int y = 0, x = 0; + cave_type *c_ptr; + monster_type *m_ptr; + if (!doit) return "whirlwind attack every 250 turns"; + + for (dir = 0; dir <= 9; dir++) + { + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + c_ptr = &cave[y][x]; + + /* Get the monster */ + m_ptr = &m_list[c_ptr->m_idx]; + + /* Hack -- attack monsters */ + if (c_ptr->m_idx && (m_ptr->ml || cave_floor_bold(y, x))) + { + py_attack(y, x, -1); + } + } + + o_ptr->timeout = 250; + + break; + } + + case ACT_VAMPIRE_2: + { + if (!doit) return "vampiric drain (3*100) every 400 turns"; + if (!get_aim_dir(&dir)) break; + for (dummy = 0; dummy < 3; dummy++) + { + if (drain_life(dir, 100)) + hp_player(100); + } + + o_ptr->timeout = 400; + + break; + } + + + case ACT_CALL_CHAOS: + { + if (!doit) return "call chaos every 350 turns"; + msg_print("It glows in scintillating colours..."); + call_chaos(); + + o_ptr->timeout = 350; + + break; + } + + case ACT_ROCKET: + { + if (!doit) return "launch rocket (120+level) every 400 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You launch a rocket!"); + fire_ball(GF_ROCKET, dir, 120 + (plev), 2); + + o_ptr->timeout = 400; + + break; + } + + case ACT_DISP_EVIL: + { + if (!doit) return "dispel evil (level*5) every 300+d300 turns"; + msg_print("It floods the area with goodness..."); + dispel_evil(p_ptr->lev * 5); + + o_ptr->timeout = rand_int(300) + 300; + + break; + } + + case ACT_DISP_GOOD: + { + if (!doit) return "dispel good (level*5) every 300+d300 turns"; + msg_print("It floods the area with evil..."); + dispel_good(p_ptr->lev * 5); + + o_ptr->timeout = rand_int(300) + 300; + + break; + } + + case ACT_BA_MISS_3: + { + if (!doit) return "elemental breath (300) every 500 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe the elements."); + fire_ball(GF_MISSILE, dir, 300, 4); + + o_ptr->timeout = 500; + + break; + } + + /* Activate for other offensive action */ + + case ACT_CONFUSE: + { + if (!doit) return "confuse monster every 15 turns"; + msg_print("It glows in scintillating colours..."); + if (!get_aim_dir(&dir)) break; + confuse_monster(dir, 20); + + o_ptr->timeout = 15; + + break; + } + + case ACT_SLEEP: + { + if (!doit) return "sleep nearby monsters every 55 turns"; + msg_print("It glows deep blue..."); + sleep_monsters_touch(); + + o_ptr->timeout = 55; + + break; + } + + case ACT_QUAKE: + { + if (!doit) return "earthquake (rad 10) every 50 turns"; + /* Prevent destruction of quest levels and town */ + if (!is_quest(dun_level) && dun_level) + { + earthquake(p_ptr->py, p_ptr->px, 10); + o_ptr->timeout = 50; + } + + break; + } + + case ACT_TERROR: + { + if (!doit) return "terror every 3 * (level+10) turns"; + turn_monsters(40 + p_ptr->lev); + + o_ptr->timeout = 3 * (p_ptr->lev + 10); + + break; + } + + case ACT_TELE_AWAY: + { + if (!doit) return "teleport away every 200 turns"; + if (!get_aim_dir(&dir)) break; + (void)fire_beam(GF_AWAY_ALL, dir, plev); + + o_ptr->timeout = 200; + + break; + } + + case ACT_BANISH_EVIL: + { + if (!doit) return "banish evil every 250+d250 turns"; + if (banish_evil(100)) + { + msg_print("The power of the artifact banishes evil!"); + } + + o_ptr->timeout = 250 + randint(250); + + break; + } + + case ACT_GENOCIDE: + { + if (!doit) return "genocide every 500 turns"; + msg_print("It glows deep blue..."); + (void)genocide(TRUE); + + o_ptr->timeout = 500; + + break; + } + + case ACT_MASS_GENO: + { + if (!doit) return "mass genocide every 1000 turns"; + msg_print("It lets out a long, shrill note..."); + (void)mass_genocide(TRUE); + + o_ptr->timeout = 1000; + + break; + } + + /* Activate for summoning / charming */ + + case ACT_CHARM_ANIMAL: + { + if (!doit) return "charm animal every 300 turns"; + if (!get_aim_dir(&dir)) break; + (void) charm_animal(dir, plev); + + o_ptr->timeout = 300; + + break; + } + + case ACT_CHARM_UNDEAD: + { + if (!doit) return "enslave undead every 333 turns"; + if (!get_aim_dir(&dir)) break; + (void)control_one_undead(dir, plev); + + o_ptr->timeout = 333; + + break; + } + + case ACT_CHARM_OTHER: + { + if (!doit) return "charm monster every 400 turns"; + if (!get_aim_dir(&dir)) break; + (void) charm_monster(dir, plev); + + o_ptr->timeout = 400; + + break; + } + + case ACT_CHARM_ANIMALS: + { + if (!doit) return "animal friendship every 500 turns"; + (void) charm_animals(plev * 2); + + o_ptr->timeout = 500; + + break; + } + + case ACT_CHARM_OTHERS: + { + if (!doit) return "mass charm every 750 turns"; + charm_monsters(plev * 2); + + o_ptr->timeout = 750; + + break; + } + + case ACT_SUMMON_ANIMAL: + { + if (!doit) return "summon animal every 200+d300 turns"; + (void)summon_specific_friendly(p_ptr->py, p_ptr->px, plev, SUMMON_ANIMAL_RANGER, TRUE); + + o_ptr->timeout = 200 + randint(300); + + break; + } + + case ACT_SUMMON_PHANTOM: + { + if (!doit) return "summon phantasmal servant every 200+d200 turns"; + msg_print("You summon a phantasmal servant."); + (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_PHANTOM, TRUE); + + o_ptr->timeout = 200 + randint(200); + + break; + } + + case ACT_SUMMON_ELEMENTAL: + { + if (!doit) return "summon elemental every 750 turns"; + if (randint(3) == 1) + { + if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_ELEMENTAL)) + { + msg_print("An elemental materialises..."); + msg_print("You fail to control it!"); + } + } + else + { + if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), + SUMMON_ELEMENTAL, (plev == 50 ? TRUE : FALSE))) + { + msg_print("An elemental materialises..."); + msg_print("It seems obedient to you."); + } + } + + o_ptr->timeout = 750; + + break; + } + + case ACT_SUMMON_DEMON: + { + if (!doit) return "summon demon every 666+d333 turns"; + if (randint(3) == 1) + { + if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_DEMON)) + { + msg_print("The area fills with a stench of sulphur and brimstone."); + msg_print("'NON SERVIAM! Wretch! I shall feast on thy mortal soul!'"); + } + } + else + { + if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), + SUMMON_DEMON, (plev == 50 ? TRUE : FALSE))) + { + msg_print("The area fills with a stench of sulphur and brimstone."); + msg_print("'What is thy bidding... Master?'"); + } + } + + o_ptr->timeout = 666 + randint(333); + + break; + } + + case ACT_SUMMON_UNDEAD: + { + if (!doit) return "summon undead every 666+d333 turns"; + if (randint(3) == 1) + { + if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), + (plev > 47 ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD))) + { + msg_print("Cold winds begin to blow around you, carrying with them the stench of decay..."); + msg_print("'The dead arise... to punish you for disturbing them!'"); + } + } + else + { + if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), + (plev > 47 ? SUMMON_HI_UNDEAD_NO_UNIQUES : SUMMON_UNDEAD), + (((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE))) + { + msg_print("Cold winds begin to blow around you, carrying with them the stench of decay..."); + msg_print("Ancient, long-dead forms arise from the ground to serve you!"); + } + } + + o_ptr->timeout = 666 + randint(333); + + break; + } + + /* Activate for healing */ + + case ACT_CURE_LW: + { + if (!doit) return format("cure light wounds every %d turns", (is_junkart ? 50 : 10)); + (void)set_afraid(0); + (void)hp_player(30); + + o_ptr->timeout = 10; + + break; + } + + case ACT_CURE_MW: + { + if (!doit) return format("cure serious wounds every %s turns", (is_junkart? "75" : "3+d3")); + msg_print("It radiates deep purple..."); + hp_player(damroll(4, 8)); + (void)set_cut((p_ptr->cut / 2) - 50); + + o_ptr->timeout = rand_int(3) + 3; + + break; + } + + case ACT_CURE_POISON: + { + if (!doit) return "remove fear and cure poison every 5 turns"; + msg_print("It glows deep blue..."); + (void)set_afraid(0); + (void)set_poisoned(0); + + o_ptr->timeout = 5; + + break; + } + + case ACT_REST_LIFE: + { + if (!doit) return "restore life levels every 450 turns"; + msg_print("It glows a deep red..."); + restore_level(); + + o_ptr->timeout = 450; + + break; + } + + case ACT_REST_ALL: + { + if (!doit) return format("restore stats and life levels every %d turns", (is_junkart ? 200 : 750)); + msg_print("It glows a deep green..."); + (void)do_res_stat(A_STR, TRUE); + (void)do_res_stat(A_INT, TRUE); + (void)do_res_stat(A_WIS, TRUE); + (void)do_res_stat(A_DEX, TRUE); + (void)do_res_stat(A_CON, TRUE); + (void)do_res_stat(A_CHR, TRUE); + (void)restore_level(); + + o_ptr->timeout = 750; + + break; + } + + case ACT_CURE_700: + { + if (!doit) return format("heal 700 hit points every %d turns", (is_junkart ? 100 : 250)); + msg_print("It glows deep blue..."); + msg_print("You feel a warm tingling inside..."); + (void)hp_player(700); + (void)set_cut(0); + + o_ptr->timeout = 250; + + break; + } + + case ACT_CURE_1000: + { + if (!doit) return "heal 1000 hit points every 888 turns"; + msg_print("It glows a bright white..."); + msg_print("You feel much better..."); + (void)hp_player(1000); + (void)set_cut(0); + + o_ptr->timeout = 888; + + break; + } + + case ACT_ESP: + { + if (!doit) return "temporary ESP (dur 25+d30) every 200 turns"; + (void)set_tim_esp(p_ptr->tim_esp + randint(30) + 25); + + o_ptr->timeout = 200; + + break; + } + + case ACT_BERSERK: + { + if (!doit) return "heroism and berserk (dur 50+d50) every 100+d100 turns"; + (void)set_shero(p_ptr->shero + randint(50) + 50); + (void)set_blessed(p_ptr->blessed + randint(50) + 50); + + o_ptr->timeout = 100 + randint(100); + + break; + } + + case ACT_PROT_EVIL: + { + if (!doit) return "protection from evil (dur level*3 + d25) every 225+d225 turns"; + msg_print("It lets out a shrill wail..."); + k = 3 * p_ptr->lev; + (void)set_protevil(p_ptr->protevil + randint(25) + k); + + o_ptr->timeout = rand_int(225) + 225; + + break; + } + + case ACT_RESIST_ALL: + { + if (!doit) return "resist elements (dur 40+d40) every 200 turns"; + msg_print("It glows many colours..."); + (void)set_oppose_acid(p_ptr->oppose_acid + randint(40) + 40); + (void)set_oppose_elec(p_ptr->oppose_elec + randint(40) + 40); + (void)set_oppose_fire(p_ptr->oppose_fire + randint(40) + 40); + (void)set_oppose_cold(p_ptr->oppose_cold + randint(40) + 40); + (void)set_oppose_pois(p_ptr->oppose_pois + randint(40) + 40); + + o_ptr->timeout = 200; + + break; + } + + case ACT_SPEED: + { + if (!doit) return "speed (dur 20+d20) every 250 turns"; + msg_print("It glows bright green..."); + if (!p_ptr->fast) + { + (void)set_fast(randint(20) + 20, 10); + } + else + { + (void)set_fast(p_ptr->fast + 5, 10); + } + + o_ptr->timeout = 250; + + break; + } + + case ACT_XTRA_SPEED: + { + if (!doit) return "speed (dur 75+d75) every 200+d200 turns"; + msg_print("It glows brightly..."); + if (!p_ptr->fast) + { + (void)set_fast(randint(75) + 75, 10); + } + else + { + (void)set_fast(p_ptr->fast + 5, 10); + } + + o_ptr->timeout = rand_int(200) + 200; + + break; + } + + case ACT_WRAITH: + { + if (!doit) return "wraith form (level/2 + d(level/2)) every 1000 turns"; + set_shadow(p_ptr->tim_wraith + randint(plev / 2) + (plev / 2)); + + o_ptr->timeout = 1000; + + break; + } + + case ACT_INVULN: + { + if (!doit) return "invulnerability (dur 8+d8) every 1000 turns"; + (void)set_invuln(p_ptr->invuln + randint(8) + 8); + + o_ptr->timeout = 1000; + + break; + } + + /* Activate for general purpose effect (detection etc.) */ + + case ACT_LIGHT: + { + if (!doit) return format("light area (dam 2d15) every %s turns", (is_junkart ? "100" : "10+d10")); + msg_print("It wells with clear light..."); + lite_area(damroll(2, 15), 3); + + o_ptr->timeout = rand_int(10) + 10; + + break; + } + + case ACT_MAP_LIGHT: + { + if (!doit) return "light (dam 2d15) & map area every 50+d50 turns"; + msg_print("It shines brightly..."); + map_area(); + lite_area(damroll(2, 15), 3); + + o_ptr->timeout = rand_int(50) + 50; + + break; + } + + case ACT_DETECT_ALL: + { + if (!doit) return "detection every 55+d55 turns"; + msg_print("It glows bright white..."); + msg_print("An image forms in your mind..."); + detect_all(DEFAULT_RADIUS); + + o_ptr->timeout = rand_int(55) + 55; + + break; + } + + case ACT_DETECT_XTRA: + { + if (!doit) return "detection, probing and identify true every 1000 turns"; + msg_print("It glows brightly..."); + detect_all(DEFAULT_RADIUS); + probing(); + identify_fully(); + + o_ptr->timeout = 1000; + + break; + } + + case ACT_ID_FULL: + { + if (!doit) return "identify true every 750 turns"; + msg_print("It glows yellow..."); + identify_fully(); + + o_ptr->timeout = 750; + + break; + } + + case ACT_ID_PLAIN: + { + if (!doit) return "identify spell every 10 turns"; + if (!ident_spell()) break; + + o_ptr->timeout = 10; + + break; + } + + case ACT_RUNE_EXPLO: + { + if (!doit) return "explosive rune every 200 turns"; + msg_print("It glows bright red..."); + explosive_rune(); + + o_ptr->timeout = 200; + + break; + } + + case ACT_RUNE_PROT: + { + if (!doit) return "rune of protection every 400 turns"; + msg_print("It glows light blue..."); + warding_glyph(); + + o_ptr->timeout = 400; + + break; + } + + case ACT_SATIATE: + { + if (!doit) return "satisfy hunger every 200 turns"; + (void)set_food(PY_FOOD_MAX - 1); + + o_ptr->timeout = 200; + + break; + } + + case ACT_DEST_DOOR: + { + if (!doit) return "destroy doors and traps every 10 turns"; + msg_print("It glows bright red..."); + destroy_doors_touch(); + + o_ptr->timeout = 10; + + break; + } + + case ACT_STONE_MUD: + { + if (!doit) return "stone to mud every 5 turns"; + msg_print("It pulsates..."); + if (!get_aim_dir(&dir)) break; + wall_to_mud(dir); + + o_ptr->timeout = 5; + + break; + } + + case ACT_RECHARGE: + { + if (!doit) return "recharging every 70 turns"; + recharge(60); + + o_ptr->timeout = 70; + + break; + } + + case ACT_ALCHEMY: + { + if (!doit) return "alchemy every 500 turns"; + msg_print("It glows bright yellow..."); + (void) alchemy(); + + o_ptr->timeout = 500; + + break; + } + + case ACT_DIM_DOOR: + { + if (!doit) return "dimension door every 100 turns"; + if (dungeon_flags2 & DF2_NO_TELEPORT) + { + msg_print("Not on special levels!"); + break; + } + + msg_print("You open a Void Jumpgate. Choose a destination."); + if (!tgt_pt(&ii, &ij)) break; + + p_ptr->energy -= 60 - plev; + + if (!cave_empty_bold(ij, ii) || (cave[ij][ii].info & CAVE_ICKY) || + (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2) || + (!rand_int(plev * plev / 2))) + { + msg_print("You fail to exit the void correctly!"); + p_ptr->energy -= 100; + get_pos_player(10, &ij, &ii); + } + + cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN); + cave_set_feat(ij, ii, FEAT_BETWEEN); + cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8); + cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8); + + o_ptr->timeout = 100; + + break; + } + + case ACT_TELEPORT: + { + if (!doit) return format("teleport (range 100) every %d turns", (is_junkart? 100 : 45)); + msg_print("It twists space around you..."); + teleport_player(100); + + o_ptr->timeout = 45; + + break; + } + + case ACT_RECALL: + { + if (!(dungeon_flags2 & DF2_ASK_LEAVE) || ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? "))) + { + if (!doit) return "word of recall every 200 turns"; + msg_print("It glows soft white..."); + recall_player(20,15); + + o_ptr->timeout = 200; + } + + break; + } + + case ACT_DEATH: + { + if (!doit) return "death"; + take_hit(5000, "activating a death spell"); + + /* Timeout is set before return */ + + break; + } + + case ACT_RUINATION: + { + if (!doit) return "Ruination"; + msg_print("Your nerves and muscles feel weak and lifeless!"); + + take_hit(damroll(10, 10), "activating Ruination"); + (void)dec_stat(A_DEX, 25, TRUE); + (void)dec_stat(A_WIS, 25, TRUE); + (void)dec_stat(A_CON, 25, TRUE); + (void)dec_stat(A_STR, 25, TRUE); + (void)dec_stat(A_CHR, 25, TRUE); + (void)dec_stat(A_INT, 25, TRUE); + + /* Timeout is set before return */ + + break; + } + + case ACT_DESTRUC: + { + if (!doit) return "Destruction every 100 turns"; + earthquake(p_ptr->py, p_ptr->px, 12); + + /* Timeout is set before return */ + + break; + } + + case ACT_UNINT: + { + if (!doit) return "decreasing Intelligence"; + (void)dec_stat(A_INT, 25, FALSE); + + /* Timeout is set before return */ + + break; + } + + case ACT_UNSTR: + { + if (!doit) return "decreasing Strength"; + (void)dec_stat(A_STR, 25, FALSE); + + /* Timeout is set before return */ + + break; + } + + case ACT_UNCON: + { + if (!doit) return "decreasing Constitution"; + (void)dec_stat(A_CON, 25, FALSE); + + /* Timeout is set before return */ + + break; + } + + case ACT_UNCHR: + { + if (!doit) return "decreasing Charisma"; + (void)dec_stat(A_CHR, 25, FALSE); + + /* Timeout is set before return */ + + break; + } + + case ACT_UNDEX: + { + if (!doit) return "decreasing Dexterity"; + (void)dec_stat(A_DEX, 25, FALSE); + + /* Timeout is set before return */ + + break; + } + + case ACT_UNWIS: + { + if (!doit) return "decreasing Wisdom"; + (void)dec_stat(A_WIS, 25, FALSE); + + /* Timeout is set before return */ + + break; + } + + case ACT_STATLOSS: + { + if (!doit) return "stat loss"; + (void)dec_stat(A_STR, 15, FALSE); + (void)dec_stat(A_INT, 15, FALSE); + (void)dec_stat(A_WIS, 15, FALSE); + (void)dec_stat(A_DEX, 15, FALSE); + (void)dec_stat(A_CON, 15, FALSE); + (void)dec_stat(A_CHR, 15, FALSE); + + /* Timeout is set before return */ + + break; + } + + case ACT_HISTATLOSS: + { + if (!doit) return "high stat loss"; + (void)dec_stat(A_STR, 25, FALSE); + (void)dec_stat(A_INT, 25, FALSE); + (void)dec_stat(A_WIS, 25, FALSE); + (void)dec_stat(A_DEX, 25, FALSE); + (void)dec_stat(A_CON, 25, FALSE); + (void)dec_stat(A_CHR, 25, FALSE); + + /* Timeout is set before return */ + + break; + } + + case ACT_EXPLOSS: + { + if (!doit) return "experience loss"; + lose_exp(p_ptr->exp / 20); + + /* Timeout is set before return */ + + break; + } + + case ACT_HIEXPLOSS: + { + if (!doit) return "high experience loss"; + lose_exp(p_ptr->exp / 10); + + /* Timeout is set before return */ + + break; + } + + case ACT_SUMMON_MONST: + { + if (!doit) return "summon monster"; + summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0); + + /* Timeout is set before return */ + + break; + } + + case ACT_PARALYZE: + { + if (!doit) return "paralyze"; + set_paralyzed(20 + randint(10)); + + /* Timeout is set before return */ + + break; + } + + case ACT_HALLU: + { + if (!doit) return "hallucination every 10 turns"; + set_image(p_ptr->image + 20 + randint(10)); + + /* Timeout is set before return */ + + break; + } + + case ACT_POISON: + { + if (!doit) return "poison"; + set_poisoned(p_ptr->poisoned + 20 + randint(10)); + + /* Timeout is set before return */ + + break; + } + + case ACT_HUNGER: + { + if (!doit) return "create hunger"; + (void)set_food(PY_FOOD_WEAK); + + /* Timeout is set before return */ + + break; + } + + case ACT_STUN: + { + if (!doit) return "stun"; + set_stun(p_ptr->stun + 20 + randint(10)); + + /* Timeout is set before return */ + + break; + } + + case ACT_CUTS: + { + if (!doit) return "cuts"; + set_cut(p_ptr->cut + 20 + randint(10)); + + /* Timeout is set before return */ + + break; + } + + case ACT_PARANO: + { + if (!doit) return "confusion"; + set_confused(p_ptr->confused + 30 + randint(10)); + + /* Timeout is set before return */ + + break; + } + + case ACT_CONFUSION: + { + if (!doit) return "confusion"; + set_confused(p_ptr->confused + 20 + randint(10)); + + /* Timeout is set before return */ + + break; + } + + case ACT_BLIND: + { + if (!doit) return "blindness"; + set_blind(p_ptr->blind + 20 + randint(10)); + + /* Timeout is set before return */ + + break; + } + + case ACT_PET_SUMMON: + { + if (!doit) return "summon pet every 101 turns"; + summon_specific_friendly(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], 0, FALSE); + + /* Timeout is set before return */ + /*FINDME*/ + + break; + } + + case ACT_CURE_PARA: + { + if (!doit) return "cure confusion every 500 turns"; + set_confused(0); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_HALLU: + { + if (!doit) return "cure hallucination every 100 turns"; + set_image(0); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_POIS: + { + if (!doit) return "cure poison every 100 turns"; + set_poisoned(0); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_HUNGER: + { + if (!doit) return "satisfy hunger every 100 turns"; + (void)set_food(PY_FOOD_MAX - 1); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_STUN: + { + if (!doit) return "cure stun every 100 turns"; + set_stun(0); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_CUTS: + { + if (!doit) return "cure cuts every 100 turns"; + set_cut(0); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_FEAR: + { + if (!doit) return "cure fear every 100 turns"; + set_afraid(0); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_CONF: + { + if (!doit) return "cure confusion every 100 turns"; + set_confused(0); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_BLIND: + { + if (!doit) return "cure blindness every 100 turns"; + set_blind(0); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURING: + { + if (!doit) return "curing every 110 turns"; + set_blind(0); + set_poisoned(0); + set_confused(0); + set_stun(0); + set_cut(0); + set_image(0); + + /* Timeout is set before return */ + + break; + } + + case ACT_DARKNESS: + { + if (!doit) return "darkness"; + unlite_area(damroll(2, 10), 10); + + /* Timeout is set before return */ + + break; + } + + case ACT_LEV_TELE: + { + if (!doit) return "teleport level every 50 turns"; + teleport_player_level(); + + /* Timeout is set before return */ + + break; + } + + case ACT_ACQUIREMENT: + { + if (!doit) return "acquirement every 3000 turns"; + acquirement(p_ptr->py, p_ptr->px, 1, FALSE, FALSE); + + /* Timeout is set before return */ + + break; + } + + case ACT_WEIRD: + { + if (!doit) return "something weird every 5 turns"; + /* It doesn't do anything */ + + /* Timeout is set before return */ + + break; + } + + case ACT_AGGRAVATE: + { + if (!doit) return "aggravate"; + aggravate_monsters(1); + + /* Timeout is set before return */ + + break; + } + + case ACT_MUT: + { + if (!doit) return "gain corruption every 10 turns"; + gain_random_corruption(); + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_INSANITY: + { + if (!doit) return "cure insanity every 200 turns"; + heal_insanity(damroll(10, 10)); + + /* Timeout is set before return */ + + break; + } + + case ACT_CURE_MUT: + { + msg_print("Ahah, you wish."); + /* Timeout is set before return */ + + break; + } + + case ACT_LIGHT_ABSORBTION: + { + int y, x, light = 0, dir; + cave_type *c_ptr; + + if (!doit) return "light absorption every 80 turns"; + + for (y = p_ptr->py - 6; y <= p_ptr->py + 6; y++) + { + for (x = p_ptr->px - 6; x <= p_ptr->px + 6; x++) + { + if (!in_bounds(y, x)) continue; + + c_ptr = &cave[y][x]; + + if (distance(y, x, p_ptr->py, p_ptr->px) > 6) continue; + + if (c_ptr->info & CAVE_GLOW) + { + light++; + + /* No longer in the array */ + c_ptr->info &= ~(CAVE_TEMP); + + /* Darken the grid */ + c_ptr->info &= ~(CAVE_GLOW); + + /* Hack -- Forget "boring" grids */ + if (cave_plain_floor_grid(c_ptr) && + !(c_ptr->info & (CAVE_TRDT))) + { + /* Forget the grid */ + c_ptr->info &= ~(CAVE_MARK); + + /* Notice */ + note_spot(y, x); + } + + /* Process affected monsters */ + if (c_ptr->m_idx) + { + /* Update the monster */ + update_mon(c_ptr->m_idx, FALSE); + } + + /* Redraw */ + lite_spot(y, x); + } + } + } + + if (!get_aim_dir(&dir)) return (FALSE); + + msg_print("The light around you is absorbed... " + "and released in a powerful bolt!"); + fire_bolt(GF_LITE, dir, damroll(light, p_ptr->lev)); + + /* Timeout is set before return */ + + break; + } + /* Horns of DragonKind (Note that these are new egos)*/ + case ACT_BA_FIRE_H: + { + if (!doit) return "large fire ball (300) every 100 turns"; + fire_ball(GF_FIRE, 5, 300, 7); + + o_ptr->timeout = 100; + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + break; + } + case ACT_BA_COLD_H: + { + if (!doit) return "large cold ball (300) every 100 turns"; + fire_ball(GF_COLD, 5, 300, 7); + + o_ptr->timeout = 100; + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + break; + } + case ACT_BA_ELEC_H: + { + if (!doit) return "large lightning ball (300) every 100 turns"; + fire_ball(GF_ELEC, 5, 300, 7); + + o_ptr->timeout = 100; + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + break; + } + case ACT_BA_ACID_H: + { + if (!doit) return "large acid ball (300) every 100 turns"; + fire_ball(GF_ACID, 5, 300, 7); + + o_ptr->timeout = 100; + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + break; + } + + case ACT_SPIN: + { + if (!doit) return "spinning around every 50+d25 turns"; + do_spin(); + + o_ptr->timeout = 50 + randint(25); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Done */ + break; + } + case ACT_NOLDOR: + { + if (!doit) return "detect treasure every 10+d20 turns"; + detect_treasure(DEFAULT_RADIUS); + + o_ptr->timeout = 10 + randint(20); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Done */ + break; + } + case ACT_SPECTRAL: + { + if (!doit) return "wraith-form every 50+d50 turns"; + if (!p_ptr->wraith_form) + { + set_shadow(20 + randint(20)); + } + else + { + set_shadow(p_ptr->tim_wraith + randint(20)); + } + + o_ptr->timeout = 50 + randint(50); + + /* Window stuff */ + p_ptr->window |= PW_INVEN | PW_EQUIP; + + /* Done */ + break; + } + case ACT_JUMP: + { + if (!doit) return "phasing every 10+d10 turns"; + teleport_player(10); + o_ptr->timeout = 10 + randint(10); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + + /* Done */ + break; + } + + case ACT_DEST_TELE: + { + if (!doit) return "teleportation and destruction of the ring"; + if (!item) + { + msg_print("You can't activate this when it's there!"); + } + if (get_check("This will destroy the ring. Do you wish to continue? ")) + { + msg_print("The ring explodes into a space distortion."); + teleport_player(200); + + /* It explodes, doesn't it ? */ + take_hit(damroll(2, 10), "an exploding ring"); + + inc_stack_size_ex(item, -255, OPTIMIZE, NO_DESCRIBE); + } + + break; + } + /*amulet of serpents dam 100, rad 2 timeout 40+d60 */ + case ACT_BA_POIS_4: + { + if (!doit) return "venom breathing every 40+d60 turns"; + /* Get a direction for breathing (or abort) */ + if (!get_aim_dir(&dir)) break; + + msg_print("You breathe venom..."); + fire_ball(GF_POIS, dir, 100, 2); + + o_ptr->timeout = rand_int(60) + 40; + + /* Window stuff */ + p_ptr->window |= PW_INVEN | PW_EQUIP; + + /* Done */ + break; + } + /*rings of X 50,50+d50 dur 20+d20 */ + case ACT_BA_COLD_4: + { + if (!doit) return "ball of cold and resist cold every 50+d50 turns"; + /* Get a direction for breathing (or abort) */ + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_COLD, dir, 50, 2); + (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20); + + o_ptr->timeout = rand_int(50) + 50; + + break; + } + + case ACT_BA_FIRE_4: + { + if (!doit) return "ball of fire and resist fire every 50+d50 turns"; + /* Get a direction for breathing (or abort) */ + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_FIRE, dir, 50, 2); + (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20); + + o_ptr->timeout = rand_int(50) + 50; + + break; + } + case ACT_BA_ACID_4: + { + if (!doit) return "ball of acid and resist acid every 50+d50 turns"; + /* Get a direction for breathing (or abort) */ + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_ACID, dir, 50, 2); + (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20); + + o_ptr->timeout = rand_int(50) + 50; + + break; + } + + case ACT_BA_ELEC_4: + { + if (!doit) return "ball of lightning and resist lightning every 50+d50 turns"; + /* Get a direction for breathing (or abort) */ + if (!get_aim_dir(&dir)) break; + + fire_ball(GF_ELEC, dir, 50, 2); + (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20); + + o_ptr->timeout = rand_int(50) + 50; + + break; + } + + case ACT_BR_ELEC: + { + if (!doit) return "breathe lightning (100) every 90+d90 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe lightning."); + fire_ball(GF_ELEC, dir, 100, 2); + + o_ptr->timeout = rand_int(90) + 90; + + break; + } + + case ACT_BR_COLD: + { + if (!doit) return "breathe frost (110) every 90+d90 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe frost."); + fire_ball(GF_COLD, dir, 110, 2); + + o_ptr->timeout = rand_int(90) + 90; + + break; + } + + case ACT_BR_FIRE: + { + if (!doit) return "breathe fire (200) every 90+d90 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe fire."); + fire_ball(GF_FIRE, dir, 200, 2); + + o_ptr->timeout = rand_int(90) + 90; + + break; + } + + case ACT_BR_ACID: + { + if (!doit) return "breathe acid (130) every 90+d90 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe acid."); + fire_ball(GF_ACID, dir, 130, 2); + + o_ptr->timeout = rand_int(90) + 90; + + break; + } + + case ACT_BR_POIS: + { + if (!doit) return "breathe poison gas (150) every 90+d90 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe poison gas."); + fire_ball(GF_POIS, dir, 150, 2); + + o_ptr->timeout = rand_int(90) + 90; + + break; + } + + case ACT_BR_MANY: + { + if (!doit) return "breathe multi-hued (250) every 60+d60 turns"; + if (!get_aim_dir(&dir)) break; + chance = rand_int(5); + msg_format("You breathe %s.", + ((chance == 1) ? "lightning" : + ((chance == 2) ? "frost" : + ((chance == 3) ? "acid" : + ((chance == 4) ? "poison gas" : "fire"))))); + fire_ball(((chance == 1) ? GF_ELEC : + ((chance == 2) ? GF_COLD : + ((chance == 3) ? GF_ACID : + ((chance == 4) ? GF_POIS : GF_FIRE)))), + dir, 250, 2); + + o_ptr->timeout = rand_int(60) + 60; + + break; + } + + case ACT_BR_CONF: + { + if (!doit) return "breathe confusion (120) every 90+d90 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe confusion."); + fire_ball(GF_CONFUSION, dir, 120, 2); + + o_ptr->timeout = rand_int(90) + 90; + + break; + } + + case ACT_BR_SOUND: + { + if (!doit) return "breathe sound (130) every 90+d90 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe sound."); + fire_ball(GF_SOUND, dir, 130, 2); + + o_ptr->timeout = rand_int(90) + 90; + + break; + } + + case ACT_BR_CHAOS: + { + if (!doit) return "breathe chaos/disenchant (220) every 60+d90 turns"; + if (!get_aim_dir(&dir)) break; + chance = rand_int(2); + msg_format("You breathe %s.", + ((chance == 1 ? "chaos" : "disenchantment"))); + fire_ball((chance == 1 ? GF_CHAOS : GF_DISENCHANT), + dir, 220, 2); + + o_ptr->timeout = rand_int(90) + 60; + + break; + } + + case ACT_BR_SHARD: + { + if (!doit) return "breathe sound/shards (230) every 60+d90 turns"; + if (!get_aim_dir(&dir)) break; + chance = rand_int(2); + msg_format("You breathe %s.", + ((chance == 1 ? "sound" : "shards"))); + fire_ball((chance == 1 ? GF_SOUND : GF_SHARDS), + dir, 230, 2); + + o_ptr->timeout = rand_int(90) + 60; + + break; + } + + case ACT_BR_BALANCE: + { + if (!doit) return "breathe balance (250) every 60+d90 turns"; + if (!get_aim_dir(&dir)) break; + chance = rand_int(4); + msg_format("You breathe %s.", + ((chance == 1) ? "chaos" : + ((chance == 2) ? "disenchantment" : + ((chance == 3) ? "sound" : "shards")))); + fire_ball(((chance == 1) ? GF_CHAOS : + ((chance == 2) ? GF_DISENCHANT : + ((chance == 3) ? GF_SOUND : GF_SHARDS))), + dir, 250, 2); + + o_ptr->timeout = rand_int(90) + 60; + + break; + } + + case ACT_BR_LIGHT: + { + if (!doit) return "breathe light/darkness (200) every 60+d90 turns"; + if (!get_aim_dir(&dir)) break; + chance = rand_int(2); + msg_format("You breathe %s.", + ((chance == 0 ? "light" : "darkness"))); + fire_ball((chance == 0 ? GF_LITE : GF_DARK), dir, 200, 2); + + o_ptr->timeout = rand_int(90) + 60; + + break; + } + case ACT_BR_POWER: + { + if (!doit) return "breathe the elements (300) every 60+d90 turns"; + if (!get_aim_dir(&dir)) break; + msg_print("You breathe the elements."); + fire_ball(GF_MISSILE, dir, 300, 3); + + o_ptr->timeout = rand_int(90) + 60; + + break; + } + case ACT_GROW_MOLD: + { + if (!doit) return "grow mushrooms every 50+d50 turns"; + msg_print("You twirl and spores fly everywhere!"); + for (i = 0; i < 8; i++) + summon_specific_friendly(p_ptr->py, p_ptr->px, p_ptr->lev, SUMMON_BIZARRE1, FALSE); + + o_ptr->timeout = randint(50) + 50; + + break; + } + case ACT_MUSIC: + /* + fall through to unknown, as music should be + handled by calling procedure. + */ + + default: + { + msg_format("Unknown activation effect: %d.", spell); + if ( !doit ) return "Unknown Activation"; + return NULL; + } + } + } + + /* Set timeout for junkarts + * Note that I still need to set the timeouts for other + * (non-random) artifacts above + */ + if (is_junkart && doit) + o_ptr->timeout = activation_info[o_ptr->pval2].cost / 10; + + return NULL; +} + + +static bool_ activate_spell(object_type * o_ptr, byte choice) +{ + int mana = 0, gf = 0, mod = 0; + + rune_spell s_ptr; + + + if (choice == 1) + { + gf = o_ptr->pval & 0xFFFF; + mod = o_ptr->pval3 & 0xFFFF; + mana = o_ptr->pval2 & 0xFF; + } + else if (choice == 2) + { + gf = o_ptr->pval >> 16; + mod = o_ptr->pval3 >> 16; + mana = o_ptr->pval2 >> 8; + } + + s_ptr.type = gf; + s_ptr.rune2 = 1 << mod; + s_ptr.mana = mana; + + /* Execute */ + rune_exec(&s_ptr, 0); + + if (choice == 1) o_ptr->timeout = mana * 5; + if (choice == 2) o_ptr->xtra2 = mana * 5; + + return (TRUE); +} diff --git a/src/cmd7.c b/src/cmd7.c deleted file mode 100644 index c9be0c66..00000000 --- a/src/cmd7.c +++ /dev/null @@ -1,7632 +0,0 @@ -/* File: cmd7.c */ - -/* Purpose: More Class commands */ - -/* - * Copyright (c) 1999 Dark God - * - * 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 "angband.h" - -#include "quark.h" -#include "hooks.h" - -/* - * Describe class powers of Mindcrafters - * - * 'p' points to a 80 byte long buffer - */ -void mindcraft_info(char *p, int power) -{ - int plev = get_skill(SKILL_MINDCRAFT); - - - /* Clear buffer */ - strcpy(p, ""); - - /* Fill the buffer with requested power description */ - switch (power) - { - case 0: - strnfmt(p, 80, " rad %d", DEFAULT_RADIUS); - break; - case 1: - strnfmt(p, 80, " dam %dd%d", 3 + ((plev - 1) / 4), 3 + plev / 15); - break; - case 2: - strnfmt(p, 80, " range %d", (plev < 25 ? 10 : plev + 2 + p_ptr->to_s * 3)); - break; - case 3: - strnfmt(p, 80, " range %d", plev * 5); - break; - case 4: - strnfmt(p, 80, " power %d", plev * (plev < 30 ? 1 : 2)); - break; - case 5: - if (plev > 20) - strnfmt(p, 80, " dam %dd8 rad %d", 8 + ((plev - 5) / 4), (plev - 20)/8 + 1); - else - strnfmt(p, 80, " dam %dd8", 8 + ((plev - 5) / 4)); - break; - case 6: - strnfmt(p, 80, " dur %d", plev); - break; - case 7: - break; - case 8: - if (plev < 25) - strnfmt(p, 80, " dam %d rad %d", (3 * plev) / 2, 2 + (plev / 10)); - else - strnfmt(p, 80, " dam %d", plev * ((plev - 5) / 10 + 1)); - break; - case 9: - strnfmt(p, 80, " dur 11-%d", 10 + plev + plev / 2); - break; - case 10: - strnfmt(p, 80, " dam %dd6 rad %d", plev / 2, 0 + (plev - 25) / 10); - break; - case 11: - strnfmt(p, 80, " dam %d rad %d", plev * (plev > 39 ? 4 : 3), 3 + plev / 10); - break; - } -} - - -/* - * Describe class powers of Mimics - * - * 'p' points to a 80 byte long buffer - */ -void mimic_info(char *p, int power) -{ - int plev = get_skill(SKILL_MIMICRY); - object_type *o_ptr = &p_ptr->inventory[INVEN_OUTER]; - - /* Clear the buffer */ - strcpy(p, ""); - - /* Fill the buffer with requested power description */ - switch (power) - { - case 0: - strnfmt(p, 80, " dur %d", k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000)); - break; - case 1: - strnfmt(p, 80, " dur %d+d20", 10 + plev); - break; - case 2: - strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev)); - break; - case 3: - strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev)); - break; - case 4: - strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev)); - break; - } -} - -/** - * Show magic powers that user can choose from - */ -static void display_magic_powers( - magic_power *powers, - int max_powers, - void (*power_info)(char *p, int power), - int plev, - int cast_stat, - int y, - int x) -{ - char psi_desc[80]; - magic_power spell; - int i; - int chance = 0; - int minfail = 0; - char comment[80]; - - /* Display a list of spells */ - prt("", 1, x); - prt("", y, x); - put_str("Name", y, x + 5); - put_str("Lv Mana Fail Info", y, x + 35); - - /* Dump the spells */ - for (i = 0; i < max_powers; i++) - { - /* Access the spell */ - spell = powers[i]; - if (spell.min_lev > plev) - { - break; - } - - chance = spell.fail; - /* Reduce failure rate by "effective" level adjustment */ - chance -= 3 * (plev - spell.min_lev); - - /* Reduce failure rate by INT/WIS adjustment */ - chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[cast_stat]] - 1); - - /* Not enough mana to cast */ - if (spell.mana_cost > p_ptr->csp) - { - chance += 5 * (spell.mana_cost - p_ptr->csp); - } - - /* Extract the minimum failure rate */ - minfail = adj_mag_fail[p_ptr->stat_ind[cast_stat]]; - - /* Failure rate */ - chance = clamp_failure_chance(chance, minfail); - - /* Get info */ - power_info(comment, i); - - /* Dump the spell --(-- */ - strnfmt(psi_desc, 80, " %c) %-30s%2d %4d %3d%%%s", - I2A(i), spell.name, - spell.min_lev, spell.mana_cost, chance, comment); - prt(psi_desc, y + i + 1, x); - } - - /* Clear the bottom line */ - prt("", y + i + 1, x); -} - -/* - * Allow user to choose a magic power. - * - * If a valid spell is chosen, saves it in '*sn' and returns TRUE - * If the user hits escape, returns FALSE, and set '*sn' to -1 - * If there are no legal choices, returns FALSE, and sets '*sn' to -2 - * - * The "prompt" should be "cast", "recite", or "study" - * The "known" should be TRUE for cast/pray, FALSE for study - * - * nb: This function has a (trivial) display bug which will be obvious - * when you run it. It's probably easy to fix but I haven't tried, - * sorry. - */ -static bool_ get_magic_power(int *sn, magic_power *powers, int max_powers, - void (*power_info)(char *p, int power), int plev, int cast_stat) -{ - int i; - - int num = 0; - - int y = 2; - - int x = 18; - - int info; - - char choice; - - char out_val[160]; - - cptr p = "power"; - - magic_power spell; - - bool_ flag; - - - /* Assume cancelled */ - *sn = ( -1); - - /* Get the spell, if available */ - if (repeat_pull(sn)) - { - /* Verify the spell */ - if (powers[*sn].min_lev <= plev) - { - /* Success */ - return (TRUE); - } - } - - /* Nothing chosen yet */ - flag = FALSE; - - /* Count number of powers that satisfies minimum plev requirement */ - for (i = 0; i < max_powers; i++) - { - if (powers[i].min_lev <= plev) - { - num++; - } - } - - /* Build a prompt (accept all spells) */ - strnfmt(out_val, 78, "(%^ss %c-%c, ESC=exit, %c-%c=Info) Use which %s? ", - p, I2A(0), I2A(num - 1), toupper(I2A(0)), toupper(I2A(num - 1)), p); - - /* Save the screen */ - character_icky = TRUE; - Term_save(); - - /* Show the list */ - display_magic_powers(powers, max_powers, power_info, plev, cast_stat, y, x); - - /* Get a spell from the user */ - while (!flag && get_com(out_val, &choice)) - { - /* Note verify */ - info = (isupper(choice)); - - /* Lowercase */ - if (info) choice = tolower(choice); - - /* Extract request */ - i = (islower(choice) ? A2I(choice) : -1); - - /* Totally Illegal */ - if ((i < 0) || (i >= num)) - { - bell(); - continue; - } - - /* Save the spell index */ - spell = powers[i]; - - /* Provides info */ - if (info) - { - c_prt(TERM_L_BLUE, spell.desc, 1, 0); - - /* Restore the screen */ - inkey(); - Term_load(); - character_icky = FALSE; - - /* Redisplay choices */ - display_magic_powers(powers, max_powers, power_info, plev, cast_stat, y, x); - continue; - } - - /* Stop the loop */ - flag = TRUE; - } - - /* Restore the screen */ - Term_load(); - character_icky = FALSE; - - /* Abort if needed */ - if (!flag) return (FALSE); - - /* Save the choice */ - (*sn) = i; - - - repeat_push(*sn); - - /* Success */ - return (TRUE); -} - - -/* - * do_cmd_cast calls this function if the player's class - * is 'mindcrafter'. - */ -void do_cmd_mindcraft(void) -{ - int n = 0, b = 0; - - int chance; - - int dir; - - int minfail = 0; - - int plev = get_skill(SKILL_MINDCRAFT); - - magic_power spell; - - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - - /* not if confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - /* get power */ - if (!get_magic_power(&n, mindcraft_powers, MAX_MINDCRAFT_POWERS, - mindcraft_info, plev, A_WIS)) return; - - spell = mindcraft_powers[n]; - - /* Verify "dangerous" spells */ - if (spell.mana_cost > p_ptr->csp) - { - /* Warning */ - msg_print("You do not have enough mana to use this power."); - - /* Verify */ - if (!get_check("Attempt it anyway? ")) return; - } - - /* Spell failure chance */ - chance = spell.fail; - - /* Reduce failure rate by "effective" level adjustment */ - chance -= 3 * (get_skill(SKILL_MINDCRAFT) - spell.min_lev); - - /* Reduce failure rate by INT/WIS adjustment */ - chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_WIS]] - 1); - - /* Not enough mana to cast */ - if (spell.mana_cost > p_ptr->csp) - { - chance += 5 * (spell.mana_cost - p_ptr->csp); - } - - /* Extract the minimum failure rate */ - minfail = adj_mag_fail[p_ptr->stat_ind[A_WIS]]; - - /* Failure rate */ - chance = clamp_failure_chance(chance, minfail); - - /* Failed spell */ - if (rand_int(100) < chance) - { - if (flush_failure) flush(); - - msg_format("You failed to concentrate hard enough!"); - - sound(SOUND_FAIL); - - if (randint(100) < (chance / 2)) - { - /* Backfire */ - b = randint(100); - if (b < 5) - { - msg_print("Oh, no! Your mind has gone blank!"); - lose_all_info(); - } - else if (b < 15) - { - msg_print("Weird visions seem to dance before your eyes..."); - set_image(p_ptr->image + 5 + randint(10)); - } - else if (b < 45) - { - msg_print("Your brain is addled!"); - set_confused(p_ptr->confused + randint(8)); - } - else if (b < 90) - { - set_stun(p_ptr->stun + randint(8)); - } - else - { - /* Mana storm */ - msg_print("Your mind unleashes its power in an uncontrollable storm!"); - project(1, 2 + plev / 10, p_ptr->py, p_ptr->px, plev * 2, - GF_MANA, PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM); - p_ptr->csp = MAX(0, p_ptr->csp - plev * MAX(1, plev / 10)); - } - } - } - - /* Successful spells */ - else - { - sound(SOUND_ZAP); - - /* spell code */ - switch (n) - { - /* Precog */ - case 0: - { - /* Magic mapping */ - if (plev > 44) - { - wiz_lite(); - } - else if (plev > 19) - { - map_area(); - } - - /* Detection */ - if (plev < 30) - { - b = detect_monsters_normal(DEFAULT_RADIUS); - if (plev > 14) b |= detect_monsters_invis(DEFAULT_RADIUS); - if (plev > 4) b |= detect_traps(DEFAULT_RADIUS); - } - else - { - b = detect_all(DEFAULT_RADIUS); - } - - /* Telepathy */ - if (plev > 24) - { - set_tim_esp(p_ptr->tim_esp + plev); - - /* If plvl >= 40, we should have permanent ESP */ - } - - if (!b) msg_print("You feel safe."); - - break; - } - - /* Mindblast */ - case 1: - { - if (!get_aim_dir(&dir)) return; - - if (randint(100) < plev * 2) - { - fire_beam(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15))); - } - else - { - fire_ball(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)), 0); - } - - break; - } - - /* Minor displace */ - case 2: - { - if (plev < 25) - { - teleport_player(10); - } - else - { - int ii, ij; - - if (dungeon_flags2 & DF2_NO_TELEPORT) - { - msg_print("Not on special levels!"); - break; - } - - msg_print("You open a Void Jumpgate. Choose a destination."); - - if (!tgt_pt(&ii, &ij)) return; - p_ptr->energy -= 60 - plev; - - if (!cave_empty_bold(ij, ii) || - (cave[ij][ii].info & CAVE_ICKY) || - (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2 + (p_ptr->to_s*3)) || - (rand_int(plev * plev / 2) == 0)) - { - msg_print("You fail to exit the void correctly!"); - p_ptr->energy -= 100; - get_pos_player(10 + p_ptr->to_s / 2, &ij, &ii); - } - - cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN); - cave_set_feat(ij, ii, FEAT_BETWEEN); - cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8); - cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8); - } - - break; - } - - /* Major displace */ - case 3: - { - if (plev > 29) banish_monsters(plev); - teleport_player(plev * 5); - - break; - } - - /* Domination */ - case 4: - { - if (plev < 30) - { - if (!get_aim_dir(&dir)) return; - fire_ball(GF_DOMINATION, dir, plev, 0); - } - else - { - charm_monsters(plev * 2); - } - - break; - } - - /* Fist of Force --- not 'true' TK */ - case 5: - { - if (!get_aim_dir(&dir)) return; - fire_ball(GF_SOUND, dir, damroll(8 + ((plev - 5) / 4), 8), - (plev > 20 ? (plev - 20) / 8 + 1 : 0)); - - break; - } - - /* Character Armour */ - case 6: - { - set_shield(p_ptr->shield + plev, plev, 0, 0, 0); - if (plev > 14) set_oppose_acid(p_ptr->oppose_acid + plev); - if (plev > 19) set_oppose_fire(p_ptr->oppose_fire + plev); - if (plev > 24) set_oppose_cold(p_ptr->oppose_cold + plev); - if (plev > 29) set_oppose_elec(p_ptr->oppose_elec + plev); - if (plev > 34) set_oppose_pois(p_ptr->oppose_pois + plev); - - break; - } - - /* Psychometry */ - case 7: - { - ident_spell(); - break; - } - - /* Mindwave */ - case 8: - { - msg_print("Mind-warping forces emanate from your brain!"); - if (plev < 25) - { - project(0, 2 + plev / 10, p_ptr->py, p_ptr->px, - (plev*3) / 2, GF_PSI, PROJECT_KILL); - } - else - { - (void)mindblast_monsters(plev * ((plev - 5) / 10 + 1)); - } - - break; - } - - /* Adrenaline */ - case 9: - { - set_afraid(0); - set_stun(0); - hp_player(plev); - - b = 10 + randint((plev * 3) / 2); - - if (plev < 35) - { - set_hero(p_ptr->hero + b); - } - else - { - set_shero(p_ptr->shero + b); - } - - if (!p_ptr->fast) - { - /* Haste */ - (void)set_fast(b, plev / 5); - } - else - { - (void)set_fast(p_ptr->fast + b, plev / 5); - } - - break; - } - - /* Psychic Drain */ - case 10: - { - if (!get_aim_dir(&dir)) return; - - b = damroll(plev / 2, 6); - - if (fire_ball(GF_PSI_DRAIN, dir, b, 0 + (plev - 25) / 10)) - { - p_ptr->energy -= randint(150); - } - - break; - } - - /* Telekinesis */ - case 11: - { - msg_print("A wave of pure physical force radiates out from your body!"); - project(0, 3 + plev / 10, p_ptr->py, p_ptr->px, - plev * (plev > 39 ? 4 : 3), GF_TELEKINESIS, - PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID); - - break; - } - - default: - { - msg_print("Zap?"); - - break; - } - } - } - - /* Take a turn */ - energy_use = 100; - - /* Sufficient mana */ - if (spell.mana_cost <= p_ptr->csp) - { - /* Use some mana */ - p_ptr->csp -= spell.mana_cost; - } - - /* Over-exert the player */ - else - { - int oops = spell.mana_cost - p_ptr->csp; - - /* No mana left */ - p_ptr->csp = 0; - p_ptr->csp_frac = 0; - - /* Message */ - msg_print("You faint from the effort!"); - - /* Hack -- Bypass free action */ - (void)set_paralyzed(randint(5 * oops + 1)); - - /* Damage WIS (possibly permanently) */ - if (rand_int(100) < 50) - { - bool_ perm = (rand_int(100) < 25); - - /* Message */ - msg_print("You have damaged your mind!"); - - /* Reduce constitution */ - (void)dec_stat(A_WIS, 15 + randint(10), perm); - } - } - - /* Redraw mana */ - p_ptr->redraw |= (PR_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); -} - - -static int get_mimic_chance(int mimic) -{ - s32b chance; - - chance = get_mimic_level(mimic); - chance *= 3; - - chance -= get_skill_scale(SKILL_MIMICRY, 150); - chance -= 3 * adj_mag_stat[p_ptr->stat_ind[A_DEX]]; - - /* Return the chance */ - return clamp_failure_chance(chance, 2); -} - - -void do_cmd_mimic_lore() -{ - int fail; - - object_type *o_ptr; - - - /* Player has to be able to see */ - if (p_ptr->blind || no_lite()) - { - msg_print("You cannot see!"); - return; - } - - /* No transformations when confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - - /* Already in a mimic form -- Allow cancelling */ - if (p_ptr->mimic_form) - { - msg_print("You morph back to your natural form!"); - - set_mimic(0, 0, 0); - } - - /* Not in mimic forms -- Allow transformations */ - else - { - o_ptr = &p_ptr->inventory[INVEN_OUTER]; - - if ((o_ptr->tval != TV_CLOAK) || (o_ptr->sval != SV_MIMIC_CLOAK)) - { - msg_print("You are not wearing any cloaks of mimicry."); - return; - } - - /* Calculate failure rate */ - fail = get_mimic_chance(o_ptr->pval2); - - if (fail > 75) - { - msg_print("You feel uneasy with this shape-change."); - - if (!get_check("Try it anyway? ")) return; - } - - /* Fumble */ - if (randint(100) < fail) - { - msg_print("Your shape-change goes horribly wrong!"); - - if (randint(100) < p_ptr->skill_sav) - { - msg_print("You manage to wrest your body back under control."); - return; - } - - set_mimic(30, resolve_mimic_name("Abomination"), get_skill(SKILL_MIMICRY)); - } - - /* Success */ - else - { - set_mimic(k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000), o_ptr->pval2, get_skill(SKILL_MIMICRY)); - } - } - - - /* Redraw title */ - p_ptr->redraw |= (PR_TITLE); - - /* Recalculate bonuses */ - p_ptr->update |= (PU_BONUS); -} - -static bool_ mimic_forbid_travel(const char *fmt) -{ - u32b value = p_ptr->mimic_extra >> 16; - u32b att = p_ptr->mimic_extra & 0xFFFF; - - if(value > 0 && (att & CLASS_ARMS || att & CLASS_LEGS)) - { - msg_print("You had best not travel with your extra limbs."); - return TRUE; - } - - return FALSE; -} - -/* - * do_cmd_cast calls this function if the player's class - * is 'mimic'. - */ -void do_cmd_mimic(void) -{ - int n = 0, b = 0; - - int fail; - - int minfail = 0; - - int plev = get_skill(SKILL_MIMICRY); - - magic_power spell; - - static bool_ added_hooks = FALSE; - if(!added_hooks) - { - add_hook(HOOK_FORBID_TRAVEL, mimic_forbid_travel, "mimic_forbid_travel"); - added_hooks = TRUE; - } - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - - /* not if confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - /* get power */ - if (!get_magic_power(&n, mimic_powers, MAX_MIMIC_POWERS, mimic_info, - plev, A_DEX)) return; - - spell = mimic_powers[n]; - - /* Verify "dangerous" spells */ - if (spell.mana_cost > p_ptr->csp) - { - /* Warning */ - msg_print("You do not have enough mana to use this power."); - - /* Verify */ - if (!get_check("Attempt it anyway? ")) return; - } - - /* Spell failure chance */ - fail = spell.fail; - - /* Reduce failure rate by "effective" level adjustment */ - fail -= 3 * (plev - spell.min_lev); - - /* Reduce failure rate by INT/WIS adjustment */ - fail -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1); - - /* Not enough mana to cast */ - if (spell.mana_cost > p_ptr->csp) - { - fail += 5 * (spell.mana_cost - p_ptr->csp); - } - - /* Extract the minimum failure rate */ - minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]]; - - /* Minimum failure rate */ - if (fail < minfail) fail = minfail; - - /* Stunning makes spells harder */ - if (p_ptr->stun > 50) fail += 25; - else if (p_ptr->stun) fail += 15; - - /* Always a 5 percent chance of working */ - if (fail > 95) fail = 95; - - /* Failed spell */ - if (rand_int(100) < fail) - { - if (flush_failure) flush(); - - msg_format("You failed to concentrate hard enough!"); - - sound(SOUND_FAIL); - - if (randint(100) < (fail / 2)) - { - /* Backfire */ - b = randint(100); - - if (b < 5) - { - msg_print("Oh, no! Your mind has gone blank!"); - lose_all_info(); - } - else if (b < 15) - { - msg_print("Weird visions seem to dance before your eyes..."); - set_image(p_ptr->image + 5 + randint(10)); - } - else if (b < 45) - { - msg_print("Your brain is addled!"); - set_confused(p_ptr->confused + randint(8)); - } - else - { - set_stun(p_ptr->stun + randint(8)); - } - } - } - - /* Successful spells */ - else - { - sound(SOUND_ZAP); - - /* spell code */ - switch (n) - { - /* Mimic */ - case 0: - { - do_cmd_mimic_lore(); - - break; - } - - /* Invisibility */ - case 1: - { - int ii = 10 + plev + randint(20) + p_ptr->to_s; - - set_invis(p_ptr->tim_invisible + ii, 50); - set_tim_invis(p_ptr->tim_invisible + ii); - - break; - } - - /* Legs Mimicry */ - case 2: - { - /* Extract the value and the flags */ - u32b value = p_ptr->mimic_extra >> 16; - u32b att = p_ptr->mimic_extra & 0xFFFF; - - /* Clear useless things */ - att &= ~(CLASS_ARMS); - att &= ~(CLASS_WALL); - - if (att & CLASS_LEGS) - { - value += 50 + randint(50 + (2 * plev)); - } - else - { - msg_print("You mimic a new pair of legs."); - - value = 50 + randint(50 + (2 * plev)); - att |= (CLASS_LEGS); - } - - if (value > 10000) value = 10000; - - p_ptr->mimic_extra = att + (value << 16); - p_ptr->update |= (PU_BODY); - - break; - } - - /* Wall Mimicry */ - case 3: - { - /* Extract the value and the flags */ - u32b value = p_ptr->mimic_extra >> 16; - u32b att = p_ptr->mimic_extra & 0xFFFF; - - /* Clear useless things */ - att &= ~(CLASS_ARMS); - att &= ~(CLASS_LEGS); - - if (att & CLASS_WALL) - { - value += 50 + randint(50 + (2 * plev)); - } - else - { - msg_print("You grow an affinity for walls."); - - value = 50 + randint(50 + (2 * plev)); - att |= (CLASS_WALL); - } - - if (value > 10000) value = 10000; - - p_ptr->mimic_extra = att + (value << 16); - p_ptr->update |= (PU_BODY); - - break; - } - - case 4: /* Arms Mimicry */ - { - /* Extract the value and the flags */ - u32b value = p_ptr->mimic_extra >> 16; - u32b att = p_ptr->mimic_extra & 0xFFFF; - - /* Clear useless things */ - att &= ~(CLASS_LEGS); - att &= ~(CLASS_WALL); - - if (att & CLASS_ARMS) - { - value += 50 + randint(50 + (2 * plev)); - } - else - { - msg_print("You mimic a new pair of arms."); - - value = 50 + randint(50 + (2 * plev)); - att |= (CLASS_ARMS); - } - - if (value > 10000) value = 10000; - - p_ptr->mimic_extra = att + (value << 16); - p_ptr->update |= (PU_BODY); - - break; - } - - default: - { - msg_print("Zap?"); - - break; - } - } - } - - - /* Take a turn */ - energy_use = 100; - - /* Sufficient mana */ - if (spell.mana_cost <= p_ptr->csp) - { - /* Use some mana */ - p_ptr->csp -= spell.mana_cost; - } - - /* Over-exert the player */ - else - { - int oops = spell.mana_cost - p_ptr->csp; - - /* No mana left */ - p_ptr->csp = 0; - p_ptr->csp_frac = 0; - - /* Message */ - msg_print("You faint from the effort!"); - - /* Hack -- Bypass free action */ - (void)set_paralyzed(randint(5 * oops + 1)); - - /* Damage WIS (possibly permanently) */ - if (rand_int(100) < 50) - { - bool_ perm = (rand_int(100) < 25); - - /* Message */ - msg_print("You have damaged your mind!"); - - /* Reduce constitution */ - (void)dec_stat(A_DEX, 15 + randint(10), perm); - } - } - - /* Redraw mana */ - p_ptr->redraw |= (PR_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); -} - - -/* - * do_cmd_cast calls this function if the player's class - * is 'beastmaster'. - */ -void do_cmd_beastmaster(void) -{ - int plev = p_ptr->lev, i, num; - - monster_type *m_ptr; - - - /* Process the monsters (backwards) */ - num = 0; - for (i = m_max - 1; i >= 1; i--) - { - /* Access the monster */ - m_ptr = &m_list[i]; - - if (m_ptr->status == MSTATUS_PET) - { - num++; - } - } - - if (num < plev * 2) - { - /* XXX XXX */ - if (rand_int(80-(plev) - p_ptr->stat_use[5]-p_ptr->to_s) < 20) - { - summon_specific_friendly(p_ptr->py, p_ptr->px, plev, rand_int(plev / 2), FALSE); - } - } - else msg_print("You can't summon more pets"); - - /* Take a turn */ - if (is_magestaff()) energy_use = 80; - else energy_use = 100; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); -} - - -/* - * Set of variables and functions to create an artifact - */ - - -/* LOG2 is a constant (compile-time) method of converting a single - * set bit into a number. Works well, but for variable (runtime) - * expressions, use a loop instead.. much smaller code*/ -#define LOG2(x) ( (x) & 0xFFFF? BLOG16(x) : BLOG16((x)>>16) + 16 ) -#define BLOG16(x) ( (x) & 0xFF ? BLOG8(x) : BLOG8 ((x)>>8 ) + 8 ) -#define BLOG8(x) ( (x) & 0xF ? BLOG4(x) : BLOG4 ((x)>>4 ) + 4 ) -#define BLOG4(x) ( (x) & 0x3 ? BLOG2(x) : BLOG2 ((x)>>2 ) + 2 ) -#define BLOG2(x) ( (x) & 0x1 ? 0 : 1 ) - -int flags_select[32*5]; -int activation_select; - -/* Return true if the player is wielding the philosopher's stone - */ -bool_ alchemist_has_stone(void) -{ - if (p_ptr->inventory[INVEN_LITE].name1 == 209) - return TRUE; - else - return FALSE; -} - -/* - Display a group of flags from a_select flags, and return - the number of flags displayed (even invisible ones) - */ -int show_flags(byte group, int pval) -{ - int i, x, color = TERM_WHITE; - int items = 0; - - char ttt[80]; - - Term_clear(); - - group++; /* Adjust - no zero group */ - - for ( i = 0 ; a_select_flags[i].group ; i++) - { - if (a_select_flags[i].group != group) - continue; - - if (a_select_flags[i].xp == 0) - break; - else - { - sprintf(ttt, "%c) %s", - (items < 26) ? I2A(items) : ('0' + items - 26), - al_name + a_select_flags[i].desc); - if ( wizard || alchemist_has_stone()) - sprintf(ttt, "%c) %s (exp " FMTu32b ")", - (items < 26) ? I2A(items) : ('0' + items - 26), - al_name + a_select_flags[i].desc, - a_select_flags[i].xp); - - /* Note: Somebody is VERY clever, and it wasn't me. Text printed as - * TERM_DARK is actually printed as TERM_BLUE *SPACES* to prevent the - * player from using a 'cut-and-paste' enabled terminal to see - * what he shouldn't. Thus, simply setting the color to TERM_DARK - * will entirely prevent the unspoiled player from knowing that it's - * even possible. */ - - switch (flags_select[i]) - { - case 1: - color = TERM_YELLOW; - break; /* Flag was set by the player (just now)*/ - case 0: - color = TERM_WHITE; - break; /* This flag can be set, player is 'aware' of it*/ - case - 1: - color = TERM_L_GREEN; - break; /* Flag is already set*/ - case - 2: - color = TERM_DARK; - break; /* Invisible option */ - case - 3: - color = TERM_RED; - break; /* Flag is set, but player isn't 'aware' of it */ - case - 4: - color = TERM_L_DARK; - break; /* Flag is not set, player is 'aware', but it's beyond thier skill */ - default: - color = TERM_DARK; - break; /* Just in Case*/ - } - } - /* For alchemists who have the stone, at least show all the flags... */ - if ((alchemist_has_stone() || wizard) && color == TERM_DARK) - color = TERM_BLUE; - - if (items < 16) x = 5; - else x = 45; - c_prt(color, ttt, ((items < 16) ? items : items - 16) + 5, x); - items++; - - } - return items; -} - -void show_levels(void) -{ - Term_clear(); - c_prt(TERM_WHITE, "[a] Stats, sustains, luck, speed, vision, etc. ", 3, 10); - c_prt(TERM_WHITE, "[b] Misc. (Auras, light, see invis, etc) ", 4, 10); - c_prt(TERM_WHITE, "[c] Weapon Branding ", 5, 10); - c_prt(TERM_WHITE, "[d] Resistances and Immunities ", 6, 10); - c_prt(TERM_WHITE, "[e] ESP and Curses ", 7, 10); - c_prt(TERM_WHITE, "[f] Activation ", 8, 10); - c_prt(TERM_DARK , "[g] Abilities Gained ", 9, 10); - c_prt(TERM_WHITE, "[h] Display Required Essences and items ", 10, 10); - c_prt(TERM_WHITE, "[i] Done! Finalize and commit changes. ", 11, 10); - /*No need to return anything - if the valid selections change, it'll be a code level change.*/ -} - -s32b get_flags_exp(int pval, int oldpval) -{ - int i; - s32b exp = 0; - - for (i = 0 ; a_select_flags[i].group ; i++ ) - { - if (a_select_flags[i].xp == 0) - break; - else - { - if ( a_select_flags[i].group <= 5 && flags_select[i] ) - { - s32b xp = a_select_flags[i].xp; - int factor = 1, oldfactor = 0; - - /* don't even look at flags which the user can't set - * because they also can't change the pval when a pval- - * dependant flag is set, flags which they can't set - * cannot effect the exp in any way, whether their set or not - */ - if ( flags_select[i] < -1 ) - continue; - if ( flags_select[i] == -1 ) - oldfactor = 1; - - if (a_select_flags[i].pval) - { - /* (1/4)x^2 + x - * I wanted something smaller than x^2 or x^x - * this is because although a ring of speed +10 is - * more than 10 times better than a ring of speed +1, - * I don't think it's 100 times better. More like 30. - * this function yields: - * 1=1 * 2=3 * 3=5 * 4=8 * 5=11 * 6=15 * 7=21 - * 8=24 * 9=29 * 10=35 * 11=41 * 12=48 * 13=55 - * 14=63 * 15=71 * 20=120 * 25=181 * 30=255 - * which I think is acceptable. - * briefly, to get a +30 speed ring, it would be: - * 255*50000 or over 12 million experience - * points. For reference, a level 50 human requires - * 5 million xp. I'm sure it's doable, but it'd be - * *HARD* - * a speed+10 artifact would require 1.75 million. - * much more doable, but not too easily. - */ - factor = (pval * pval / 4 + pval); - if ( flags_select[i] == -1 ) - { - oldfactor = oldpval * oldpval / 4 + oldpval; - } - } - exp += xp * factor - xp * oldfactor; - } - if ( a_select_flags[i].group == 88 && a_select_flags[i].flag == -activation_select ) - { - exp += a_select_flags[i].xp; - } - } - } - if ( alchemist_has_stone() ) exp = exp / 4; - return exp; -} - -/* returns the 'real quantity' of items needed to empower - * a particular flag to a particular pval. - * Note that this routine returns zero for any flag that - * doesn't require some sort of action. - */ -int calc_rqty(int i, int pval, int oldpval) -{ - /* return 0 if flag is greater than size of flags_select && ! activation */ - if ( a_select_flags[i].group > 5 ) - { - if ( activation_select == a_select_flags[i].flag) - return 1; - else - return 0; - } - - /* return 0 if the flag wasn't set */ - if ( flags_select[i] < -1 || flags_select[i] == 0 ) - return 0; - - /* Return change in pval if the flag was already set */ - if ( flags_select[i] == -1 && a_select_flags[i].pval) - return pval - oldpval; - - /* Return pval if the flag will be set this time */ - else if ( a_select_flags[i].pval ) - return pval; - - /* Return 0 if the flag is unknown */ - else if ( flags_select[i] == -1 ) - return 0; - return 1; -} - -/* Handle the various items that creating artifacts requires. - * Mode = 0 to print a description, - * 1 to use up the items - * -1 to check to see if the items exist - * Note that this function is called ONLY from the - * other artifact item helper function. - */ - - -int check_artifact_items(int pval, int oldpval, int mode) -{ - int i, j, k, row = 1 , col = 15, rqty, orqty, trqty; - bool_ good = TRUE; - int temporary = -1; - char ch; - - /* For temporary items, waive the item requirements, - * except for the corpse... */ - for ( j = 0 ; a_select_flags[j].group ; j++) - if (a_select_flags[j].flag == 4*32 && flags_select[j] == 1 ) - temporary = j; - /* Check for enough items */ - for (i = 0; a_select_flags[i].group ; i++) - { - /* For temporary items, ignore - everything except the one item - */ - if (temporary != -1 && i != temporary) - continue; - - /* Calc quantity is done per flag, because - some have a pval, some don't, some where already - set at pval=2, etc - */ - rqty = orqty = calc_rqty(i, pval, oldpval); - - /* If no item is associated with this flag, - or this flag wasn't set or didn't change */ - if ( !a_select_flags[i].rtval || !rqty) - continue; - - for ( k = 0 ; k < INVEN_WIELD ; k++ ) - { - object_type *o_ptr = &p_ptr->inventory[k]; - - /* Note here that an rsval of -1 (which is read is 0xff - for a byte..) matches anything. */ - if (o_ptr->tval == a_select_flags[i].rtval - && (o_ptr->sval == a_select_flags[i].rsval - || a_select_flags[i].rsval == (byte) - 1 ) ) - { - /* Corpse validation is COMPLICATED! - * But at least we don't have to do this twice. - */ - if ( a_select_flags[i].rtval == TV_CORPSE ) - { - bool_ itemgood = TRUE; - - /*Specified race not this one */ - if ( o_ptr->pval2 != a_select_flags[i].rpval && a_select_flags[i].rpval) - continue; - - /* Race flag (any monster who...)*/ - for ( j = 0 ; !a_select_flags[i].rpval && a_select_flags[i].rflag[j] && j < 6 && itemgood ; j++) - { - int flag = a_select_flags[i].rflag[j] / 32; - u32b mask = 1 << (a_select_flags[i].rflag[j] % 32); - - switch (flag) - { - case 0: - if ( !(r_info[o_ptr->pval2].flags1 & mask) ) itemgood = FALSE; - break; - case 1: - if ( !(r_info[o_ptr->pval2].flags2 & mask) ) itemgood = FALSE; - break; - case 2: - if ( !(r_info[o_ptr->pval2].flags3 & mask) ) itemgood = FALSE; - break; - case 3: - if ( !(r_info[o_ptr->pval2].flags4 & mask) ) itemgood = FALSE; - break; - case 4: - if ( !(r_info[o_ptr->pval2].flags5 & mask) ) itemgood = FALSE; - break; - case 5: - if ( !(r_info[o_ptr->pval2].flags6 & mask) ) itemgood = FALSE; - break; - case 6: - if ( !(r_info[o_ptr->pval2].flags7 & mask) ) itemgood = FALSE; - break; - case 7: - if ( !(r_info[o_ptr->pval2].flags8 & mask) ) itemgood = FALSE; - break; - case 8: - if ( !(r_info[o_ptr->pval2].flags9 & mask) ) itemgood = FALSE; - break; - default: - msg_print("This code should never be hit!"); - } - } - if ( ! itemgood ) - continue; - - } - /* Validate pval of good item */ - else if ( a_select_flags[i].rpval) - { - /* Must have matching signs */ - if ( (o_ptr->pval < 0) != (a_select_flags[i].rpval < 0)) - continue; - /* Must be greater than */ - if ( abs(o_ptr->pval) < abs(a_select_flags[i].rpval)) - continue; - } - - trqty = MIN(o_ptr->number, rqty); - rqty -= trqty; - - if ( mode == 1 ) - { - inc_stack_size_ex(k, -trqty, NO_OPTIMIZE, DESCRIBE); - } - }/* if p_ptr->inventory item is acceptable */ - - } /*end of looping through the p_ptr->inventory*/ - - if (rqty) - { - good = FALSE; - /* Oops, we didn't have enough of this object - when actually creating the artifact. - unset this flag - */ - if ( mode == 1 ) - { - flags_select[i] = -4; - } - /* we only return false for mode -1, - * for mode 0 we display stuff, and for - * mode 1 we want to continue destroying things - * even if the player is missing one small item, - * because there's no way to change things now. - * We may have already destroyed a unique corpse, - * or some other hard-to-find item. - */ - if ( mode == -1 ) - return FALSE; - } - - /* Display a description of the required object, if needed */ - /* Note that the tests for good items HAVE to be in a different - place, because otherwise we don't know how many the player - has, as opposed to how many they need. - */ - if ( mode == 0 ) - { - char *o_name = al_name + a_select_flags[i].item_desc; - if (orqty > 1 && a_select_flags[i].pval && a_select_flags[i].item_descp) - o_name = al_name + a_select_flags[i].item_descp; - - if ( rqty ) - { - if ( orqty > 1 ) - c_prt(TERM_RED, format(" you are missing %d of the %d %s", rqty, orqty, o_name), row++, col); - else if ( is_a_vowel(o_name[0])) - c_prt(TERM_RED, format(" you are missing an %s", o_name), row++, col); - else - c_prt(TERM_RED, format(" you are missing a %s", o_name), row++, col); - } - else - { - if ( orqty > 1 ) - c_prt(TERM_GREEN, format(" you have the %d %s", orqty, o_name), row++, col); - else if ( is_a_vowel(o_name[0])) - c_prt(TERM_GREEN, format(" you have an %s", o_name), row++, col); - else - c_prt(TERM_GREEN, format(" you have a %s", o_name), row++, col); - } - - if ( row > 21 ) - { - row = 1; - if (!good) - (void)get_com("You are missing some items:", &ch); - else - (void)get_com("You have these needed items on hand:", &ch); - } - - } - - } /* End of group associated with this a_select_flags entry */ - - if ( mode == 0 ) - { - while ( row < 22 ) - c_prt(TERM_GREEN, " ", row++, col); - if (!good) - (void)get_com("You are missing some items:", &ch); - else - (void)get_com("You have these needed items on hand:", &ch); - } - return good; -} - -/* Display a list of required essences, - * and/or use up the essences. */ -bool_ artifact_display_or_use(int pval, int oldpval, bool_ use) -{ - int essence[MAX_BATERIE_SVAL]; - int essenceh[MAX_BATERIE_SVAL]; - int al_idx, i, j, k; - bool_ enough; - - /* Temporary Items require only one item, and no essences. */ - for ( i = 0 ; a_select_flags[i].group ; i++) - if ( a_select_flags[i].flag == 32*4) - { - if ( use ) - return check_artifact_items(pval, oldpval, 1); - else - return check_artifact_items(pval, oldpval, 0); - } - - for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++ ) - essence[i] = essenceh[i] = 0; - - /* Accumulate a list of required essences */ - for ( al_idx = 0; al_idx < max_al_idx ; al_idx++ ) - if ( alchemist_recipes[al_idx].tval == 0 ) - for ( i = 0 ; a_select_flags[i].group ; i++) - { - int rqty = calc_rqty(i, pval, oldpval); - - /* If the flag isn't being set, rqty will be zero */ - if ( !rqty) - continue; - - if ( alchemist_recipes[al_idx].sval == a_select_flags[i].flag ) - essence[alchemist_recipes[al_idx].sval_essence] += - alchemist_recipes[al_idx].qty * rqty; - } - - /* The essence array now contains a list of all essences - * that will be consumed in the creation of this artifact */ - - /* Check for existence of required quatities of essences. */ - for ( i = 0 ; i < INVEN_WIELD ; i++ ) - { - for ( j = 0 ; j < MAX_BATERIE_SVAL ; j++) - if ( p_ptr->inventory[i].tval == TV_BATERIE && p_ptr->inventory[i].sval == j + 1) - { - essenceh[j] += p_ptr->inventory[i].number; - } - } - - /* Check for enough essences */ - enough = TRUE; - for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++) - if ( essenceh[i] < essence[i] ) - { - enough = FALSE; - break; - } - - /* Check for items */ - if ( enough ) - enough = check_artifact_items(pval, oldpval, -1); - - - /* Display recipe list if they don't have enough, or not enough exp */ - if (!enough || !use ) - { - int row = 1 , col = 15; - bool_ good = FALSE; - char ch; - - /* display of list of required essences */ - /* Note: there are only 12 or so essences, so this list - * will ALWAYS fit on the screen */ - for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++) - if ( essence[i] ) - { - int missing = -MIN(essenceh[i] - essence[i], 0); - good = TRUE; - if ( missing ) - c_prt(TERM_RED, format("%d of the required %d essences of %s", - missing, essence[i], - k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ), - row++, col); - else - c_prt(TERM_GREEN, format("you have the needed %d essences of %s", - essence[i], - k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ), - row++, col); - } - - if (good) - { - /* blank the bottom row */ - c_prt(TERM_WHITE, " ", row++, col); - - /* and wait for a key */ - (void)get_com("You are currently missing:", &ch); - } - - /* Display a list of needed items as well */ - check_artifact_items(pval, oldpval, 0); - - return FALSE; - } - - /* If we get to this point in the code, then the player - * has the required essences and items in their p_ptr->inventory */ - - /* If they do have enough, and they have enough exp, consume them */ - for (i = 0 ; i < MAX_BATERIE_SVAL ; i++) - for ( k = 0 ; k < INVEN_WIELD && essence[i] > 0 ; k++) - if (p_ptr->inventory[k].tval == TV_BATERIE - && p_ptr->inventory[k].sval == i + 1 - && essence[i]) - { - int num = p_ptr->inventory[k].number; - - inc_stack_size_ex(k, MAX( -essence[i], -num), NO_OPTIMIZE, DESCRIBE); - - essence[i] -= MIN(num, essence[i]); - } - - /* Destroy the items needed */ - check_artifact_items(pval, oldpval, 1); - - return TRUE; -} - - -void display_activation_info(int num) -{ - object_type forge; - int i; - - - /* find the a_select_flags number of this activation type... */ - for ( i = 0 ; a_select_flags[i].group ; i++) - if (a_select_flags[i].group == 88 && a_select_flags[i].flag == -num ) - break; - - object_wipe(&forge); - forge.xtra2 = num; - /* Print out various information about this activation... */ - /* min level, experience, required items (and essences) - full description (from activation_aux) */ - if (wizard) - c_prt(TERM_WHITE, format(" number:%d ", num), 5, 5); - else - c_prt(TERM_WHITE, " ", 5, 5); - c_prt(TERM_WHITE, format(" Level:%d ", a_select_flags[i].level), 6, 5); - c_prt(TERM_WHITE, format(" Exp :%d ", a_select_flags[i].xp), 7, 5); - c_prt(TERM_WHITE, format(" Item :%s ", al_name + a_select_flags[i].item_desc), 8, 5); - c_prt(TERM_WHITE, " ", 9, 5); - c_prt(TERM_WHITE, format(" %s ", activation_aux(&forge, 0, 0)), 9, 5); - c_prt(TERM_WHITE, " ", 10, 5); - inkey(); -} - -void select_an_activation(void) -{ - int i, lev, wid, hgt, begin = 0, sel = 0; - u32b max; - cptr act_list[150]; /* currently, ~127 hardcoded activations */ - int act_ref[150]; - char c; - /* How do we want to do this? */ - /* Ideally, we let them select from a list, which includes all the activations that they've ecountered in any form. - Problems with this idea include mainly the lack of any (current) place to store which activations they've seen, and - that they'll not get credit for any seen before we start tracking it. - - So - list is everything. If they select one which they're to low-level for - or if the explicitly request it, we'll display info about this item. - We'll also get our descriptions from the activation_aux(ACT_CONSTANT) - function, because they are more complete, and include even lua-scripted ones. - msg_print("Since the code to actually let you select one isn't here"); - msg_print("You will automatically get the activation 'Dawn'"); - activation_select = ACT_DAWN; - */ - - /* Build a list of available activations at the player's level */ - lev = get_skill(SKILL_ALCHEMY); - for ( i = max = 0 ; max < (sizeof(act_list) / sizeof(cptr)) && a_select_flags[i].group ; i++) - if (a_select_flags[i].group == 88 && a_select_flags[i].level <= lev ) - { - act_ref[max] = -a_select_flags[i].flag; /* Activation number */ - act_list[max++] = al_name + a_select_flags[i].desc; /* Description */ - } - - /* Select from that list, using the util.c function display_list to display the scrolled list */ - /* Note: I think that there is only one other place that uses this function. Should be more! */ - while (1) - { - Term_clear(); - Term_get_size(&wid, &hgt); - - c_prt(TERM_WHITE, "Enter to select, ? for more information, 2 and 8 to scroll ", 0, 0); - display_list(1, 0, hgt - 2, wid - 2, "Select an Activation", act_list, max, begin, sel, TERM_L_GREEN); - - c = inkey(); - - if (c == ESCAPE) break; - else if (c == '8') - { - sel--; - if (sel < 0) - { - sel = max - 1; - begin = max - hgt; - if (begin < 0) begin = 0; - } - if (sel < begin) begin = sel; - } - else if (c == '2') - { - sel++; - if (sel >= (s32b)max) - { - sel = 0; - begin = 0; - } - if (sel >= begin + hgt - 1) begin++; - } - else if (c == '?') - { - display_activation_info(act_ref[sel]); - } - else if (c == '\r') - { - display_activation_info(act_ref[sel]); - activation_select = act_ref[sel]; - return; - } - } - activation_select = 0; -} - - -/* Consume 'num' magic essences and return true. - * If there aren't enough essences, return false */ - -bool_ magic_essence(int num) -{ - int i; - int j = 0; - - for (i = 0; i < INVEN_WIELD; i++) - { - object_type *o_ptr = &p_ptr->inventory[i]; - - /* Count the magic essences */ - if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC)) j += o_ptr->number; - } - - /* Abort if not enough essences. */ - if (j < num) return FALSE; - - /* Consume them */ - i = 0; - j = num; - while (i < INVEN_WIELD) - { - object_type *o_ptr = &p_ptr->inventory[i]; - - if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC)) - { - /* This can lead to invalid object pointer for objects - * that come after the magic essences. Therefore, every - * artifactable object should come before the essences. - */ - j -= o_ptr->number; - inc_stack_size(i, -num); - num = j; - if (num <= 0) break; - /* Stay on this slot; do not increment i. */ - } - else - { - /* Move on to the next slot. */ - i++; - } - } - - /* Sanity check. */ - if (num > 0) - { - msg_format("ERROR: Couldn't destroy %d essences!", num); - return FALSE; - } - - return TRUE; -} - - -void do_cmd_create_artifact(object_type *q_ptr) -{ - int max, i = 0, j, cur_set = 0, abord = FALSE, done = FALSE; - int skill; - s32b exp = 0; - - char out_val[160]; - char choice = 0; - bool_ lockpval = FALSE; - int pval; - int oldpval; - energy_use = 100; - - pval = q_ptr->pval; - oldpval = pval; - skill = get_skill(SKILL_ALCHEMY); - - if ( !pval ) - pval = 1; - /* No activation added on this round */ - activation_select = 0; - - /* Save the current flags */ - for (i = 0 ; a_select_flags[i].group ; i++) - { - if ( a_select_flags[i].flag < 0 || a_select_flags[i].group > 5) - continue; - - flags_select[i] = 0; - - switch (a_select_flags[i].flag / 32) - { - case 0: - if (q_ptr->art_flags1 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; - break; - case 1: - if (q_ptr->art_flags2 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; - break; - case 2: - if (q_ptr->art_flags3 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; - break; - case 3: - if (q_ptr->art_flags4 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; - break; - case 4: - if (q_ptr->art_flags5 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; - break; - case 5: - if (q_ptr->art_esp & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; - break; - default: - /*This will not be hit, inspite of activations, because of the <= 5 above...*/ - break; - } - /* - this would learn about ALL flags.... - if(wizard) - alchemist_known_artifacts[a_select_flags[i].flag/32] = 0xffffffffL; - */ - - /* Set various flags if they haven't *ID*'d an artifact with this flag set.*/ - if ( !(alchemist_known_artifacts[a_select_flags[i].flag / 32] & (1 << (a_select_flags[i].flag % 32)) )) - { - /* If this item has an ability that depends on pval which the player - * cannot set, don't allow them to change the pval either. */ - if ( a_select_flags[i].pval && flags_select[i]) - lockpval = TRUE; - - /* Set the color and set-ablitity of this flag */ - if ( flags_select[i] ) - flags_select[i] = -3; - else - flags_select[i] = -2; - continue; - } - else if ( skill < a_select_flags[i].level ) - { - /* If the alchemist has not passed the skill level for this flag, - Set this flag as unsettable. - */ - if ( flags_select[i]) - lockpval = TRUE; - else - flags_select[i] = -4; - } - } - - /* Save the screen */ - character_icky = TRUE; - Term_save(); - Term_clear(); - - - /* Everlasting love ... ... nevermind :) */ - while ( !done && !abord) - { - c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0); - - /* Display the menu, but don't display it if we just - * displayed a message (it erases the screen, creating a blink message */ - if ( cur_set < 6 || cur_set == 7 ) - show_levels(); - - c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0); - - prt("Enter to accept, Escape to abort", 1, 0); - - abord = !get_com("Play around with which group of powers?[a-g]", &choice); - - if ( choice == ESCAPE) - abord = TRUE; - - if ( abord ) - continue; /*or break, same diff */ - - if ( isalpha(choice)) - { - if (isupper(choice)) - choice = tolower(choice); - cur_set = A2I(choice); - } - else - { - bell(); - continue; - } - - if ( cur_set == 5 ) - { - if (q_ptr->xtra2 && !activation_select - && !get_check("This item already activates! Choose a different activation?")) continue; - select_an_activation(); - exp = get_flags_exp(pval, oldpval); - continue; - } - if ( cur_set == 6 ) - { - msg_print("This option is not available"); - continue; - } - if ( cur_set == 7 ) - { - artifact_display_or_use(pval, oldpval, FALSE); - continue; - } - if ( cur_set == 8 ) - { - if (q_ptr->exp - exp < 0) - msg_print("Not enough experience for the flags you've selected."); - else - done = TRUE; - continue; - } - - if (cur_set < 0 || cur_set > 4 ) - { - bell(); - continue; - } - - - while (!done && !abord) - { - /* Chose the flags */ - exp = 0; - max = show_flags(cur_set, pval); - exp = get_flags_exp(pval, oldpval); - c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0); - - /* Build a prompt (accept all flags) */ - if (max <= 26) - { - /* Build a prompt (accept all flags) */ - strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ", - I2A(0), I2A(max - 1)); - } - else - { - strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ", - I2A(0), '0' + max - 27); - } - c_prt(TERM_L_BLUE, format("Power(I/D to increase/decrease): %d", pval), 3, 0); - - /* Get a spell from the user */ - while (!(done = !get_com(out_val, &choice))) - { - if (choice == 'I') - { - if ( lockpval ) - { - msg_print("You cannot do that - you don't know how!"); - continue; - } - if (q_ptr->exp - exp < 0) - { - msg_print("Not enough experience. Decrease power or deselect flags."); - continue; - } - pval++; - break; - } - else if (choice == 'D') - { - if ( lockpval ) - { - msg_print("You cannot do that - you don't know how!"); - continue; - } - pval--; - if (pval < oldpval) pval = oldpval; - break; - } - else if (choice == '\r' || choice == ESCAPE || choice == ' ') - { - done = TRUE; - break; - } - else if (isalpha(choice)) - { - /* Lowercase */ - if (isupper(choice)) choice = tolower(choice); - - /* Extract request */ - i = (islower(choice) ? A2I(choice) : -1); - } - else - { - i = D2I(choice) + 26; - - /* Illegal */ - if (i < 26) i = -1; - } - - /* Totally Illegal */ - if ((i < 0) || (i >= max)) - { - bell(); - continue; - } - else - { - /*Find the i'th flag in group cur_set...*/ - for ( j = 0 ; a_select_flags[j].group ; j++) - if (a_select_flags[j].group == cur_set + 1) - if (!i--) break; - - if ( flags_select[j] == -4 ) - { - msg_format("You need at least %d skill in alchemy.", - a_select_flags[j].level); - continue; - } - if ( flags_select[j] != 0 && flags_select[j] != 1) - { - bell(); - continue; - } - if (flags_select[j]) flags_select[j] = 0; - else if (!flags_select[j]) - { - if (q_ptr->exp - exp < 0) - { - msg_print("Not enough experience. Decrease power or deselect flags."); - continue; - } - flags_select[j] = 1; - } - break; - } - } - }/*sub-screen select and redraw loop*/ - done = FALSE; - Term_clear(); - }/* main screen (flag select screen) select and redraw loop*/ - - /* Abort if not enough experience, or no flags added */ - if ( q_ptr->exp - exp < 0 || exp == 0 ) - abord = TRUE; - - /* Display the recipe, or use up the essences. - * Note that this has to be done before the screen - * is restored. This is because it's also called from - * within the loop to display the required items. */ - if ( !abord ) - if (!artifact_display_or_use(pval, oldpval, TRUE)) - abord = TRUE; - - /* Restore the screen */ - Term_load(); - character_icky = FALSE; - - /* Return if abort, or missing ingredients */ - if ( abord ) - return; - - /* Actually create the artifact */ - q_ptr->exp -= exp; - q_ptr->art_flags4 &= ~TR4_ART_EXP; - q_ptr->pval = pval; - - /* Just to be sure */ - q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC | - TR3_IGNORE_FIRE | TR3_IGNORE_COLD ); - - { - int now = 0, before = 0; - char dummy_name[80]; - char new_name[80]; - - /* Apply the flags */ - for (i = 0; a_select_flags[i].group ; i++) - { - if (flags_select[i] < 0) - before++; - else if ( flags_select[i] == 1) - { - now++; - switch (a_select_flags[i].flag / 32) - { - case 0: - q_ptr->art_flags1 |= 1 << (a_select_flags[i].flag % 32); - break; - case 1: - q_ptr->art_flags2 |= 1 << (a_select_flags[i].flag % 32); - break; - case 2: - q_ptr->art_flags3 |= 1 << (a_select_flags[i].flag % 32); - break; - case 3: - q_ptr->art_flags4 |= 1 << (a_select_flags[i].flag % 32); - break; - case 4: - q_ptr->art_flags5 |= 1 << (a_select_flags[i].flag % 32); - break; - case 5: - q_ptr->art_esp |= 1 << (a_select_flags[i].flag % 32); - break; - default: - msg_print("error: this code can't ever be hit!"); - } - } - } - - if ( activation_select ) - { - q_ptr->art_flags3 |= TR3_ACTIVATE; - q_ptr->xtra2 = activation_select; - } - - - /* Set the 'show modifier' flag */ - q_ptr->art_flags3 |= TR3_SHOW_MODS; - - /* For temporary items, set a timeout. - * alchemist_skill^2 for now */ - if ( q_ptr->art_flags5 & TR5_TEMPORARY ) - { - int lev = get_skill(SKILL_ALCHEMY); - q_ptr->timeout = lev * lev * 3; - } - - /* Describe the new artifact */ - object_out_desc(q_ptr, NULL, FALSE, TRUE); - - - /* Name the new artifact */ - strcpy(dummy_name, "of an Alchemist"); - if (!(get_string("What do you want to call the artifact? ", dummy_name, 80))) - strcpy(new_name, "of an Alchemist"); - else - { - if ((strncmp(dummy_name, "of ", 3) == 0) || - (strncmp(dummy_name, "Of ", 3) == 0) || - ((dummy_name[0] == '\'') && - (dummy_name[strlen(dummy_name) - 1] == '\''))) - { - strcpy(new_name, dummy_name); - } - else - { - strcpy(new_name, "called '"); - strcat(new_name, dummy_name); - strcat(new_name, "'"); - } - } - /* Identify it fully */ - object_aware(q_ptr); - object_known(q_ptr); - - /* Mark the item as fully known */ - q_ptr->ident |= (IDENT_MENTAL); - q_ptr->ident |= IDENT_STOREB; /* This will be used later on... */ - - /* Save the inscription */ - q_ptr->art_name = quark_add(new_name); - q_ptr->found = OBJ_FOUND_SELFMADE; - - done = FALSE; - while (!done && get_com("Do you want to let this item continue to gain experience?", &choice)) - { - switch (choice) - { - case 'y': - case 'Y': - if (magic_essence(get_skill(SKILL_ALCHEMY))) - q_ptr->art_flags4 |= TR4_ART_EXP; - else - msg_format("Oh, NO! You don't have enough magic essences. You needed %d.", get_skill(SKILL_ALCHEMY)); - done = TRUE; - break; - case 'n': - case 'N': - q_ptr->exp = 0; - done = TRUE; - break; - } - } - - /* Cycle through the p_ptr->inventory, and optimize everything. - * This wasn't done earlier, because if we had, then - * things in the p_ptr->inventory would shift around, and q_ptr - * wouldn't point to the right thing. BUT, at this point - * we don't need q_ptr anymore, so optimizing the p_ptr->inventory - * becomes sane. Sticky bug to figure out, let me tell you. - * Note also that this is cycling backwards - this is so - * that the same effect doesn't cause us to skip items. */ - for ( i = INVEN_WIELD - 1 ; i >= 0 ; i-- ) - inven_item_optimize(i); - } - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); -} - -/* - * Test to see if this tval/sval combo is in the alchemists' - * recipes as a createable item. Used to determine if we - * should extract from it. - */ -bool_ alchemist_exists(int tval, int sval, int ego, int artifact) -{ - int al_idx; - - /* To prevent conflicts with recipes for ego-items. - * artifact not used, simplifies the loop below. */ - if ((tval == 1) || artifact) - return FALSE; - - /*Search for recipes with this tval/sval combo as the final result*/ - for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++) - { - int rtval = alchemist_recipes[al_idx].tval; - int rsval = alchemist_recipes[al_idx].sval; - - /* Accept ego wands and staves since ego is extracted last */ - if (((!ego || tval == TV_WAND || tval == TV_STAFF) && rtval == tval && rsval == sval) || - ( ego && rtval == 1 && rsval == ego)) - { - return TRUE; - } - } - return FALSE; -} - - -/* - * Hook to determine if an object can have things extracted from it. - */ -bool_ item_tester_hook_extractable(object_type *o_ptr) -{ - - /* No artifacts */ - if (artifact_p(o_ptr)) return (FALSE); - - /* No cursed things */ - if (cursed_p(o_ptr)) return (FALSE); - - /* If we REALLY wanted to rebalance alchemists, - * we'd test for 'fully identified this object kind' here. - */ - - return ((o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != 0) - || alchemist_exists(o_ptr->tval, o_ptr->sval, o_ptr->name2, o_ptr->name1)); -} - -/* - * Hook to determine if an object is empowerable (NOT rechargeable) - */ -bool_ item_tester_hook_empower(object_type *o_ptr) -{ - int sval = -1; - int lev = get_skill(SKILL_ALCHEMY); - /* after level 25, can empower ego items to create artifacts - * and double ego items. - * after level 50, can empower artifacts to create powerful artifacts - */ - - /* Never Empower a cursed item */ - if ( cursed_p(o_ptr)) - { - return FALSE; - } - - /* Allow finalizing a self created artifact */ - if (artifact_p(o_ptr) - && (o_ptr->art_flags4 & TR4_ART_EXP) - && !(o_ptr->art_flags4 & TR4_ULTIMATE)) - return TRUE; - - switch ( o_ptr->tval) - { - /* Empowerable objects: Traditional alchemist stuff */ - case TV_WAND: - sval = SV_WAND_NOTHING; - break; - case TV_RING: - sval = SV_RING_NOTHING; - break; - case TV_STAFF: - sval = SV_STAFF_NOTHING; - break; - case TV_BOTTLE: - sval = 1; - break; - case TV_AMULET: - sval = SV_AMULET_NOTHING; - break; - case TV_SCROLL: - sval = SV_SCROLL_NOTHING; - break; - case TV_ROD: - sval = SV_ROD_NOTHING; - break; - case TV_ROD_MAIN: - sval = -1; - break; - case TV_BOOK: - sval = -1; - break; - - /* Ego item stuff */ - /* Disallow ego dragon armour before you can create artifacts.*/ - case TV_DRAG_ARMOR: - if ( lev < 25) - return FALSE; - /* FALL THROUGH! no break here. */ - - /* weapons */ - - case TV_DAEMON_BOOK: - case TV_SWORD: - case TV_HAFTED: - case TV_POLEARM: - case TV_AXE: - case TV_MSTAFF: - - /* misc other items */ - case TV_BOW: - case TV_BOOMERANG: - case TV_INSTRUMENT: - case TV_DIGGING: - case TV_LITE: - - /* Ammo */ - case TV_SHOT: - case TV_ARROW: - case TV_BOLT: - - /* Armor of various sorts */ - case TV_BOOTS: - case TV_GLOVES: - case TV_HELM: - case TV_CROWN: - case TV_SHIELD: - case TV_CLOAK: - case TV_SOFT_ARMOR: - case TV_HARD_ARMOR: - - /* Disallow ANY creation of ego items below level 5*/ - if ( lev < 5) - return FALSE; - - /* empowering an ego item creates an artifact or a - * double ego item, disallow below level 25 */ - if ( lev < 25 && o_ptr->name2) - return FALSE; - - /* Disallow double-ego and artifact unless the character has - * the artifact creation ability. */ - if (!has_ability(AB_CREATE_ART) && - (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b))) - return FALSE; - - /* Otherwise... */ - return TRUE; - - default: - return FALSE; - } - - /* Return to the traditional alchemist objects. - * All ego items and artifacts returning TRUE are accepted as artifactable - * at level 25. If we want double ego non wieldable items (Fireproof Staff - * of Plenty) the artifactable test in do_cmd_alchemist() must be changed, - * e.g. checking if the item is wearable. - * For now, we disallow non-wearable ego-items and artifacts here. - */ - - if ((o_ptr->name2 || artifact_p(o_ptr)) && - o_ptr->tval != TV_RING && o_ptr->tval != TV_AMULET) - return FALSE; - - /* return true if it's a 'of nothing' item; - * does nothing for TV_ROD_MAIN and TV_BOOK - */ - return (sval == o_ptr->sval - - /* or if it's artifactable */ - || ((lev >= 50 || (lev >= 25 && !artifact_p(o_ptr))) && - (o_ptr->tval == TV_RING || o_ptr->tval == TV_AMULET)) - - /* or if it's egoable (note that normal egos start at level 5, wands and such start at 15) */ - || (!o_ptr->name2 && lev >= 15)); -} - -/* Extract a rod tip from a rod */ -void rod_tip_extract(object_type *o_ptr) -{ - object_type *q_ptr; - object_type forge; - - /* Get local object */ - q_ptr = &forge; - - /* Paranoia, return if it's a rod of nothing */ - if (o_ptr->pval == SV_ROD_NOTHING) - return; - - /* Extract the rod tip */ - object_prep(q_ptr, lookup_kind(TV_ROD, o_ptr->pval)); - - q_ptr->number = o_ptr->number; - - object_aware(q_ptr); - object_known(q_ptr); - (void)inven_carry(q_ptr, FALSE); - - /* Remove it from the rod */ - o_ptr->pval = SV_ROD_NOTHING; - - /* Window stuff */ - p_ptr->window |= (PW_INVEN); -} - - -/* Begin & finish an art */ -void do_cmd_toggle_artifact(object_type *o_ptr) -{ - char o_name[80]; - - if (!(o_ptr->art_flags4 & TR4_ART_EXP)) - { - bool_ okay = TRUE; - - if ( !alchemist_has_stone()) - { - msg_print("Creating an artifact will result into a permanent loss of 10 hp."); - if (!get_check("Are you sure you want to do that?")) return; - } - - if (!magic_essence(get_skill(SKILL_ALCHEMY))) - { - msg_format("You need %d magic essences.", get_skill(SKILL_ALCHEMY)); - return; - } - - /* Description */ - object_desc(o_name, o_ptr, FALSE, 0); - - if (o_ptr->number > 1) - { - msg_print("Not enough energy to enchant more than one object!"); - msg_format("%d of your %s %s destroyed!", (o_ptr->number) - 1, o_name, (o_ptr->number > 2 ? "were" : "was")); - o_ptr->number = 1; - } - okay = TRUE; - - if (!okay) return; - - /* he/she got warned */ - p_ptr->hp_mod -= 10; - - /* Ok toggle it */ - o_ptr->art_flags4 |= TR4_ART_EXP; - o_ptr->name2 = 0; - o_ptr->name2b = 0; - o_ptr->art_name = quark_add("Becoming"); - - /* Copy the object_kind flags to the artifact flags. - * Note that this is only needed so that flags set in the - * 'kind' area are visible when finalizing the artifact. - */ - { - u32b f1, f2, f3, f4, f5, esp; - - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - o_ptr->art_flags1 |= f1; - o_ptr->art_flags2 |= f2; - o_ptr->art_flags3 |= f3; - o_ptr->art_flags4 |= f4; - o_ptr->art_flags5 |= f5; - o_ptr->art_esp |= esp; - } - - p_ptr->update |= (PU_HP); - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - } - else - { - do_cmd_create_artifact(o_ptr); - } -} - -/* - * Test to see if they have all the ingredients to create an item. - * (doesn't count base item) - * creates 'tocreate' items (may be -1, but no more than that!) - * if tocreate=0, will return true if the player has enough - * in their p_ptr->inventory to empower that item. - */ -bool_ alchemist_items_check(int tval, int sval, int ego, int tocreate, bool_ message) -{ - int al_idx, j; - bool_ exists = FALSE; - - - for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ ) - if ((ego && alchemist_recipes[al_idx].sval == ego - && alchemist_recipes[al_idx].tval == 1 ) - || (!ego && alchemist_recipes[al_idx].sval == sval - && alchemist_recipes[al_idx].tval == tval)) - { - exists = TRUE; - /* Create the essences */ - if (tocreate > 0) - { - object_type forge; - object_type *o_ptr = &forge; - - object_wipe(o_ptr); - object_prep(o_ptr, lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence)); - o_ptr->number = alchemist_recipes[al_idx].qty * tocreate; - /* Don't bother with apply_magic */ - - /* Randomly decrease the number of essences created */ - if ( randint(3) == 1 - && randint(52) > get_skill(SKILL_ALCHEMY) - && !alchemist_has_stone()) - o_ptr->number /= randint(2) + 1; - if ( o_ptr->number == 0) - continue; - object_aware(o_ptr); - object_known(o_ptr); - if (inven_carry_okay(o_ptr)) - { - int i; - inven_carry(o_ptr, FALSE); - for (i = 0; i < INVEN_WIELD ; i++) - if (p_ptr->inventory[i].tval == o_ptr->tval && p_ptr->inventory[i].sval == o_ptr->sval) - { - if ( message ) - inven_item_describe(i); - break; - } - - } - else - drop_near(o_ptr, 0, p_ptr->py, p_ptr->px); - - o_ptr->ident |= IDENT_STOREB; - } - else if ( tocreate < -1) - { - /*It's not valid to create more than one - * thing at a time, so if it's less than -1, - * it must be time to display a recipe - */ - msg_format("%d essences of %d", - alchemist_recipes[al_idx].qty, - al_idx); - } - else /* Destroy the essences (tocreate == -1) - * or check for existence(tocreate == 0)*/ - { - int rqty = alchemist_recipes[al_idx].qty; - for (j = 0; j < INVEN_WIELD; j++) - { - object_type *o_ptr = &p_ptr->inventory[j]; - if (o_ptr->k_idx - && (o_ptr->tval == TV_BATERIE ) - && (o_ptr->sval == alchemist_recipes[al_idx].sval_essence ) - && (o_ptr->number >= rqty )) - { - /* At this point, the item is required, destroy it. */ - if ( tocreate ) - { - inc_stack_size_ex(j, 0 - rqty, OPTIMIZE, message ? DESCRIBE : NO_DESCRIBE); - } - - /* When we find enough of the item, break out of the - * 'search through the p_ptr->inventory' loop */ - break; - } - } - if ( j == INVEN_WIELD) - /* This ingredient was not found, cannot do recipe */ - return FALSE; - }/*destroying items, or just checking for existence */ - } - return exists; -} - -/* This function lists all the ingredients - * needed to create something. - */ -void alchemist_display_recipe(int tval, int sval, int ego) -{ - int al_idx; - int row = 1, col = 15; - char o_name[80]; - char ch; - object_type *o_ptr, forge; - - /* Display the ingredients for a recipe */ - for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ ) - if ((ego && alchemist_recipes[al_idx].sval == ego - && alchemist_recipes[al_idx].tval == 1 ) - || (!ego && alchemist_recipes[al_idx].sval == sval - && alchemist_recipes[al_idx].tval == tval)) - { - int qty = alchemist_recipes[al_idx].qty; - c_prt(TERM_GREEN, - format(" %d essence%s %s ", qty, - qty > 1 ? "s" : "", - k_name + k_info[lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence)].name ), - row++, col); - } - - c_prt(TERM_WHITE, " ", row++, col); - - if (!ego) - { - /* Find the name of that object */ - o_ptr = &forge; - object_prep(o_ptr, lookup_kind(tval, sval)); - o_ptr->name2 = ego; - hack_apply_magic_power = -99; - apply_magic(o_ptr, get_skill(SKILL_ALCHEMY) * 2, FALSE, FALSE, FALSE); - object_aware(o_ptr); - object_known(o_ptr); - /* the 0 mode means only the text, leaving off any numbers */ - object_desc(o_name, o_ptr, FALSE, 0); - } - else - { - /* Display the ego item name */ - strcpy(o_name, e_name + e_info[ego].name); - } - - /* Display a short message about it, and wait for a key. */ - (void)get_com(format("ingredients needed to create a %s", o_name), &ch); - -} - -/* - * - * The alchemist_recipe_select was copied from - * wiz_create_itemtype - * and then changed quite a bit. - * - */ - -/* - The select array is a simple array of 'use this char to select item x' - It has 88 items (three columns of 20 each) - selectitem is initilized with the reverse mappings: - selectitem[selectchar[x]] == x is always true. - */ -char selectchar[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*():;,.<=>[]{}/=?+'~"; -byte selectitem[256]; - -void strip_and_print(const char *str, int color, int num) -{ - int row = 2 + (num % 20), col = 40 * (num / 20); - int ch, max_len = 0; - char buf[80]; - char *string; - - if (num > 60) - { - msg_print("Attempting to display too many items!"); - return; - } - ch = selectchar[num]; - if (selectitem[ch] != num) - { - int i; - for ( i = 0 ; i < 256 ; i++) - selectitem[i] = 0xff; - for ( i = 0 ; selectchar[i] ; i++) - selectitem[(byte)selectchar[i]] = i; - } - - /* Skip past leading characters */ - while ((*str == ' ') || (*str == '&')) str++; - - /* Copy useful chars */ - for (string = buf; *str; str++) - if (*str != '~') *string++ = *str; - - /* Terminate the new name */ - *string = '\0'; - - /* strip the name down to size - if (76-col < (signed)max_len) - max_len = 76-col; - else - max_len = 30-6;*/ - max_len = 39; - - string = buf; - if (strlen(string) > (unsigned)max_len) - string = string + (strlen(string) - max_len); - - /* Print it */ - c_prt(color, format("[%c] %s", ch, string), row, col); -} - -/* Display a list of recipes that need a particular essence. - * Note that we display a list of essences first, - * so in effect, this is the alchemist's recipe book. - */ -void alchemist_recipe_book(void) -{ - int num, max_num, i, al_idx, bat, kidx; - int choice[61], choice2[61]; - int mod40; - bool_ essence[MAX_BATERIE_SVAL + 1]; - char ch; - - /* Save and clear the screen */ - character_icky = TRUE; - Term_save(); - - while ( TRUE ) - { - Term_clear(); - - num = 0; - - /* Display bateries */ - - /* start with assumption that the alchemist knows about no recipes */ - for (i = 0; i < MAX_BATERIE_SVAL + 1 ; i++) - essence[i] = FALSE; - - /* cycle through all alchemist recipes */ - for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++) - /* if we aren't already going to display this essence */ - if (!essence[alchemist_recipes[al_idx].sval_essence]) - { - - /*Note that we don't display artifact recipes here...*/ - /*This is partially because artifacts often require exotic - ingredients as well */ - - if (!alchemist_recipes[al_idx].tval) - continue; - - if (alchemist_recipes[al_idx].tval == 1) - { - if (alchemist_known_egos[alchemist_recipes[al_idx].sval / 32] - & (1 << (alchemist_recipes[al_idx].sval % 32)) ) - essence[alchemist_recipes[al_idx].sval_essence] = TRUE; - continue; - } - - kidx = lookup_kind(alchemist_recipes[al_idx].tval, alchemist_recipes[al_idx].sval); - if (alchemist_recipes[al_idx].tval != 1 && k_info[kidx].know) - essence[alchemist_recipes[al_idx].sval_essence] = TRUE; - - } - for (num = 0, i = 0; i < MAX_BATERIE_SVAL + 7 ; i++) - if (essence[i] || i > MAX_BATERIE_SVAL) - { - int kidx = lookup_kind(TV_BATERIE, i); - if (i > MAX_BATERIE_SVAL) - { - switch (i) - { - case (MAX_BATERIE_SVAL + 1): strip_and_print("Scrolls", TERM_WHITE, num); - break; - case (MAX_BATERIE_SVAL + 2): strip_and_print("Potions", TERM_WHITE, num); - break; - case (MAX_BATERIE_SVAL + 3): strip_and_print("Wands", TERM_WHITE, num); - break; - case (MAX_BATERIE_SVAL + 4): strip_and_print("Rings", TERM_WHITE, num); - break; - case (MAX_BATERIE_SVAL + 5): strip_and_print("Staves", TERM_WHITE, num); - break; - case (MAX_BATERIE_SVAL + 6): strip_and_print("Amulets", TERM_WHITE, num); - break; - default: - continue; - } - } - else - /* add this essence to the list*/ - strip_and_print(k_name + k_info[kidx].name, TERM_WHITE, num); - - choice[num++] = i; - } - max_num = num; - if ( max_num == 0) - { - /*Note that this should never actually happen, as any skill - at alchemy automatically gets you some recipes, and this - procedure shouldn't be called for players without alchemist skill - */ - msg_print("You don't know any recipes!"); - msg_print("You can't be an alchemist without recipes!"); - break; - } - - while (num == 0xff || num >= max_num) - { - ch = selectchar[max_num - 1]; - /* Choose! */ - if ( max_num == 0 || - !get_com(format("Which Type of Recipe?[a-%c]", selectchar[max_num - 1]), &ch)) - break; - - /* Analyze choice - note that the cast to byte prevents overflow*/ - num = selectitem[(byte)ch]; - - } - /* This break, and the break for no recipes above, - are the only exits from this procedure. - */ - if ( num == 0xff || num >= max_num) - break; - - /* Save the baterie index */ - bat = choice[num]; - num = 0; - - /*Display the 'type of object' recipe screen*/ - if (bat > MAX_BATERIE_SVAL) - { - int tval; - switch (bat) - { - case MAX_BATERIE_SVAL + 1: - tval = TV_SCROLL; - break; - case MAX_BATERIE_SVAL + 2: - tval = TV_POTION; - break; - case MAX_BATERIE_SVAL + 3: - tval = TV_WAND; - break; - case MAX_BATERIE_SVAL + 4: - tval = TV_RING; - break; - case MAX_BATERIE_SVAL + 5: - tval = TV_STAFF; - break; - case MAX_BATERIE_SVAL + 6: - tval = TV_AMULET; - break; - } - Term_load(); - alchemist_recipe_select(&tval, 0, FALSE, TRUE); - Term_save(); - continue; - } - mod40 = 0; - while ( TRUE ) - { - int skipped; - - Term_clear(); - num = 0; - - if (mod40) - { - strip_and_print("--MORE--", TERM_WHITE, num); - choice[num] = -2; - choice2[num++] = 0; - } - - /* Display all items made with this essence */ - for ( al_idx = 0 , skipped = 0 ; al_idx < max_al_idx ; al_idx++) - if ( alchemist_recipes[al_idx].sval_essence == bat) - { - int sval = alchemist_recipes[al_idx].sval; - int tval = alchemist_recipes[al_idx].tval; - char names[200] = ""; - - if (alchemist_recipes[al_idx].tval == 1) - { - /* Ego items */ - ego_item_type *e_ptr = &e_info[sval]; - int j, k; - - if ( !(alchemist_known_egos[sval / 32] & (1 << (sval % 32)))) - continue; - - for ( j = 0 ; j < 6 && e_ptr->tval[j] ; j ++ ) - { - if ( j > 0 && e_ptr->tval[j] == e_ptr->tval[j - 1]) - continue; - for ( k = 0; tvals[k].tval; k++) - if (tvals[k].tval == e_ptr->tval[j]) - { - strcat(names, tvals[k].desc); - strcat(names, ", "); - break; - } - } - strcat(names, e_name + e_ptr->name); - } - else - { - /* Normal Items */ - int kidx = lookup_kind(tval, sval); - int k; - if ( !k_info[kidx].know ) - continue; - - for ( k = 0; tvals[k].tval; k++) - if (tvals[k].tval == tval) - { - strcat(names, tvals[k].desc); - break; - } - strcat(names, " of "); - strcat(names, k_name + k_info[kidx].name); - - } - - /*Skip the first mod40 pages of recipes*/ - if (skipped++ < mod40*38) - continue; - - /* add this object kind to the list*/ - strip_and_print(names, TERM_WHITE, num); - choice[num] = tval; - choice2[num++] = sval; - if (num > 38) - { - strip_and_print("--MORE--", TERM_WHITE, num); - choice[num] = -1; - choice2[num++] = 0; - break; - } - - }/*Loop through tidx/sidx*/ - - max_num = num; - while (num == 0xff || num >= max_num) - { - ch = selectchar[max_num - 1]; - /* Choose! */ - if ( max_num == 0 || !get_com( - format("Examine which recipe?[%c-%c]", selectchar[0], ch) - , &ch)) - { - break; - } - - /* Analyze choice */ - num = selectitem[(byte)ch]; - } - - if ( choice[num] < 0) - { - if (choice[num] < -1) - mod40--; - else - mod40++; - continue; - } - - if ( num == 0xff || num >= max_num) - break; - - /* Display the recipe */ - if (choice[num] == 1) - alchemist_display_recipe(0, 0, choice2[num]); - else - alchemist_display_recipe(choice[num], choice2[num], 0); - } - /* - break is at top of loop, after essence list - if( num < 0 || num >= max_num) - break; - */ - - }/*show recipes*/ - - /* Restore screen contents */ - Term_load(); - character_icky = FALSE; -} - -/* Display a list of known recipies that can be made with - * materials on hand (including the passed tval). Also - * calls the recipe_display function, if requested by the - * player or there aren't enough essences to make the - * requested object. - * - * Note: sval is ignored if !ego, tval is the only determinant - * of what recipies are available otherwise. - * - * This function needs to be able to scroll a list, because - * there are SO MANY potions. :) - */ -int alchemist_recipe_select(int *tval, int sval, int ego, bool_ recipe) -{ - int i, mod40 = 0, num, max_num = 0; - - cptr tval_desc2 = ""; - char ch; - bool_ done = FALSE; - - int choice[60]; - int validc[60]; - - const char *string; - - - /* Save and clear the screen */ - character_icky = TRUE; - Term_save(); - Term_clear(); - - /* Base object type chosen, fill in tval */ - for ( num = 0 ; num < 40 ; num ++) - if (tvals[num].tval == *tval) - { - tval_desc2 = tvals[num].desc; - } - - while (!done) - { - Term_clear(); - if (ego) - { - /* Find matching ego items */ - for (num = 0, i = 1; (num < 40) && (i < max_e_idx) ; i++) - { - int j; - ego_item_type *e_ptr = &e_info[i]; - - /* Skip if unknown ego type */ - if ( !(alchemist_known_egos[i / 32] & (1 << (i % 32)))) - continue; - - /* search in permitted tvals/svals for allowed egos */ - for ( j = 0 ; j < 6 ; j ++ ) - if ( e_ptr->tval[j] == *tval - && sval >= e_ptr->min_sval[j] - && sval <= e_ptr->max_sval[j]) - { - int color = TERM_GREEN; - - /*Reject if not opposite end of name - prefixes only on postfix egos, - postfixes only on prefix egos. - */ - if (ego != -1 && e_ptr->before == e_info[ego].before) - continue; - - /*Color it red of the alchemist doesn't have the essences to create it*/ - if (!alchemist_items_check(*tval, 0, i, 0, TRUE)) - color = TERM_RED; - - /* add this ego to the list*/ - strip_and_print(e_name + e_info[i].name, color, num); - validc[num] = color; - choice[num++] = i; - break; - } - } - } - else - { - char skipped = 0; - num = 0; - if (mod40 != 0) - { - strip_and_print("--MORE--", TERM_WHITE, num); - validc[num] = TERM_WHITE; - choice[num++] = -1; - } - - for (i = 1; (num < 39) && (i < max_k_idx); i++) - { - object_kind *k_ptr = &k_info[i]; - - /* Analyze matching items */ - if (k_ptr->tval == *tval || (k_ptr->tval == TV_POTION2 && *tval == TV_POTION)) - { - char color = TERM_GREEN; - /* Hack -- Skip instant artifacts */ - if (k_ptr->flags3 & (TR3_INSTA_ART)) continue; - - /*Don't display recipes that the alchemist doesn't know about*/ - if (!k_ptr->know && !wizard) continue; - - /*Skip recipes that are somehow known, but don't exist*/ - if (!alchemist_exists(k_ptr->tval, k_ptr->sval, 0, 0)) - continue; - - /* Skip the first 39 if they hit 'more' */ - if (skipped++ < mod40*39) - continue; - - /* Color 'unable to create' items different */ - if (!alchemist_items_check(k_ptr->tval, k_ptr->sval, 0, 0, TRUE)) - color = TERM_RED; - - /* Acquire the "name" of object "i" */ - /* and print it in it's place */ - strip_and_print(k_name + k_ptr->name, color, num); - - /* Remember the object index */ - validc[num] = color; - choice[num++] = i; - } - } - if (num == 39) - { - strip_and_print("--MORE--", TERM_WHITE, num); - validc[num] = TERM_WHITE; - choice[num++] = -1; - } - } - - /* We need to know the maximal possible remembered object_index */ - max_num = num; - string = "What Kind of %s? (* to see recipe) [%c-%c,*]"; - num = 0xff; - - /* Pretend they're all undoable if we where called to display recipes */ - if (recipe) - { - for ( num = 0 ; num < max_num ; num++) - if (validc[num] != TERM_WHITE) validc[num] = TERM_RED; - string = "show which %s recipe? [%c-%c]"; - } - - while (num == 0xff || num >= max_num) - { - ch = selectchar[max_num - 1]; - /* Choose! */ - if ( max_num == 0 || !get_com(format(string, tval_desc2, selectchar[0], ch), &ch)) - { - break; - } - - /* Extra breaks for recipe */ - if (recipe && (ch == '\r' || ch == ' ' || ch == ESCAPE )) - break; - - /* Analyze choice */ - num = selectitem[(byte)ch]; - - /* Pretend that we don't have enough essences for anything */ - if (ch == '*' ) - { - for ( num = 0 ; num < max_num ; num++) - if (validc[num] != TERM_WHITE) validc[num] = TERM_RED; - string = "Show which %s recipe? [%c-%c]"; - } - } - if ( num == 0xff || max_num == 0 || num >= max_num) - break; - - if ( validc[num] == TERM_WHITE ) - { - if (num == 0) - mod40--; - else - mod40++; - if ( mod40 < 0) - mod40 = 0; - continue; - } - - /* If we don't have enough essences, or user asked for recipes */ - if ( validc[num] != TERM_GREEN ) - { - /* Display the recipe */ - if (ego) - alchemist_display_recipe(*tval, sval, choice[num]); - else - alchemist_display_recipe(k_info[choice[num]].tval, k_info[choice[num]].sval, 0); - } - else - done = TRUE; - - }/*while(!done)*/ - - /* Restore screen contents */ - Term_load(); - character_icky = FALSE; - - /* User abort, or no choices */ - if (max_num == 0 || num == 0xff || num >= max_num) - { - if (max_num == 0) - msg_print("You don't know of anything you can make using that."); - return ( -1); - } - if ( validc[num] != TERM_GREEN ) - return ( -1); - - /* And return successful */ - if ( ego ) - return choice[num]; - - /* Set the tval, should be the same unless they selected a potion2 */ - if (*tval != k_info[choice[num]].tval && *tval != TV_POTION) - msg_print("Coding error: tval != TV_POTION"); - *tval = k_info[choice[num]].tval; - return ( k_info[choice[num]].sval ); -} - -/* Set the 'known' flags for all objects with a level <= lev - * This lets the budding alchemist create basic items. - */ -void alchemist_learn_all(int lev) -{ - int i; - - if ( !get_skill(SKILL_ALCHEMY) ) - return; - - /* msg_format("You learn about level %d items",lev); */ - - for ( i = 0 ; i < max_k_idx ; i++ ) - if ( k_info[i].level <= lev ) - if (alchemist_exists(k_info[i].tval, k_info[i].sval, 0, 0)) - k_info[i].know = TRUE; -} - -void alchemist_learn_ego(int ego) -{ - char *name; - int i; - - /* some Paranoia*/ - if ( !ego || ego >= max_e_idx ) - return; - - /* Get the ego items name */ - name = e_name + e_info[ego].name; - while (strchr(name, ' ')) - name = strchr(name, ' ') + 1; - - /* Don't learn about egos without recipes, and - * always learn about the passed ego item. */ - if (alchemist_exists(0, 0, ego, 0)) - { - alchemist_known_egos[ego / 32] |= (1 << (ego % 32)); - /* msg_format("You learn about '%s' ego items.",e_name+e_info[ego].name); */ - } - else - { - return; - } - - /* Don't mass learn about egos that have no name. */ - if ( name[0] == 0 ) - { - return; - } - - /* Look through all ego's for matching name */ - /* Note that the original ego is marked here too */ - for ( i = 0 ; i < max_e_idx ; i++ ) - if ( strstr(e_name + e_info[i].name, name) != NULL /*Last word of name exists in this ego's name*/ - && alchemist_exists(0, 0, i, 0) /*There exists a recipe for this*/ - && !(alchemist_known_egos[i / 32] & (1 << (i % 32)) ) ) /*Not already known*/ - /*&& (e_name+e_info[i].name)[0])non-blank name*/ - { - alchemist_known_egos[i / 32] |= (1 << (i % 32)); - /* msg_format("You learn about '%s' ego items.",e_name+e_info[i].name); */ - } - - return; -} - -/* Alchemist has learned about a new item. - * Learn about not only it, but ALL egos with the - * same name. - */ -int alchemist_learn_object(object_type *o_ptr) -{ - - /* Allow alchemist to create this item, - and.. learn about it even if the player - doesn't currently have the alchemy skill - */ - k_info[o_ptr->k_idx].know = TRUE; - - /* Not Paranoia, identify_fully calls this always */ - if ( !get_skill(SKILL_ALCHEMY) ) - return FALSE; - - if ( artifact_p(o_ptr) ) - { - char o_name[80]; - u32b f1, f2, f3, f4, f5, esp; - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Randarts and normal artifacts both*/ - alchemist_known_artifacts[0] |= f1; - alchemist_known_artifacts[1] |= f2; - alchemist_known_artifacts[2] |= f3; - alchemist_known_artifacts[3] |= f4; - alchemist_known_artifacts[4] |= f5; - alchemist_known_artifacts[5] |= esp; - - object_desc(o_name, o_ptr, 1, 0); - msg_format("You learn all about the abilities of %s!", o_name); - } - if (o_ptr->name2) - alchemist_learn_ego(o_ptr->name2); - - if (o_ptr->name2b) - alchemist_learn_ego(o_ptr->name2b); - - return (TRUE); -} - -/* Alchemist has gained a level - set the ego flags - * for all egos <= lev/4. - */ -void alchemist_gain_level(int lev) -{ - object_type forge; - object_type *o_ptr = &forge; - - if ( lev == 0) - { - /* Learn about potions of Detonation */ - k_info[417].know = TRUE; - } - if ( lev == 5) - { - int ego; - int egos[] = { - 7/*armor of resist fire*/ - , 18/*shield of resist fire*/ - , 74/*shocking weapon*/ - , 75/*fiery weapon*/ - , 76/*frozen weapon*/ - , 77/*Venomous weapon*/ - , 78/*Chaotic weapon*/ - , 115/*projectile of venom*/ - , 116/*projectile of Acid*/ - , 122/*projectile of flame*/ - , 123/*projectile of frost*/ - , 137/*Lite of fearlessness*/ - , 0 /*terminator*/ - }; - object_wipe(o_ptr); - /* learn about some basic ego items */ - /* Note that this is just to get you started. */ - for ( ego = 0 ; egos[ego] ; ego++) - { - o_ptr->name2 = egos[ego]; - alchemist_learn_object(o_ptr); - } - msg_print("You recall your old master teaching you about elemental item infusing."); - } - if ( lev == 10) - { - /*For 'hard rooms' Players only, learn about diggers.*/ - if (ironman_rooms) - { - msg_print("There's gotta be an easier way to get into all these vaults!"); - object_wipe(o_ptr); - o_ptr->name2 = 101; /* Ego item, 'of digging' */ - alchemist_learn_object(o_ptr); - } - } - if ( lev == 25) - { - msg_print("You recall your old master reminiscing about legendary infusings"); - msg_print("and the Philosophers' stone."); - - /* No auto-learn on artifacts - by this level, you'll have *ID*'d several */ - } - if ( lev == 25) - { - msg_print("You wonder about shocking daggers of slay evil."); - } - if ( lev == 50) - { - /* learn about Temporary item creation */ - /* Note that this is the ONLY way to learn this, - because spells which create a temporary item - also fully ID it. */ - alchemist_known_artifacts[4] |= TR5_TEMPORARY; - msg_print("It suddenly occurs to you that artifacts don't *HAVE* to be permanent..."); - } - - /* Every Four Levels, learn about items that are - * less than that. - * Note that this isn't a significant effect after the - * first few levels, as the level at which you are learning - * things here quickly drops behind the level at which you - * are finding items. - */ - if ( (lev & 0x3) != 0 ) - return; - lev = (lev >> 2) + 1; - alchemist_learn_all(lev); - -} - -/* This, in combination with some code in loadsave.c, - insures that alchemist_gain_level is called EXACTLY - once with each possible value during the characters - lifetime. - */ -void alchemist_check_level() -{ - u32b lev = get_skill(SKILL_ALCHEMY); - if ( alchemist_gained > lev ) - return; - /*Paranoia*/ - if ( !lev ) - return; - while ( alchemist_gained <= lev ) - alchemist_gain_level(alchemist_gained++); -} - -/* - * do_cmd_cast calls this function if the player's class - * is 'alchemist'. - */ -void do_cmd_alchemist(void) -{ - int item, ext = 0; - int value, basechance; - int askill; - bool_ repeat = 0; - char ch; - - object_type *o_ptr, *q_ptr; - object_type forge, forge2; - byte carry_o_ptr = FALSE; - - cptr q, s; - - /* With the new skill system, we can no longer depend on - * check_exp to handle the changes and learning involved in - * gaining levels. - * So we'll have to check for it here. - */ - alchemist_check_level(); - askill = get_skill(SKILL_ALCHEMY); - - - q_ptr = &forge; - - o_ptr = &p_ptr->inventory[INVEN_HANDS]; - if ((o_ptr->tval != TV_GLOVES) || (o_ptr->sval != SV_SET_OF_LEATHER_GLOVES)) - { - msg_print("You must wear gloves in order to do alchemy."); - return; - } - - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - while (TRUE) - { - if (!get_com("[P]ower, [R]echarge or [L]eech an item, [E]xtract essences, or recipe [B]ook?", &ch)) - { - ext = 0; - break; - } - if (ch == ' ' ) - { - ext = 0; - break; - } - if (ch == 'P' || ch == 'p') - { - ext = 1; - break; - } - if (ch == 'E' || ch == 'e') - { - ext = 2; - break; - } - if (ch == 'R' || ch == 'r') - { - ext = 3; - break; - } - if (ch == 'L' || ch == 'l') - { - ext = 2; - repeat = 1; - break; - } - if (ch == 'B' || ch == 'b') - { - ext = 4; - break; - } - } - - /**********Add a power*********/ - if (ext == 1) - { - int i, qty, tval, sval = 0, ego = 0; - char o_name[200]; - - /* Get an item */ - q = "Empower which item? "; - s = "You have no empowerable items."; - item_tester_hook = item_tester_hook_empower; - - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Create an artifact from an ego or double ego item, - * from a previous artifact, or finish an artifact - */ - if ((askill >= 25) && (artifact_p(o_ptr) || o_ptr->name2) && has_ability(AB_CREATE_ART)) - { - if (get_check("Create an artifact?")) - { - do_cmd_toggle_artifact(o_ptr); - return; - } - /* Don't change artifacts or double ego items further */ - else if (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b)) - return; - } - /*Ok, now we have the item, so we can now pick recipes. - Note: No recipe is known unless we have 'extracted' from - that object type. I.E. the 'know' flag (also greater identify) - is set. - */ - - /* Here we're not setting what kind of ego item it IS, - * where' just deciding that it CAN be an ego item */ - if ( o_ptr->name2 ) /* creating a DUAL ego */ - ego = TRUE; - if ( o_ptr->tval < 40 && o_ptr->tval != TV_BOTTLE) - ego = TRUE; - if ( o_ptr->tval == TV_ROD_MAIN || o_ptr->tval == TV_DAEMON_BOOK || o_ptr->tval == TV_BOOK) - ego = TRUE; - - sval = o_ptr->sval; - if (!ego) - { - switch ( o_ptr->tval) - { - case TV_WAND: - sval = SV_WAND_NOTHING; - break; - case TV_RING: - sval = SV_RING_NOTHING; - break; - case TV_STAFF: - sval = SV_STAFF_NOTHING; - break; - case TV_BOTTLE: - sval = 1; - break; - case TV_AMULET: - sval = SV_AMULET_NOTHING; - break; - case TV_SCROLL: - sval = SV_SCROLL_NOTHING; - break; - case TV_ROD: - sval = SV_ROD_NOTHING; - break; - } - } - if ( o_ptr->sval != sval ) - ego = TRUE; - - tval = o_ptr->tval; - sval = o_ptr->sval; - - /*HACK - bottles don't have the same tval as potions*/ - /*Everything else will have the same tval after empowering*/ - if (tval == TV_BOTTLE) tval = TV_POTION; - if (ego) - if (o_ptr->name2) - ego = alchemist_recipe_select(&tval, sval, o_ptr->name2, FALSE); - else - ego = alchemist_recipe_select(&tval, sval, -1, FALSE); - else - sval = alchemist_recipe_select(&tval, 0, 0, FALSE); - - if ( sval < 0 || ego < 0) - return; - - /* Check to make sure we have enough essences */ - /* theoretically this is taken care of by recipe_select*/ - /* but we'll double check just for paranoia. */ - if (!alchemist_items_check(tval, sval, ego, 0, TRUE)) - { - msg_print("You do not have enough essences."); - return; - } - - /* Take a turn */ - energy_use = 100; - - /* Use up the essences */ - (void)alchemist_items_check(tval, sval, ego, -1, TRUE); - - /* Enchant stacks of ammunition at a time */ - if ( o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW || o_ptr->tval == TV_BOLT ) - { - qty = 1; - while (qty < o_ptr->number && alchemist_items_check(tval, sval, ego, -1, FALSE)) - qty++; - } - else - qty = 1; - - /* Copy the object */ - q_ptr = &forge; - object_copy(q_ptr, o_ptr); - - if ( o_ptr->tval == TV_WAND) - { - /* distribute charges on wands */ - q_ptr->pval = o_ptr->pval / o_ptr->number; - o_ptr->pval -= q_ptr->pval; - } - - o_ptr = q_ptr; - o_ptr->number = qty; - carry_o_ptr = TRUE; - - /* Destroy the initial object */ - inc_stack_size(item, -qty); - - - if ( ego ) - { - int pval, pval2; - s32b pval3; - - pval = o_ptr->pval; - pval2 = o_ptr->pval2; - pval3 = o_ptr->pval3; - - if (o_ptr->name2) - o_ptr->name2b = ego; - else - o_ptr->name2 = ego; - o_ptr->pval = randint(e_info[ego].max_pval - 1) + 1; - /* dilemma - how to prevent creation of cursed items, - * without allowing the creation of artifacts? - * We can't, unless we want to finalize the ego flags ourselves. - */ - apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE); - /* Remember what the old pval was, so that we can re-apply it. */ - if ( o_ptr->tval == TV_WAND - || o_ptr->tval == TV_RING - || o_ptr->tval == TV_AMULET - || o_ptr->tval == TV_STAFF) - { - o_ptr->pval = pval; - o_ptr->pval2 = pval2; - o_ptr->pval3 = pval3; - } - else if (o_ptr->tval == TV_ROD_MAIN) - { - o_ptr->pval = pval; - } - else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255)) - { - o_ptr->pval = pval; - } - else if (o_ptr->tval == TV_SHOT - || o_ptr->tval == TV_ARROW - || o_ptr->tval == TV_BOLT) - { - o_ptr->pval2 = pval2; - } - else if (o_ptr->tval == TV_INSTRUMENT) - { - o_ptr->pval2 = pval2; - } - - /* Calculate failure rate, lev=val/2500+5 */ - value = MIN(e_info[o_ptr->name2].cost, 50000); - if (o_ptr->name2b) value += MIN(e_info[o_ptr->name2b].cost, 50000); - basechance = (value / 1000 + 5 - get_skill_scale(SKILL_ALCHEMY, 100) ) * 10; - if ( basechance < 0) basechance = 0; - if ( basechance > 100) basechance = 100; - - value = object_value_real(o_ptr); - - } - else /* not an ego item */ - { - o_ptr = &forge; - object_wipe(o_ptr); - object_prep(o_ptr, lookup_kind(tval, sval)); - hack_apply_magic_power = -99; - apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE); - if ( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF) - o_ptr->pval = 0; - value = object_value_real(o_ptr); - - basechance = k_info[o_ptr->k_idx].level - askill * 2; - basechance *= 10; - - /* Can't fail more that 100% of the time... */ - if (basechance > 100) - basechance = 100; - /* Always success in creation of potion of detonations */ - if (o_ptr->tval == TV_POTION && o_ptr->sval == SV_POTION_DETONATIONS) - { - basechance /= 10; - } - } - - /* Use up gold to create items */ - /* this has the effect of making the alchemist - chronically short of funds, unless he finds the - philosopher's stone. It also means the easiest - things to make are 'bad', like a potion of - detonations... - */ - /* Problem - to restrictive. We need something - which requires less money. But at the same time, - we don't want an 'easy cash' situation. Maybe something - like '10% * level difference', meaning at skill level 5, - level one items are free? But egos are frequently level - zero! Maybe egos are forced to level 25? with a cost ceiling? - I mean, Potions and scrolls are really the problem causing the - 'easy cash' situation, it's ego items. Ego items require - relatively few essences, and the rewards are HUGE. Most powerful - potions and scrolls require rare essences. Maybe force all egos - to require a magic essence? But then you'd get lots of magic - from distilling them. Maybe consumed in the creation? then when - you got a powerful item, you could make one ego item... - But if making things doesn't take gold, what about the cash - does the Philosopher's stone do? - Time*/ - - /* 0% failure if you have the stone */ - if ( alchemist_has_stone()) - basechance = 0; - - if (basechance > 0 && value) - { - char string[80]; - string[0] = '0'; - string[1] = 0; - - msg_format("The chance of success is only %d%%!", 100-basechance); - get_string("How much gold do you want to add?", string, 50); - i = atoi(string); - /* Note: don't trust the user to enter a positive number... */ - if ( i < 0) - i = 0; - if ( i > p_ptr->au) - i = p_ptr->au; - - if (i) - { - basechance = basechance - (i * 20) / value; - msg_format("The chance of success improved to %d%%.", 100-basechance); - } - - if (randint(100) < basechance ) - /*creation failed, even with the extra gold...*/ - carry_o_ptr = FALSE; - - /* Redraw gold */ - p_ptr->au -= i; - p_ptr->redraw |= (PR_GOLD); - } - - /* Set fully identified - * After all, the player just made it... - */ - object_aware(o_ptr); - object_known(o_ptr); - o_ptr->ident |= IDENT_MENTAL; - o_ptr->found = OBJ_FOUND_SELFMADE; - - object_desc(o_name, o_ptr, FALSE, 0); - - if ( carry_o_ptr) - { - msg_format("You have successfully created %s %s", - (o_ptr->number > 1 ? "some" : (is_a_vowel(o_name[0]) ? "an" : "a")), - o_name); - - if (inven_carry_okay(o_ptr)) - inven_carry(o_ptr, FALSE); - else - { - drop_near(o_ptr, 0, p_ptr->py, p_ptr->px); - msg_format("You drop the %s", o_name); - } - carry_o_ptr = FALSE; - } - else /* don't carry, or in other words... */ - { - int level = k_info[o_ptr->k_idx].level; - if (o_ptr->name1) /* created ego item */ - level += e_info[o_ptr->name2].level; - - msg_format("Your attempt backfires! Your %s explodes!", o_name); - take_hit(damroll(3, level - askill ) , "Alchemical Explosion"); - p_ptr->redraw |= (PR_HP); - } - - /* Combine / Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - /* Optimize the entire p_ptr->inventory - needed because we - don't know how many essences where used, and we may - have 'used up' a wielded item as well. - */ - for ( item = 0 ; item < INVEN_TOTAL ; item++ ) - inven_item_optimize(item); - - /**********Extract a power*********/ - } - else if (ext == 2) - { - int ego; - bool_ discharge_stick = FALSE; - - /* s_ptr holds the empty items */ - object_type *s_ptr = NULL; - bool_ carry_s_ptr = FALSE; - - item_tester_hook = item_tester_hook_extractable; - - /* Get an item */ - q = "Extract from which item? "; - s = "You have no item to extract power from."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* This is to prevent creating magic essences by extracting - * from a recharged wand of dragon breath or something. - */ - if (( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF ) - && o_ptr->art_flags4 & TR4_RECHARGED) - { - msg_print("You cannot extract essences after it's been magically recharged."); - return; - } - - /* Take a turn */ - energy_use = 100; - - /* Handle Rods before the loop, since they don't stack */ - if (o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != SV_ROD_NOTHING) - { - rod_tip_extract(o_ptr); - return; - } - - do - { /* Repeat (for leech command) */ - - /* Create the items. - * we don't care if they drop to the ground, - * and if no action was taken, return - */ - ego = 0; - if ( o_ptr->name2) - ego = o_ptr->name2; - - /* For ego staves and wands (not of nothing), discharge before extracting the ego */ - discharge_stick = (o_ptr->pval > 0 && - ((o_ptr->tval == TV_STAFF && o_ptr->sval != SV_STAFF_NOTHING) || - (o_ptr->tval == TV_WAND && o_ptr->sval != SV_WAND_NOTHING))); - if (discharge_stick) - ego = 0; - - if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, ego, 1, TRUE)) - { - msg_print("You cannot extract anything from that item."); - return; - } - - if (o_ptr->name2b && !alchemist_items_check(o_ptr->tval, o_ptr->sval, o_ptr->name2b, 1, TRUE)) - { - /* do nothing - if the second ego can't be extracted - because there is no recipe for it, simply destroy it - */ - } - - /* Once in three times, learn how to make the item */ - /* Sorry for the complicated if! Basically, if it's an - * unknown regular item or an unknown ego item, there's - * a one in 3 chance that it'll be id'd */ - if (((!ego && !k_info[o_ptr->k_idx].know) - || (ego && !(alchemist_known_egos[ego / 32] & (1 << (ego % 32))))) - && randint(3) == 1) - { - msg_print("While destroying it, you gain insight into this item."); - /* If over level 10, the player has a chance of 'greater ID' - * on extracted items - */ - if (askill > 9) - object_out_desc(o_ptr, NULL, FALSE, TRUE); - alchemist_learn_object(o_ptr); - } - - /* Always learn what kind of thing it is */ - object_known(o_ptr); - object_aware(o_ptr); - - /* If it's a wand or staff with charges (but not of nothing), - * decrease number of charges, unstacking if needed. - * Otherwise, create the 'of nothing' item and destroy the old one. - */ - if (discharge_stick) - { - /* Unstack staves */ - if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1)) - { - /* Create one local copy of the staff */ - q_ptr = &forge2; - object_copy(q_ptr, o_ptr); - - /* Modify quantity */ - q_ptr->number = 1; - - /* Unstack the copied staff */ - o_ptr->number--; - - /* Use the local copy of the staff */ - o_ptr = q_ptr; - carry_o_ptr = TRUE; - } - /* remove one charge */ - o_ptr->pval--; - } - else - { - /* Create the empty, plain item */ - /* If the item was already created, increase the number */ - if (carry_s_ptr) - { - s_ptr->number++; - } - else - { - /* Otherwise we must create a local copy of the empty item */ - int tval, sval; - bool_ create_item = TRUE; - - tval = o_ptr->tval; - if ( !ego && (tval == TV_POTION || tval == TV_POTION2)) - tval = TV_BOTTLE; - - sval = o_ptr->sval; - - if (!ego) - { - switch ( tval) - { - case TV_WAND: - sval = SV_WAND_NOTHING; - break; - case TV_RING: - sval = SV_RING_NOTHING; - break; - case TV_STAFF: - sval = SV_STAFF_NOTHING; - break; - case TV_BOTTLE: - sval = 1; - break; - case TV_AMULET: - sval = SV_AMULET_NOTHING; - break; - case TV_SCROLL: - sval = SV_SCROLL_NOTHING; - break; - case TV_ROD: - sval = SV_ROD_NOTHING; - break; - default: - create_item = FALSE; - } - } - - if (create_item) - { - /* Create the empty item */ - s_ptr = &forge; - object_wipe(s_ptr); - object_prep(s_ptr, lookup_kind(tval, sval)); - s_ptr->number = 1; - - /* Force creation of non ego non cursed */ - hack_apply_magic_power = -99; - apply_magic(s_ptr, 0, FALSE, FALSE, FALSE); - - /* Hack -- remove possible curse */ - if (cursed_p(s_ptr)) - { - s_ptr->art_flags3 &= ~(TR3_CURSED | TR3_HEAVY_CURSE); - s_ptr->ident &= ~(IDENT_CURSED); - } - - /* Restore pvals (e.g. charges ==0) of the item */ - if (ego && ((tval == TV_WAND) || (tval == TV_STAFF) || - (tval == TV_RING) || (tval == TV_AMULET))) - { - s_ptr->pval = o_ptr->pval; - s_ptr->pval2 = o_ptr->pval2; - s_ptr->pval3 = o_ptr->pval3; - } - /* Restore the spell stored in a random book */ - else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255)) - { - s_ptr->pval = o_ptr->pval; - } - /* Restore the type of explosive ammo */ - else if (o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW - || o_ptr->tval == TV_BOLT) - { - s_ptr->pval2 = o_ptr->pval2; - } - /* Restore the music stored in an instrument */ - else if (o_ptr->tval == TV_INSTRUMENT) - { - s_ptr->pval2 = o_ptr->pval2; - } - - object_aware(s_ptr); - object_known(s_ptr); - s_ptr->ident |= IDENT_STOREB; - - /* The empty item will be added to the p_ptr->inventory later */ - carry_s_ptr = TRUE; - } - } - - /* Now, we can delete the original (leeched) object. - * Is o_ptr an p_ptr->inventory / floor item or a local copy? - */ - if (!carry_o_ptr) - { - /* Break the leech-loop if it was the last item */ - if (o_ptr->number == 1) - repeat = 0; - - inc_stack_size(item, -1); - } - else - { - /* Forget the local object */ - carry_o_ptr = FALSE; - - /* reset o_ptr to the original stack, - * which contains at least another item */ - o_ptr = get_object(item); - } - } - } - while ( repeat == 1); - - /* If we carry empty items, add them to the p_ptr->inventory */ - if (carry_s_ptr) - inven_carry(s_ptr, TRUE); - - /* Combine / Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - /******* Recharge an item *******/ - } - else if (ext == 3) - { - int item; - - cptr q, s; - - item_tester_hook = item_tester_hook_recharge; - - /* Get an item */ - q = "Recharge which item? "; - s = "You have no rechargable items."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR ))) return; - - /* Get the item */ - o_ptr = get_object(item); - - /* Make sure we have enough essences to recharge this */ - if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, 0, TRUE)) - { - msg_print("You don't have the essences to recharge this item."); - return; - } - - /* Take a turn */ - energy_use = 100; - - /* Destroy the essences */ - (void)alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, -1, TRUE); - - if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1)) - { - /* Unstack staves */ - /* Get local object */ - q_ptr = &forge2; - - /* Obtain a local object */ - object_copy(q_ptr, o_ptr); - - /* Modify quantity */ - q_ptr->number = 1; - - /* Unstack the used item */ - o_ptr->number--; - - o_ptr = q_ptr; - carry_o_ptr = TRUE; - } - o_ptr->pval++; - } - else if ( ext == 4) - { - alchemist_recipe_book(); - } - /* Just in case - */ - if (carry_o_ptr) - { - /* the o_ptr item was probably an unstacked staff - * Anyway, we need to add it to the p_ptr->inventory */ - if (inven_carry_okay(o_ptr)) - inven_carry(o_ptr, TRUE); - else - drop_near(o_ptr, 0, p_ptr->py, p_ptr->px); - } -} - - -/* - * Command to ask favors from your god. - */ -void do_cmd_pray(void) -{ - if (p_ptr->pgod == GOD_NONE) - { - msg_print("Pray hard enough and your prayers might be answered."); - return; - } - else - { - if (!p_ptr->praying) - msg_format("You start praying to %s.", deity_info[p_ptr->pgod].name); - else - msg_format("You stop praying to %s.", deity_info[p_ptr->pgod].name); - p_ptr->praying = !p_ptr->praying; - - /* Update stuffs */ - p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | - PU_SANITY | PU_BODY); - - p_ptr->redraw |= PR_PIETY | PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP; - energy_use = 100; - } -} - - -/* - * Return percentage chance of spell failure. - */ -int spell_chance_random(random_spell* rspell) -{ - int chance, minfail; - - - /* Extract the base spell failure rate */ - chance = rspell->level + 10; - - /* Reduce failure rate by "effective" level adjustment */ - chance -= 3 * (get_skill(SKILL_THAUMATURGY) - rspell->level); - - /* Reduce failure rate by INT/WIS adjustment */ - chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1); - - /* Not enough mana to cast */ - if (rspell->mana > p_ptr->csp) - { - chance += 5 * (rspell->mana - p_ptr->csp); - } - - /* Extract the minimum failure rate */ - minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]]; - - /* Failure rate */ - return clamp_failure_chance(chance, minfail); -} - - - - -/* - * Print a batch of spells. - */ -static void print_spell_batch(int batch, int max) -{ - char buff[80]; - - random_spell* rspell; - - int i; - - - prt(format(" %-30s Lev Fail Mana Damage ", "Name"), 1, 20); - - for (i = 0; i < max; i++) - { - rspell = &random_spells[batch * 10 + i]; - - if (rspell->untried) - { - strnfmt(buff, 80, " %c) %-30s (Spell untried) ", - I2A(i), rspell->name); - - } - else - { - strnfmt(buff, 80, " %c) %-30s %3d %4d%% %3d %3dd%d ", - I2A(i), rspell->name, - rspell->level, spell_chance_random(rspell), rspell->mana, - rspell->dam_dice, rspell->dam_sides); - } - - prt(buff, 2 + i, 20); - } - - prt("", 2 + i, 20); -} - - - -/* - * List ten random spells and ask to pick one. - */ -static random_spell* select_spell_from_batch(int batch) -{ - char tmp[160]; - - char out_val[30]; - - char which; - - int mut_max = 10; - - random_spell* ret; - - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - if (spell_num < (batch + 1) * 10) - { - mut_max = spell_num - batch * 10; - } - - strnfmt(tmp, 160, "(a-%c, A-%cto browse, / to rename, - to comment) Select a power: ", - I2A(mut_max - 1), I2A(mut_max - 1) - 'a' + 'A'); - - prt(tmp, 0, 0); - - while (1) - { - /* Print power list */ - print_spell_batch(batch, mut_max); - - /* Get a command */ - which = inkey(); - - /* Abort */ - if (which == ESCAPE) - { - /* No selection */ - ret = NULL; - - /* Leave the command loop */ - break; - - } - - /* Accept default */ - if (which == '\r') - { - /* There are no other choices */ - if (mut_max == 1) - { - ret = &random_spells[batch * 10]; - - /* Leave the command loop */ - break; - } - - /* Wait for next command */ - continue; - } - - /* Rename */ - if (which == '/') - { - prt("Rename which power: ", 0, 0); - which = tolower(inkey()); - - if (isalpha(which) && (A2I(which) <= mut_max)) - { - strcpy(out_val, random_spells[batch*10 + A2I(which)].name); - if (get_string("Name this power: ", out_val, 29)) - { - strcpy(random_spells[batch*10 + A2I(which)].name, out_val); - } - prt(tmp, 0, 0); - } - else - { - bell(); - prt(tmp, 0, 0); - } - - /* Wait for next command */ - continue; - } - - /* Comment */ - if (which == '-') - { - prt("Comment which power: ", 0, 0); - which = tolower(inkey()); - - if (isalpha(which) && (A2I(which) <= mut_max)) - { - strcpy(out_val, random_spells[batch*10 + A2I(which)].desc); - if (get_string("Comment this power: ", out_val, 29)) - { - strcpy(random_spells[batch*10 + A2I(which)].desc, out_val); - } - prt(tmp, 0, 0); - } - else - { - bell(); - prt(tmp, 0, 0); - } - - /* Wait for next command */ - continue; - } - - if (isalpha(which) && isupper(which)) - { - which = tolower(which); - c_prt(TERM_L_BLUE, format("%s : %s", random_spells[batch*10 + A2I(which)].name, random_spells[batch*10 + A2I(which)].desc), 0, 0); - inkey(); - prt(tmp, 0, 0); - continue; - } - else if (isalpha(which) && (A2I(which) < mut_max)) - { - /* Pick the power */ - ret = &random_spells[batch * 10 + A2I(which)]; - - /* Leave the command loop */ - break; - } - else - { - bell(); - } - } - - /* Restore the screen */ - Term_load(); - - /* Leave "icky" mode */ - character_icky = FALSE; - - /* Return selection */ - return (ret); -} - - -/* - * Pick a random spell from a menu - */ -static random_spell* select_spell() -{ - char tmp[160]; - - char which; - - int batch_max = (spell_num - 1) / 10; - - random_spell *ret; - - - /* Too confused */ - if (p_ptr->confused) - { - msg_print("You can't use your powers while confused!"); - return NULL; - } - - /* No spells available */ - if (spell_num == 0) - { - msg_print("There are no spells you can cast."); - return NULL; - } - - /* Enter "icky" mode */ - character_icky = TRUE; - - /* Save the screen */ - Term_save(); - - strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max)); - - prt(tmp, 0, 0); - - while (1) - { - which = inkey(); - - if (which == ESCAPE) - { - Term_load(); - - ret = NULL; - - break; - } - - if (which == '\r') - { - if (batch_max == 0) - { - Term_load(); - - ret = select_spell_from_batch(0); - - break; - } - - continue; - } - - which = tolower(which); - if (isalpha(which) && (A2I(which) <= batch_max)) - { - Term_load(); - - ret = select_spell_from_batch(A2I(which)); - - break; - } - else - { - bell(); - } - } - - /* Leave "icky" mode */ - character_icky = FALSE; - - return (ret); -} - - -void do_cmd_powermage(void) -{ - random_spell *s_ptr; - - u32b proj_flags; - - int dir, chance; - - int ty = 0, tx = 0; - - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - - s_ptr = select_spell(); - - if (s_ptr == NULL) return; - - if (p_ptr->csp < s_ptr->mana) - { - msg_print("You do not have enough mana."); - return; - } - - /* Spell failure chance */ - chance = spell_chance_random(s_ptr); - - /* Failed spell */ - if (rand_int(100) < chance) - { - int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane; - char sfail[80]; - - /* Flush input if told so */ - if (flush_failure) flush(); - - /* Insane players can see something strange */ - if (rand_int(100) < insanity) - { - get_rnd_line("sfail.txt", sfail); - msg_format("A cloud of %s appears above you.", sfail); - } - - /* Normal failure messages */ - else - { - msg_print("You failed to get the spell off!"); - } - - sound(SOUND_FAIL); - - /* Let time pass */ - if (is_magestaff()) energy_use = 80; - else energy_use = 100; - - /* Mana is spent anyway */ - p_ptr->csp -= s_ptr->mana; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); - - return; - } - - - p_ptr->csp -= s_ptr->mana; - - s_ptr->untried = FALSE; - proj_flags = s_ptr->proj_flags; - - /* Hack -- Spell needs a target */ - if ((s_ptr->proj_flags & PROJECT_BEAM) || - (s_ptr->proj_flags & PROJECT_STOP)) - { - if (!get_aim_dir(&dir)) return; - - /* Hack -- Use an actual "target" */ - if ((dir == 5) && target_okay()) - { - tx = target_col; - ty = target_row; - - /* Mega-Hack -- Beam spells should continue through - * the target; bolt spells should stop at the - * target. --dsb */ - if (s_ptr->proj_flags & PROJECT_BEAM) - proj_flags |= PROJECT_THRU; - } - else - { - /* Use the given direction */ - ty = p_ptr->py + ddy[dir]; - tx = p_ptr->px + ddx[dir]; - - /* Mega-Hack -- Both beam and bolt spells should - * continue through this fake target. --dsb */ - proj_flags |= PROJECT_THRU; - } - } - - if (s_ptr->proj_flags & PROJECT_BLAST) - { - ty = p_ptr->py; - tx = p_ptr->px; - } - - if (s_ptr->proj_flags & PROJECT_VIEWABLE) - { - project_hack(s_ptr->GF, damroll(s_ptr->dam_dice, s_ptr->dam_sides)); - } - else if (s_ptr->proj_flags & PROJECT_METEOR_SHOWER) - { - project_meteor(s_ptr->radius, s_ptr->GF, - damroll(s_ptr->dam_dice, s_ptr->dam_sides), - s_ptr->proj_flags); - } - else - { - project(0, s_ptr->radius, ty, tx, - damroll(s_ptr->dam_dice, s_ptr->dam_sides), - s_ptr->GF, proj_flags); - } - - /* Take a turn */ - if (is_magestaff()) energy_use = 80; - else energy_use = 100; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); -} - - -/* - * Brand some ammunition. Used by Cubragol and a mage spell. The spell was - * moved here from cmd6.c where it used to be for Cubragol only. I've also - * expanded it to do either frost, fire or venom, at random. -GJW -KMW- - */ -void brand_ammo(int brand_type, int bolts_only) -{ - int a; - - for (a = 0; a < INVEN_PACK; a++) - { - object_type *o_ptr = &p_ptr->inventory[a]; - - if (bolts_only && (o_ptr->tval != TV_BOLT)) continue; - - if (!bolts_only && (o_ptr->tval != TV_BOLT) && - (o_ptr->tval != TV_ARROW) && (o_ptr->tval != TV_SHOT)) - continue; - - if (!artifact_p(o_ptr) && !ego_item_p(o_ptr) && - !cursed_p(o_ptr)) - break; - } - - /* Enchant the ammo (or fail) */ - if ((a < INVEN_PACK) && (rand_int(100) < 50)) - { - object_type *o_ptr = &p_ptr->inventory[a]; - const char *ammo_name; - const char *aura_name; - char msg[48]; - int aura_type, r; - - /* fire only */ - if (brand_type == 1) r = 0; - - /* cold only */ - else if (brand_type == 2) r = 99; - - /* No bias */ - else r = rand_int(100); - - if (r < 50) - { - aura_name = "fiery"; - aura_type = EGO_FLAME; - } - else - { - aura_name = "frosty"; - aura_type = EGO_FROST; - } - - if (o_ptr->tval == TV_BOLT) - { - ammo_name = "bolts"; - } - else if (o_ptr->tval == TV_ARROW) - { - ammo_name = "arrows"; - } - else - { - ammo_name = "shots"; - } - - strnfmt(msg, 48, "Your %s are covered in a %s aura!", - ammo_name, aura_name); - msg_print(msg); - - o_ptr->name2 = aura_type; - - /* Apply the ego */ - apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE); - o_ptr->discount = 100; - - enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM); - } - else - { - if (flush_failure) flush(); - msg_print("The enchantment failed."); - } -} - - -/* - * From Kamband by Ivan Tkatchev - */ -void summon_monster(int sumtype) -{ - /* Take a turn */ - energy_use = 100; - - if (p_ptr->inside_arena) - { - msg_print("This place seems devoid of life."); - msg_print(NULL); - return; - } - - if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level + randint(5), sumtype, TRUE)) - { - msg_print("You summon some help."); - } - else - { - msg_print("You called, but no help came."); - } -} - - - -/* - * Use a class power of Possessor - */ -void do_cmd_possessor() -{ - char ch, ext; - - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - - while (TRUE) - { - if (!get_com("Use your [R]ace powers or your [I]ncarnating powers?", &ch)) - { - ext = 0; - break; - } - if ((ch == 'R') || (ch == 'r')) - { - ext = 1; - break; - } - if ((ch == 'I') || (ch == 'i')) - { - ext = 2; - break; - } - } - - if (ext == 1) - { - bool_ use_great = FALSE; - - if (p_ptr->disembodied) - { - msg_print("You don't currently own a body to use."); - return; - } - - /* Do we have access to all the powers ? */ - if (get_skill_scale(SKILL_POSSESSION, 100) >= r_info[p_ptr->body_monster].level) - use_great = TRUE; - - use_symbiotic_power(p_ptr->body_monster, use_great, FALSE, FALSE); - - if (p_ptr->csp < 0) - { - msg_print("You lose control of your body!"); - if (!do_cmd_leave_body(FALSE)) - { - cmsg_print(TERM_VIOLET, - "You are forced back into your body by your cursed items, " - "you suffer a system shock!"); - - p_ptr->chp = 1; - - /* Display the hitpoints */ - p_ptr->redraw |= (PR_HP); - } - } - } - else if (ext == 2) - { - if (p_ptr->disembodied) - { - do_cmd_integrate_body(); - } - else - { - do_cmd_leave_body(TRUE); - } - } - else - { - return; - } - - /* Take a turn */ - energy_use = 100; -} - - -/* - * Hook to determine if an object is contertible in an arrow/bolt - */ -static bool_ item_tester_hook_convertible(object_type *o_ptr) -{ - if ((o_ptr->tval == TV_JUNK) || (o_ptr->tval == TV_SKELETON)) return TRUE; - - /* Assume not */ - return (FALSE); -} - - -/* - * do_cmd_cast calls this function if the player's class - * is 'archer'. - */ -void do_cmd_archer(void) -{ - int ext = 0; - char ch; - - object_type forge; - object_type *q_ptr; - - char com[80]; - - - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - if (p_ptr->blind) - { - msg_print("You are blind!"); - return; - } - - - if (get_skill(SKILL_ARCHERY) >= 20) - { - strnfmt(com, 80, "Create [S]hots, [A]rrows or [B]olts? "); - } - else if (get_skill(SKILL_ARCHERY) >= 10) - { - strnfmt(com, 80, "Create [S]hots or [A]rrows? "); - } - else - { - strnfmt(com, 80, "Create [S]hots? "); - } - - while (TRUE) - { - if (!get_com(com, &ch)) - { - ext = 0; - break; - } - if ((ch == 'S') || (ch == 's')) - { - ext = 1; - break; - } - if (((ch == 'A') || (ch == 'a')) && (get_skill(SKILL_ARCHERY) >= 10)) - { - ext = 2; - break; - } - if (((ch == 'B') || (ch == 'b')) && (get_skill(SKILL_ARCHERY) >= 20)) - { - ext = 3; - break; - } - } - - /* Prepare for object creation */ - q_ptr = &forge; - - /**********Create shots*********/ - if (ext == 1) - { - int x, y, dir; - cave_type *c_ptr; - - if (!get_rep_dir(&dir)) return; - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - c_ptr = &cave[y][x]; - if (c_ptr->feat == FEAT_RUBBLE) - { - /* Get local object */ - q_ptr = &forge; - - /* Hack -- Give the player some shots */ - object_prep(q_ptr, lookup_kind(TV_SHOT, m_bonus(2, dun_level))); - if (!artifact_p(q_ptr)) - q_ptr->number = (byte)rand_range(15, 30); - else - q_ptr->number = 1; - object_aware(q_ptr); - object_known(q_ptr); - q_ptr->ident |= IDENT_MENTAL; - apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE); - q_ptr->discount = 90; - q_ptr->found = OBJ_FOUND_SELFMADE; - - (void)inven_carry(q_ptr, FALSE); - - msg_print("You make some ammo."); - - (void)wall_to_mud(dir); - p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); - p_ptr->window |= (PW_OVERHEAD); - } - } - - /**********Create arrows*********/ - else if (ext == 2) - { - int item; - - cptr q, s; - - item_tester_hook = item_tester_hook_convertible; - - /* Get an item */ - q = "Convert which item? "; - s = "You have no item to convert."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get local object */ - q_ptr = &forge; - - /* Hack -- Give the player some arrows */ - object_prep(q_ptr, lookup_kind(TV_ARROW, m_bonus(1, dun_level) + 1)); - q_ptr->number = (byte)rand_range(15, 25); - if (!artifact_p(q_ptr)) - q_ptr->number = (byte)rand_range(15, 30); - else - q_ptr->number = 1; - object_aware(q_ptr); - object_known(q_ptr); - q_ptr->ident |= IDENT_MENTAL; - apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE); - q_ptr->discount = 90; - q_ptr->found = OBJ_FOUND_SELFMADE; - - msg_print("You make some ammo."); - - inc_stack_size(item, -1); - - (void)inven_carry(q_ptr, FALSE); - } - - /**********Create bolts*********/ - else if (ext == 3) - { - int item; - - cptr q, s; - - item_tester_hook = item_tester_hook_convertible; - - /* Get an item */ - q = "Convert which item? "; - s = "You have no item to convert."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get local object */ - q_ptr = &forge; - - /* Hack -- Give the player some bolts */ - object_prep(q_ptr, lookup_kind(TV_BOLT, m_bonus(1, dun_level) + 1)); - q_ptr->number = (byte)rand_range(15, 25); - if (!artifact_p(q_ptr)) - q_ptr->number = (byte)rand_range(15, 30); - else - q_ptr->number = 1; - object_aware(q_ptr); - object_known(q_ptr); - q_ptr->ident |= IDENT_MENTAL; - apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE); - q_ptr->discount = 90; - q_ptr->found = OBJ_FOUND_SELFMADE; - - msg_print("You make some ammo."); - - inc_stack_size(item, -1); - - (void)inven_carry(q_ptr, FALSE); - } -} - -/* - * Control whether shots are allowed to pierce - */ -void do_cmd_set_piercing(void) -{ - char ch; - char com[80]; - - if ((get_skill(SKILL_BOW) <= 25) && (get_skill(SKILL_XBOW) <= 25) && - (get_skill(SKILL_SLING) <= 25)) - { - msg_print("You can't fire piercing shots yet."); - return; - } - - strnfmt(com, 80, "Allow shots to pierce? "); - - while (TRUE) - { - if (!get_com(com, &ch)) - { - break; - } - if ((ch == 'Y') || (ch == 'y')) - { - p_ptr->use_piercing_shots = 1; - msg_print("Piercing shots activated."); - break; - } - if ((ch == 'N') || (ch == 'n')) - { - p_ptr->use_piercing_shots = 0; - msg_print("Piercing shots deactivated."); - break; - } - } -} -/* - * Helper function to describe necro powers - */ -void necro_info(char *p, int power) -{ - int plev = get_skill(SKILL_NECROMANCY); - - strcpy(p, ""); - - switch (power) - { - case 0: - { - if (p_ptr->to_s) - strnfmt(p, 80, " power %dd%d+%d", 2 + (plev * 2 / 3), 4, (p_ptr->to_s * 2)); - else - strnfmt(p, 80, " power %dd%d", 2 + (plev * 2 / 3), 4); - break; - } - case 2: - { - strnfmt(p, 80, " dur d%d+%d", 100 + (plev * 4), 200 + (plev * 3)); - break; - } - case 3: - { - strnfmt(p, 80, " dur d%d+%d", 30 + (plev * 2), 50 + plev); - break; - } - } -} - - -/* - * Cast a Necromancy spell - */ -void do_cmd_necromancer(void) -{ - int n = 0, b = 0; - int chance; - int dir; - int minfail = 0; - int plev = get_skill(SKILL_NECROMANCY); - magic_power spell; - int to_s2 = p_ptr->to_s / 2; - int mto_s2 = p_ptr->to_s / 2; - - - if (mto_s2 == 0) mto_s2 = 1; - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - /* not if confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - /* get power */ - if (!get_magic_power(&n, necro_powers, MAX_NECRO_POWERS, necro_info, - get_skill(SKILL_NECROMANCY), A_CON)) return; - - spell = necro_powers[n]; - - /* Verify "dangerous" spells */ - if (spell.mana_cost > p_ptr->csp) - { - /* Warning */ - msg_print("You do not have enough mana to use this power."); - - /* Verify */ - if (!get_check("Attempt it anyway? ")) return; - } - - /* Spell failure chance */ - chance = spell.fail; - - /* Reduce failure rate by "effective" level adjustment */ - chance -= 3 * (plev - spell.min_lev); - - /* Reduce failure rate by INT/WIS adjustment */ - chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_CON]] - 1); - - /* Not enough mana to cast */ - if (spell.mana_cost > p_ptr->csp) - { - chance += 5 * (spell.mana_cost - p_ptr->csp); - } - - /* Extract the minimum failure rate */ - minfail = adj_mag_fail[p_ptr->stat_ind[A_CON]]; - - /* Failure rate */ - chance = clamp_failure_chance(chance, minfail); - - /* Failed spell */ - if (rand_int(100) < chance) - { - if (flush_failure) flush(); - msg_format("You failed to concentrate hard enough!"); - sound(SOUND_FAIL); - - if (randint(100) < (chance / 2)) - { - /* Backfire */ - b = randint(100); - if (b < 10) - { - msg_print("Oh, no! You become undead!"); - - p_ptr->necro_extra |= CLASS_UNDEAD; - p_ptr->necro_extra2 = 2 * plev; - msg_format("You have to kill %d monster%s to be brought back to life.", - p_ptr->necro_extra2, - (p_ptr->necro_extra2 == 1) ? "" : "s"); - - /* MEGA-HACK !!! */ - calc_hitpoints(); - - /* Enforce maximum */ - p_ptr->chp = p_ptr->mhp; - p_ptr->chp_frac = 0; - - /* Display the hitpoints */ - p_ptr->redraw |= (PR_HP); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - } - else if (b < 40) - { - msg_print("Suddenly you feel that you're in a bad situation..."); - summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], - (plev >= 30) ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD); - } - else - { - msg_print("Your body is damaged by the horrible forces of the spell!"); - take_hit(damroll(5, plev), "using necromancy unwisely"); - } - } - } - else - { - sound(SOUND_ZAP); - - /* spell code */ - switch (n) - { - /* Horrify */ - case 0: - { - int dam = damroll(2 + (plev * 2 / 3), 4) + (p_ptr->to_s * 2); - - if (plev > 45) - { - project_hack(GF_STUN, dam); - project_hack(GF_TURN_ALL, dam); - } - else if (plev > 35) - { - if (!get_aim_dir(&dir)) return; - fire_ball(GF_STUN, dir, dam, 3 + (plev / 10)); - fire_ball(GF_TURN_ALL, dir, dam, 3 + (plev / 10)); - } - else if (plev > 20) - { - if (!get_aim_dir(&dir)) return; - fire_beam(GF_STUN, dir, dam); - fire_beam(GF_TURN_ALL, dir, dam); - } - else - { - if (!get_aim_dir(&dir)) return; - fire_bolt(GF_STUN, dir, dam); - fire_bolt(GF_TURN_ALL, dir, dam); - } - - break; - } - - /* Raise Death */ - case 1: - { - fire_ball(GF_RAISE, 0, plev * 3, 1 + to_s2 + (plev / 10)); - - break; - } - - /* Conjures temporary weapon */ - case 2: - { - int dur = randint(100 + (plev * 4)) + 200 + (plev * 3); - object_type forge, *o_ptr = &forge; - int k_idx = test_item_name("& Necromantic Teeth~"); - - k_allow_special[k_idx] = TRUE; - - object_prep(o_ptr, k_idx); - apply_magic(o_ptr, plev * 2, TRUE, TRUE, TRUE); - - o_ptr->art_flags5 |= TR5_TEMPORARY; - o_ptr->timeout = dur; - - /* These objects are "storebought" */ - o_ptr->ident |= IDENT_MENTAL; - o_ptr->number = 1; - - object_aware(o_ptr); - object_known(o_ptr); - (void)inven_carry(o_ptr, FALSE); - - k_allow_special[k_idx] = FALSE; - - break; - } - - /* Absorb souls */ - case 3: - { - set_absorb_soul(randint(30 + (plev * 2)) + 50 + plev); - break; - } - - /* Vampirism */ - case 4: - { - int i; - if (!get_aim_dir(&dir)) return; - for (i = 0; i < 1 + to_s2 + (plev / 15); i++) - { - if (drain_life(dir, 100)) - hp_player(100); - } - - break; - } - - /* Death */ - case 5: - { - if (get_check("Using the Death word will leave you undead, with 1 DP. Do you *really* want to use it? ")) - { - if (!get_aim_dir(&dir)) return; - fire_bolt(GF_DEATH, dir, 1); - - p_ptr->necro_extra |= CLASS_UNDEAD; - p_ptr->necro_extra2 = plev + (rand_int(plev / 2) - (plev / 4)); - msg_format("You have to kill %d monster%s to be brought back to life.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s"); - - /* MEGA-HACK !!! */ - calc_hitpoints(); - - /* Enforce 1 DP */ - p_ptr->chp = p_ptr->mhp; - p_ptr->chp_frac = 0; - - /* Display the hitpoints */ - p_ptr->redraw |= (PR_HP); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - } - - break; - } - - default: - { - msg_print("Zap?"); - - break; - } - } - } - - /* Take a turn */ - if (is_magestaff()) energy_use = 80; - else energy_use = 100; - - /* Sufficient mana */ - if (spell.mana_cost <= p_ptr->csp) - { - /* Use some mana */ - p_ptr->csp -= spell.mana_cost; - } - - /* Over-exert the player */ - else - { - int oops = spell.mana_cost - p_ptr->csp; - - /* No mana left */ - p_ptr->csp = 0; - p_ptr->csp_frac = 0; - - /* Message */ - msg_print("You faint from the effort!"); - - /* Hack -- Bypass free action */ - (void)set_paralyzed(randint(5 * oops + 1)); - - /* Damage CON (possibly permanently) */ - if (rand_int(100) < 50) - { - bool_ perm = (rand_int(100) < 25); - - /* Message */ - msg_print("You have damaged your body!"); - - /* Reduce constitution */ - (void)dec_stat(A_CON, 15 + randint(10), perm); - } - } - - /* Redraw mana */ - p_ptr->redraw |= (PR_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); -} - -/* Runecrafters -- Move this into variable.c XXX XXX XXX */ -static s32b rune_combine = 0; - -/* - * Hook to determine if an object is "runestone" - */ -static bool_ item_tester_hook_runestone(object_type *o_ptr) -{ - if (o_ptr->tval != TV_RUNE2) return (FALSE); - - if (o_ptr->sval != RUNE_STONE) return (FALSE); - - if (o_ptr->pval != 0) return (FALSE); - - /* Assume yes */ - return (TRUE); -} - - -static bool_ item_tester_hook_runestone_full(object_type *o_ptr) -{ - if (o_ptr->tval != TV_RUNE2) return (FALSE); - - if (o_ptr->sval != RUNE_STONE) return (FALSE); - - if (o_ptr->pval == 0) return (FALSE); - - /* Assume yes */ - return (TRUE); -} - - -/* - * Hook to determine if an object is "rune-able" - */ -static bool_ item_tester_hook_runeable1(object_type *o_ptr) -{ - if (o_ptr->tval != TV_RUNE1) return (FALSE); - - /* Assume yes */ - return (TRUE); -} - - -/* - * Hook to determine if an object is "rune-able" - */ -static bool_ item_tester_hook_runeable2(object_type *o_ptr) -{ - if (o_ptr->tval != TV_RUNE2) return (FALSE); - - if (o_ptr->sval == RUNE_STONE) return (FALSE); - - if (rune_combine & BIT(o_ptr->sval)) return (FALSE); - - /* Assume yes */ - return (TRUE); -} - - -/* - * math.h(sqrt) is banned of angband so ... :) - */ -s32b sroot(s32b n) -{ - s32b i = n / 2; - - if (n < 2) return (n); - - while (1) - { - s32b err = (i - n / (i + 1)) / 2; - - if (!err) break; - - i -= err; - } - - return ((n / i < i) ? (i - 1) : i); -} - - -/* - * Damage formula, for runes - */ -void rune_calc_power(s32b *power, s32b *powerdiv) -{ - /* Not too weak power(paranoia) */ - *power = (*power < 1) ? 1 : *power; - *power += 3; - - *power = 37 * sroot(*power) / 10; - - /* To reduce the high level power, while increasing the low levels */ - *powerdiv = *power / 3; - if (*powerdiv < 1) *powerdiv = 1; - - /* Use the spell multiplicator */ - *power *= (p_ptr->to_s / 2) ? (p_ptr->to_s / 2) : 1; -} - - -/* - * Return percentage chance of runespell failure. - */ -int spell_chance_rune(rune_spell* spell) -{ - int chance, minfail; - - s32b power = spell->mana, power_rune = 0, powerdiv = 0; - - - if (spell->rune2 & RUNE_POWER_SURGE) - { - power_rune += 4; - } - if (spell->rune2 & RUNE_ARMAGEDDON) - { - power_rune += 3; - } - if (spell->rune2 & RUNE_SPHERE) - { - power_rune += 2; - } - if (spell->rune2 & RUNE_RAY) - { - power_rune += 1; - } - - rune_calc_power(&power, &powerdiv); - - chance = (5 * power_rune) + (power); - - /* Reduce failure rate by INT/WIS adjustment */ - chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1); - - /* Extract the minimum failure rate */ - minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]]; - - /* Return the chance */ - return clamp_failure_chance(chance, minfail); -} - - -/* - * Combine the Runes - */ -int rune_exec(rune_spell *spell, int cost) -{ - int dir, power_rune = 0, mana_used, plev = get_skill(SKILL_RUNECRAFT); - - int chance; - - s32b power, powerdiv; - - int rad = 0, ty = -1, tx = -1, dam = 0, flg = 0; - - - if (spell->rune2 & RUNE_POWER_SURGE) - { - power_rune += 4; - } - if (spell->rune2 & RUNE_ARMAGEDDON) - { - power_rune += 3; - } - if (spell->rune2 & RUNE_SPHERE) - { - power_rune += 2; - } - if (spell->rune2 & RUNE_RAY) - { - power_rune += 1; - } - - - power = spell->mana; - - if (cost && ((power * cost / 100) > p_ptr->csp - (power_rune * (plev / 5)))) - { - power = p_ptr->csp - (power_rune * (plev / 5)); - mana_used = power + (power_rune * (plev / 5)); - } - else - { - mana_used = (power * cost / 100) + (power_rune * (plev / 5)); - } - - rune_calc_power(&power, &powerdiv); - - dam = damroll(powerdiv, power); - - if (wizard) msg_format("Rune %dd%d = dam %d", powerdiv, power, dam); - - /* Extract the base spell failure rate */ - chance = spell_chance_rune(spell); - - /* Failure ? */ - if (rand_int(100) < chance) - { - int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane; - char sfail[80]; - - /* Flush input if told so */ - if (flush_failure) flush(); - - /* Insane players can see something strange */ - if (rand_int(100) < insanity) - { - get_rnd_line("sfail.txt", sfail); - msg_format("A cloud of %s appears above you.", sfail); - } - - /* Normal failure messages */ - else - { - msg_print("You failed to get the spell off!"); - } - - sound(SOUND_FAIL); - - if (is_magestaff()) energy_use = 80; - else energy_use = 100; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); - return (mana_used); - } - - if (spell->rune2 & RUNE_POWER_SURGE) - { - flg |= (PROJECT_VIEWABLE); - ty = p_ptr->py; - tx = p_ptr->px; - } - - if (spell->rune2 & RUNE_ARMAGEDDON) - { - flg |= (PROJECT_THRU); - flg |= (PROJECT_KILL); - flg |= (PROJECT_ITEM); - flg |= (PROJECT_GRID); - flg |= (PROJECT_METEOR_SHOWER); - rad = (power / 8 == 0) ? 1 : power / 8; - rad = (rad > 10) ? 10 : rad; - ty = p_ptr->py; - tx = p_ptr->px; - } - - if (spell->rune2 & RUNE_SPHERE) - { - flg |= (PROJECT_THRU); - flg |= (PROJECT_KILL); - flg |= (PROJECT_ITEM); - flg |= (PROJECT_GRID); - rad = (power / 8 == 0) ? 1 : power / 8; - rad = (rad > 10) ? 10 : rad; - ty = p_ptr->py; - tx = p_ptr->px; - } - - if (spell->rune2 & RUNE_RAY) - { - flg |= (PROJECT_THRU); - flg |= (PROJECT_KILL); - flg |= (PROJECT_BEAM); - ty = -1; - tx = -1; - } - if (spell->rune2 & RUNE_ARROW) - { - flg |= (PROJECT_THRU); - flg |= (PROJECT_STOP); - flg |= (PROJECT_KILL); - ty = -1; - tx = -1; - } - if (spell->rune2 & RUNE_SELF) - { - flg |= (PROJECT_THRU); - flg |= (PROJECT_STOP); - flg |= (PROJECT_KILL); - ty = p_ptr->py; - tx = p_ptr->px; - unsafe = TRUE; - } - - if ((ty == -1) && (tx == -1)) - { - if (!get_aim_dir(&dir)) return (mana_used); - - /* Use the given direction */ - tx = p_ptr->px + ddx[dir]; - ty = p_ptr->py + ddy[dir]; - - /* Hack -- Use an actual "target" */ - if ((dir == 5) && target_okay()) - { - tx = target_col; - ty = target_row; - } - } - - if (flg & PROJECT_VIEWABLE) - { - project_hack(spell->type, dam); - } - else if (flg & PROJECT_METEOR_SHOWER) - { - project_meteor(rad, spell->type, dam, flg); - } - else project(0, rad, ty, tx, dam, spell->type, flg); - - if (unsafe) unsafe = FALSE; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); - - return (mana_used); -} - - -/* - * Test if all runes needed at in the player p_ptr->inventory - */ -bool_ test_runespell(rune_spell *spell) -{ - int i; - - object_type *o_ptr; - - bool_ typeok = FALSE; - - int rune2 = 0; - - - for (i = 0; i < INVEN_WIELD; i++) - { - o_ptr = &p_ptr->inventory[i]; - - if (!o_ptr->k_idx) continue; - - /* Does the rune1(type) match ? */ - if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == spell->type)) - { - typeok = TRUE; - } - - if ((o_ptr->tval == TV_RUNE2) && (o_ptr->sval != RUNE_STONE)) - { - /* Add it to the list */ - rune2 |= 1 << o_ptr->sval; - } - } - - /* Need all runes to be present */ - return (typeok && ((rune2 & spell->rune2) == spell->rune2)); -} - - -/* - * Ask for rune, rune2 and mana - */ -bool_ get_runespell(rune_spell *spell) -{ - int item, power_rune = 0, rune2 = 0, plev = get_skill(SKILL_RUNECRAFT); - - s32b power; - - int type = 0; - - object_type *o_ptr; - - cptr q, s; - - bool_ OK = FALSE; - - - rune_combine = 0; - - /* Restrict choices to unused runes */ - item_tester_hook = item_tester_hook_runeable1; - - /* Get an item */ - q = "Use which rune? "; - s = "You have no rune to use."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return FALSE; - - /* Get the item */ - o_ptr = get_object(item); - type = o_ptr->sval; - - while (1) - { - /* Restrict choices to unused secondary runes */ - item_tester_hook = item_tester_hook_runeable2; - - OK = !get_item(&item, q, s, (USE_INVEN | USE_FLOOR)); - - if (OK) break; - - /* Get the item */ - o_ptr = get_object(item); - - rune_combine |= 1 << o_ptr->sval; - rune2 |= 1 << o_ptr->sval; - } - - if (!rune2) - { - msg_print("You have not selected a second rune!"); - return (FALSE); - } - - power = get_quantity("Which amount of Mana?", - p_ptr->csp - (power_rune * (plev / 5))); - if (power < 1) power = 1; - - spell->mana = power; - spell->type = type; - spell->rune2 = rune2; - - return (TRUE); -} - - -void do_cmd_rune(void) -{ - rune_spell spell; - - - /* Require some mana */ - if (p_ptr->csp <= 0) - { - msg_print("You have no mana!"); - return; - } - - /* Require lite */ - if (p_ptr->blind || no_lite()) - { - msg_print("You cannot see!"); - return; - } - - /* Not when confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - if (!get_runespell(&spell)) return; - - /* Execute at normal mana cost */ - p_ptr->csp -= rune_exec(&spell, 100); - - /* Safety :) */ - if (p_ptr->csp < 0) p_ptr->csp = 0; - - /* Take a turn */ - if (is_magestaff()) energy_use = 80; - else energy_use = 100; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); -} - - -/* - * Print a batch of runespells. - */ -static void print_runespell_batch(int batch, int max) -{ - char buff[80]; - - rune_spell* spell; - - int i; - - s32b power, powerdiv; - - int p, dp; - - - prt(format(" %-30s Fail Mana Power", "Name"), 1, 20); - - for (i = 0; i < max; i++) - { - spell = &rune_spells[batch * 10 + i]; - - power = spell->mana; - rune_calc_power(&power, &powerdiv); - p = power; - dp = powerdiv; - - strnfmt(buff, 80, " %c) %-30s %4d%% %4d %dd%d ", I2A(i), spell->name, - spell_chance_rune(spell), spell->mana, dp, p); - - prt(buff, 2 + i, 20); - } - prt("", 2 + i, 20); -} - - - -/* - * List ten random spells and ask to pick one. - */ - -static rune_spell* select_runespell_from_batch(int batch, int *s_idx) -{ - char tmp[160]; - - char out_val[30]; - - char which; - - int mut_max = 10; - - rune_spell* ret; - - - character_icky = TRUE; - - if (rune_num < (batch + 1) * 10) - { - mut_max = rune_num - batch * 10; - } - - strnfmt(tmp, 160, "(a-%c, * to list, / to rename, - to comment) Select a power: ", - I2A(mut_max - 1)); - - prt(tmp, 0, 0); - - while (1) - { - Term_save(); - - print_runespell_batch(batch, mut_max); - - which = inkey(); - - Term_load(); - - if (which == ESCAPE) - { - *s_idx = -1; - ret = NULL; - break; - } - else if ((which == '*') || (which == '?') || (which == ' ')) - { - print_runespell_batch(batch, mut_max); - } - else if ((which == '\r') && (mut_max == 1)) - { - *s_idx = batch * 10; - ret = &rune_spells[batch * 10]; - break; - } - else if (which == '/') - { - prt("Rename which power: ", 0, 0); - which = tolower(inkey()); - - if (isalpha(which) && (A2I(which) <= mut_max)) - { - strcpy(out_val, rune_spells[batch*10 + A2I(which)].name); - if (get_string("Name this power: ", out_val, 29)) - { - strcpy(rune_spells[batch*10 + A2I(which)].name, out_val); - } - prt(tmp, 0, 0); - } - else - { - bell(); - prt(tmp, 0, 0); - } - } - else - { - which = tolower(which); - if (isalpha(which) && (A2I(which) < mut_max)) - { - *s_idx = batch * 10 + A2I(which); - ret = &rune_spells[batch * 10 + A2I(which)]; - break; - } - else - { - bell(); - } - } - } - - character_icky = FALSE; - - return (ret); -} - - -/* - * Pick a random spell from a menu - */ - -rune_spell* select_runespell(int *s_idx) -{ - char tmp[160]; - - char which; - - int batch_max = (rune_num - 1) / 10; - - if (rune_num == 0) - { - msg_print("There are no runespells you can cast."); - return (NULL); - } - - character_icky = TRUE; - Term_save(); - - strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max)); - - prt(tmp, 0, 0); - - while (1) - { - which = inkey(); - - if (which == ESCAPE) - { - Term_load(); - character_icky = FALSE; - return (NULL); - } - else if ((which == '\r') && (batch_max == 0)) - { - Term_load(); - character_icky = FALSE; - return (select_runespell_from_batch(0, s_idx)); - - } - else - { - which = tolower(which); - if (isalpha(which) && (A2I(which) <= batch_max)) - { - Term_load(); - character_icky = FALSE; - return (select_runespell_from_batch(A2I(which), s_idx)); - } - else - { - bell(); - } - } - } -} - - -/* - * Cast a memorized runespell - * Note that the only limits are antimagic & conf, NOT blind - */ -void do_cmd_rune_cast() -{ - rune_spell *s_ptr; - - int s_idx; - - - /* Require some mana */ - if (p_ptr->csp <= 0) - { - msg_print("You have no mana!"); - return; - } - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - /* Not when confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - s_ptr = select_runespell(&s_idx); - - if (s_ptr == NULL) return; - - /* Need the runes */ - if (!test_runespell(s_ptr)) - { - msg_print("You lack some essential rune(s) for this runespell!"); - return; - } - - /* Execute at normal mana cost */ - p_ptr->csp -= rune_exec(s_ptr, 100); - - /* Safety :) */ - if (p_ptr->csp < 0) p_ptr->csp = 0; - - /* Take a turn */ - if (is_magestaff()) energy_use = 80; - else energy_use = 100; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); -} - - -/* - * Cast a runespell from a carved runestone - */ -void do_cmd_runestone() -{ - rune_spell s_ptr; - - object_type *o_ptr; - - cptr q, s; - - int item; - - - /* Require some mana */ - if (p_ptr->csp <= 0) - { - msg_print("You have no mana!"); - return; - } - - /* Require lite */ - if (p_ptr->blind || no_lite()) - { - msg_print("You cannot see!"); - return; - } - - /* Not when confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - /* Restrict choices to unused runes */ - item_tester_hook = item_tester_hook_runestone_full; - - /* Get an item */ - q = "Cast from which runestone? "; - s = "You have no runestone to cast from."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - s_ptr.type = o_ptr->pval; - s_ptr.rune2 = o_ptr->pval2; - s_ptr.mana = o_ptr->pval3; - - /* Execute less mana */ - p_ptr->csp -= rune_exec(&s_ptr, 75); - - /* Safety :) */ - if (p_ptr->csp < 0) p_ptr->csp = 0; - - /* Take a turn */ - energy_use = 100; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); -} - - -/* - * Add a runespell to the list - */ -void do_cmd_rune_add_mem() -{ - rune_spell s_ptr; - - rune_spell *ds_ptr = &rune_spells[rune_num]; - - - /* Not when confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - - if (rune_num >= MAX_RUNES) - { - msg_print("You have already learn the maximun number of runespells!"); - return; - } - - if (!get_runespell(&s_ptr)) return; - - ds_ptr->type = s_ptr.type; - ds_ptr->rune2 = s_ptr.rune2; - ds_ptr->mana = s_ptr.mana; - strcpy(ds_ptr->name, "Unnamed Runespell"); - - get_string("Name this runespell: ", ds_ptr->name, 29); - - rune_num++; - - /* Take a turn */ - energy_use = 100; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); -} - - -/* - * Carve a runespell onto a Runestone - */ -void do_cmd_rune_carve() -{ - rune_spell s_ptr; - - object_type *o_ptr; - - cptr q, s; - - int item, i; - - char out_val[80]; - - - /* Not when confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - /* Require lite */ - if (p_ptr->blind || no_lite()) - { - msg_print("You cannot see!"); - return; - } - - if (!get_check("Beware, this will destroy the involved runes, continue?")) - { - return; - } - - if (!get_runespell(&s_ptr)) return; - - /* Restrict choices to unused runes */ - item_tester_hook = item_tester_hook_runestone; - - /* Get an item */ - q = "Use which runestone? "; - s = "You have no runestone to use."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - o_ptr->pval = s_ptr.type; - o_ptr->pval2 = s_ptr.rune2; - o_ptr->pval3 = s_ptr.mana; - - /* Start with nothing */ - strcpy(out_val, ""); - - /* Use old inscription */ - if (o_ptr->note) - { - /* Start with the old inscription */ - strcpy(out_val, quark_str(o_ptr->note)); - } - - /* Get a new inscription (possibly empty) */ - if (get_string("Name this runestone: ", out_val, 80)) - { - /* Save the inscription */ - o_ptr->note = quark_add(out_val); - - /* Combine the pack */ - p_ptr->notice |= (PN_COMBINE); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - } - - /* Delete the runes */ - for (i = 0; i < INVEN_WIELD; i++) - { - o_ptr = &p_ptr->inventory[i]; - - if (o_ptr->k_idx) - { - bool_ do_del = FALSE; - - if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == s_ptr.type)) do_del = TRUE; - if ((o_ptr->tval == TV_RUNE2) && (BIT(o_ptr->sval) & s_ptr.rune2)) do_del = TRUE; - - if (do_del) - { - inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE); - } - } - } - - /* Take a turn -- Carving takes a LONG time */ - energy_use = 400; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); -} - - -/* - * Remove a runespell - */ -void do_cmd_rune_del() -{ - rune_spell *s_ptr; - - int s_idx; - - int i; - - - /* Not when confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - s_ptr = select_runespell(&s_idx); - - if (s_ptr == NULL) return; - - /* Delete and move */ - for (i = s_idx + 1; i < rune_num; i++) - { - rune_spells[i - 1].type = rune_spells[i].type; - rune_spells[i - 1].rune2 = rune_spells[i].rune2; - rune_spells[i - 1].mana = rune_spells[i].mana; - strcpy(rune_spells[i - 1].name, rune_spells[i].name); - } - rune_num--; - - /* Take a turn */ - energy_use = 100; - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - p_ptr->redraw |= (PR_MANA); -} - - -void do_cmd_rune_add() -{ - int ext = 0; - - char ch; - - - /* Select what to do */ - while (TRUE) - { - if (!get_com("Add to [M]emory(need runes to cast) or " - "Carve a [R]unestone(less mana to cast)", &ch)) - { - ext = 0; - break; - } - if ((ch == 'M') || (ch == 'm')) - { - ext = 1; - break; - } - if ((ch == 'R') || (ch == 'r')) - { - ext = 2; - break; - } - } - - switch (ext) - { - /* Create a Spell in memory */ - case 1: - { - do_cmd_rune_add_mem(); - break; - } - - /* Carve a Runestone */ - case 2: - { - do_cmd_rune_carve(); - break; - } - } -} - - -void do_cmd_runecrafter() -{ - int ext = 0; - - char ch; - - - /* Select what to do */ - while (TRUE) - { - if (!get_com("Rune Spell:[C]reate, [D]elete, C[a]st, D[i]rectly Cast " - "or Use [R]unestone", &ch)) - { - ext = 0; - break; - } - if ((ch == 'C') || (ch == 'c')) - { - ext = 1; - break; - } - if ((ch == 'D') || (ch == 'd')) - { - ext = 2; - break; - } - if ((ch == 'A') || (ch == 'a')) - { - ext = 3; - break; - } - if ((ch == 'I') || (ch == 'i')) - { - ext = 4; - break; - } - if ((ch == 'R') || (ch == 'r')) - { - ext = 5; - break; - } - } - - switch (ext) - { - /* Create a Spell */ - case 1: - { - do_cmd_rune_add(); - break; - } - - /* Delete a Spell */ - case 2: - { - do_cmd_rune_del(); - break; - } - - /* Cast a Spell */ - case 3: - { - do_cmd_rune_cast(); - break; - } - - /* Directly Cast a Spell */ - case 4: - { - do_cmd_rune(); - break; - } - - /* Cast a Runestone */ - case 5: - { - do_cmd_runestone(); - break; - } - } -} - - -void do_cmd_unbeliever_antimagic() -{ - if (get_skill(SKILL_ANTIMAGIC) < 20) - { - msg_print("You must have at least a level 20 antimagic skill " - "to be able to disrupt the magic continuum."); - return; - } - - if (p_ptr->antimagic_extra & CLASS_ANTIMAGIC) - { - p_ptr->antimagic_extra &= ~CLASS_ANTIMAGIC; - msg_print("You stop disrupting the magic continuum."); - } - else - { - p_ptr->antimagic_extra |= CLASS_ANTIMAGIC; - msg_print("You start disrupting the magic continuum."); - } - - /* Recalculate bonuses */ - p_ptr->update |= (PU_BONUS); -} - - -/* - * Detect traps + kill traps - */ -void do_cmd_unbeliever() -{ - int ext = 0; - - char ch; - - - /* Select what to do */ - while (TRUE) - { - if (!get_com("Disrupt [C]ontinuum or [D]etect Traps", &ch)) - { - ext = 0; - break; - } - if ((ch == 'C') || (ch == 'c')) - { - ext = 1; - break; - } - if ((ch == 'D') || (ch == 'd')) - { - ext = 2; - break; - } - } - - switch (ext) - { - /* Disrupt Continuum */ - case 1: - { - do_cmd_unbeliever_antimagic(); - break; - } - - /* Detect Traps */ - case 2: - { - s16b skill = get_skill(SKILL_ANTIMAGIC); - - if (skill < 25) - { - msg_print("You cannot use your detection abilities yet."); - break; - } - - detect_traps(DEFAULT_RADIUS); - - if (skill >= 35) destroy_doors_touch(); - - break; - } - } -} - -/* - * Hook to determine if an object is totemable - */ -static bool_ item_tester_hook_totemable(object_type *o_ptr) -{ - /* Only full corpse */ - if ((o_ptr->tval == TV_CORPSE) && - ((o_ptr->sval == SV_CORPSE_CORPSE) || (o_ptr->sval == SV_CORPSE_SKELETON))) - { - return (TRUE); - } - - /* Assume not */ - return (FALSE); -} - - -/* - * Summoners - */ -void do_cmd_summoner_extract() -{ - object_type *o_ptr, forge, *q_ptr; - - cptr q, s; - - int item, r; - - bool_ partial; - - - /* Not when confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - /* Require lite */ - if (p_ptr->blind || no_lite()) - { - msg_print("You cannot see!"); - return; - } - - item_tester_hook = item_tester_hook_totemable; - - /* Get an item */ - q = "Use which corpse? "; - s = "You have no corpse to use."; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Get the item */ - o_ptr = get_object(item); - - - if (r_info[o_ptr->pval2].flags1 & RF1_UNIQUE) - { - partial = FALSE; - } - else - { - partial = get_check("Do you want to create a partial totem?"); - } - - r = o_ptr->pval2; - - inc_stack_size(item, -1); - - if (magik(r_info[o_ptr->pval2].level - get_skill(SKILL_SUMMON))) - { - msg_print("You failed to extract a totem."); - energy_use += 100; - return; - } - - /* Prepare for object creation */ - q_ptr = &forge; - - /* Create the object */ - object_prep(q_ptr, lookup_kind(TV_TOTEM, partial ? 1 : 2)); - q_ptr->pval = r; - q_ptr->pval2 = 0; - q_ptr->number = 1; - q_ptr->found = OBJ_FOUND_SELFMADE; - object_aware(q_ptr); - object_known(q_ptr); - q_ptr->ident |= IDENT_MENTAL; - (void)inven_carry(q_ptr, FALSE); - - msg_print("You extract a totem from the dead corpse."); - energy_use += 100; -} - - -void summon_true(int r_idx, int item) -{ - int i, status, x = 1, y = 1, rx, ry = 0, chance; - - bool_ used; - - monster_race *r_ptr = &r_info[r_idx]; - - - /* Uniques are less likely to be nice */ - if (r_ptr->flags1 & (RF1_UNIQUE)) - { - /* Because it's unique, it will always be destroyed */ - used = TRUE; - - /* About twice as hard as non-uniques */ - chance = (get_skill(SKILL_SUMMON) * 70 / (r_ptr->level + 1)); - - if (magik(chance)) - { - status = MSTATUS_PET; - } - else - { - status = MSTATUS_ENEMY; - } - } - - /* Non-uniques are easier to handle */ - else - { - if (get_skill(SKILL_SUMMON) == 0) - { - used = TRUE; - } - else - { - /* It can be used multiple times */ - used = FALSE; - - /* But it is not 100% sure (note: skill > 0) */ - chance = (r_ptr->level * 25 / get_skill(SKILL_SUMMON)); - if (magik(chance)) used = TRUE; - } - - chance = (get_skill(SKILL_SUMMON) * 130 / (r_ptr->level + 1)); - - if (magik(chance)) - { - status = MSTATUS_PET; - } - else - { - status = MSTATUS_ENEMY; - } - } - - /* Find a grid where the monster is summoned */ - for (i = 0; i < 40; i++) - { - rx = (rand_int(8) - 4) + p_ptr->px; - ry = (rand_int(8) - 4) + p_ptr->py; - if (in_bounds(ry, rx) && cave_empty_bold(ry, rx)) - { - x = rx; - y = ry; - break; - } - } - - /* No room found */ - if (i == 40) - { - msg_print("The summoning fails due to lack of room."); - return; - } - - /* Summon the monster */ - bypass_r_ptr_max_num = TRUE; - if (!(i = place_monster_one (y, x, r_idx, 0, 0, status))) - { - msg_print("The summoning fails."); - } - else - { - m_list[i].status = status; - m_list[i].mflag |= MFLAG_NO_DROP; - } - bypass_r_ptr_max_num = FALSE; - - /* Destroy the totem if the used flag is set */ - if (used) - { - /* Eliminate the totem */ - inc_stack_size(item, -1); - } - - /* Done */ - return; -} - - -void do_cmd_summoner_summon() -{ - int item, x = 1, y = 1, rx, ry, m_idx = 0, i; - - cptr q, s; - - object_type *o_ptr; - - monster_type *m_ptr; - - - /* Which Totem? */ - item_tester_tval = TV_TOTEM; - - q = "Summon from which Totem?"; - s = "There are no totems to summon from!"; - if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; - - /* Access the item */ - o_ptr = get_object(item); - - /* Take a turn */ - energy_use = 100; - - /* True Totems have their own function. */ - if (o_ptr->sval == 2) - { - summon_true(o_ptr->pval, item); - return; - } - - /* Handle partial totems */ - - /* Find a grid where the monster is summoned */ - for (i = 0; i < 40; i++) - { - rx = (rand_int(8) - 4) + p_ptr->px; - ry = (rand_int(8) - 4) + p_ptr->py; - if (in_bounds(ry, rx) && cave_empty_bold(ry, rx)) - { - x = rx; - y = ry; - break; - } - } - - /* No room found */ - if (i == 40) - { - msg_print("The summoning fails due to lack of room."); - return; - } - - /* Summon the monster */ - bypass_r_ptr_max_num = TRUE; - place_monster_one_no_drop = TRUE; - m_idx = place_monster_one(y, x, o_ptr->pval, 0, 0, MSTATUS_PET); - bypass_r_ptr_max_num = FALSE; - - /* Failure. */ - if (!m_idx) - { - msg_print("The summoning fails."); - } - - /* Mark the monster as a "partial" ally */ - m_ptr = &m_list[m_idx]; - m_ptr->mflag |= MFLAG_PARTIAL | MFLAG_NO_DROP; -} - - -void do_cmd_summoner(void) -{ - int ext = 0; - - char ch; - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - /* not if confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - /* not if blind */ - if (p_ptr->blind || no_lite()) - { - msg_print("You cannot see!"); - return; - } - - /* Select what to do */ - while (TRUE) - { - if (!get_com("[E]xtract a totem, [S]ummon", &ch)) - { - ext = 0; - break; - } - if ((ch == 'E') || (ch == 'e')) - { - ext = 1; - break; - } - if ((ch == 's') || (ch == 'S')) - { - ext = 2; - break; - } - } - - switch (ext) - { - case 1: - { - do_cmd_summoner_extract(); - break; - } - - case 2: - { - do_cmd_summoner_summon(); - break; - } - } -} - - -/* - * Fighters may invoke The Rush. - */ -void do_cmd_blade(void) -{ - /* Are we already Rushed? */ - if (p_ptr->rush) - { - msg_format("You have %d turns of The Rush remaining", p_ptr->rush); - return; - } - - /* Are you sure? */ - if (!get_check("Are you sure you want to invoke The Rush?")) return; - - /* Let's Rush! */ - set_rush(2 + p_ptr->lev / 2 + randint(p_ptr->lev / 2)); -} - - -/* - * Dodge Chance Feedback. - */ -void use_ability_blade(void) -{ - int chance = p_ptr->dodge_chance - ((dun_level * 5) / 6); - - if (chance < 0) chance = 0; - if (wizard) - { - msg_format("You have exactly %d chances of dodging a level %d monster.", chance, dun_level); - } - - if (chance < 5) - { - msg_format("You have almost no chance of dodging a level %d monster.", dun_level); - } - else if (chance < 10) - { - msg_format("You have a slight chance of dodging a level %d monster.", dun_level); - } - else if (chance < 20) - { - msg_format("You have a significant chance of dodging a level %d monster.", dun_level); - } - else if (chance < 40) - { - msg_format("You have a large chance of dodging a level %d monster.", dun_level); - } - else if (chance < 70) - { - msg_format("You have a high chance of dodging a level %d monster.", dun_level); - } - else - { - msg_format("You will usually dodge successfully a level %d monster.", dun_level); - } - - return; -} - -/* - * Helper function to describe symbiotic powers - */ -void symbiotic_info(char *p, int power) -{ - int plev = get_skill(SKILL_SYMBIOTIC); - - strcpy(p, ""); - - switch (power) - { - case 2: - { - strnfmt(p, 80, " power %d", plev * 3); - break; - } - case 5: - { - strnfmt(p, 80, " heal %d%%", 15 + get_skill_scale(SKILL_SYMBIOTIC, 35)); - break; - } - } -} - - -/* - * Cast a symbiotic spell - */ -void do_cmd_symbiotic(void) -{ - int n = 0; - int chance; - int minfail = 0; - int plev = get_skill(SKILL_SYMBIOTIC); - magic_power spell; - - /* Get the carried monster */ - object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY]; - - /* No magic */ - if (p_ptr->antimagic) - { - msg_print("Your anti-magic field disrupts any magic attempts."); - return; - } - - /* No magic */ - if (p_ptr->anti_magic) - { - msg_print("Your anti-magic shell disrupts any magic attempts."); - return; - } - - /* not if confused */ - if (p_ptr->confused) - { - msg_print("You are too confused!"); - return; - } - - /* get power */ - if (!get_magic_power(&n, symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info, - get_skill(SKILL_SYMBIOTIC), A_INT)) return; - - spell = symbiotic_powers[n]; - - /* Verify "dangerous" spells */ - if (spell.mana_cost > p_ptr->csp) - { - /* Warning */ - msg_print("You do not have enough mana to use this power."); - - /* Verify */ - if (!get_check("Attempt it anyway? ")) return; - } - - /* Spell failure chance */ - chance = spell.fail; - - /* Reduce failure rate by "effective" level adjustment */ - chance -= 3 * (plev - spell.min_lev); - - /* Reduce failure rate by INT/WIS adjustment */ - chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1); - - /* Not enough mana to cast */ - if (spell.mana_cost > p_ptr->csp) - { - chance += 5 * (spell.mana_cost - p_ptr->csp); - } - - /* Extract the minimum failure rate */ - minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]]; - - /* Failure rate */ - chance = clamp_failure_chance(chance, minfail); - - /* Failed spell */ - if (rand_int(100) < chance) - { - if (flush_failure) flush(); - msg_format("You failed to concentrate hard enough!"); - sound(SOUND_FAIL); - } - else - { - sound(SOUND_ZAP); - - /* spell code */ - switch (n) - { - case 0: - { - int dir, x, y; - cave_type *c_ptr; - monster_type *m_ptr; - monster_race *r_ptr; - object_type *q_ptr; - object_type forge; - - msg_print("Hypnotise which pet?"); - if (!get_rep_dir(&dir)) return; - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - c_ptr = &cave[y][x]; - if (c_ptr->m_idx) - { - m_ptr = &m_list[c_ptr->m_idx]; - r_ptr = race_inf(m_ptr); - - if (!(r_ptr->flags1 & RF1_NEVER_MOVE)) - { - msg_print("You can only hypnotise monsters that cannot move."); - } - else if (m_ptr->status < MSTATUS_PET) - { - msg_print("You can only hypnotise pets and companions."); - } - else if (r_ptr->flags9 & RF9_SPECIAL_GENE) - { - msg_print("You cannot hypnotise this monster."); - } - else - { - /* TODO fix this hack hack hack hackity hack with ToME 3 flags */ - q_ptr = &forge; - object_prep(q_ptr, lookup_kind(TV_HYPNOS, 1)); - q_ptr->number = 1; - q_ptr->pval = m_ptr->r_idx; - q_ptr->pval2 = m_ptr->hp; - q_ptr->pval3 = m_ptr->maxhp; - /* overflow alert */ - q_ptr->exp = m_ptr->exp; - q_ptr->elevel = m_ptr->level; - object_aware(q_ptr); - object_known(q_ptr); - - q_ptr->ident |= IDENT_STOREB; - - drop_near(q_ptr, 0, y, x); - - delete_monster(y, x); - health_who = 0; - } - } - else - { - msg_print("There is no pet here !"); - } - - break; - } - - case 1: - { - monster_type *m_ptr; - int m_idx; - int item, x, y, d; - object_type *o_ptr; - - cptr q, s; - - /* Restrict choices to monsters */ - item_tester_tval = TV_HYPNOS; - - /* Get an item */ - q = "Awaken which monster? "; - s = "You have no monster to awaken."; - if (!get_item(&item, q, s, (USE_FLOOR))) return; - - o_ptr = &o_list[0 - item]; - - d = 2; - while (d < 100) - { - scatter(&y, &x, p_ptr->py, p_ptr->px, d); - - if (cave_floor_bold(y, x) && (!cave[y][x].m_idx)) break; - - d++; - } - - if (d >= 100) return; - - if ((m_idx = place_monster_one(y, x, o_ptr->pval, 0, FALSE, MSTATUS_PET)) == 0) return; - - /* TODO fix this hack hack hack hackity hack with ToME 3 flags */ - /* Have to be careful here; releasing the symbiote into a - * dungeon with leveled monsters will level the symbiote - * before we can get hold of it. We'll be nice and use the - * larger of the saved exp and the exp that the newly-generated - * monster starts with. */ - m_ptr = &m_list[m_idx]; - if (m_ptr->exp < o_ptr->exp) - { - m_ptr->exp = o_ptr->exp; - monster_check_experience(m_idx, TRUE); - if (m_ptr->level != o_ptr->elevel) - cmsg_format(TERM_VIOLET, "ERROR: level-%d HYPNOS becomes level-%d symbiote", o_ptr->elevel, m_ptr->level); - } - m_ptr->hp = o_ptr->pval2; - m_ptr->maxhp = o_ptr->pval3; - - floor_item_increase(0 - item, -1); - floor_item_describe(0 - item); - floor_item_optimize(0 - item); - break; - } - - /* Charm */ - case 2: - { - int dir; - - if (!get_aim_dir(&dir)) return; - - fire_bolt(GF_CHARM_UNMOVING, dir, plev * 3); - - break; - } - - /* Life Share */ - case 3: - { - s32b percent1, percent2; - - if (!o_ptr->k_idx) - { - msg_print("You are not in symbiosis."); - break; - } - - percent1 = p_ptr->chp; - percent1 = (percent1 * 100) / p_ptr->mhp; - - percent2 = o_ptr->pval2; - percent2 = (percent2 * 100) / o_ptr->pval3; - - /* Now get the average */ - percent1 = (percent1 + percent2) / 2; - - /* And set the hp of monster & player to it */ - p_ptr->chp = (percent1 * p_ptr->mhp) / 100; - o_ptr->pval2 = (percent1 * o_ptr->pval3) / 100; - - /* Redraw */ - p_ptr->redraw |= (PR_HP); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - - /* Display the monster hitpoints */ - p_ptr->redraw |= (PR_MH); - - break; - } - - /* Minor Symbiotic Powers */ - case 4: - { - if (!o_ptr->k_idx) - { - msg_print("You are not in symbiosis."); - break; - } - - if (0 > use_symbiotic_power(o_ptr->pval, FALSE, FALSE, TRUE)) - return; - - break; - } - - /* Heal Symbiote */ - case 5: - { - int hp; - - if (!o_ptr->k_idx) - { - msg_print("You are not in symbiosis."); - break; - } - - hp = o_ptr->pval3 * (15 + get_skill_scale(SKILL_SYMBIOTIC, 35)) / 100; - o_ptr->pval2 += hp; - if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3; - - msg_format("%s is healed.", symbiote_name(TRUE)); - - /* Display the monster hitpoints */ - p_ptr->redraw |= (PR_MH); - - break; - } - - - /* Major Symbiotic Powers */ - case 6: - { - if (!o_ptr->k_idx) - { - msg_print("You are not in symbiosis."); - break; - } - - if(0 > use_symbiotic_power(o_ptr->pval, TRUE, FALSE, TRUE)) - return; - - break; - } - - /* Summon never-moving pet */ - case 7: - { - summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE); - - break; - } - - /* Force Symbiosis */ - case 8: - { - int y, x; - cave_type *c_ptr; - monster_type *m_ptr; - - if (!tgt_pt(&x, &y)) return; - - c_ptr = &cave[y][x]; - - if (!c_ptr->m_idx) break; - - m_ptr = &m_list[c_ptr->m_idx]; - use_symbiotic_power(m_ptr->r_idx, TRUE, FALSE, TRUE); - - break; - } - - - default: - { - msg_print("Zap?"); - - break; - } - } - } - - /* Take a turn */ - energy_use = 100; - - /* Sufficient mana */ - if (spell.mana_cost <= p_ptr->csp) - { - /* Use some mana */ - p_ptr->csp -= spell.mana_cost; - } - - /* Over-exert the player */ - else - { - int oops = spell.mana_cost - p_ptr->csp; - - /* No mana left */ - p_ptr->csp = 0; - p_ptr->csp_frac = 0; - - /* Message */ - msg_print("You faint from the effort!"); - - /* Hack -- Bypass free action */ - (void)set_paralyzed(randint(5 * oops + 1)); - - /* Damage CON (possibly permanently) */ - if (rand_int(100) < 50) - { - bool_ perm = (rand_int(100) < 25); - - /* Message */ - msg_print("You have damaged your body!"); - - /* Reduce constitution */ - (void)dec_stat(A_CHR, 15 + randint(10), perm); - } - } - - /* Redraw mana */ - p_ptr->redraw |= (PR_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); -} - -/* - * Boulder creation .. sorry :) - */ -void do_cmd_create_boulder() -{ - int x, y, dir; - cave_type *c_ptr; - - if (!get_rep_dir(&dir)) return; - y = p_ptr->py + ddy[dir]; - x = p_ptr->px + ddx[dir]; - c_ptr = &cave[y][x]; - - /* Granite -- How about other wall types? */ - if (((c_ptr->feat >= FEAT_WALL_EXTRA) && (c_ptr->feat <= FEAT_WALL_SOLID)) || - ((c_ptr->feat >= FEAT_MAGMA_H) && (c_ptr->feat <= FEAT_QUARTZ_K)) || - ((c_ptr->feat == FEAT_MAGMA) || - (c_ptr->feat == FEAT_QUARTZ))) - { - object_type forge; - object_type *q_ptr; - - (void)wall_to_mud(dir); - - /* Get local object */ - q_ptr = &forge; - - /* Hack -- Give the player some shots */ - object_prep(q_ptr, lookup_kind(TV_JUNK, SV_BOULDER)); - q_ptr->number = (byte)rand_range(2, 5); - object_aware(q_ptr); - object_known(q_ptr); - q_ptr->ident |= IDENT_MENTAL; - q_ptr->discount = 90; - q_ptr->found = OBJ_FOUND_SELFMADE; - - (void)inven_carry(q_ptr, FALSE); - - msg_print("You make some boulders."); - - p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); - p_ptr->window |= (PW_OVERHEAD); - - /* Take a turn */ - energy_use = 100; - } -} - -/* - * Clamp failure chance - */ -extern int clamp_failure_chance(int chance, int minfail) -{ - if (minfail < 0) minfail = 0; - - /* Minimum failure rate */ - if (chance < minfail) chance = minfail; - - /* Stunning makes spells harder */ - if (p_ptr->stun > 50) chance += 25; - else if (p_ptr->stun) chance += 15; - - /* Always a 5 percent chance of working */ - if (chance > 95) chance = 95; - - return chance; -} diff --git a/src/cmd7.cc b/src/cmd7.cc new file mode 100644 index 00000000..c9be0c66 --- /dev/null +++ b/src/cmd7.cc @@ -0,0 +1,7632 @@ +/* File: cmd7.c */ + +/* Purpose: More Class commands */ + +/* + * Copyright (c) 1999 Dark God + * + * 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 "angband.h" + +#include "quark.h" +#include "hooks.h" + +/* + * Describe class powers of Mindcrafters + * + * 'p' points to a 80 byte long buffer + */ +void mindcraft_info(char *p, int power) +{ + int plev = get_skill(SKILL_MINDCRAFT); + + + /* Clear buffer */ + strcpy(p, ""); + + /* Fill the buffer with requested power description */ + switch (power) + { + case 0: + strnfmt(p, 80, " rad %d", DEFAULT_RADIUS); + break; + case 1: + strnfmt(p, 80, " dam %dd%d", 3 + ((plev - 1) / 4), 3 + plev / 15); + break; + case 2: + strnfmt(p, 80, " range %d", (plev < 25 ? 10 : plev + 2 + p_ptr->to_s * 3)); + break; + case 3: + strnfmt(p, 80, " range %d", plev * 5); + break; + case 4: + strnfmt(p, 80, " power %d", plev * (plev < 30 ? 1 : 2)); + break; + case 5: + if (plev > 20) + strnfmt(p, 80, " dam %dd8 rad %d", 8 + ((plev - 5) / 4), (plev - 20)/8 + 1); + else + strnfmt(p, 80, " dam %dd8", 8 + ((plev - 5) / 4)); + break; + case 6: + strnfmt(p, 80, " dur %d", plev); + break; + case 7: + break; + case 8: + if (plev < 25) + strnfmt(p, 80, " dam %d rad %d", (3 * plev) / 2, 2 + (plev / 10)); + else + strnfmt(p, 80, " dam %d", plev * ((plev - 5) / 10 + 1)); + break; + case 9: + strnfmt(p, 80, " dur 11-%d", 10 + plev + plev / 2); + break; + case 10: + strnfmt(p, 80, " dam %dd6 rad %d", plev / 2, 0 + (plev - 25) / 10); + break; + case 11: + strnfmt(p, 80, " dam %d rad %d", plev * (plev > 39 ? 4 : 3), 3 + plev / 10); + break; + } +} + + +/* + * Describe class powers of Mimics + * + * 'p' points to a 80 byte long buffer + */ +void mimic_info(char *p, int power) +{ + int plev = get_skill(SKILL_MIMICRY); + object_type *o_ptr = &p_ptr->inventory[INVEN_OUTER]; + + /* Clear the buffer */ + strcpy(p, ""); + + /* Fill the buffer with requested power description */ + switch (power) + { + case 0: + strnfmt(p, 80, " dur %d", k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000)); + break; + case 1: + strnfmt(p, 80, " dur %d+d20", 10 + plev); + break; + case 2: + strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev)); + break; + case 3: + strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev)); + break; + case 4: + strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev)); + break; + } +} + +/** + * Show magic powers that user can choose from + */ +static void display_magic_powers( + magic_power *powers, + int max_powers, + void (*power_info)(char *p, int power), + int plev, + int cast_stat, + int y, + int x) +{ + char psi_desc[80]; + magic_power spell; + int i; + int chance = 0; + int minfail = 0; + char comment[80]; + + /* Display a list of spells */ + prt("", 1, x); + prt("", y, x); + put_str("Name", y, x + 5); + put_str("Lv Mana Fail Info", y, x + 35); + + /* Dump the spells */ + for (i = 0; i < max_powers; i++) + { + /* Access the spell */ + spell = powers[i]; + if (spell.min_lev > plev) + { + break; + } + + chance = spell.fail; + /* Reduce failure rate by "effective" level adjustment */ + chance -= 3 * (plev - spell.min_lev); + + /* Reduce failure rate by INT/WIS adjustment */ + chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[cast_stat]] - 1); + + /* Not enough mana to cast */ + if (spell.mana_cost > p_ptr->csp) + { + chance += 5 * (spell.mana_cost - p_ptr->csp); + } + + /* Extract the minimum failure rate */ + minfail = adj_mag_fail[p_ptr->stat_ind[cast_stat]]; + + /* Failure rate */ + chance = clamp_failure_chance(chance, minfail); + + /* Get info */ + power_info(comment, i); + + /* Dump the spell --(-- */ + strnfmt(psi_desc, 80, " %c) %-30s%2d %4d %3d%%%s", + I2A(i), spell.name, + spell.min_lev, spell.mana_cost, chance, comment); + prt(psi_desc, y + i + 1, x); + } + + /* Clear the bottom line */ + prt("", y + i + 1, x); +} + +/* + * Allow user to choose a magic power. + * + * If a valid spell is chosen, saves it in '*sn' and returns TRUE + * If the user hits escape, returns FALSE, and set '*sn' to -1 + * If there are no legal choices, returns FALSE, and sets '*sn' to -2 + * + * The "prompt" should be "cast", "recite", or "study" + * The "known" should be TRUE for cast/pray, FALSE for study + * + * nb: This function has a (trivial) display bug which will be obvious + * when you run it. It's probably easy to fix but I haven't tried, + * sorry. + */ +static bool_ get_magic_power(int *sn, magic_power *powers, int max_powers, + void (*power_info)(char *p, int power), int plev, int cast_stat) +{ + int i; + + int num = 0; + + int y = 2; + + int x = 18; + + int info; + + char choice; + + char out_val[160]; + + cptr p = "power"; + + magic_power spell; + + bool_ flag; + + + /* Assume cancelled */ + *sn = ( -1); + + /* Get the spell, if available */ + if (repeat_pull(sn)) + { + /* Verify the spell */ + if (powers[*sn].min_lev <= plev) + { + /* Success */ + return (TRUE); + } + } + + /* Nothing chosen yet */ + flag = FALSE; + + /* Count number of powers that satisfies minimum plev requirement */ + for (i = 0; i < max_powers; i++) + { + if (powers[i].min_lev <= plev) + { + num++; + } + } + + /* Build a prompt (accept all spells) */ + strnfmt(out_val, 78, "(%^ss %c-%c, ESC=exit, %c-%c=Info) Use which %s? ", + p, I2A(0), I2A(num - 1), toupper(I2A(0)), toupper(I2A(num - 1)), p); + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + + /* Show the list */ + display_magic_powers(powers, max_powers, power_info, plev, cast_stat, y, x); + + /* Get a spell from the user */ + while (!flag && get_com(out_val, &choice)) + { + /* Note verify */ + info = (isupper(choice)); + + /* Lowercase */ + if (info) choice = tolower(choice); + + /* Extract request */ + i = (islower(choice) ? A2I(choice) : -1); + + /* Totally Illegal */ + if ((i < 0) || (i >= num)) + { + bell(); + continue; + } + + /* Save the spell index */ + spell = powers[i]; + + /* Provides info */ + if (info) + { + c_prt(TERM_L_BLUE, spell.desc, 1, 0); + + /* Restore the screen */ + inkey(); + Term_load(); + character_icky = FALSE; + + /* Redisplay choices */ + display_magic_powers(powers, max_powers, power_info, plev, cast_stat, y, x); + continue; + } + + /* Stop the loop */ + flag = TRUE; + } + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + + /* Abort if needed */ + if (!flag) return (FALSE); + + /* Save the choice */ + (*sn) = i; + + + repeat_push(*sn); + + /* Success */ + return (TRUE); +} + + +/* + * do_cmd_cast calls this function if the player's class + * is 'mindcrafter'. + */ +void do_cmd_mindcraft(void) +{ + int n = 0, b = 0; + + int chance; + + int dir; + + int minfail = 0; + + int plev = get_skill(SKILL_MINDCRAFT); + + magic_power spell; + + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + + /* not if confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + /* get power */ + if (!get_magic_power(&n, mindcraft_powers, MAX_MINDCRAFT_POWERS, + mindcraft_info, plev, A_WIS)) return; + + spell = mindcraft_powers[n]; + + /* Verify "dangerous" spells */ + if (spell.mana_cost > p_ptr->csp) + { + /* Warning */ + msg_print("You do not have enough mana to use this power."); + + /* Verify */ + if (!get_check("Attempt it anyway? ")) return; + } + + /* Spell failure chance */ + chance = spell.fail; + + /* Reduce failure rate by "effective" level adjustment */ + chance -= 3 * (get_skill(SKILL_MINDCRAFT) - spell.min_lev); + + /* Reduce failure rate by INT/WIS adjustment */ + chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_WIS]] - 1); + + /* Not enough mana to cast */ + if (spell.mana_cost > p_ptr->csp) + { + chance += 5 * (spell.mana_cost - p_ptr->csp); + } + + /* Extract the minimum failure rate */ + minfail = adj_mag_fail[p_ptr->stat_ind[A_WIS]]; + + /* Failure rate */ + chance = clamp_failure_chance(chance, minfail); + + /* Failed spell */ + if (rand_int(100) < chance) + { + if (flush_failure) flush(); + + msg_format("You failed to concentrate hard enough!"); + + sound(SOUND_FAIL); + + if (randint(100) < (chance / 2)) + { + /* Backfire */ + b = randint(100); + if (b < 5) + { + msg_print("Oh, no! Your mind has gone blank!"); + lose_all_info(); + } + else if (b < 15) + { + msg_print("Weird visions seem to dance before your eyes..."); + set_image(p_ptr->image + 5 + randint(10)); + } + else if (b < 45) + { + msg_print("Your brain is addled!"); + set_confused(p_ptr->confused + randint(8)); + } + else if (b < 90) + { + set_stun(p_ptr->stun + randint(8)); + } + else + { + /* Mana storm */ + msg_print("Your mind unleashes its power in an uncontrollable storm!"); + project(1, 2 + plev / 10, p_ptr->py, p_ptr->px, plev * 2, + GF_MANA, PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM); + p_ptr->csp = MAX(0, p_ptr->csp - plev * MAX(1, plev / 10)); + } + } + } + + /* Successful spells */ + else + { + sound(SOUND_ZAP); + + /* spell code */ + switch (n) + { + /* Precog */ + case 0: + { + /* Magic mapping */ + if (plev > 44) + { + wiz_lite(); + } + else if (plev > 19) + { + map_area(); + } + + /* Detection */ + if (plev < 30) + { + b = detect_monsters_normal(DEFAULT_RADIUS); + if (plev > 14) b |= detect_monsters_invis(DEFAULT_RADIUS); + if (plev > 4) b |= detect_traps(DEFAULT_RADIUS); + } + else + { + b = detect_all(DEFAULT_RADIUS); + } + + /* Telepathy */ + if (plev > 24) + { + set_tim_esp(p_ptr->tim_esp + plev); + + /* If plvl >= 40, we should have permanent ESP */ + } + + if (!b) msg_print("You feel safe."); + + break; + } + + /* Mindblast */ + case 1: + { + if (!get_aim_dir(&dir)) return; + + if (randint(100) < plev * 2) + { + fire_beam(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15))); + } + else + { + fire_ball(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)), 0); + } + + break; + } + + /* Minor displace */ + case 2: + { + if (plev < 25) + { + teleport_player(10); + } + else + { + int ii, ij; + + if (dungeon_flags2 & DF2_NO_TELEPORT) + { + msg_print("Not on special levels!"); + break; + } + + msg_print("You open a Void Jumpgate. Choose a destination."); + + if (!tgt_pt(&ii, &ij)) return; + p_ptr->energy -= 60 - plev; + + if (!cave_empty_bold(ij, ii) || + (cave[ij][ii].info & CAVE_ICKY) || + (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2 + (p_ptr->to_s*3)) || + (rand_int(plev * plev / 2) == 0)) + { + msg_print("You fail to exit the void correctly!"); + p_ptr->energy -= 100; + get_pos_player(10 + p_ptr->to_s / 2, &ij, &ii); + } + + cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN); + cave_set_feat(ij, ii, FEAT_BETWEEN); + cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8); + cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8); + } + + break; + } + + /* Major displace */ + case 3: + { + if (plev > 29) banish_monsters(plev); + teleport_player(plev * 5); + + break; + } + + /* Domination */ + case 4: + { + if (plev < 30) + { + if (!get_aim_dir(&dir)) return; + fire_ball(GF_DOMINATION, dir, plev, 0); + } + else + { + charm_monsters(plev * 2); + } + + break; + } + + /* Fist of Force --- not 'true' TK */ + case 5: + { + if (!get_aim_dir(&dir)) return; + fire_ball(GF_SOUND, dir, damroll(8 + ((plev - 5) / 4), 8), + (plev > 20 ? (plev - 20) / 8 + 1 : 0)); + + break; + } + + /* Character Armour */ + case 6: + { + set_shield(p_ptr->shield + plev, plev, 0, 0, 0); + if (plev > 14) set_oppose_acid(p_ptr->oppose_acid + plev); + if (plev > 19) set_oppose_fire(p_ptr->oppose_fire + plev); + if (plev > 24) set_oppose_cold(p_ptr->oppose_cold + plev); + if (plev > 29) set_oppose_elec(p_ptr->oppose_elec + plev); + if (plev > 34) set_oppose_pois(p_ptr->oppose_pois + plev); + + break; + } + + /* Psychometry */ + case 7: + { + ident_spell(); + break; + } + + /* Mindwave */ + case 8: + { + msg_print("Mind-warping forces emanate from your brain!"); + if (plev < 25) + { + project(0, 2 + plev / 10, p_ptr->py, p_ptr->px, + (plev*3) / 2, GF_PSI, PROJECT_KILL); + } + else + { + (void)mindblast_monsters(plev * ((plev - 5) / 10 + 1)); + } + + break; + } + + /* Adrenaline */ + case 9: + { + set_afraid(0); + set_stun(0); + hp_player(plev); + + b = 10 + randint((plev * 3) / 2); + + if (plev < 35) + { + set_hero(p_ptr->hero + b); + } + else + { + set_shero(p_ptr->shero + b); + } + + if (!p_ptr->fast) + { + /* Haste */ + (void)set_fast(b, plev / 5); + } + else + { + (void)set_fast(p_ptr->fast + b, plev / 5); + } + + break; + } + + /* Psychic Drain */ + case 10: + { + if (!get_aim_dir(&dir)) return; + + b = damroll(plev / 2, 6); + + if (fire_ball(GF_PSI_DRAIN, dir, b, 0 + (plev - 25) / 10)) + { + p_ptr->energy -= randint(150); + } + + break; + } + + /* Telekinesis */ + case 11: + { + msg_print("A wave of pure physical force radiates out from your body!"); + project(0, 3 + plev / 10, p_ptr->py, p_ptr->px, + plev * (plev > 39 ? 4 : 3), GF_TELEKINESIS, + PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID); + + break; + } + + default: + { + msg_print("Zap?"); + + break; + } + } + } + + /* Take a turn */ + energy_use = 100; + + /* Sufficient mana */ + if (spell.mana_cost <= p_ptr->csp) + { + /* Use some mana */ + p_ptr->csp -= spell.mana_cost; + } + + /* Over-exert the player */ + else + { + int oops = spell.mana_cost - p_ptr->csp; + + /* No mana left */ + p_ptr->csp = 0; + p_ptr->csp_frac = 0; + + /* Message */ + msg_print("You faint from the effort!"); + + /* Hack -- Bypass free action */ + (void)set_paralyzed(randint(5 * oops + 1)); + + /* Damage WIS (possibly permanently) */ + if (rand_int(100) < 50) + { + bool_ perm = (rand_int(100) < 25); + + /* Message */ + msg_print("You have damaged your mind!"); + + /* Reduce constitution */ + (void)dec_stat(A_WIS, 15 + randint(10), perm); + } + } + + /* Redraw mana */ + p_ptr->redraw |= (PR_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); +} + + +static int get_mimic_chance(int mimic) +{ + s32b chance; + + chance = get_mimic_level(mimic); + chance *= 3; + + chance -= get_skill_scale(SKILL_MIMICRY, 150); + chance -= 3 * adj_mag_stat[p_ptr->stat_ind[A_DEX]]; + + /* Return the chance */ + return clamp_failure_chance(chance, 2); +} + + +void do_cmd_mimic_lore() +{ + int fail; + + object_type *o_ptr; + + + /* Player has to be able to see */ + if (p_ptr->blind || no_lite()) + { + msg_print("You cannot see!"); + return; + } + + /* No transformations when confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + + /* Already in a mimic form -- Allow cancelling */ + if (p_ptr->mimic_form) + { + msg_print("You morph back to your natural form!"); + + set_mimic(0, 0, 0); + } + + /* Not in mimic forms -- Allow transformations */ + else + { + o_ptr = &p_ptr->inventory[INVEN_OUTER]; + + if ((o_ptr->tval != TV_CLOAK) || (o_ptr->sval != SV_MIMIC_CLOAK)) + { + msg_print("You are not wearing any cloaks of mimicry."); + return; + } + + /* Calculate failure rate */ + fail = get_mimic_chance(o_ptr->pval2); + + if (fail > 75) + { + msg_print("You feel uneasy with this shape-change."); + + if (!get_check("Try it anyway? ")) return; + } + + /* Fumble */ + if (randint(100) < fail) + { + msg_print("Your shape-change goes horribly wrong!"); + + if (randint(100) < p_ptr->skill_sav) + { + msg_print("You manage to wrest your body back under control."); + return; + } + + set_mimic(30, resolve_mimic_name("Abomination"), get_skill(SKILL_MIMICRY)); + } + + /* Success */ + else + { + set_mimic(k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000), o_ptr->pval2, get_skill(SKILL_MIMICRY)); + } + } + + + /* Redraw title */ + p_ptr->redraw |= (PR_TITLE); + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); +} + +static bool_ mimic_forbid_travel(const char *fmt) +{ + u32b value = p_ptr->mimic_extra >> 16; + u32b att = p_ptr->mimic_extra & 0xFFFF; + + if(value > 0 && (att & CLASS_ARMS || att & CLASS_LEGS)) + { + msg_print("You had best not travel with your extra limbs."); + return TRUE; + } + + return FALSE; +} + +/* + * do_cmd_cast calls this function if the player's class + * is 'mimic'. + */ +void do_cmd_mimic(void) +{ + int n = 0, b = 0; + + int fail; + + int minfail = 0; + + int plev = get_skill(SKILL_MIMICRY); + + magic_power spell; + + static bool_ added_hooks = FALSE; + if(!added_hooks) + { + add_hook(HOOK_FORBID_TRAVEL, mimic_forbid_travel, "mimic_forbid_travel"); + added_hooks = TRUE; + } + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + + /* not if confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + /* get power */ + if (!get_magic_power(&n, mimic_powers, MAX_MIMIC_POWERS, mimic_info, + plev, A_DEX)) return; + + spell = mimic_powers[n]; + + /* Verify "dangerous" spells */ + if (spell.mana_cost > p_ptr->csp) + { + /* Warning */ + msg_print("You do not have enough mana to use this power."); + + /* Verify */ + if (!get_check("Attempt it anyway? ")) return; + } + + /* Spell failure chance */ + fail = spell.fail; + + /* Reduce failure rate by "effective" level adjustment */ + fail -= 3 * (plev - spell.min_lev); + + /* Reduce failure rate by INT/WIS adjustment */ + fail -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1); + + /* Not enough mana to cast */ + if (spell.mana_cost > p_ptr->csp) + { + fail += 5 * (spell.mana_cost - p_ptr->csp); + } + + /* Extract the minimum failure rate */ + minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]]; + + /* Minimum failure rate */ + if (fail < minfail) fail = minfail; + + /* Stunning makes spells harder */ + if (p_ptr->stun > 50) fail += 25; + else if (p_ptr->stun) fail += 15; + + /* Always a 5 percent chance of working */ + if (fail > 95) fail = 95; + + /* Failed spell */ + if (rand_int(100) < fail) + { + if (flush_failure) flush(); + + msg_format("You failed to concentrate hard enough!"); + + sound(SOUND_FAIL); + + if (randint(100) < (fail / 2)) + { + /* Backfire */ + b = randint(100); + + if (b < 5) + { + msg_print("Oh, no! Your mind has gone blank!"); + lose_all_info(); + } + else if (b < 15) + { + msg_print("Weird visions seem to dance before your eyes..."); + set_image(p_ptr->image + 5 + randint(10)); + } + else if (b < 45) + { + msg_print("Your brain is addled!"); + set_confused(p_ptr->confused + randint(8)); + } + else + { + set_stun(p_ptr->stun + randint(8)); + } + } + } + + /* Successful spells */ + else + { + sound(SOUND_ZAP); + + /* spell code */ + switch (n) + { + /* Mimic */ + case 0: + { + do_cmd_mimic_lore(); + + break; + } + + /* Invisibility */ + case 1: + { + int ii = 10 + plev + randint(20) + p_ptr->to_s; + + set_invis(p_ptr->tim_invisible + ii, 50); + set_tim_invis(p_ptr->tim_invisible + ii); + + break; + } + + /* Legs Mimicry */ + case 2: + { + /* Extract the value and the flags */ + u32b value = p_ptr->mimic_extra >> 16; + u32b att = p_ptr->mimic_extra & 0xFFFF; + + /* Clear useless things */ + att &= ~(CLASS_ARMS); + att &= ~(CLASS_WALL); + + if (att & CLASS_LEGS) + { + value += 50 + randint(50 + (2 * plev)); + } + else + { + msg_print("You mimic a new pair of legs."); + + value = 50 + randint(50 + (2 * plev)); + att |= (CLASS_LEGS); + } + + if (value > 10000) value = 10000; + + p_ptr->mimic_extra = att + (value << 16); + p_ptr->update |= (PU_BODY); + + break; + } + + /* Wall Mimicry */ + case 3: + { + /* Extract the value and the flags */ + u32b value = p_ptr->mimic_extra >> 16; + u32b att = p_ptr->mimic_extra & 0xFFFF; + + /* Clear useless things */ + att &= ~(CLASS_ARMS); + att &= ~(CLASS_LEGS); + + if (att & CLASS_WALL) + { + value += 50 + randint(50 + (2 * plev)); + } + else + { + msg_print("You grow an affinity for walls."); + + value = 50 + randint(50 + (2 * plev)); + att |= (CLASS_WALL); + } + + if (value > 10000) value = 10000; + + p_ptr->mimic_extra = att + (value << 16); + p_ptr->update |= (PU_BODY); + + break; + } + + case 4: /* Arms Mimicry */ + { + /* Extract the value and the flags */ + u32b value = p_ptr->mimic_extra >> 16; + u32b att = p_ptr->mimic_extra & 0xFFFF; + + /* Clear useless things */ + att &= ~(CLASS_LEGS); + att &= ~(CLASS_WALL); + + if (att & CLASS_ARMS) + { + value += 50 + randint(50 + (2 * plev)); + } + else + { + msg_print("You mimic a new pair of arms."); + + value = 50 + randint(50 + (2 * plev)); + att |= (CLASS_ARMS); + } + + if (value > 10000) value = 10000; + + p_ptr->mimic_extra = att + (value << 16); + p_ptr->update |= (PU_BODY); + + break; + } + + default: + { + msg_print("Zap?"); + + break; + } + } + } + + + /* Take a turn */ + energy_use = 100; + + /* Sufficient mana */ + if (spell.mana_cost <= p_ptr->csp) + { + /* Use some mana */ + p_ptr->csp -= spell.mana_cost; + } + + /* Over-exert the player */ + else + { + int oops = spell.mana_cost - p_ptr->csp; + + /* No mana left */ + p_ptr->csp = 0; + p_ptr->csp_frac = 0; + + /* Message */ + msg_print("You faint from the effort!"); + + /* Hack -- Bypass free action */ + (void)set_paralyzed(randint(5 * oops + 1)); + + /* Damage WIS (possibly permanently) */ + if (rand_int(100) < 50) + { + bool_ perm = (rand_int(100) < 25); + + /* Message */ + msg_print("You have damaged your mind!"); + + /* Reduce constitution */ + (void)dec_stat(A_DEX, 15 + randint(10), perm); + } + } + + /* Redraw mana */ + p_ptr->redraw |= (PR_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); +} + + +/* + * do_cmd_cast calls this function if the player's class + * is 'beastmaster'. + */ +void do_cmd_beastmaster(void) +{ + int plev = p_ptr->lev, i, num; + + monster_type *m_ptr; + + + /* Process the monsters (backwards) */ + num = 0; + for (i = m_max - 1; i >= 1; i--) + { + /* Access the monster */ + m_ptr = &m_list[i]; + + if (m_ptr->status == MSTATUS_PET) + { + num++; + } + } + + if (num < plev * 2) + { + /* XXX XXX */ + if (rand_int(80-(plev) - p_ptr->stat_use[5]-p_ptr->to_s) < 20) + { + summon_specific_friendly(p_ptr->py, p_ptr->px, plev, rand_int(plev / 2), FALSE); + } + } + else msg_print("You can't summon more pets"); + + /* Take a turn */ + if (is_magestaff()) energy_use = 80; + else energy_use = 100; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); +} + + +/* + * Set of variables and functions to create an artifact + */ + + +/* LOG2 is a constant (compile-time) method of converting a single + * set bit into a number. Works well, but for variable (runtime) + * expressions, use a loop instead.. much smaller code*/ +#define LOG2(x) ( (x) & 0xFFFF? BLOG16(x) : BLOG16((x)>>16) + 16 ) +#define BLOG16(x) ( (x) & 0xFF ? BLOG8(x) : BLOG8 ((x)>>8 ) + 8 ) +#define BLOG8(x) ( (x) & 0xF ? BLOG4(x) : BLOG4 ((x)>>4 ) + 4 ) +#define BLOG4(x) ( (x) & 0x3 ? BLOG2(x) : BLOG2 ((x)>>2 ) + 2 ) +#define BLOG2(x) ( (x) & 0x1 ? 0 : 1 ) + +int flags_select[32*5]; +int activation_select; + +/* Return true if the player is wielding the philosopher's stone + */ +bool_ alchemist_has_stone(void) +{ + if (p_ptr->inventory[INVEN_LITE].name1 == 209) + return TRUE; + else + return FALSE; +} + +/* + Display a group of flags from a_select flags, and return + the number of flags displayed (even invisible ones) + */ +int show_flags(byte group, int pval) +{ + int i, x, color = TERM_WHITE; + int items = 0; + + char ttt[80]; + + Term_clear(); + + group++; /* Adjust - no zero group */ + + for ( i = 0 ; a_select_flags[i].group ; i++) + { + if (a_select_flags[i].group != group) + continue; + + if (a_select_flags[i].xp == 0) + break; + else + { + sprintf(ttt, "%c) %s", + (items < 26) ? I2A(items) : ('0' + items - 26), + al_name + a_select_flags[i].desc); + if ( wizard || alchemist_has_stone()) + sprintf(ttt, "%c) %s (exp " FMTu32b ")", + (items < 26) ? I2A(items) : ('0' + items - 26), + al_name + a_select_flags[i].desc, + a_select_flags[i].xp); + + /* Note: Somebody is VERY clever, and it wasn't me. Text printed as + * TERM_DARK is actually printed as TERM_BLUE *SPACES* to prevent the + * player from using a 'cut-and-paste' enabled terminal to see + * what he shouldn't. Thus, simply setting the color to TERM_DARK + * will entirely prevent the unspoiled player from knowing that it's + * even possible. */ + + switch (flags_select[i]) + { + case 1: + color = TERM_YELLOW; + break; /* Flag was set by the player (just now)*/ + case 0: + color = TERM_WHITE; + break; /* This flag can be set, player is 'aware' of it*/ + case - 1: + color = TERM_L_GREEN; + break; /* Flag is already set*/ + case - 2: + color = TERM_DARK; + break; /* Invisible option */ + case - 3: + color = TERM_RED; + break; /* Flag is set, but player isn't 'aware' of it */ + case - 4: + color = TERM_L_DARK; + break; /* Flag is not set, player is 'aware', but it's beyond thier skill */ + default: + color = TERM_DARK; + break; /* Just in Case*/ + } + } + /* For alchemists who have the stone, at least show all the flags... */ + if ((alchemist_has_stone() || wizard) && color == TERM_DARK) + color = TERM_BLUE; + + if (items < 16) x = 5; + else x = 45; + c_prt(color, ttt, ((items < 16) ? items : items - 16) + 5, x); + items++; + + } + return items; +} + +void show_levels(void) +{ + Term_clear(); + c_prt(TERM_WHITE, "[a] Stats, sustains, luck, speed, vision, etc. ", 3, 10); + c_prt(TERM_WHITE, "[b] Misc. (Auras, light, see invis, etc) ", 4, 10); + c_prt(TERM_WHITE, "[c] Weapon Branding ", 5, 10); + c_prt(TERM_WHITE, "[d] Resistances and Immunities ", 6, 10); + c_prt(TERM_WHITE, "[e] ESP and Curses ", 7, 10); + c_prt(TERM_WHITE, "[f] Activation ", 8, 10); + c_prt(TERM_DARK , "[g] Abilities Gained ", 9, 10); + c_prt(TERM_WHITE, "[h] Display Required Essences and items ", 10, 10); + c_prt(TERM_WHITE, "[i] Done! Finalize and commit changes. ", 11, 10); + /*No need to return anything - if the valid selections change, it'll be a code level change.*/ +} + +s32b get_flags_exp(int pval, int oldpval) +{ + int i; + s32b exp = 0; + + for (i = 0 ; a_select_flags[i].group ; i++ ) + { + if (a_select_flags[i].xp == 0) + break; + else + { + if ( a_select_flags[i].group <= 5 && flags_select[i] ) + { + s32b xp = a_select_flags[i].xp; + int factor = 1, oldfactor = 0; + + /* don't even look at flags which the user can't set + * because they also can't change the pval when a pval- + * dependant flag is set, flags which they can't set + * cannot effect the exp in any way, whether their set or not + */ + if ( flags_select[i] < -1 ) + continue; + if ( flags_select[i] == -1 ) + oldfactor = 1; + + if (a_select_flags[i].pval) + { + /* (1/4)x^2 + x + * I wanted something smaller than x^2 or x^x + * this is because although a ring of speed +10 is + * more than 10 times better than a ring of speed +1, + * I don't think it's 100 times better. More like 30. + * this function yields: + * 1=1 * 2=3 * 3=5 * 4=8 * 5=11 * 6=15 * 7=21 + * 8=24 * 9=29 * 10=35 * 11=41 * 12=48 * 13=55 + * 14=63 * 15=71 * 20=120 * 25=181 * 30=255 + * which I think is acceptable. + * briefly, to get a +30 speed ring, it would be: + * 255*50000 or over 12 million experience + * points. For reference, a level 50 human requires + * 5 million xp. I'm sure it's doable, but it'd be + * *HARD* + * a speed+10 artifact would require 1.75 million. + * much more doable, but not too easily. + */ + factor = (pval * pval / 4 + pval); + if ( flags_select[i] == -1 ) + { + oldfactor = oldpval * oldpval / 4 + oldpval; + } + } + exp += xp * factor - xp * oldfactor; + } + if ( a_select_flags[i].group == 88 && a_select_flags[i].flag == -activation_select ) + { + exp += a_select_flags[i].xp; + } + } + } + if ( alchemist_has_stone() ) exp = exp / 4; + return exp; +} + +/* returns the 'real quantity' of items needed to empower + * a particular flag to a particular pval. + * Note that this routine returns zero for any flag that + * doesn't require some sort of action. + */ +int calc_rqty(int i, int pval, int oldpval) +{ + /* return 0 if flag is greater than size of flags_select && ! activation */ + if ( a_select_flags[i].group > 5 ) + { + if ( activation_select == a_select_flags[i].flag) + return 1; + else + return 0; + } + + /* return 0 if the flag wasn't set */ + if ( flags_select[i] < -1 || flags_select[i] == 0 ) + return 0; + + /* Return change in pval if the flag was already set */ + if ( flags_select[i] == -1 && a_select_flags[i].pval) + return pval - oldpval; + + /* Return pval if the flag will be set this time */ + else if ( a_select_flags[i].pval ) + return pval; + + /* Return 0 if the flag is unknown */ + else if ( flags_select[i] == -1 ) + return 0; + return 1; +} + +/* Handle the various items that creating artifacts requires. + * Mode = 0 to print a description, + * 1 to use up the items + * -1 to check to see if the items exist + * Note that this function is called ONLY from the + * other artifact item helper function. + */ + + +int check_artifact_items(int pval, int oldpval, int mode) +{ + int i, j, k, row = 1 , col = 15, rqty, orqty, trqty; + bool_ good = TRUE; + int temporary = -1; + char ch; + + /* For temporary items, waive the item requirements, + * except for the corpse... */ + for ( j = 0 ; a_select_flags[j].group ; j++) + if (a_select_flags[j].flag == 4*32 && flags_select[j] == 1 ) + temporary = j; + /* Check for enough items */ + for (i = 0; a_select_flags[i].group ; i++) + { + /* For temporary items, ignore + everything except the one item + */ + if (temporary != -1 && i != temporary) + continue; + + /* Calc quantity is done per flag, because + some have a pval, some don't, some where already + set at pval=2, etc + */ + rqty = orqty = calc_rqty(i, pval, oldpval); + + /* If no item is associated with this flag, + or this flag wasn't set or didn't change */ + if ( !a_select_flags[i].rtval || !rqty) + continue; + + for ( k = 0 ; k < INVEN_WIELD ; k++ ) + { + object_type *o_ptr = &p_ptr->inventory[k]; + + /* Note here that an rsval of -1 (which is read is 0xff + for a byte..) matches anything. */ + if (o_ptr->tval == a_select_flags[i].rtval + && (o_ptr->sval == a_select_flags[i].rsval + || a_select_flags[i].rsval == (byte) - 1 ) ) + { + /* Corpse validation is COMPLICATED! + * But at least we don't have to do this twice. + */ + if ( a_select_flags[i].rtval == TV_CORPSE ) + { + bool_ itemgood = TRUE; + + /*Specified race not this one */ + if ( o_ptr->pval2 != a_select_flags[i].rpval && a_select_flags[i].rpval) + continue; + + /* Race flag (any monster who...)*/ + for ( j = 0 ; !a_select_flags[i].rpval && a_select_flags[i].rflag[j] && j < 6 && itemgood ; j++) + { + int flag = a_select_flags[i].rflag[j] / 32; + u32b mask = 1 << (a_select_flags[i].rflag[j] % 32); + + switch (flag) + { + case 0: + if ( !(r_info[o_ptr->pval2].flags1 & mask) ) itemgood = FALSE; + break; + case 1: + if ( !(r_info[o_ptr->pval2].flags2 & mask) ) itemgood = FALSE; + break; + case 2: + if ( !(r_info[o_ptr->pval2].flags3 & mask) ) itemgood = FALSE; + break; + case 3: + if ( !(r_info[o_ptr->pval2].flags4 & mask) ) itemgood = FALSE; + break; + case 4: + if ( !(r_info[o_ptr->pval2].flags5 & mask) ) itemgood = FALSE; + break; + case 5: + if ( !(r_info[o_ptr->pval2].flags6 & mask) ) itemgood = FALSE; + break; + case 6: + if ( !(r_info[o_ptr->pval2].flags7 & mask) ) itemgood = FALSE; + break; + case 7: + if ( !(r_info[o_ptr->pval2].flags8 & mask) ) itemgood = FALSE; + break; + case 8: + if ( !(r_info[o_ptr->pval2].flags9 & mask) ) itemgood = FALSE; + break; + default: + msg_print("This code should never be hit!"); + } + } + if ( ! itemgood ) + continue; + + } + /* Validate pval of good item */ + else if ( a_select_flags[i].rpval) + { + /* Must have matching signs */ + if ( (o_ptr->pval < 0) != (a_select_flags[i].rpval < 0)) + continue; + /* Must be greater than */ + if ( abs(o_ptr->pval) < abs(a_select_flags[i].rpval)) + continue; + } + + trqty = MIN(o_ptr->number, rqty); + rqty -= trqty; + + if ( mode == 1 ) + { + inc_stack_size_ex(k, -trqty, NO_OPTIMIZE, DESCRIBE); + } + }/* if p_ptr->inventory item is acceptable */ + + } /*end of looping through the p_ptr->inventory*/ + + if (rqty) + { + good = FALSE; + /* Oops, we didn't have enough of this object + when actually creating the artifact. + unset this flag + */ + if ( mode == 1 ) + { + flags_select[i] = -4; + } + /* we only return false for mode -1, + * for mode 0 we display stuff, and for + * mode 1 we want to continue destroying things + * even if the player is missing one small item, + * because there's no way to change things now. + * We may have already destroyed a unique corpse, + * or some other hard-to-find item. + */ + if ( mode == -1 ) + return FALSE; + } + + /* Display a description of the required object, if needed */ + /* Note that the tests for good items HAVE to be in a different + place, because otherwise we don't know how many the player + has, as opposed to how many they need. + */ + if ( mode == 0 ) + { + char *o_name = al_name + a_select_flags[i].item_desc; + if (orqty > 1 && a_select_flags[i].pval && a_select_flags[i].item_descp) + o_name = al_name + a_select_flags[i].item_descp; + + if ( rqty ) + { + if ( orqty > 1 ) + c_prt(TERM_RED, format(" you are missing %d of the %d %s", rqty, orqty, o_name), row++, col); + else if ( is_a_vowel(o_name[0])) + c_prt(TERM_RED, format(" you are missing an %s", o_name), row++, col); + else + c_prt(TERM_RED, format(" you are missing a %s", o_name), row++, col); + } + else + { + if ( orqty > 1 ) + c_prt(TERM_GREEN, format(" you have the %d %s", orqty, o_name), row++, col); + else if ( is_a_vowel(o_name[0])) + c_prt(TERM_GREEN, format(" you have an %s", o_name), row++, col); + else + c_prt(TERM_GREEN, format(" you have a %s", o_name), row++, col); + } + + if ( row > 21 ) + { + row = 1; + if (!good) + (void)get_com("You are missing some items:", &ch); + else + (void)get_com("You have these needed items on hand:", &ch); + } + + } + + } /* End of group associated with this a_select_flags entry */ + + if ( mode == 0 ) + { + while ( row < 22 ) + c_prt(TERM_GREEN, " ", row++, col); + if (!good) + (void)get_com("You are missing some items:", &ch); + else + (void)get_com("You have these needed items on hand:", &ch); + } + return good; +} + +/* Display a list of required essences, + * and/or use up the essences. */ +bool_ artifact_display_or_use(int pval, int oldpval, bool_ use) +{ + int essence[MAX_BATERIE_SVAL]; + int essenceh[MAX_BATERIE_SVAL]; + int al_idx, i, j, k; + bool_ enough; + + /* Temporary Items require only one item, and no essences. */ + for ( i = 0 ; a_select_flags[i].group ; i++) + if ( a_select_flags[i].flag == 32*4) + { + if ( use ) + return check_artifact_items(pval, oldpval, 1); + else + return check_artifact_items(pval, oldpval, 0); + } + + for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++ ) + essence[i] = essenceh[i] = 0; + + /* Accumulate a list of required essences */ + for ( al_idx = 0; al_idx < max_al_idx ; al_idx++ ) + if ( alchemist_recipes[al_idx].tval == 0 ) + for ( i = 0 ; a_select_flags[i].group ; i++) + { + int rqty = calc_rqty(i, pval, oldpval); + + /* If the flag isn't being set, rqty will be zero */ + if ( !rqty) + continue; + + if ( alchemist_recipes[al_idx].sval == a_select_flags[i].flag ) + essence[alchemist_recipes[al_idx].sval_essence] += + alchemist_recipes[al_idx].qty * rqty; + } + + /* The essence array now contains a list of all essences + * that will be consumed in the creation of this artifact */ + + /* Check for existence of required quatities of essences. */ + for ( i = 0 ; i < INVEN_WIELD ; i++ ) + { + for ( j = 0 ; j < MAX_BATERIE_SVAL ; j++) + if ( p_ptr->inventory[i].tval == TV_BATERIE && p_ptr->inventory[i].sval == j + 1) + { + essenceh[j] += p_ptr->inventory[i].number; + } + } + + /* Check for enough essences */ + enough = TRUE; + for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++) + if ( essenceh[i] < essence[i] ) + { + enough = FALSE; + break; + } + + /* Check for items */ + if ( enough ) + enough = check_artifact_items(pval, oldpval, -1); + + + /* Display recipe list if they don't have enough, or not enough exp */ + if (!enough || !use ) + { + int row = 1 , col = 15; + bool_ good = FALSE; + char ch; + + /* display of list of required essences */ + /* Note: there are only 12 or so essences, so this list + * will ALWAYS fit on the screen */ + for ( i = 0 ; i < MAX_BATERIE_SVAL ; i++) + if ( essence[i] ) + { + int missing = -MIN(essenceh[i] - essence[i], 0); + good = TRUE; + if ( missing ) + c_prt(TERM_RED, format("%d of the required %d essences of %s", + missing, essence[i], + k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ), + row++, col); + else + c_prt(TERM_GREEN, format("you have the needed %d essences of %s", + essence[i], + k_name + k_info[lookup_kind(TV_BATERIE, i + 1)].name ), + row++, col); + } + + if (good) + { + /* blank the bottom row */ + c_prt(TERM_WHITE, " ", row++, col); + + /* and wait for a key */ + (void)get_com("You are currently missing:", &ch); + } + + /* Display a list of needed items as well */ + check_artifact_items(pval, oldpval, 0); + + return FALSE; + } + + /* If we get to this point in the code, then the player + * has the required essences and items in their p_ptr->inventory */ + + /* If they do have enough, and they have enough exp, consume them */ + for (i = 0 ; i < MAX_BATERIE_SVAL ; i++) + for ( k = 0 ; k < INVEN_WIELD && essence[i] > 0 ; k++) + if (p_ptr->inventory[k].tval == TV_BATERIE + && p_ptr->inventory[k].sval == i + 1 + && essence[i]) + { + int num = p_ptr->inventory[k].number; + + inc_stack_size_ex(k, MAX( -essence[i], -num), NO_OPTIMIZE, DESCRIBE); + + essence[i] -= MIN(num, essence[i]); + } + + /* Destroy the items needed */ + check_artifact_items(pval, oldpval, 1); + + return TRUE; +} + + +void display_activation_info(int num) +{ + object_type forge; + int i; + + + /* find the a_select_flags number of this activation type... */ + for ( i = 0 ; a_select_flags[i].group ; i++) + if (a_select_flags[i].group == 88 && a_select_flags[i].flag == -num ) + break; + + object_wipe(&forge); + forge.xtra2 = num; + /* Print out various information about this activation... */ + /* min level, experience, required items (and essences) + full description (from activation_aux) */ + if (wizard) + c_prt(TERM_WHITE, format(" number:%d ", num), 5, 5); + else + c_prt(TERM_WHITE, " ", 5, 5); + c_prt(TERM_WHITE, format(" Level:%d ", a_select_flags[i].level), 6, 5); + c_prt(TERM_WHITE, format(" Exp :%d ", a_select_flags[i].xp), 7, 5); + c_prt(TERM_WHITE, format(" Item :%s ", al_name + a_select_flags[i].item_desc), 8, 5); + c_prt(TERM_WHITE, " ", 9, 5); + c_prt(TERM_WHITE, format(" %s ", activation_aux(&forge, 0, 0)), 9, 5); + c_prt(TERM_WHITE, " ", 10, 5); + inkey(); +} + +void select_an_activation(void) +{ + int i, lev, wid, hgt, begin = 0, sel = 0; + u32b max; + cptr act_list[150]; /* currently, ~127 hardcoded activations */ + int act_ref[150]; + char c; + /* How do we want to do this? */ + /* Ideally, we let them select from a list, which includes all the activations that they've ecountered in any form. + Problems with this idea include mainly the lack of any (current) place to store which activations they've seen, and + that they'll not get credit for any seen before we start tracking it. + + So - list is everything. If they select one which they're to low-level for + or if the explicitly request it, we'll display info about this item. + We'll also get our descriptions from the activation_aux(ACT_CONSTANT) + function, because they are more complete, and include even lua-scripted ones. + msg_print("Since the code to actually let you select one isn't here"); + msg_print("You will automatically get the activation 'Dawn'"); + activation_select = ACT_DAWN; + */ + + /* Build a list of available activations at the player's level */ + lev = get_skill(SKILL_ALCHEMY); + for ( i = max = 0 ; max < (sizeof(act_list) / sizeof(cptr)) && a_select_flags[i].group ; i++) + if (a_select_flags[i].group == 88 && a_select_flags[i].level <= lev ) + { + act_ref[max] = -a_select_flags[i].flag; /* Activation number */ + act_list[max++] = al_name + a_select_flags[i].desc; /* Description */ + } + + /* Select from that list, using the util.c function display_list to display the scrolled list */ + /* Note: I think that there is only one other place that uses this function. Should be more! */ + while (1) + { + Term_clear(); + Term_get_size(&wid, &hgt); + + c_prt(TERM_WHITE, "Enter to select, ? for more information, 2 and 8 to scroll ", 0, 0); + display_list(1, 0, hgt - 2, wid - 2, "Select an Activation", act_list, max, begin, sel, TERM_L_GREEN); + + c = inkey(); + + if (c == ESCAPE) break; + else if (c == '8') + { + sel--; + if (sel < 0) + { + sel = max - 1; + begin = max - hgt; + if (begin < 0) begin = 0; + } + if (sel < begin) begin = sel; + } + else if (c == '2') + { + sel++; + if (sel >= (s32b)max) + { + sel = 0; + begin = 0; + } + if (sel >= begin + hgt - 1) begin++; + } + else if (c == '?') + { + display_activation_info(act_ref[sel]); + } + else if (c == '\r') + { + display_activation_info(act_ref[sel]); + activation_select = act_ref[sel]; + return; + } + } + activation_select = 0; +} + + +/* Consume 'num' magic essences and return true. + * If there aren't enough essences, return false */ + +bool_ magic_essence(int num) +{ + int i; + int j = 0; + + for (i = 0; i < INVEN_WIELD; i++) + { + object_type *o_ptr = &p_ptr->inventory[i]; + + /* Count the magic essences */ + if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC)) j += o_ptr->number; + } + + /* Abort if not enough essences. */ + if (j < num) return FALSE; + + /* Consume them */ + i = 0; + j = num; + while (i < INVEN_WIELD) + { + object_type *o_ptr = &p_ptr->inventory[i]; + + if (o_ptr->k_idx && (o_ptr->tval == TV_BATERIE) && (o_ptr->sval == SV_BATERIE_MAGIC)) + { + /* This can lead to invalid object pointer for objects + * that come after the magic essences. Therefore, every + * artifactable object should come before the essences. + */ + j -= o_ptr->number; + inc_stack_size(i, -num); + num = j; + if (num <= 0) break; + /* Stay on this slot; do not increment i. */ + } + else + { + /* Move on to the next slot. */ + i++; + } + } + + /* Sanity check. */ + if (num > 0) + { + msg_format("ERROR: Couldn't destroy %d essences!", num); + return FALSE; + } + + return TRUE; +} + + +void do_cmd_create_artifact(object_type *q_ptr) +{ + int max, i = 0, j, cur_set = 0, abord = FALSE, done = FALSE; + int skill; + s32b exp = 0; + + char out_val[160]; + char choice = 0; + bool_ lockpval = FALSE; + int pval; + int oldpval; + energy_use = 100; + + pval = q_ptr->pval; + oldpval = pval; + skill = get_skill(SKILL_ALCHEMY); + + if ( !pval ) + pval = 1; + /* No activation added on this round */ + activation_select = 0; + + /* Save the current flags */ + for (i = 0 ; a_select_flags[i].group ; i++) + { + if ( a_select_flags[i].flag < 0 || a_select_flags[i].group > 5) + continue; + + flags_select[i] = 0; + + switch (a_select_flags[i].flag / 32) + { + case 0: + if (q_ptr->art_flags1 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; + break; + case 1: + if (q_ptr->art_flags2 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; + break; + case 2: + if (q_ptr->art_flags3 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; + break; + case 3: + if (q_ptr->art_flags4 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; + break; + case 4: + if (q_ptr->art_flags5 & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; + break; + case 5: + if (q_ptr->art_esp & 1 << (a_select_flags[i].flag % 32)) flags_select[i] = -1; + break; + default: + /*This will not be hit, inspite of activations, because of the <= 5 above...*/ + break; + } + /* + this would learn about ALL flags.... + if(wizard) + alchemist_known_artifacts[a_select_flags[i].flag/32] = 0xffffffffL; + */ + + /* Set various flags if they haven't *ID*'d an artifact with this flag set.*/ + if ( !(alchemist_known_artifacts[a_select_flags[i].flag / 32] & (1 << (a_select_flags[i].flag % 32)) )) + { + /* If this item has an ability that depends on pval which the player + * cannot set, don't allow them to change the pval either. */ + if ( a_select_flags[i].pval && flags_select[i]) + lockpval = TRUE; + + /* Set the color and set-ablitity of this flag */ + if ( flags_select[i] ) + flags_select[i] = -3; + else + flags_select[i] = -2; + continue; + } + else if ( skill < a_select_flags[i].level ) + { + /* If the alchemist has not passed the skill level for this flag, + Set this flag as unsettable. + */ + if ( flags_select[i]) + lockpval = TRUE; + else + flags_select[i] = -4; + } + } + + /* Save the screen */ + character_icky = TRUE; + Term_save(); + Term_clear(); + + + /* Everlasting love ... ... nevermind :) */ + while ( !done && !abord) + { + c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0); + + /* Display the menu, but don't display it if we just + * displayed a message (it erases the screen, creating a blink message */ + if ( cur_set < 6 || cur_set == 7 ) + show_levels(); + + c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0); + + prt("Enter to accept, Escape to abort", 1, 0); + + abord = !get_com("Play around with which group of powers?[a-g]", &choice); + + if ( choice == ESCAPE) + abord = TRUE; + + if ( abord ) + continue; /*or break, same diff */ + + if ( isalpha(choice)) + { + if (isupper(choice)) + choice = tolower(choice); + cur_set = A2I(choice); + } + else + { + bell(); + continue; + } + + if ( cur_set == 5 ) + { + if (q_ptr->xtra2 && !activation_select + && !get_check("This item already activates! Choose a different activation?")) continue; + select_an_activation(); + exp = get_flags_exp(pval, oldpval); + continue; + } + if ( cur_set == 6 ) + { + msg_print("This option is not available"); + continue; + } + if ( cur_set == 7 ) + { + artifact_display_or_use(pval, oldpval, FALSE); + continue; + } + if ( cur_set == 8 ) + { + if (q_ptr->exp - exp < 0) + msg_print("Not enough experience for the flags you've selected."); + else + done = TRUE; + continue; + } + + if (cur_set < 0 || cur_set > 4 ) + { + bell(); + continue; + } + + + while (!done && !abord) + { + /* Chose the flags */ + exp = 0; + max = show_flags(cur_set, pval); + exp = get_flags_exp(pval, oldpval); + c_prt((q_ptr->exp - exp > 0) ? TERM_L_GREEN : TERM_L_RED, format("Experience left: %ld", q_ptr->exp - exp), 2, 0); + + /* Build a prompt (accept all flags) */ + if (max <= 26) + { + /* Build a prompt (accept all flags) */ + strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ", + I2A(0), I2A(max - 1)); + } + else + { + strnfmt(out_val, 78, "(Flags %c-%c, I,D to change power level) Add/Remove which flag? ", + I2A(0), '0' + max - 27); + } + c_prt(TERM_L_BLUE, format("Power(I/D to increase/decrease): %d", pval), 3, 0); + + /* Get a spell from the user */ + while (!(done = !get_com(out_val, &choice))) + { + if (choice == 'I') + { + if ( lockpval ) + { + msg_print("You cannot do that - you don't know how!"); + continue; + } + if (q_ptr->exp - exp < 0) + { + msg_print("Not enough experience. Decrease power or deselect flags."); + continue; + } + pval++; + break; + } + else if (choice == 'D') + { + if ( lockpval ) + { + msg_print("You cannot do that - you don't know how!"); + continue; + } + pval--; + if (pval < oldpval) pval = oldpval; + break; + } + else if (choice == '\r' || choice == ESCAPE || choice == ' ') + { + done = TRUE; + break; + } + else if (isalpha(choice)) + { + /* Lowercase */ + if (isupper(choice)) choice = tolower(choice); + + /* Extract request */ + i = (islower(choice) ? A2I(choice) : -1); + } + else + { + i = D2I(choice) + 26; + + /* Illegal */ + if (i < 26) i = -1; + } + + /* Totally Illegal */ + if ((i < 0) || (i >= max)) + { + bell(); + continue; + } + else + { + /*Find the i'th flag in group cur_set...*/ + for ( j = 0 ; a_select_flags[j].group ; j++) + if (a_select_flags[j].group == cur_set + 1) + if (!i--) break; + + if ( flags_select[j] == -4 ) + { + msg_format("You need at least %d skill in alchemy.", + a_select_flags[j].level); + continue; + } + if ( flags_select[j] != 0 && flags_select[j] != 1) + { + bell(); + continue; + } + if (flags_select[j]) flags_select[j] = 0; + else if (!flags_select[j]) + { + if (q_ptr->exp - exp < 0) + { + msg_print("Not enough experience. Decrease power or deselect flags."); + continue; + } + flags_select[j] = 1; + } + break; + } + } + }/*sub-screen select and redraw loop*/ + done = FALSE; + Term_clear(); + }/* main screen (flag select screen) select and redraw loop*/ + + /* Abort if not enough experience, or no flags added */ + if ( q_ptr->exp - exp < 0 || exp == 0 ) + abord = TRUE; + + /* Display the recipe, or use up the essences. + * Note that this has to be done before the screen + * is restored. This is because it's also called from + * within the loop to display the required items. */ + if ( !abord ) + if (!artifact_display_or_use(pval, oldpval, TRUE)) + abord = TRUE; + + /* Restore the screen */ + Term_load(); + character_icky = FALSE; + + /* Return if abort, or missing ingredients */ + if ( abord ) + return; + + /* Actually create the artifact */ + q_ptr->exp -= exp; + q_ptr->art_flags4 &= ~TR4_ART_EXP; + q_ptr->pval = pval; + + /* Just to be sure */ + q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC | + TR3_IGNORE_FIRE | TR3_IGNORE_COLD ); + + { + int now = 0, before = 0; + char dummy_name[80]; + char new_name[80]; + + /* Apply the flags */ + for (i = 0; a_select_flags[i].group ; i++) + { + if (flags_select[i] < 0) + before++; + else if ( flags_select[i] == 1) + { + now++; + switch (a_select_flags[i].flag / 32) + { + case 0: + q_ptr->art_flags1 |= 1 << (a_select_flags[i].flag % 32); + break; + case 1: + q_ptr->art_flags2 |= 1 << (a_select_flags[i].flag % 32); + break; + case 2: + q_ptr->art_flags3 |= 1 << (a_select_flags[i].flag % 32); + break; + case 3: + q_ptr->art_flags4 |= 1 << (a_select_flags[i].flag % 32); + break; + case 4: + q_ptr->art_flags5 |= 1 << (a_select_flags[i].flag % 32); + break; + case 5: + q_ptr->art_esp |= 1 << (a_select_flags[i].flag % 32); + break; + default: + msg_print("error: this code can't ever be hit!"); + } + } + } + + if ( activation_select ) + { + q_ptr->art_flags3 |= TR3_ACTIVATE; + q_ptr->xtra2 = activation_select; + } + + + /* Set the 'show modifier' flag */ + q_ptr->art_flags3 |= TR3_SHOW_MODS; + + /* For temporary items, set a timeout. + * alchemist_skill^2 for now */ + if ( q_ptr->art_flags5 & TR5_TEMPORARY ) + { + int lev = get_skill(SKILL_ALCHEMY); + q_ptr->timeout = lev * lev * 3; + } + + /* Describe the new artifact */ + object_out_desc(q_ptr, NULL, FALSE, TRUE); + + + /* Name the new artifact */ + strcpy(dummy_name, "of an Alchemist"); + if (!(get_string("What do you want to call the artifact? ", dummy_name, 80))) + strcpy(new_name, "of an Alchemist"); + else + { + if ((strncmp(dummy_name, "of ", 3) == 0) || + (strncmp(dummy_name, "Of ", 3) == 0) || + ((dummy_name[0] == '\'') && + (dummy_name[strlen(dummy_name) - 1] == '\''))) + { + strcpy(new_name, dummy_name); + } + else + { + strcpy(new_name, "called '"); + strcat(new_name, dummy_name); + strcat(new_name, "'"); + } + } + /* Identify it fully */ + object_aware(q_ptr); + object_known(q_ptr); + + /* Mark the item as fully known */ + q_ptr->ident |= (IDENT_MENTAL); + q_ptr->ident |= IDENT_STOREB; /* This will be used later on... */ + + /* Save the inscription */ + q_ptr->art_name = quark_add(new_name); + q_ptr->found = OBJ_FOUND_SELFMADE; + + done = FALSE; + while (!done && get_com("Do you want to let this item continue to gain experience?", &choice)) + { + switch (choice) + { + case 'y': + case 'Y': + if (magic_essence(get_skill(SKILL_ALCHEMY))) + q_ptr->art_flags4 |= TR4_ART_EXP; + else + msg_format("Oh, NO! You don't have enough magic essences. You needed %d.", get_skill(SKILL_ALCHEMY)); + done = TRUE; + break; + case 'n': + case 'N': + q_ptr->exp = 0; + done = TRUE; + break; + } + } + + /* Cycle through the p_ptr->inventory, and optimize everything. + * This wasn't done earlier, because if we had, then + * things in the p_ptr->inventory would shift around, and q_ptr + * wouldn't point to the right thing. BUT, at this point + * we don't need q_ptr anymore, so optimizing the p_ptr->inventory + * becomes sane. Sticky bug to figure out, let me tell you. + * Note also that this is cycling backwards - this is so + * that the same effect doesn't cause us to skip items. */ + for ( i = INVEN_WIELD - 1 ; i >= 0 ; i-- ) + inven_item_optimize(i); + } + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); +} + +/* + * Test to see if this tval/sval combo is in the alchemists' + * recipes as a createable item. Used to determine if we + * should extract from it. + */ +bool_ alchemist_exists(int tval, int sval, int ego, int artifact) +{ + int al_idx; + + /* To prevent conflicts with recipes for ego-items. + * artifact not used, simplifies the loop below. */ + if ((tval == 1) || artifact) + return FALSE; + + /*Search for recipes with this tval/sval combo as the final result*/ + for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++) + { + int rtval = alchemist_recipes[al_idx].tval; + int rsval = alchemist_recipes[al_idx].sval; + + /* Accept ego wands and staves since ego is extracted last */ + if (((!ego || tval == TV_WAND || tval == TV_STAFF) && rtval == tval && rsval == sval) || + ( ego && rtval == 1 && rsval == ego)) + { + return TRUE; + } + } + return FALSE; +} + + +/* + * Hook to determine if an object can have things extracted from it. + */ +bool_ item_tester_hook_extractable(object_type *o_ptr) +{ + + /* No artifacts */ + if (artifact_p(o_ptr)) return (FALSE); + + /* No cursed things */ + if (cursed_p(o_ptr)) return (FALSE); + + /* If we REALLY wanted to rebalance alchemists, + * we'd test for 'fully identified this object kind' here. + */ + + return ((o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != 0) + || alchemist_exists(o_ptr->tval, o_ptr->sval, o_ptr->name2, o_ptr->name1)); +} + +/* + * Hook to determine if an object is empowerable (NOT rechargeable) + */ +bool_ item_tester_hook_empower(object_type *o_ptr) +{ + int sval = -1; + int lev = get_skill(SKILL_ALCHEMY); + /* after level 25, can empower ego items to create artifacts + * and double ego items. + * after level 50, can empower artifacts to create powerful artifacts + */ + + /* Never Empower a cursed item */ + if ( cursed_p(o_ptr)) + { + return FALSE; + } + + /* Allow finalizing a self created artifact */ + if (artifact_p(o_ptr) + && (o_ptr->art_flags4 & TR4_ART_EXP) + && !(o_ptr->art_flags4 & TR4_ULTIMATE)) + return TRUE; + + switch ( o_ptr->tval) + { + /* Empowerable objects: Traditional alchemist stuff */ + case TV_WAND: + sval = SV_WAND_NOTHING; + break; + case TV_RING: + sval = SV_RING_NOTHING; + break; + case TV_STAFF: + sval = SV_STAFF_NOTHING; + break; + case TV_BOTTLE: + sval = 1; + break; + case TV_AMULET: + sval = SV_AMULET_NOTHING; + break; + case TV_SCROLL: + sval = SV_SCROLL_NOTHING; + break; + case TV_ROD: + sval = SV_ROD_NOTHING; + break; + case TV_ROD_MAIN: + sval = -1; + break; + case TV_BOOK: + sval = -1; + break; + + /* Ego item stuff */ + /* Disallow ego dragon armour before you can create artifacts.*/ + case TV_DRAG_ARMOR: + if ( lev < 25) + return FALSE; + /* FALL THROUGH! no break here. */ + + /* weapons */ + + case TV_DAEMON_BOOK: + case TV_SWORD: + case TV_HAFTED: + case TV_POLEARM: + case TV_AXE: + case TV_MSTAFF: + + /* misc other items */ + case TV_BOW: + case TV_BOOMERANG: + case TV_INSTRUMENT: + case TV_DIGGING: + case TV_LITE: + + /* Ammo */ + case TV_SHOT: + case TV_ARROW: + case TV_BOLT: + + /* Armor of various sorts */ + case TV_BOOTS: + case TV_GLOVES: + case TV_HELM: + case TV_CROWN: + case TV_SHIELD: + case TV_CLOAK: + case TV_SOFT_ARMOR: + case TV_HARD_ARMOR: + + /* Disallow ANY creation of ego items below level 5*/ + if ( lev < 5) + return FALSE; + + /* empowering an ego item creates an artifact or a + * double ego item, disallow below level 25 */ + if ( lev < 25 && o_ptr->name2) + return FALSE; + + /* Disallow double-ego and artifact unless the character has + * the artifact creation ability. */ + if (!has_ability(AB_CREATE_ART) && + (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b))) + return FALSE; + + /* Otherwise... */ + return TRUE; + + default: + return FALSE; + } + + /* Return to the traditional alchemist objects. + * All ego items and artifacts returning TRUE are accepted as artifactable + * at level 25. If we want double ego non wieldable items (Fireproof Staff + * of Plenty) the artifactable test in do_cmd_alchemist() must be changed, + * e.g. checking if the item is wearable. + * For now, we disallow non-wearable ego-items and artifacts here. + */ + + if ((o_ptr->name2 || artifact_p(o_ptr)) && + o_ptr->tval != TV_RING && o_ptr->tval != TV_AMULET) + return FALSE; + + /* return true if it's a 'of nothing' item; + * does nothing for TV_ROD_MAIN and TV_BOOK + */ + return (sval == o_ptr->sval + + /* or if it's artifactable */ + || ((lev >= 50 || (lev >= 25 && !artifact_p(o_ptr))) && + (o_ptr->tval == TV_RING || o_ptr->tval == TV_AMULET)) + + /* or if it's egoable (note that normal egos start at level 5, wands and such start at 15) */ + || (!o_ptr->name2 && lev >= 15)); +} + +/* Extract a rod tip from a rod */ +void rod_tip_extract(object_type *o_ptr) +{ + object_type *q_ptr; + object_type forge; + + /* Get local object */ + q_ptr = &forge; + + /* Paranoia, return if it's a rod of nothing */ + if (o_ptr->pval == SV_ROD_NOTHING) + return; + + /* Extract the rod tip */ + object_prep(q_ptr, lookup_kind(TV_ROD, o_ptr->pval)); + + q_ptr->number = o_ptr->number; + + object_aware(q_ptr); + object_known(q_ptr); + (void)inven_carry(q_ptr, FALSE); + + /* Remove it from the rod */ + o_ptr->pval = SV_ROD_NOTHING; + + /* Window stuff */ + p_ptr->window |= (PW_INVEN); +} + + +/* Begin & finish an art */ +void do_cmd_toggle_artifact(object_type *o_ptr) +{ + char o_name[80]; + + if (!(o_ptr->art_flags4 & TR4_ART_EXP)) + { + bool_ okay = TRUE; + + if ( !alchemist_has_stone()) + { + msg_print("Creating an artifact will result into a permanent loss of 10 hp."); + if (!get_check("Are you sure you want to do that?")) return; + } + + if (!magic_essence(get_skill(SKILL_ALCHEMY))) + { + msg_format("You need %d magic essences.", get_skill(SKILL_ALCHEMY)); + return; + } + + /* Description */ + object_desc(o_name, o_ptr, FALSE, 0); + + if (o_ptr->number > 1) + { + msg_print("Not enough energy to enchant more than one object!"); + msg_format("%d of your %s %s destroyed!", (o_ptr->number) - 1, o_name, (o_ptr->number > 2 ? "were" : "was")); + o_ptr->number = 1; + } + okay = TRUE; + + if (!okay) return; + + /* he/she got warned */ + p_ptr->hp_mod -= 10; + + /* Ok toggle it */ + o_ptr->art_flags4 |= TR4_ART_EXP; + o_ptr->name2 = 0; + o_ptr->name2b = 0; + o_ptr->art_name = quark_add("Becoming"); + + /* Copy the object_kind flags to the artifact flags. + * Note that this is only needed so that flags set in the + * 'kind' area are visible when finalizing the artifact. + */ + { + u32b f1, f2, f3, f4, f5, esp; + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + o_ptr->art_flags1 |= f1; + o_ptr->art_flags2 |= f2; + o_ptr->art_flags3 |= f3; + o_ptr->art_flags4 |= f4; + o_ptr->art_flags5 |= f5; + o_ptr->art_esp |= esp; + } + + p_ptr->update |= (PU_HP); + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + } + else + { + do_cmd_create_artifact(o_ptr); + } +} + +/* + * Test to see if they have all the ingredients to create an item. + * (doesn't count base item) + * creates 'tocreate' items (may be -1, but no more than that!) + * if tocreate=0, will return true if the player has enough + * in their p_ptr->inventory to empower that item. + */ +bool_ alchemist_items_check(int tval, int sval, int ego, int tocreate, bool_ message) +{ + int al_idx, j; + bool_ exists = FALSE; + + + for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ ) + if ((ego && alchemist_recipes[al_idx].sval == ego + && alchemist_recipes[al_idx].tval == 1 ) + || (!ego && alchemist_recipes[al_idx].sval == sval + && alchemist_recipes[al_idx].tval == tval)) + { + exists = TRUE; + /* Create the essences */ + if (tocreate > 0) + { + object_type forge; + object_type *o_ptr = &forge; + + object_wipe(o_ptr); + object_prep(o_ptr, lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence)); + o_ptr->number = alchemist_recipes[al_idx].qty * tocreate; + /* Don't bother with apply_magic */ + + /* Randomly decrease the number of essences created */ + if ( randint(3) == 1 + && randint(52) > get_skill(SKILL_ALCHEMY) + && !alchemist_has_stone()) + o_ptr->number /= randint(2) + 1; + if ( o_ptr->number == 0) + continue; + object_aware(o_ptr); + object_known(o_ptr); + if (inven_carry_okay(o_ptr)) + { + int i; + inven_carry(o_ptr, FALSE); + for (i = 0; i < INVEN_WIELD ; i++) + if (p_ptr->inventory[i].tval == o_ptr->tval && p_ptr->inventory[i].sval == o_ptr->sval) + { + if ( message ) + inven_item_describe(i); + break; + } + + } + else + drop_near(o_ptr, 0, p_ptr->py, p_ptr->px); + + o_ptr->ident |= IDENT_STOREB; + } + else if ( tocreate < -1) + { + /*It's not valid to create more than one + * thing at a time, so if it's less than -1, + * it must be time to display a recipe + */ + msg_format("%d essences of %d", + alchemist_recipes[al_idx].qty, + al_idx); + } + else /* Destroy the essences (tocreate == -1) + * or check for existence(tocreate == 0)*/ + { + int rqty = alchemist_recipes[al_idx].qty; + for (j = 0; j < INVEN_WIELD; j++) + { + object_type *o_ptr = &p_ptr->inventory[j]; + if (o_ptr->k_idx + && (o_ptr->tval == TV_BATERIE ) + && (o_ptr->sval == alchemist_recipes[al_idx].sval_essence ) + && (o_ptr->number >= rqty )) + { + /* At this point, the item is required, destroy it. */ + if ( tocreate ) + { + inc_stack_size_ex(j, 0 - rqty, OPTIMIZE, message ? DESCRIBE : NO_DESCRIBE); + } + + /* When we find enough of the item, break out of the + * 'search through the p_ptr->inventory' loop */ + break; + } + } + if ( j == INVEN_WIELD) + /* This ingredient was not found, cannot do recipe */ + return FALSE; + }/*destroying items, or just checking for existence */ + } + return exists; +} + +/* This function lists all the ingredients + * needed to create something. + */ +void alchemist_display_recipe(int tval, int sval, int ego) +{ + int al_idx; + int row = 1, col = 15; + char o_name[80]; + char ch; + object_type *o_ptr, forge; + + /* Display the ingredients for a recipe */ + for ( al_idx = 0 ; al_idx < max_al_idx ; al_idx++ ) + if ((ego && alchemist_recipes[al_idx].sval == ego + && alchemist_recipes[al_idx].tval == 1 ) + || (!ego && alchemist_recipes[al_idx].sval == sval + && alchemist_recipes[al_idx].tval == tval)) + { + int qty = alchemist_recipes[al_idx].qty; + c_prt(TERM_GREEN, + format(" %d essence%s %s ", qty, + qty > 1 ? "s" : "", + k_name + k_info[lookup_kind(TV_BATERIE, alchemist_recipes[al_idx].sval_essence)].name ), + row++, col); + } + + c_prt(TERM_WHITE, " ", row++, col); + + if (!ego) + { + /* Find the name of that object */ + o_ptr = &forge; + object_prep(o_ptr, lookup_kind(tval, sval)); + o_ptr->name2 = ego; + hack_apply_magic_power = -99; + apply_magic(o_ptr, get_skill(SKILL_ALCHEMY) * 2, FALSE, FALSE, FALSE); + object_aware(o_ptr); + object_known(o_ptr); + /* the 0 mode means only the text, leaving off any numbers */ + object_desc(o_name, o_ptr, FALSE, 0); + } + else + { + /* Display the ego item name */ + strcpy(o_name, e_name + e_info[ego].name); + } + + /* Display a short message about it, and wait for a key. */ + (void)get_com(format("ingredients needed to create a %s", o_name), &ch); + +} + +/* + * + * The alchemist_recipe_select was copied from + * wiz_create_itemtype + * and then changed quite a bit. + * + */ + +/* + The select array is a simple array of 'use this char to select item x' + It has 88 items (three columns of 20 each) + selectitem is initilized with the reverse mappings: + selectitem[selectchar[x]] == x is always true. + */ +char selectchar[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*():;,.<=>[]{}/=?+'~"; +byte selectitem[256]; + +void strip_and_print(const char *str, int color, int num) +{ + int row = 2 + (num % 20), col = 40 * (num / 20); + int ch, max_len = 0; + char buf[80]; + char *string; + + if (num > 60) + { + msg_print("Attempting to display too many items!"); + return; + } + ch = selectchar[num]; + if (selectitem[ch] != num) + { + int i; + for ( i = 0 ; i < 256 ; i++) + selectitem[i] = 0xff; + for ( i = 0 ; selectchar[i] ; i++) + selectitem[(byte)selectchar[i]] = i; + } + + /* Skip past leading characters */ + while ((*str == ' ') || (*str == '&')) str++; + + /* Copy useful chars */ + for (string = buf; *str; str++) + if (*str != '~') *string++ = *str; + + /* Terminate the new name */ + *string = '\0'; + + /* strip the name down to size + if (76-col < (signed)max_len) + max_len = 76-col; + else + max_len = 30-6;*/ + max_len = 39; + + string = buf; + if (strlen(string) > (unsigned)max_len) + string = string + (strlen(string) - max_len); + + /* Print it */ + c_prt(color, format("[%c] %s", ch, string), row, col); +} + +/* Display a list of recipes that need a particular essence. + * Note that we display a list of essences first, + * so in effect, this is the alchemist's recipe book. + */ +void alchemist_recipe_book(void) +{ + int num, max_num, i, al_idx, bat, kidx; + int choice[61], choice2[61]; + int mod40; + bool_ essence[MAX_BATERIE_SVAL + 1]; + char ch; + + /* Save and clear the screen */ + character_icky = TRUE; + Term_save(); + + while ( TRUE ) + { + Term_clear(); + + num = 0; + + /* Display bateries */ + + /* start with assumption that the alchemist knows about no recipes */ + for (i = 0; i < MAX_BATERIE_SVAL + 1 ; i++) + essence[i] = FALSE; + + /* cycle through all alchemist recipes */ + for (al_idx = 0 ; al_idx < max_al_idx ; al_idx++) + /* if we aren't already going to display this essence */ + if (!essence[alchemist_recipes[al_idx].sval_essence]) + { + + /*Note that we don't display artifact recipes here...*/ + /*This is partially because artifacts often require exotic + ingredients as well */ + + if (!alchemist_recipes[al_idx].tval) + continue; + + if (alchemist_recipes[al_idx].tval == 1) + { + if (alchemist_known_egos[alchemist_recipes[al_idx].sval / 32] + & (1 << (alchemist_recipes[al_idx].sval % 32)) ) + essence[alchemist_recipes[al_idx].sval_essence] = TRUE; + continue; + } + + kidx = lookup_kind(alchemist_recipes[al_idx].tval, alchemist_recipes[al_idx].sval); + if (alchemist_recipes[al_idx].tval != 1 && k_info[kidx].know) + essence[alchemist_recipes[al_idx].sval_essence] = TRUE; + + } + for (num = 0, i = 0; i < MAX_BATERIE_SVAL + 7 ; i++) + if (essence[i] || i > MAX_BATERIE_SVAL) + { + int kidx = lookup_kind(TV_BATERIE, i); + if (i > MAX_BATERIE_SVAL) + { + switch (i) + { + case (MAX_BATERIE_SVAL + 1): strip_and_print("Scrolls", TERM_WHITE, num); + break; + case (MAX_BATERIE_SVAL + 2): strip_and_print("Potions", TERM_WHITE, num); + break; + case (MAX_BATERIE_SVAL + 3): strip_and_print("Wands", TERM_WHITE, num); + break; + case (MAX_BATERIE_SVAL + 4): strip_and_print("Rings", TERM_WHITE, num); + break; + case (MAX_BATERIE_SVAL + 5): strip_and_print("Staves", TERM_WHITE, num); + break; + case (MAX_BATERIE_SVAL + 6): strip_and_print("Amulets", TERM_WHITE, num); + break; + default: + continue; + } + } + else + /* add this essence to the list*/ + strip_and_print(k_name + k_info[kidx].name, TERM_WHITE, num); + + choice[num++] = i; + } + max_num = num; + if ( max_num == 0) + { + /*Note that this should never actually happen, as any skill + at alchemy automatically gets you some recipes, and this + procedure shouldn't be called for players without alchemist skill + */ + msg_print("You don't know any recipes!"); + msg_print("You can't be an alchemist without recipes!"); + break; + } + + while (num == 0xff || num >= max_num) + { + ch = selectchar[max_num - 1]; + /* Choose! */ + if ( max_num == 0 || + !get_com(format("Which Type of Recipe?[a-%c]", selectchar[max_num - 1]), &ch)) + break; + + /* Analyze choice - note that the cast to byte prevents overflow*/ + num = selectitem[(byte)ch]; + + } + /* This break, and the break for no recipes above, + are the only exits from this procedure. + */ + if ( num == 0xff || num >= max_num) + break; + + /* Save the baterie index */ + bat = choice[num]; + num = 0; + + /*Display the 'type of object' recipe screen*/ + if (bat > MAX_BATERIE_SVAL) + { + int tval; + switch (bat) + { + case MAX_BATERIE_SVAL + 1: + tval = TV_SCROLL; + break; + case MAX_BATERIE_SVAL + 2: + tval = TV_POTION; + break; + case MAX_BATERIE_SVAL + 3: + tval = TV_WAND; + break; + case MAX_BATERIE_SVAL + 4: + tval = TV_RING; + break; + case MAX_BATERIE_SVAL + 5: + tval = TV_STAFF; + break; + case MAX_BATERIE_SVAL + 6: + tval = TV_AMULET; + break; + } + Term_load(); + alchemist_recipe_select(&tval, 0, FALSE, TRUE); + Term_save(); + continue; + } + mod40 = 0; + while ( TRUE ) + { + int skipped; + + Term_clear(); + num = 0; + + if (mod40) + { + strip_and_print("--MORE--", TERM_WHITE, num); + choice[num] = -2; + choice2[num++] = 0; + } + + /* Display all items made with this essence */ + for ( al_idx = 0 , skipped = 0 ; al_idx < max_al_idx ; al_idx++) + if ( alchemist_recipes[al_idx].sval_essence == bat) + { + int sval = alchemist_recipes[al_idx].sval; + int tval = alchemist_recipes[al_idx].tval; + char names[200] = ""; + + if (alchemist_recipes[al_idx].tval == 1) + { + /* Ego items */ + ego_item_type *e_ptr = &e_info[sval]; + int j, k; + + if ( !(alchemist_known_egos[sval / 32] & (1 << (sval % 32)))) + continue; + + for ( j = 0 ; j < 6 && e_ptr->tval[j] ; j ++ ) + { + if ( j > 0 && e_ptr->tval[j] == e_ptr->tval[j - 1]) + continue; + for ( k = 0; tvals[k].tval; k++) + if (tvals[k].tval == e_ptr->tval[j]) + { + strcat(names, tvals[k].desc); + strcat(names, ", "); + break; + } + } + strcat(names, e_name + e_ptr->name); + } + else + { + /* Normal Items */ + int kidx = lookup_kind(tval, sval); + int k; + if ( !k_info[kidx].know ) + continue; + + for ( k = 0; tvals[k].tval; k++) + if (tvals[k].tval == tval) + { + strcat(names, tvals[k].desc); + break; + } + strcat(names, " of "); + strcat(names, k_name + k_info[kidx].name); + + } + + /*Skip the first mod40 pages of recipes*/ + if (skipped++ < mod40*38) + continue; + + /* add this object kind to the list*/ + strip_and_print(names, TERM_WHITE, num); + choice[num] = tval; + choice2[num++] = sval; + if (num > 38) + { + strip_and_print("--MORE--", TERM_WHITE, num); + choice[num] = -1; + choice2[num++] = 0; + break; + } + + }/*Loop through tidx/sidx*/ + + max_num = num; + while (num == 0xff || num >= max_num) + { + ch = selectchar[max_num - 1]; + /* Choose! */ + if ( max_num == 0 || !get_com( + format("Examine which recipe?[%c-%c]", selectchar[0], ch) + , &ch)) + { + break; + } + + /* Analyze choice */ + num = selectitem[(byte)ch]; + } + + if ( choice[num] < 0) + { + if (choice[num] < -1) + mod40--; + else + mod40++; + continue; + } + + if ( num == 0xff || num >= max_num) + break; + + /* Display the recipe */ + if (choice[num] == 1) + alchemist_display_recipe(0, 0, choice2[num]); + else + alchemist_display_recipe(choice[num], choice2[num], 0); + } + /* + break is at top of loop, after essence list + if( num < 0 || num >= max_num) + break; + */ + + }/*show recipes*/ + + /* Restore screen contents */ + Term_load(); + character_icky = FALSE; +} + +/* Display a list of known recipies that can be made with + * materials on hand (including the passed tval). Also + * calls the recipe_display function, if requested by the + * player or there aren't enough essences to make the + * requested object. + * + * Note: sval is ignored if !ego, tval is the only determinant + * of what recipies are available otherwise. + * + * This function needs to be able to scroll a list, because + * there are SO MANY potions. :) + */ +int alchemist_recipe_select(int *tval, int sval, int ego, bool_ recipe) +{ + int i, mod40 = 0, num, max_num = 0; + + cptr tval_desc2 = ""; + char ch; + bool_ done = FALSE; + + int choice[60]; + int validc[60]; + + const char *string; + + + /* Save and clear the screen */ + character_icky = TRUE; + Term_save(); + Term_clear(); + + /* Base object type chosen, fill in tval */ + for ( num = 0 ; num < 40 ; num ++) + if (tvals[num].tval == *tval) + { + tval_desc2 = tvals[num].desc; + } + + while (!done) + { + Term_clear(); + if (ego) + { + /* Find matching ego items */ + for (num = 0, i = 1; (num < 40) && (i < max_e_idx) ; i++) + { + int j; + ego_item_type *e_ptr = &e_info[i]; + + /* Skip if unknown ego type */ + if ( !(alchemist_known_egos[i / 32] & (1 << (i % 32)))) + continue; + + /* search in permitted tvals/svals for allowed egos */ + for ( j = 0 ; j < 6 ; j ++ ) + if ( e_ptr->tval[j] == *tval + && sval >= e_ptr->min_sval[j] + && sval <= e_ptr->max_sval[j]) + { + int color = TERM_GREEN; + + /*Reject if not opposite end of name + prefixes only on postfix egos, + postfixes only on prefix egos. + */ + if (ego != -1 && e_ptr->before == e_info[ego].before) + continue; + + /*Color it red of the alchemist doesn't have the essences to create it*/ + if (!alchemist_items_check(*tval, 0, i, 0, TRUE)) + color = TERM_RED; + + /* add this ego to the list*/ + strip_and_print(e_name + e_info[i].name, color, num); + validc[num] = color; + choice[num++] = i; + break; + } + } + } + else + { + char skipped = 0; + num = 0; + if (mod40 != 0) + { + strip_and_print("--MORE--", TERM_WHITE, num); + validc[num] = TERM_WHITE; + choice[num++] = -1; + } + + for (i = 1; (num < 39) && (i < max_k_idx); i++) + { + object_kind *k_ptr = &k_info[i]; + + /* Analyze matching items */ + if (k_ptr->tval == *tval || (k_ptr->tval == TV_POTION2 && *tval == TV_POTION)) + { + char color = TERM_GREEN; + /* Hack -- Skip instant artifacts */ + if (k_ptr->flags3 & (TR3_INSTA_ART)) continue; + + /*Don't display recipes that the alchemist doesn't know about*/ + if (!k_ptr->know && !wizard) continue; + + /*Skip recipes that are somehow known, but don't exist*/ + if (!alchemist_exists(k_ptr->tval, k_ptr->sval, 0, 0)) + continue; + + /* Skip the first 39 if they hit 'more' */ + if (skipped++ < mod40*39) + continue; + + /* Color 'unable to create' items different */ + if (!alchemist_items_check(k_ptr->tval, k_ptr->sval, 0, 0, TRUE)) + color = TERM_RED; + + /* Acquire the "name" of object "i" */ + /* and print it in it's place */ + strip_and_print(k_name + k_ptr->name, color, num); + + /* Remember the object index */ + validc[num] = color; + choice[num++] = i; + } + } + if (num == 39) + { + strip_and_print("--MORE--", TERM_WHITE, num); + validc[num] = TERM_WHITE; + choice[num++] = -1; + } + } + + /* We need to know the maximal possible remembered object_index */ + max_num = num; + string = "What Kind of %s? (* to see recipe) [%c-%c,*]"; + num = 0xff; + + /* Pretend they're all undoable if we where called to display recipes */ + if (recipe) + { + for ( num = 0 ; num < max_num ; num++) + if (validc[num] != TERM_WHITE) validc[num] = TERM_RED; + string = "show which %s recipe? [%c-%c]"; + } + + while (num == 0xff || num >= max_num) + { + ch = selectchar[max_num - 1]; + /* Choose! */ + if ( max_num == 0 || !get_com(format(string, tval_desc2, selectchar[0], ch), &ch)) + { + break; + } + + /* Extra breaks for recipe */ + if (recipe && (ch == '\r' || ch == ' ' || ch == ESCAPE )) + break; + + /* Analyze choice */ + num = selectitem[(byte)ch]; + + /* Pretend that we don't have enough essences for anything */ + if (ch == '*' ) + { + for ( num = 0 ; num < max_num ; num++) + if (validc[num] != TERM_WHITE) validc[num] = TERM_RED; + string = "Show which %s recipe? [%c-%c]"; + } + } + if ( num == 0xff || max_num == 0 || num >= max_num) + break; + + if ( validc[num] == TERM_WHITE ) + { + if (num == 0) + mod40--; + else + mod40++; + if ( mod40 < 0) + mod40 = 0; + continue; + } + + /* If we don't have enough essences, or user asked for recipes */ + if ( validc[num] != TERM_GREEN ) + { + /* Display the recipe */ + if (ego) + alchemist_display_recipe(*tval, sval, choice[num]); + else + alchemist_display_recipe(k_info[choice[num]].tval, k_info[choice[num]].sval, 0); + } + else + done = TRUE; + + }/*while(!done)*/ + + /* Restore screen contents */ + Term_load(); + character_icky = FALSE; + + /* User abort, or no choices */ + if (max_num == 0 || num == 0xff || num >= max_num) + { + if (max_num == 0) + msg_print("You don't know of anything you can make using that."); + return ( -1); + } + if ( validc[num] != TERM_GREEN ) + return ( -1); + + /* And return successful */ + if ( ego ) + return choice[num]; + + /* Set the tval, should be the same unless they selected a potion2 */ + if (*tval != k_info[choice[num]].tval && *tval != TV_POTION) + msg_print("Coding error: tval != TV_POTION"); + *tval = k_info[choice[num]].tval; + return ( k_info[choice[num]].sval ); +} + +/* Set the 'known' flags for all objects with a level <= lev + * This lets the budding alchemist create basic items. + */ +void alchemist_learn_all(int lev) +{ + int i; + + if ( !get_skill(SKILL_ALCHEMY) ) + return; + + /* msg_format("You learn about level %d items",lev); */ + + for ( i = 0 ; i < max_k_idx ; i++ ) + if ( k_info[i].level <= lev ) + if (alchemist_exists(k_info[i].tval, k_info[i].sval, 0, 0)) + k_info[i].know = TRUE; +} + +void alchemist_learn_ego(int ego) +{ + char *name; + int i; + + /* some Paranoia*/ + if ( !ego || ego >= max_e_idx ) + return; + + /* Get the ego items name */ + name = e_name + e_info[ego].name; + while (strchr(name, ' ')) + name = strchr(name, ' ') + 1; + + /* Don't learn about egos without recipes, and + * always learn about the passed ego item. */ + if (alchemist_exists(0, 0, ego, 0)) + { + alchemist_known_egos[ego / 32] |= (1 << (ego % 32)); + /* msg_format("You learn about '%s' ego items.",e_name+e_info[ego].name); */ + } + else + { + return; + } + + /* Don't mass learn about egos that have no name. */ + if ( name[0] == 0 ) + { + return; + } + + /* Look through all ego's for matching name */ + /* Note that the original ego is marked here too */ + for ( i = 0 ; i < max_e_idx ; i++ ) + if ( strstr(e_name + e_info[i].name, name) != NULL /*Last word of name exists in this ego's name*/ + && alchemist_exists(0, 0, i, 0) /*There exists a recipe for this*/ + && !(alchemist_known_egos[i / 32] & (1 << (i % 32)) ) ) /*Not already known*/ + /*&& (e_name+e_info[i].name)[0])non-blank name*/ + { + alchemist_known_egos[i / 32] |= (1 << (i % 32)); + /* msg_format("You learn about '%s' ego items.",e_name+e_info[i].name); */ + } + + return; +} + +/* Alchemist has learned about a new item. + * Learn about not only it, but ALL egos with the + * same name. + */ +int alchemist_learn_object(object_type *o_ptr) +{ + + /* Allow alchemist to create this item, + and.. learn about it even if the player + doesn't currently have the alchemy skill + */ + k_info[o_ptr->k_idx].know = TRUE; + + /* Not Paranoia, identify_fully calls this always */ + if ( !get_skill(SKILL_ALCHEMY) ) + return FALSE; + + if ( artifact_p(o_ptr) ) + { + char o_name[80]; + u32b f1, f2, f3, f4, f5, esp; + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Randarts and normal artifacts both*/ + alchemist_known_artifacts[0] |= f1; + alchemist_known_artifacts[1] |= f2; + alchemist_known_artifacts[2] |= f3; + alchemist_known_artifacts[3] |= f4; + alchemist_known_artifacts[4] |= f5; + alchemist_known_artifacts[5] |= esp; + + object_desc(o_name, o_ptr, 1, 0); + msg_format("You learn all about the abilities of %s!", o_name); + } + if (o_ptr->name2) + alchemist_learn_ego(o_ptr->name2); + + if (o_ptr->name2b) + alchemist_learn_ego(o_ptr->name2b); + + return (TRUE); +} + +/* Alchemist has gained a level - set the ego flags + * for all egos <= lev/4. + */ +void alchemist_gain_level(int lev) +{ + object_type forge; + object_type *o_ptr = &forge; + + if ( lev == 0) + { + /* Learn about potions of Detonation */ + k_info[417].know = TRUE; + } + if ( lev == 5) + { + int ego; + int egos[] = { + 7/*armor of resist fire*/ + , 18/*shield of resist fire*/ + , 74/*shocking weapon*/ + , 75/*fiery weapon*/ + , 76/*frozen weapon*/ + , 77/*Venomous weapon*/ + , 78/*Chaotic weapon*/ + , 115/*projectile of venom*/ + , 116/*projectile of Acid*/ + , 122/*projectile of flame*/ + , 123/*projectile of frost*/ + , 137/*Lite of fearlessness*/ + , 0 /*terminator*/ + }; + object_wipe(o_ptr); + /* learn about some basic ego items */ + /* Note that this is just to get you started. */ + for ( ego = 0 ; egos[ego] ; ego++) + { + o_ptr->name2 = egos[ego]; + alchemist_learn_object(o_ptr); + } + msg_print("You recall your old master teaching you about elemental item infusing."); + } + if ( lev == 10) + { + /*For 'hard rooms' Players only, learn about diggers.*/ + if (ironman_rooms) + { + msg_print("There's gotta be an easier way to get into all these vaults!"); + object_wipe(o_ptr); + o_ptr->name2 = 101; /* Ego item, 'of digging' */ + alchemist_learn_object(o_ptr); + } + } + if ( lev == 25) + { + msg_print("You recall your old master reminiscing about legendary infusings"); + msg_print("and the Philosophers' stone."); + + /* No auto-learn on artifacts - by this level, you'll have *ID*'d several */ + } + if ( lev == 25) + { + msg_print("You wonder about shocking daggers of slay evil."); + } + if ( lev == 50) + { + /* learn about Temporary item creation */ + /* Note that this is the ONLY way to learn this, + because spells which create a temporary item + also fully ID it. */ + alchemist_known_artifacts[4] |= TR5_TEMPORARY; + msg_print("It suddenly occurs to you that artifacts don't *HAVE* to be permanent..."); + } + + /* Every Four Levels, learn about items that are + * less than that. + * Note that this isn't a significant effect after the + * first few levels, as the level at which you are learning + * things here quickly drops behind the level at which you + * are finding items. + */ + if ( (lev & 0x3) != 0 ) + return; + lev = (lev >> 2) + 1; + alchemist_learn_all(lev); + +} + +/* This, in combination with some code in loadsave.c, + insures that alchemist_gain_level is called EXACTLY + once with each possible value during the characters + lifetime. + */ +void alchemist_check_level() +{ + u32b lev = get_skill(SKILL_ALCHEMY); + if ( alchemist_gained > lev ) + return; + /*Paranoia*/ + if ( !lev ) + return; + while ( alchemist_gained <= lev ) + alchemist_gain_level(alchemist_gained++); +} + +/* + * do_cmd_cast calls this function if the player's class + * is 'alchemist'. + */ +void do_cmd_alchemist(void) +{ + int item, ext = 0; + int value, basechance; + int askill; + bool_ repeat = 0; + char ch; + + object_type *o_ptr, *q_ptr; + object_type forge, forge2; + byte carry_o_ptr = FALSE; + + cptr q, s; + + /* With the new skill system, we can no longer depend on + * check_exp to handle the changes and learning involved in + * gaining levels. + * So we'll have to check for it here. + */ + alchemist_check_level(); + askill = get_skill(SKILL_ALCHEMY); + + + q_ptr = &forge; + + o_ptr = &p_ptr->inventory[INVEN_HANDS]; + if ((o_ptr->tval != TV_GLOVES) || (o_ptr->sval != SV_SET_OF_LEATHER_GLOVES)) + { + msg_print("You must wear gloves in order to do alchemy."); + return; + } + + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + while (TRUE) + { + if (!get_com("[P]ower, [R]echarge or [L]eech an item, [E]xtract essences, or recipe [B]ook?", &ch)) + { + ext = 0; + break; + } + if (ch == ' ' ) + { + ext = 0; + break; + } + if (ch == 'P' || ch == 'p') + { + ext = 1; + break; + } + if (ch == 'E' || ch == 'e') + { + ext = 2; + break; + } + if (ch == 'R' || ch == 'r') + { + ext = 3; + break; + } + if (ch == 'L' || ch == 'l') + { + ext = 2; + repeat = 1; + break; + } + if (ch == 'B' || ch == 'b') + { + ext = 4; + break; + } + } + + /**********Add a power*********/ + if (ext == 1) + { + int i, qty, tval, sval = 0, ego = 0; + char o_name[200]; + + /* Get an item */ + q = "Empower which item? "; + s = "You have no empowerable items."; + item_tester_hook = item_tester_hook_empower; + + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Create an artifact from an ego or double ego item, + * from a previous artifact, or finish an artifact + */ + if ((askill >= 25) && (artifact_p(o_ptr) || o_ptr->name2) && has_ability(AB_CREATE_ART)) + { + if (get_check("Create an artifact?")) + { + do_cmd_toggle_artifact(o_ptr); + return; + } + /* Don't change artifacts or double ego items further */ + else if (artifact_p(o_ptr) || (o_ptr->name2 && o_ptr->name2b)) + return; + } + /*Ok, now we have the item, so we can now pick recipes. + Note: No recipe is known unless we have 'extracted' from + that object type. I.E. the 'know' flag (also greater identify) + is set. + */ + + /* Here we're not setting what kind of ego item it IS, + * where' just deciding that it CAN be an ego item */ + if ( o_ptr->name2 ) /* creating a DUAL ego */ + ego = TRUE; + if ( o_ptr->tval < 40 && o_ptr->tval != TV_BOTTLE) + ego = TRUE; + if ( o_ptr->tval == TV_ROD_MAIN || o_ptr->tval == TV_DAEMON_BOOK || o_ptr->tval == TV_BOOK) + ego = TRUE; + + sval = o_ptr->sval; + if (!ego) + { + switch ( o_ptr->tval) + { + case TV_WAND: + sval = SV_WAND_NOTHING; + break; + case TV_RING: + sval = SV_RING_NOTHING; + break; + case TV_STAFF: + sval = SV_STAFF_NOTHING; + break; + case TV_BOTTLE: + sval = 1; + break; + case TV_AMULET: + sval = SV_AMULET_NOTHING; + break; + case TV_SCROLL: + sval = SV_SCROLL_NOTHING; + break; + case TV_ROD: + sval = SV_ROD_NOTHING; + break; + } + } + if ( o_ptr->sval != sval ) + ego = TRUE; + + tval = o_ptr->tval; + sval = o_ptr->sval; + + /*HACK - bottles don't have the same tval as potions*/ + /*Everything else will have the same tval after empowering*/ + if (tval == TV_BOTTLE) tval = TV_POTION; + if (ego) + if (o_ptr->name2) + ego = alchemist_recipe_select(&tval, sval, o_ptr->name2, FALSE); + else + ego = alchemist_recipe_select(&tval, sval, -1, FALSE); + else + sval = alchemist_recipe_select(&tval, 0, 0, FALSE); + + if ( sval < 0 || ego < 0) + return; + + /* Check to make sure we have enough essences */ + /* theoretically this is taken care of by recipe_select*/ + /* but we'll double check just for paranoia. */ + if (!alchemist_items_check(tval, sval, ego, 0, TRUE)) + { + msg_print("You do not have enough essences."); + return; + } + + /* Take a turn */ + energy_use = 100; + + /* Use up the essences */ + (void)alchemist_items_check(tval, sval, ego, -1, TRUE); + + /* Enchant stacks of ammunition at a time */ + if ( o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW || o_ptr->tval == TV_BOLT ) + { + qty = 1; + while (qty < o_ptr->number && alchemist_items_check(tval, sval, ego, -1, FALSE)) + qty++; + } + else + qty = 1; + + /* Copy the object */ + q_ptr = &forge; + object_copy(q_ptr, o_ptr); + + if ( o_ptr->tval == TV_WAND) + { + /* distribute charges on wands */ + q_ptr->pval = o_ptr->pval / o_ptr->number; + o_ptr->pval -= q_ptr->pval; + } + + o_ptr = q_ptr; + o_ptr->number = qty; + carry_o_ptr = TRUE; + + /* Destroy the initial object */ + inc_stack_size(item, -qty); + + + if ( ego ) + { + int pval, pval2; + s32b pval3; + + pval = o_ptr->pval; + pval2 = o_ptr->pval2; + pval3 = o_ptr->pval3; + + if (o_ptr->name2) + o_ptr->name2b = ego; + else + o_ptr->name2 = ego; + o_ptr->pval = randint(e_info[ego].max_pval - 1) + 1; + /* dilemma - how to prevent creation of cursed items, + * without allowing the creation of artifacts? + * We can't, unless we want to finalize the ego flags ourselves. + */ + apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE); + /* Remember what the old pval was, so that we can re-apply it. */ + if ( o_ptr->tval == TV_WAND + || o_ptr->tval == TV_RING + || o_ptr->tval == TV_AMULET + || o_ptr->tval == TV_STAFF) + { + o_ptr->pval = pval; + o_ptr->pval2 = pval2; + o_ptr->pval3 = pval3; + } + else if (o_ptr->tval == TV_ROD_MAIN) + { + o_ptr->pval = pval; + } + else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255)) + { + o_ptr->pval = pval; + } + else if (o_ptr->tval == TV_SHOT + || o_ptr->tval == TV_ARROW + || o_ptr->tval == TV_BOLT) + { + o_ptr->pval2 = pval2; + } + else if (o_ptr->tval == TV_INSTRUMENT) + { + o_ptr->pval2 = pval2; + } + + /* Calculate failure rate, lev=val/2500+5 */ + value = MIN(e_info[o_ptr->name2].cost, 50000); + if (o_ptr->name2b) value += MIN(e_info[o_ptr->name2b].cost, 50000); + basechance = (value / 1000 + 5 - get_skill_scale(SKILL_ALCHEMY, 100) ) * 10; + if ( basechance < 0) basechance = 0; + if ( basechance > 100) basechance = 100; + + value = object_value_real(o_ptr); + + } + else /* not an ego item */ + { + o_ptr = &forge; + object_wipe(o_ptr); + object_prep(o_ptr, lookup_kind(tval, sval)); + hack_apply_magic_power = -99; + apply_magic(o_ptr, askill * 2, FALSE, FALSE, FALSE); + if ( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF) + o_ptr->pval = 0; + value = object_value_real(o_ptr); + + basechance = k_info[o_ptr->k_idx].level - askill * 2; + basechance *= 10; + + /* Can't fail more that 100% of the time... */ + if (basechance > 100) + basechance = 100; + /* Always success in creation of potion of detonations */ + if (o_ptr->tval == TV_POTION && o_ptr->sval == SV_POTION_DETONATIONS) + { + basechance /= 10; + } + } + + /* Use up gold to create items */ + /* this has the effect of making the alchemist + chronically short of funds, unless he finds the + philosopher's stone. It also means the easiest + things to make are 'bad', like a potion of + detonations... + */ + /* Problem - to restrictive. We need something + which requires less money. But at the same time, + we don't want an 'easy cash' situation. Maybe something + like '10% * level difference', meaning at skill level 5, + level one items are free? But egos are frequently level + zero! Maybe egos are forced to level 25? with a cost ceiling? + I mean, Potions and scrolls are really the problem causing the + 'easy cash' situation, it's ego items. Ego items require + relatively few essences, and the rewards are HUGE. Most powerful + potions and scrolls require rare essences. Maybe force all egos + to require a magic essence? But then you'd get lots of magic + from distilling them. Maybe consumed in the creation? then when + you got a powerful item, you could make one ego item... + But if making things doesn't take gold, what about the cash + does the Philosopher's stone do? + Time*/ + + /* 0% failure if you have the stone */ + if ( alchemist_has_stone()) + basechance = 0; + + if (basechance > 0 && value) + { + char string[80]; + string[0] = '0'; + string[1] = 0; + + msg_format("The chance of success is only %d%%!", 100-basechance); + get_string("How much gold do you want to add?", string, 50); + i = atoi(string); + /* Note: don't trust the user to enter a positive number... */ + if ( i < 0) + i = 0; + if ( i > p_ptr->au) + i = p_ptr->au; + + if (i) + { + basechance = basechance - (i * 20) / value; + msg_format("The chance of success improved to %d%%.", 100-basechance); + } + + if (randint(100) < basechance ) + /*creation failed, even with the extra gold...*/ + carry_o_ptr = FALSE; + + /* Redraw gold */ + p_ptr->au -= i; + p_ptr->redraw |= (PR_GOLD); + } + + /* Set fully identified + * After all, the player just made it... + */ + object_aware(o_ptr); + object_known(o_ptr); + o_ptr->ident |= IDENT_MENTAL; + o_ptr->found = OBJ_FOUND_SELFMADE; + + object_desc(o_name, o_ptr, FALSE, 0); + + if ( carry_o_ptr) + { + msg_format("You have successfully created %s %s", + (o_ptr->number > 1 ? "some" : (is_a_vowel(o_name[0]) ? "an" : "a")), + o_name); + + if (inven_carry_okay(o_ptr)) + inven_carry(o_ptr, FALSE); + else + { + drop_near(o_ptr, 0, p_ptr->py, p_ptr->px); + msg_format("You drop the %s", o_name); + } + carry_o_ptr = FALSE; + } + else /* don't carry, or in other words... */ + { + int level = k_info[o_ptr->k_idx].level; + if (o_ptr->name1) /* created ego item */ + level += e_info[o_ptr->name2].level; + + msg_format("Your attempt backfires! Your %s explodes!", o_name); + take_hit(damroll(3, level - askill ) , "Alchemical Explosion"); + p_ptr->redraw |= (PR_HP); + } + + /* Combine / Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + /* Optimize the entire p_ptr->inventory - needed because we + don't know how many essences where used, and we may + have 'used up' a wielded item as well. + */ + for ( item = 0 ; item < INVEN_TOTAL ; item++ ) + inven_item_optimize(item); + + /**********Extract a power*********/ + } + else if (ext == 2) + { + int ego; + bool_ discharge_stick = FALSE; + + /* s_ptr holds the empty items */ + object_type *s_ptr = NULL; + bool_ carry_s_ptr = FALSE; + + item_tester_hook = item_tester_hook_extractable; + + /* Get an item */ + q = "Extract from which item? "; + s = "You have no item to extract power from."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* This is to prevent creating magic essences by extracting + * from a recharged wand of dragon breath or something. + */ + if (( o_ptr->tval == TV_WAND || o_ptr->tval == TV_STAFF ) + && o_ptr->art_flags4 & TR4_RECHARGED) + { + msg_print("You cannot extract essences after it's been magically recharged."); + return; + } + + /* Take a turn */ + energy_use = 100; + + /* Handle Rods before the loop, since they don't stack */ + if (o_ptr->tval == TV_ROD_MAIN && o_ptr->pval != SV_ROD_NOTHING) + { + rod_tip_extract(o_ptr); + return; + } + + do + { /* Repeat (for leech command) */ + + /* Create the items. + * we don't care if they drop to the ground, + * and if no action was taken, return + */ + ego = 0; + if ( o_ptr->name2) + ego = o_ptr->name2; + + /* For ego staves and wands (not of nothing), discharge before extracting the ego */ + discharge_stick = (o_ptr->pval > 0 && + ((o_ptr->tval == TV_STAFF && o_ptr->sval != SV_STAFF_NOTHING) || + (o_ptr->tval == TV_WAND && o_ptr->sval != SV_WAND_NOTHING))); + if (discharge_stick) + ego = 0; + + if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, ego, 1, TRUE)) + { + msg_print("You cannot extract anything from that item."); + return; + } + + if (o_ptr->name2b && !alchemist_items_check(o_ptr->tval, o_ptr->sval, o_ptr->name2b, 1, TRUE)) + { + /* do nothing - if the second ego can't be extracted + because there is no recipe for it, simply destroy it + */ + } + + /* Once in three times, learn how to make the item */ + /* Sorry for the complicated if! Basically, if it's an + * unknown regular item or an unknown ego item, there's + * a one in 3 chance that it'll be id'd */ + if (((!ego && !k_info[o_ptr->k_idx].know) + || (ego && !(alchemist_known_egos[ego / 32] & (1 << (ego % 32))))) + && randint(3) == 1) + { + msg_print("While destroying it, you gain insight into this item."); + /* If over level 10, the player has a chance of 'greater ID' + * on extracted items + */ + if (askill > 9) + object_out_desc(o_ptr, NULL, FALSE, TRUE); + alchemist_learn_object(o_ptr); + } + + /* Always learn what kind of thing it is */ + object_known(o_ptr); + object_aware(o_ptr); + + /* If it's a wand or staff with charges (but not of nothing), + * decrease number of charges, unstacking if needed. + * Otherwise, create the 'of nothing' item and destroy the old one. + */ + if (discharge_stick) + { + /* Unstack staves */ + if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1)) + { + /* Create one local copy of the staff */ + q_ptr = &forge2; + object_copy(q_ptr, o_ptr); + + /* Modify quantity */ + q_ptr->number = 1; + + /* Unstack the copied staff */ + o_ptr->number--; + + /* Use the local copy of the staff */ + o_ptr = q_ptr; + carry_o_ptr = TRUE; + } + /* remove one charge */ + o_ptr->pval--; + } + else + { + /* Create the empty, plain item */ + /* If the item was already created, increase the number */ + if (carry_s_ptr) + { + s_ptr->number++; + } + else + { + /* Otherwise we must create a local copy of the empty item */ + int tval, sval; + bool_ create_item = TRUE; + + tval = o_ptr->tval; + if ( !ego && (tval == TV_POTION || tval == TV_POTION2)) + tval = TV_BOTTLE; + + sval = o_ptr->sval; + + if (!ego) + { + switch ( tval) + { + case TV_WAND: + sval = SV_WAND_NOTHING; + break; + case TV_RING: + sval = SV_RING_NOTHING; + break; + case TV_STAFF: + sval = SV_STAFF_NOTHING; + break; + case TV_BOTTLE: + sval = 1; + break; + case TV_AMULET: + sval = SV_AMULET_NOTHING; + break; + case TV_SCROLL: + sval = SV_SCROLL_NOTHING; + break; + case TV_ROD: + sval = SV_ROD_NOTHING; + break; + default: + create_item = FALSE; + } + } + + if (create_item) + { + /* Create the empty item */ + s_ptr = &forge; + object_wipe(s_ptr); + object_prep(s_ptr, lookup_kind(tval, sval)); + s_ptr->number = 1; + + /* Force creation of non ego non cursed */ + hack_apply_magic_power = -99; + apply_magic(s_ptr, 0, FALSE, FALSE, FALSE); + + /* Hack -- remove possible curse */ + if (cursed_p(s_ptr)) + { + s_ptr->art_flags3 &= ~(TR3_CURSED | TR3_HEAVY_CURSE); + s_ptr->ident &= ~(IDENT_CURSED); + } + + /* Restore pvals (e.g. charges ==0) of the item */ + if (ego && ((tval == TV_WAND) || (tval == TV_STAFF) || + (tval == TV_RING) || (tval == TV_AMULET))) + { + s_ptr->pval = o_ptr->pval; + s_ptr->pval2 = o_ptr->pval2; + s_ptr->pval3 = o_ptr->pval3; + } + /* Restore the spell stored in a random book */ + else if ((o_ptr->tval == TV_BOOK) && (o_ptr->sval == 255)) + { + s_ptr->pval = o_ptr->pval; + } + /* Restore the type of explosive ammo */ + else if (o_ptr->tval == TV_SHOT || o_ptr->tval == TV_ARROW + || o_ptr->tval == TV_BOLT) + { + s_ptr->pval2 = o_ptr->pval2; + } + /* Restore the music stored in an instrument */ + else if (o_ptr->tval == TV_INSTRUMENT) + { + s_ptr->pval2 = o_ptr->pval2; + } + + object_aware(s_ptr); + object_known(s_ptr); + s_ptr->ident |= IDENT_STOREB; + + /* The empty item will be added to the p_ptr->inventory later */ + carry_s_ptr = TRUE; + } + } + + /* Now, we can delete the original (leeched) object. + * Is o_ptr an p_ptr->inventory / floor item or a local copy? + */ + if (!carry_o_ptr) + { + /* Break the leech-loop if it was the last item */ + if (o_ptr->number == 1) + repeat = 0; + + inc_stack_size(item, -1); + } + else + { + /* Forget the local object */ + carry_o_ptr = FALSE; + + /* reset o_ptr to the original stack, + * which contains at least another item */ + o_ptr = get_object(item); + } + } + } + while ( repeat == 1); + + /* If we carry empty items, add them to the p_ptr->inventory */ + if (carry_s_ptr) + inven_carry(s_ptr, TRUE); + + /* Combine / Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + /******* Recharge an item *******/ + } + else if (ext == 3) + { + int item; + + cptr q, s; + + item_tester_hook = item_tester_hook_recharge; + + /* Get an item */ + q = "Recharge which item? "; + s = "You have no rechargable items."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR ))) return; + + /* Get the item */ + o_ptr = get_object(item); + + /* Make sure we have enough essences to recharge this */ + if (!alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, 0, TRUE)) + { + msg_print("You don't have the essences to recharge this item."); + return; + } + + /* Take a turn */ + energy_use = 100; + + /* Destroy the essences */ + (void)alchemist_items_check(o_ptr->tval, o_ptr->sval, 0, -1, TRUE); + + if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1)) + { + /* Unstack staves */ + /* Get local object */ + q_ptr = &forge2; + + /* Obtain a local object */ + object_copy(q_ptr, o_ptr); + + /* Modify quantity */ + q_ptr->number = 1; + + /* Unstack the used item */ + o_ptr->number--; + + o_ptr = q_ptr; + carry_o_ptr = TRUE; + } + o_ptr->pval++; + } + else if ( ext == 4) + { + alchemist_recipe_book(); + } + /* Just in case - */ + if (carry_o_ptr) + { + /* the o_ptr item was probably an unstacked staff + * Anyway, we need to add it to the p_ptr->inventory */ + if (inven_carry_okay(o_ptr)) + inven_carry(o_ptr, TRUE); + else + drop_near(o_ptr, 0, p_ptr->py, p_ptr->px); + } +} + + +/* + * Command to ask favors from your god. + */ +void do_cmd_pray(void) +{ + if (p_ptr->pgod == GOD_NONE) + { + msg_print("Pray hard enough and your prayers might be answered."); + return; + } + else + { + if (!p_ptr->praying) + msg_format("You start praying to %s.", deity_info[p_ptr->pgod].name); + else + msg_format("You stop praying to %s.", deity_info[p_ptr->pgod].name); + p_ptr->praying = !p_ptr->praying; + + /* Update stuffs */ + p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | + PU_SANITY | PU_BODY); + + p_ptr->redraw |= PR_PIETY | PR_WIPE | PR_BASIC | PR_EXTRA | PR_MAP; + energy_use = 100; + } +} + + +/* + * Return percentage chance of spell failure. + */ +int spell_chance_random(random_spell* rspell) +{ + int chance, minfail; + + + /* Extract the base spell failure rate */ + chance = rspell->level + 10; + + /* Reduce failure rate by "effective" level adjustment */ + chance -= 3 * (get_skill(SKILL_THAUMATURGY) - rspell->level); + + /* Reduce failure rate by INT/WIS adjustment */ + chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1); + + /* Not enough mana to cast */ + if (rspell->mana > p_ptr->csp) + { + chance += 5 * (rspell->mana - p_ptr->csp); + } + + /* Extract the minimum failure rate */ + minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]]; + + /* Failure rate */ + return clamp_failure_chance(chance, minfail); +} + + + + +/* + * Print a batch of spells. + */ +static void print_spell_batch(int batch, int max) +{ + char buff[80]; + + random_spell* rspell; + + int i; + + + prt(format(" %-30s Lev Fail Mana Damage ", "Name"), 1, 20); + + for (i = 0; i < max; i++) + { + rspell = &random_spells[batch * 10 + i]; + + if (rspell->untried) + { + strnfmt(buff, 80, " %c) %-30s (Spell untried) ", + I2A(i), rspell->name); + + } + else + { + strnfmt(buff, 80, " %c) %-30s %3d %4d%% %3d %3dd%d ", + I2A(i), rspell->name, + rspell->level, spell_chance_random(rspell), rspell->mana, + rspell->dam_dice, rspell->dam_sides); + } + + prt(buff, 2 + i, 20); + } + + prt("", 2 + i, 20); +} + + + +/* + * List ten random spells and ask to pick one. + */ +static random_spell* select_spell_from_batch(int batch) +{ + char tmp[160]; + + char out_val[30]; + + char which; + + int mut_max = 10; + + random_spell* ret; + + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + if (spell_num < (batch + 1) * 10) + { + mut_max = spell_num - batch * 10; + } + + strnfmt(tmp, 160, "(a-%c, A-%cto browse, / to rename, - to comment) Select a power: ", + I2A(mut_max - 1), I2A(mut_max - 1) - 'a' + 'A'); + + prt(tmp, 0, 0); + + while (1) + { + /* Print power list */ + print_spell_batch(batch, mut_max); + + /* Get a command */ + which = inkey(); + + /* Abort */ + if (which == ESCAPE) + { + /* No selection */ + ret = NULL; + + /* Leave the command loop */ + break; + + } + + /* Accept default */ + if (which == '\r') + { + /* There are no other choices */ + if (mut_max == 1) + { + ret = &random_spells[batch * 10]; + + /* Leave the command loop */ + break; + } + + /* Wait for next command */ + continue; + } + + /* Rename */ + if (which == '/') + { + prt("Rename which power: ", 0, 0); + which = tolower(inkey()); + + if (isalpha(which) && (A2I(which) <= mut_max)) + { + strcpy(out_val, random_spells[batch*10 + A2I(which)].name); + if (get_string("Name this power: ", out_val, 29)) + { + strcpy(random_spells[batch*10 + A2I(which)].name, out_val); + } + prt(tmp, 0, 0); + } + else + { + bell(); + prt(tmp, 0, 0); + } + + /* Wait for next command */ + continue; + } + + /* Comment */ + if (which == '-') + { + prt("Comment which power: ", 0, 0); + which = tolower(inkey()); + + if (isalpha(which) && (A2I(which) <= mut_max)) + { + strcpy(out_val, random_spells[batch*10 + A2I(which)].desc); + if (get_string("Comment this power: ", out_val, 29)) + { + strcpy(random_spells[batch*10 + A2I(which)].desc, out_val); + } + prt(tmp, 0, 0); + } + else + { + bell(); + prt(tmp, 0, 0); + } + + /* Wait for next command */ + continue; + } + + if (isalpha(which) && isupper(which)) + { + which = tolower(which); + c_prt(TERM_L_BLUE, format("%s : %s", random_spells[batch*10 + A2I(which)].name, random_spells[batch*10 + A2I(which)].desc), 0, 0); + inkey(); + prt(tmp, 0, 0); + continue; + } + else if (isalpha(which) && (A2I(which) < mut_max)) + { + /* Pick the power */ + ret = &random_spells[batch * 10 + A2I(which)]; + + /* Leave the command loop */ + break; + } + else + { + bell(); + } + } + + /* Restore the screen */ + Term_load(); + + /* Leave "icky" mode */ + character_icky = FALSE; + + /* Return selection */ + return (ret); +} + + +/* + * Pick a random spell from a menu + */ +static random_spell* select_spell() +{ + char tmp[160]; + + char which; + + int batch_max = (spell_num - 1) / 10; + + random_spell *ret; + + + /* Too confused */ + if (p_ptr->confused) + { + msg_print("You can't use your powers while confused!"); + return NULL; + } + + /* No spells available */ + if (spell_num == 0) + { + msg_print("There are no spells you can cast."); + return NULL; + } + + /* Enter "icky" mode */ + character_icky = TRUE; + + /* Save the screen */ + Term_save(); + + strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max)); + + prt(tmp, 0, 0); + + while (1) + { + which = inkey(); + + if (which == ESCAPE) + { + Term_load(); + + ret = NULL; + + break; + } + + if (which == '\r') + { + if (batch_max == 0) + { + Term_load(); + + ret = select_spell_from_batch(0); + + break; + } + + continue; + } + + which = tolower(which); + if (isalpha(which) && (A2I(which) <= batch_max)) + { + Term_load(); + + ret = select_spell_from_batch(A2I(which)); + + break; + } + else + { + bell(); + } + } + + /* Leave "icky" mode */ + character_icky = FALSE; + + return (ret); +} + + +void do_cmd_powermage(void) +{ + random_spell *s_ptr; + + u32b proj_flags; + + int dir, chance; + + int ty = 0, tx = 0; + + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + + s_ptr = select_spell(); + + if (s_ptr == NULL) return; + + if (p_ptr->csp < s_ptr->mana) + { + msg_print("You do not have enough mana."); + return; + } + + /* Spell failure chance */ + chance = spell_chance_random(s_ptr); + + /* Failed spell */ + if (rand_int(100) < chance) + { + int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane; + char sfail[80]; + + /* Flush input if told so */ + if (flush_failure) flush(); + + /* Insane players can see something strange */ + if (rand_int(100) < insanity) + { + get_rnd_line("sfail.txt", sfail); + msg_format("A cloud of %s appears above you.", sfail); + } + + /* Normal failure messages */ + else + { + msg_print("You failed to get the spell off!"); + } + + sound(SOUND_FAIL); + + /* Let time pass */ + if (is_magestaff()) energy_use = 80; + else energy_use = 100; + + /* Mana is spent anyway */ + p_ptr->csp -= s_ptr->mana; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); + + return; + } + + + p_ptr->csp -= s_ptr->mana; + + s_ptr->untried = FALSE; + proj_flags = s_ptr->proj_flags; + + /* Hack -- Spell needs a target */ + if ((s_ptr->proj_flags & PROJECT_BEAM) || + (s_ptr->proj_flags & PROJECT_STOP)) + { + if (!get_aim_dir(&dir)) return; + + /* Hack -- Use an actual "target" */ + if ((dir == 5) && target_okay()) + { + tx = target_col; + ty = target_row; + + /* Mega-Hack -- Beam spells should continue through + * the target; bolt spells should stop at the + * target. --dsb */ + if (s_ptr->proj_flags & PROJECT_BEAM) + proj_flags |= PROJECT_THRU; + } + else + { + /* Use the given direction */ + ty = p_ptr->py + ddy[dir]; + tx = p_ptr->px + ddx[dir]; + + /* Mega-Hack -- Both beam and bolt spells should + * continue through this fake target. --dsb */ + proj_flags |= PROJECT_THRU; + } + } + + if (s_ptr->proj_flags & PROJECT_BLAST) + { + ty = p_ptr->py; + tx = p_ptr->px; + } + + if (s_ptr->proj_flags & PROJECT_VIEWABLE) + { + project_hack(s_ptr->GF, damroll(s_ptr->dam_dice, s_ptr->dam_sides)); + } + else if (s_ptr->proj_flags & PROJECT_METEOR_SHOWER) + { + project_meteor(s_ptr->radius, s_ptr->GF, + damroll(s_ptr->dam_dice, s_ptr->dam_sides), + s_ptr->proj_flags); + } + else + { + project(0, s_ptr->radius, ty, tx, + damroll(s_ptr->dam_dice, s_ptr->dam_sides), + s_ptr->GF, proj_flags); + } + + /* Take a turn */ + if (is_magestaff()) energy_use = 80; + else energy_use = 100; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); +} + + +/* + * Brand some ammunition. Used by Cubragol and a mage spell. The spell was + * moved here from cmd6.c where it used to be for Cubragol only. I've also + * expanded it to do either frost, fire or venom, at random. -GJW -KMW- + */ +void brand_ammo(int brand_type, int bolts_only) +{ + int a; + + for (a = 0; a < INVEN_PACK; a++) + { + object_type *o_ptr = &p_ptr->inventory[a]; + + if (bolts_only && (o_ptr->tval != TV_BOLT)) continue; + + if (!bolts_only && (o_ptr->tval != TV_BOLT) && + (o_ptr->tval != TV_ARROW) && (o_ptr->tval != TV_SHOT)) + continue; + + if (!artifact_p(o_ptr) && !ego_item_p(o_ptr) && + !cursed_p(o_ptr)) + break; + } + + /* Enchant the ammo (or fail) */ + if ((a < INVEN_PACK) && (rand_int(100) < 50)) + { + object_type *o_ptr = &p_ptr->inventory[a]; + const char *ammo_name; + const char *aura_name; + char msg[48]; + int aura_type, r; + + /* fire only */ + if (brand_type == 1) r = 0; + + /* cold only */ + else if (brand_type == 2) r = 99; + + /* No bias */ + else r = rand_int(100); + + if (r < 50) + { + aura_name = "fiery"; + aura_type = EGO_FLAME; + } + else + { + aura_name = "frosty"; + aura_type = EGO_FROST; + } + + if (o_ptr->tval == TV_BOLT) + { + ammo_name = "bolts"; + } + else if (o_ptr->tval == TV_ARROW) + { + ammo_name = "arrows"; + } + else + { + ammo_name = "shots"; + } + + strnfmt(msg, 48, "Your %s are covered in a %s aura!", + ammo_name, aura_name); + msg_print(msg); + + o_ptr->name2 = aura_type; + + /* Apply the ego */ + apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE); + o_ptr->discount = 100; + + enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM); + } + else + { + if (flush_failure) flush(); + msg_print("The enchantment failed."); + } +} + + +/* + * From Kamband by Ivan Tkatchev + */ +void summon_monster(int sumtype) +{ + /* Take a turn */ + energy_use = 100; + + if (p_ptr->inside_arena) + { + msg_print("This place seems devoid of life."); + msg_print(NULL); + return; + } + + if (summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level + randint(5), sumtype, TRUE)) + { + msg_print("You summon some help."); + } + else + { + msg_print("You called, but no help came."); + } +} + + + +/* + * Use a class power of Possessor + */ +void do_cmd_possessor() +{ + char ch, ext; + + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + + while (TRUE) + { + if (!get_com("Use your [R]ace powers or your [I]ncarnating powers?", &ch)) + { + ext = 0; + break; + } + if ((ch == 'R') || (ch == 'r')) + { + ext = 1; + break; + } + if ((ch == 'I') || (ch == 'i')) + { + ext = 2; + break; + } + } + + if (ext == 1) + { + bool_ use_great = FALSE; + + if (p_ptr->disembodied) + { + msg_print("You don't currently own a body to use."); + return; + } + + /* Do we have access to all the powers ? */ + if (get_skill_scale(SKILL_POSSESSION, 100) >= r_info[p_ptr->body_monster].level) + use_great = TRUE; + + use_symbiotic_power(p_ptr->body_monster, use_great, FALSE, FALSE); + + if (p_ptr->csp < 0) + { + msg_print("You lose control of your body!"); + if (!do_cmd_leave_body(FALSE)) + { + cmsg_print(TERM_VIOLET, + "You are forced back into your body by your cursed items, " + "you suffer a system shock!"); + + p_ptr->chp = 1; + + /* Display the hitpoints */ + p_ptr->redraw |= (PR_HP); + } + } + } + else if (ext == 2) + { + if (p_ptr->disembodied) + { + do_cmd_integrate_body(); + } + else + { + do_cmd_leave_body(TRUE); + } + } + else + { + return; + } + + /* Take a turn */ + energy_use = 100; +} + + +/* + * Hook to determine if an object is contertible in an arrow/bolt + */ +static bool_ item_tester_hook_convertible(object_type *o_ptr) +{ + if ((o_ptr->tval == TV_JUNK) || (o_ptr->tval == TV_SKELETON)) return TRUE; + + /* Assume not */ + return (FALSE); +} + + +/* + * do_cmd_cast calls this function if the player's class + * is 'archer'. + */ +void do_cmd_archer(void) +{ + int ext = 0; + char ch; + + object_type forge; + object_type *q_ptr; + + char com[80]; + + + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + if (p_ptr->blind) + { + msg_print("You are blind!"); + return; + } + + + if (get_skill(SKILL_ARCHERY) >= 20) + { + strnfmt(com, 80, "Create [S]hots, [A]rrows or [B]olts? "); + } + else if (get_skill(SKILL_ARCHERY) >= 10) + { + strnfmt(com, 80, "Create [S]hots or [A]rrows? "); + } + else + { + strnfmt(com, 80, "Create [S]hots? "); + } + + while (TRUE) + { + if (!get_com(com, &ch)) + { + ext = 0; + break; + } + if ((ch == 'S') || (ch == 's')) + { + ext = 1; + break; + } + if (((ch == 'A') || (ch == 'a')) && (get_skill(SKILL_ARCHERY) >= 10)) + { + ext = 2; + break; + } + if (((ch == 'B') || (ch == 'b')) && (get_skill(SKILL_ARCHERY) >= 20)) + { + ext = 3; + break; + } + } + + /* Prepare for object creation */ + q_ptr = &forge; + + /**********Create shots*********/ + if (ext == 1) + { + int x, y, dir; + cave_type *c_ptr; + + if (!get_rep_dir(&dir)) return; + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + c_ptr = &cave[y][x]; + if (c_ptr->feat == FEAT_RUBBLE) + { + /* Get local object */ + q_ptr = &forge; + + /* Hack -- Give the player some shots */ + object_prep(q_ptr, lookup_kind(TV_SHOT, m_bonus(2, dun_level))); + if (!artifact_p(q_ptr)) + q_ptr->number = (byte)rand_range(15, 30); + else + q_ptr->number = 1; + object_aware(q_ptr); + object_known(q_ptr); + q_ptr->ident |= IDENT_MENTAL; + apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE); + q_ptr->discount = 90; + q_ptr->found = OBJ_FOUND_SELFMADE; + + (void)inven_carry(q_ptr, FALSE); + + msg_print("You make some ammo."); + + (void)wall_to_mud(dir); + p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); + p_ptr->window |= (PW_OVERHEAD); + } + } + + /**********Create arrows*********/ + else if (ext == 2) + { + int item; + + cptr q, s; + + item_tester_hook = item_tester_hook_convertible; + + /* Get an item */ + q = "Convert which item? "; + s = "You have no item to convert."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get local object */ + q_ptr = &forge; + + /* Hack -- Give the player some arrows */ + object_prep(q_ptr, lookup_kind(TV_ARROW, m_bonus(1, dun_level) + 1)); + q_ptr->number = (byte)rand_range(15, 25); + if (!artifact_p(q_ptr)) + q_ptr->number = (byte)rand_range(15, 30); + else + q_ptr->number = 1; + object_aware(q_ptr); + object_known(q_ptr); + q_ptr->ident |= IDENT_MENTAL; + apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE); + q_ptr->discount = 90; + q_ptr->found = OBJ_FOUND_SELFMADE; + + msg_print("You make some ammo."); + + inc_stack_size(item, -1); + + (void)inven_carry(q_ptr, FALSE); + } + + /**********Create bolts*********/ + else if (ext == 3) + { + int item; + + cptr q, s; + + item_tester_hook = item_tester_hook_convertible; + + /* Get an item */ + q = "Convert which item? "; + s = "You have no item to convert."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get local object */ + q_ptr = &forge; + + /* Hack -- Give the player some bolts */ + object_prep(q_ptr, lookup_kind(TV_BOLT, m_bonus(1, dun_level) + 1)); + q_ptr->number = (byte)rand_range(15, 25); + if (!artifact_p(q_ptr)) + q_ptr->number = (byte)rand_range(15, 30); + else + q_ptr->number = 1; + object_aware(q_ptr); + object_known(q_ptr); + q_ptr->ident |= IDENT_MENTAL; + apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE); + q_ptr->discount = 90; + q_ptr->found = OBJ_FOUND_SELFMADE; + + msg_print("You make some ammo."); + + inc_stack_size(item, -1); + + (void)inven_carry(q_ptr, FALSE); + } +} + +/* + * Control whether shots are allowed to pierce + */ +void do_cmd_set_piercing(void) +{ + char ch; + char com[80]; + + if ((get_skill(SKILL_BOW) <= 25) && (get_skill(SKILL_XBOW) <= 25) && + (get_skill(SKILL_SLING) <= 25)) + { + msg_print("You can't fire piercing shots yet."); + return; + } + + strnfmt(com, 80, "Allow shots to pierce? "); + + while (TRUE) + { + if (!get_com(com, &ch)) + { + break; + } + if ((ch == 'Y') || (ch == 'y')) + { + p_ptr->use_piercing_shots = 1; + msg_print("Piercing shots activated."); + break; + } + if ((ch == 'N') || (ch == 'n')) + { + p_ptr->use_piercing_shots = 0; + msg_print("Piercing shots deactivated."); + break; + } + } +} +/* + * Helper function to describe necro powers + */ +void necro_info(char *p, int power) +{ + int plev = get_skill(SKILL_NECROMANCY); + + strcpy(p, ""); + + switch (power) + { + case 0: + { + if (p_ptr->to_s) + strnfmt(p, 80, " power %dd%d+%d", 2 + (plev * 2 / 3), 4, (p_ptr->to_s * 2)); + else + strnfmt(p, 80, " power %dd%d", 2 + (plev * 2 / 3), 4); + break; + } + case 2: + { + strnfmt(p, 80, " dur d%d+%d", 100 + (plev * 4), 200 + (plev * 3)); + break; + } + case 3: + { + strnfmt(p, 80, " dur d%d+%d", 30 + (plev * 2), 50 + plev); + break; + } + } +} + + +/* + * Cast a Necromancy spell + */ +void do_cmd_necromancer(void) +{ + int n = 0, b = 0; + int chance; + int dir; + int minfail = 0; + int plev = get_skill(SKILL_NECROMANCY); + magic_power spell; + int to_s2 = p_ptr->to_s / 2; + int mto_s2 = p_ptr->to_s / 2; + + + if (mto_s2 == 0) mto_s2 = 1; + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + /* not if confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + /* get power */ + if (!get_magic_power(&n, necro_powers, MAX_NECRO_POWERS, necro_info, + get_skill(SKILL_NECROMANCY), A_CON)) return; + + spell = necro_powers[n]; + + /* Verify "dangerous" spells */ + if (spell.mana_cost > p_ptr->csp) + { + /* Warning */ + msg_print("You do not have enough mana to use this power."); + + /* Verify */ + if (!get_check("Attempt it anyway? ")) return; + } + + /* Spell failure chance */ + chance = spell.fail; + + /* Reduce failure rate by "effective" level adjustment */ + chance -= 3 * (plev - spell.min_lev); + + /* Reduce failure rate by INT/WIS adjustment */ + chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_CON]] - 1); + + /* Not enough mana to cast */ + if (spell.mana_cost > p_ptr->csp) + { + chance += 5 * (spell.mana_cost - p_ptr->csp); + } + + /* Extract the minimum failure rate */ + minfail = adj_mag_fail[p_ptr->stat_ind[A_CON]]; + + /* Failure rate */ + chance = clamp_failure_chance(chance, minfail); + + /* Failed spell */ + if (rand_int(100) < chance) + { + if (flush_failure) flush(); + msg_format("You failed to concentrate hard enough!"); + sound(SOUND_FAIL); + + if (randint(100) < (chance / 2)) + { + /* Backfire */ + b = randint(100); + if (b < 10) + { + msg_print("Oh, no! You become undead!"); + + p_ptr->necro_extra |= CLASS_UNDEAD; + p_ptr->necro_extra2 = 2 * plev; + msg_format("You have to kill %d monster%s to be brought back to life.", + p_ptr->necro_extra2, + (p_ptr->necro_extra2 == 1) ? "" : "s"); + + /* MEGA-HACK !!! */ + calc_hitpoints(); + + /* Enforce maximum */ + p_ptr->chp = p_ptr->mhp; + p_ptr->chp_frac = 0; + + /* Display the hitpoints */ + p_ptr->redraw |= (PR_HP); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + } + else if (b < 40) + { + msg_print("Suddenly you feel that you're in a bad situation..."); + summon_specific(p_ptr->py, p_ptr->px, max_dlv[dungeon_type], + (plev >= 30) ? SUMMON_HI_UNDEAD : SUMMON_UNDEAD); + } + else + { + msg_print("Your body is damaged by the horrible forces of the spell!"); + take_hit(damroll(5, plev), "using necromancy unwisely"); + } + } + } + else + { + sound(SOUND_ZAP); + + /* spell code */ + switch (n) + { + /* Horrify */ + case 0: + { + int dam = damroll(2 + (plev * 2 / 3), 4) + (p_ptr->to_s * 2); + + if (plev > 45) + { + project_hack(GF_STUN, dam); + project_hack(GF_TURN_ALL, dam); + } + else if (plev > 35) + { + if (!get_aim_dir(&dir)) return; + fire_ball(GF_STUN, dir, dam, 3 + (plev / 10)); + fire_ball(GF_TURN_ALL, dir, dam, 3 + (plev / 10)); + } + else if (plev > 20) + { + if (!get_aim_dir(&dir)) return; + fire_beam(GF_STUN, dir, dam); + fire_beam(GF_TURN_ALL, dir, dam); + } + else + { + if (!get_aim_dir(&dir)) return; + fire_bolt(GF_STUN, dir, dam); + fire_bolt(GF_TURN_ALL, dir, dam); + } + + break; + } + + /* Raise Death */ + case 1: + { + fire_ball(GF_RAISE, 0, plev * 3, 1 + to_s2 + (plev / 10)); + + break; + } + + /* Conjures temporary weapon */ + case 2: + { + int dur = randint(100 + (plev * 4)) + 200 + (plev * 3); + object_type forge, *o_ptr = &forge; + int k_idx = test_item_name("& Necromantic Teeth~"); + + k_allow_special[k_idx] = TRUE; + + object_prep(o_ptr, k_idx); + apply_magic(o_ptr, plev * 2, TRUE, TRUE, TRUE); + + o_ptr->art_flags5 |= TR5_TEMPORARY; + o_ptr->timeout = dur; + + /* These objects are "storebought" */ + o_ptr->ident |= IDENT_MENTAL; + o_ptr->number = 1; + + object_aware(o_ptr); + object_known(o_ptr); + (void)inven_carry(o_ptr, FALSE); + + k_allow_special[k_idx] = FALSE; + + break; + } + + /* Absorb souls */ + case 3: + { + set_absorb_soul(randint(30 + (plev * 2)) + 50 + plev); + break; + } + + /* Vampirism */ + case 4: + { + int i; + if (!get_aim_dir(&dir)) return; + for (i = 0; i < 1 + to_s2 + (plev / 15); i++) + { + if (drain_life(dir, 100)) + hp_player(100); + } + + break; + } + + /* Death */ + case 5: + { + if (get_check("Using the Death word will leave you undead, with 1 DP. Do you *really* want to use it? ")) + { + if (!get_aim_dir(&dir)) return; + fire_bolt(GF_DEATH, dir, 1); + + p_ptr->necro_extra |= CLASS_UNDEAD; + p_ptr->necro_extra2 = plev + (rand_int(plev / 2) - (plev / 4)); + msg_format("You have to kill %d monster%s to be brought back to life.", p_ptr->necro_extra2, (p_ptr->necro_extra2 == 1) ? "" : "s"); + + /* MEGA-HACK !!! */ + calc_hitpoints(); + + /* Enforce 1 DP */ + p_ptr->chp = p_ptr->mhp; + p_ptr->chp_frac = 0; + + /* Display the hitpoints */ + p_ptr->redraw |= (PR_HP); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + } + + break; + } + + default: + { + msg_print("Zap?"); + + break; + } + } + } + + /* Take a turn */ + if (is_magestaff()) energy_use = 80; + else energy_use = 100; + + /* Sufficient mana */ + if (spell.mana_cost <= p_ptr->csp) + { + /* Use some mana */ + p_ptr->csp -= spell.mana_cost; + } + + /* Over-exert the player */ + else + { + int oops = spell.mana_cost - p_ptr->csp; + + /* No mana left */ + p_ptr->csp = 0; + p_ptr->csp_frac = 0; + + /* Message */ + msg_print("You faint from the effort!"); + + /* Hack -- Bypass free action */ + (void)set_paralyzed(randint(5 * oops + 1)); + + /* Damage CON (possibly permanently) */ + if (rand_int(100) < 50) + { + bool_ perm = (rand_int(100) < 25); + + /* Message */ + msg_print("You have damaged your body!"); + + /* Reduce constitution */ + (void)dec_stat(A_CON, 15 + randint(10), perm); + } + } + + /* Redraw mana */ + p_ptr->redraw |= (PR_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); +} + +/* Runecrafters -- Move this into variable.c XXX XXX XXX */ +static s32b rune_combine = 0; + +/* + * Hook to determine if an object is "runestone" + */ +static bool_ item_tester_hook_runestone(object_type *o_ptr) +{ + if (o_ptr->tval != TV_RUNE2) return (FALSE); + + if (o_ptr->sval != RUNE_STONE) return (FALSE); + + if (o_ptr->pval != 0) return (FALSE); + + /* Assume yes */ + return (TRUE); +} + + +static bool_ item_tester_hook_runestone_full(object_type *o_ptr) +{ + if (o_ptr->tval != TV_RUNE2) return (FALSE); + + if (o_ptr->sval != RUNE_STONE) return (FALSE); + + if (o_ptr->pval == 0) return (FALSE); + + /* Assume yes */ + return (TRUE); +} + + +/* + * Hook to determine if an object is "rune-able" + */ +static bool_ item_tester_hook_runeable1(object_type *o_ptr) +{ + if (o_ptr->tval != TV_RUNE1) return (FALSE); + + /* Assume yes */ + return (TRUE); +} + + +/* + * Hook to determine if an object is "rune-able" + */ +static bool_ item_tester_hook_runeable2(object_type *o_ptr) +{ + if (o_ptr->tval != TV_RUNE2) return (FALSE); + + if (o_ptr->sval == RUNE_STONE) return (FALSE); + + if (rune_combine & BIT(o_ptr->sval)) return (FALSE); + + /* Assume yes */ + return (TRUE); +} + + +/* + * math.h(sqrt) is banned of angband so ... :) + */ +s32b sroot(s32b n) +{ + s32b i = n / 2; + + if (n < 2) return (n); + + while (1) + { + s32b err = (i - n / (i + 1)) / 2; + + if (!err) break; + + i -= err; + } + + return ((n / i < i) ? (i - 1) : i); +} + + +/* + * Damage formula, for runes + */ +void rune_calc_power(s32b *power, s32b *powerdiv) +{ + /* Not too weak power(paranoia) */ + *power = (*power < 1) ? 1 : *power; + *power += 3; + + *power = 37 * sroot(*power) / 10; + + /* To reduce the high level power, while increasing the low levels */ + *powerdiv = *power / 3; + if (*powerdiv < 1) *powerdiv = 1; + + /* Use the spell multiplicator */ + *power *= (p_ptr->to_s / 2) ? (p_ptr->to_s / 2) : 1; +} + + +/* + * Return percentage chance of runespell failure. + */ +int spell_chance_rune(rune_spell* spell) +{ + int chance, minfail; + + s32b power = spell->mana, power_rune = 0, powerdiv = 0; + + + if (spell->rune2 & RUNE_POWER_SURGE) + { + power_rune += 4; + } + if (spell->rune2 & RUNE_ARMAGEDDON) + { + power_rune += 3; + } + if (spell->rune2 & RUNE_SPHERE) + { + power_rune += 2; + } + if (spell->rune2 & RUNE_RAY) + { + power_rune += 1; + } + + rune_calc_power(&power, &powerdiv); + + chance = (5 * power_rune) + (power); + + /* Reduce failure rate by INT/WIS adjustment */ + chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1); + + /* Extract the minimum failure rate */ + minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]]; + + /* Return the chance */ + return clamp_failure_chance(chance, minfail); +} + + +/* + * Combine the Runes + */ +int rune_exec(rune_spell *spell, int cost) +{ + int dir, power_rune = 0, mana_used, plev = get_skill(SKILL_RUNECRAFT); + + int chance; + + s32b power, powerdiv; + + int rad = 0, ty = -1, tx = -1, dam = 0, flg = 0; + + + if (spell->rune2 & RUNE_POWER_SURGE) + { + power_rune += 4; + } + if (spell->rune2 & RUNE_ARMAGEDDON) + { + power_rune += 3; + } + if (spell->rune2 & RUNE_SPHERE) + { + power_rune += 2; + } + if (spell->rune2 & RUNE_RAY) + { + power_rune += 1; + } + + + power = spell->mana; + + if (cost && ((power * cost / 100) > p_ptr->csp - (power_rune * (plev / 5)))) + { + power = p_ptr->csp - (power_rune * (plev / 5)); + mana_used = power + (power_rune * (plev / 5)); + } + else + { + mana_used = (power * cost / 100) + (power_rune * (plev / 5)); + } + + rune_calc_power(&power, &powerdiv); + + dam = damroll(powerdiv, power); + + if (wizard) msg_format("Rune %dd%d = dam %d", powerdiv, power, dam); + + /* Extract the base spell failure rate */ + chance = spell_chance_rune(spell); + + /* Failure ? */ + if (rand_int(100) < chance) + { + int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane; + char sfail[80]; + + /* Flush input if told so */ + if (flush_failure) flush(); + + /* Insane players can see something strange */ + if (rand_int(100) < insanity) + { + get_rnd_line("sfail.txt", sfail); + msg_format("A cloud of %s appears above you.", sfail); + } + + /* Normal failure messages */ + else + { + msg_print("You failed to get the spell off!"); + } + + sound(SOUND_FAIL); + + if (is_magestaff()) energy_use = 80; + else energy_use = 100; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); + return (mana_used); + } + + if (spell->rune2 & RUNE_POWER_SURGE) + { + flg |= (PROJECT_VIEWABLE); + ty = p_ptr->py; + tx = p_ptr->px; + } + + if (spell->rune2 & RUNE_ARMAGEDDON) + { + flg |= (PROJECT_THRU); + flg |= (PROJECT_KILL); + flg |= (PROJECT_ITEM); + flg |= (PROJECT_GRID); + flg |= (PROJECT_METEOR_SHOWER); + rad = (power / 8 == 0) ? 1 : power / 8; + rad = (rad > 10) ? 10 : rad; + ty = p_ptr->py; + tx = p_ptr->px; + } + + if (spell->rune2 & RUNE_SPHERE) + { + flg |= (PROJECT_THRU); + flg |= (PROJECT_KILL); + flg |= (PROJECT_ITEM); + flg |= (PROJECT_GRID); + rad = (power / 8 == 0) ? 1 : power / 8; + rad = (rad > 10) ? 10 : rad; + ty = p_ptr->py; + tx = p_ptr->px; + } + + if (spell->rune2 & RUNE_RAY) + { + flg |= (PROJECT_THRU); + flg |= (PROJECT_KILL); + flg |= (PROJECT_BEAM); + ty = -1; + tx = -1; + } + if (spell->rune2 & RUNE_ARROW) + { + flg |= (PROJECT_THRU); + flg |= (PROJECT_STOP); + flg |= (PROJECT_KILL); + ty = -1; + tx = -1; + } + if (spell->rune2 & RUNE_SELF) + { + flg |= (PROJECT_THRU); + flg |= (PROJECT_STOP); + flg |= (PROJECT_KILL); + ty = p_ptr->py; + tx = p_ptr->px; + unsafe = TRUE; + } + + if ((ty == -1) && (tx == -1)) + { + if (!get_aim_dir(&dir)) return (mana_used); + + /* Use the given direction */ + tx = p_ptr->px + ddx[dir]; + ty = p_ptr->py + ddy[dir]; + + /* Hack -- Use an actual "target" */ + if ((dir == 5) && target_okay()) + { + tx = target_col; + ty = target_row; + } + } + + if (flg & PROJECT_VIEWABLE) + { + project_hack(spell->type, dam); + } + else if (flg & PROJECT_METEOR_SHOWER) + { + project_meteor(rad, spell->type, dam, flg); + } + else project(0, rad, ty, tx, dam, spell->type, flg); + + if (unsafe) unsafe = FALSE; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); + + return (mana_used); +} + + +/* + * Test if all runes needed at in the player p_ptr->inventory + */ +bool_ test_runespell(rune_spell *spell) +{ + int i; + + object_type *o_ptr; + + bool_ typeok = FALSE; + + int rune2 = 0; + + + for (i = 0; i < INVEN_WIELD; i++) + { + o_ptr = &p_ptr->inventory[i]; + + if (!o_ptr->k_idx) continue; + + /* Does the rune1(type) match ? */ + if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == spell->type)) + { + typeok = TRUE; + } + + if ((o_ptr->tval == TV_RUNE2) && (o_ptr->sval != RUNE_STONE)) + { + /* Add it to the list */ + rune2 |= 1 << o_ptr->sval; + } + } + + /* Need all runes to be present */ + return (typeok && ((rune2 & spell->rune2) == spell->rune2)); +} + + +/* + * Ask for rune, rune2 and mana + */ +bool_ get_runespell(rune_spell *spell) +{ + int item, power_rune = 0, rune2 = 0, plev = get_skill(SKILL_RUNECRAFT); + + s32b power; + + int type = 0; + + object_type *o_ptr; + + cptr q, s; + + bool_ OK = FALSE; + + + rune_combine = 0; + + /* Restrict choices to unused runes */ + item_tester_hook = item_tester_hook_runeable1; + + /* Get an item */ + q = "Use which rune? "; + s = "You have no rune to use."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return FALSE; + + /* Get the item */ + o_ptr = get_object(item); + type = o_ptr->sval; + + while (1) + { + /* Restrict choices to unused secondary runes */ + item_tester_hook = item_tester_hook_runeable2; + + OK = !get_item(&item, q, s, (USE_INVEN | USE_FLOOR)); + + if (OK) break; + + /* Get the item */ + o_ptr = get_object(item); + + rune_combine |= 1 << o_ptr->sval; + rune2 |= 1 << o_ptr->sval; + } + + if (!rune2) + { + msg_print("You have not selected a second rune!"); + return (FALSE); + } + + power = get_quantity("Which amount of Mana?", + p_ptr->csp - (power_rune * (plev / 5))); + if (power < 1) power = 1; + + spell->mana = power; + spell->type = type; + spell->rune2 = rune2; + + return (TRUE); +} + + +void do_cmd_rune(void) +{ + rune_spell spell; + + + /* Require some mana */ + if (p_ptr->csp <= 0) + { + msg_print("You have no mana!"); + return; + } + + /* Require lite */ + if (p_ptr->blind || no_lite()) + { + msg_print("You cannot see!"); + return; + } + + /* Not when confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + if (!get_runespell(&spell)) return; + + /* Execute at normal mana cost */ + p_ptr->csp -= rune_exec(&spell, 100); + + /* Safety :) */ + if (p_ptr->csp < 0) p_ptr->csp = 0; + + /* Take a turn */ + if (is_magestaff()) energy_use = 80; + else energy_use = 100; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); +} + + +/* + * Print a batch of runespells. + */ +static void print_runespell_batch(int batch, int max) +{ + char buff[80]; + + rune_spell* spell; + + int i; + + s32b power, powerdiv; + + int p, dp; + + + prt(format(" %-30s Fail Mana Power", "Name"), 1, 20); + + for (i = 0; i < max; i++) + { + spell = &rune_spells[batch * 10 + i]; + + power = spell->mana; + rune_calc_power(&power, &powerdiv); + p = power; + dp = powerdiv; + + strnfmt(buff, 80, " %c) %-30s %4d%% %4d %dd%d ", I2A(i), spell->name, + spell_chance_rune(spell), spell->mana, dp, p); + + prt(buff, 2 + i, 20); + } + prt("", 2 + i, 20); +} + + + +/* + * List ten random spells and ask to pick one. + */ + +static rune_spell* select_runespell_from_batch(int batch, int *s_idx) +{ + char tmp[160]; + + char out_val[30]; + + char which; + + int mut_max = 10; + + rune_spell* ret; + + + character_icky = TRUE; + + if (rune_num < (batch + 1) * 10) + { + mut_max = rune_num - batch * 10; + } + + strnfmt(tmp, 160, "(a-%c, * to list, / to rename, - to comment) Select a power: ", + I2A(mut_max - 1)); + + prt(tmp, 0, 0); + + while (1) + { + Term_save(); + + print_runespell_batch(batch, mut_max); + + which = inkey(); + + Term_load(); + + if (which == ESCAPE) + { + *s_idx = -1; + ret = NULL; + break; + } + else if ((which == '*') || (which == '?') || (which == ' ')) + { + print_runespell_batch(batch, mut_max); + } + else if ((which == '\r') && (mut_max == 1)) + { + *s_idx = batch * 10; + ret = &rune_spells[batch * 10]; + break; + } + else if (which == '/') + { + prt("Rename which power: ", 0, 0); + which = tolower(inkey()); + + if (isalpha(which) && (A2I(which) <= mut_max)) + { + strcpy(out_val, rune_spells[batch*10 + A2I(which)].name); + if (get_string("Name this power: ", out_val, 29)) + { + strcpy(rune_spells[batch*10 + A2I(which)].name, out_val); + } + prt(tmp, 0, 0); + } + else + { + bell(); + prt(tmp, 0, 0); + } + } + else + { + which = tolower(which); + if (isalpha(which) && (A2I(which) < mut_max)) + { + *s_idx = batch * 10 + A2I(which); + ret = &rune_spells[batch * 10 + A2I(which)]; + break; + } + else + { + bell(); + } + } + } + + character_icky = FALSE; + + return (ret); +} + + +/* + * Pick a random spell from a menu + */ + +rune_spell* select_runespell(int *s_idx) +{ + char tmp[160]; + + char which; + + int batch_max = (rune_num - 1) / 10; + + if (rune_num == 0) + { + msg_print("There are no runespells you can cast."); + return (NULL); + } + + character_icky = TRUE; + Term_save(); + + strnfmt(tmp, 160, "(a-%c) Select batch of powers: ", I2A(batch_max)); + + prt(tmp, 0, 0); + + while (1) + { + which = inkey(); + + if (which == ESCAPE) + { + Term_load(); + character_icky = FALSE; + return (NULL); + } + else if ((which == '\r') && (batch_max == 0)) + { + Term_load(); + character_icky = FALSE; + return (select_runespell_from_batch(0, s_idx)); + + } + else + { + which = tolower(which); + if (isalpha(which) && (A2I(which) <= batch_max)) + { + Term_load(); + character_icky = FALSE; + return (select_runespell_from_batch(A2I(which), s_idx)); + } + else + { + bell(); + } + } + } +} + + +/* + * Cast a memorized runespell + * Note that the only limits are antimagic & conf, NOT blind + */ +void do_cmd_rune_cast() +{ + rune_spell *s_ptr; + + int s_idx; + + + /* Require some mana */ + if (p_ptr->csp <= 0) + { + msg_print("You have no mana!"); + return; + } + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + /* Not when confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + s_ptr = select_runespell(&s_idx); + + if (s_ptr == NULL) return; + + /* Need the runes */ + if (!test_runespell(s_ptr)) + { + msg_print("You lack some essential rune(s) for this runespell!"); + return; + } + + /* Execute at normal mana cost */ + p_ptr->csp -= rune_exec(s_ptr, 100); + + /* Safety :) */ + if (p_ptr->csp < 0) p_ptr->csp = 0; + + /* Take a turn */ + if (is_magestaff()) energy_use = 80; + else energy_use = 100; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); +} + + +/* + * Cast a runespell from a carved runestone + */ +void do_cmd_runestone() +{ + rune_spell s_ptr; + + object_type *o_ptr; + + cptr q, s; + + int item; + + + /* Require some mana */ + if (p_ptr->csp <= 0) + { + msg_print("You have no mana!"); + return; + } + + /* Require lite */ + if (p_ptr->blind || no_lite()) + { + msg_print("You cannot see!"); + return; + } + + /* Not when confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + /* Restrict choices to unused runes */ + item_tester_hook = item_tester_hook_runestone_full; + + /* Get an item */ + q = "Cast from which runestone? "; + s = "You have no runestone to cast from."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + s_ptr.type = o_ptr->pval; + s_ptr.rune2 = o_ptr->pval2; + s_ptr.mana = o_ptr->pval3; + + /* Execute less mana */ + p_ptr->csp -= rune_exec(&s_ptr, 75); + + /* Safety :) */ + if (p_ptr->csp < 0) p_ptr->csp = 0; + + /* Take a turn */ + energy_use = 100; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); +} + + +/* + * Add a runespell to the list + */ +void do_cmd_rune_add_mem() +{ + rune_spell s_ptr; + + rune_spell *ds_ptr = &rune_spells[rune_num]; + + + /* Not when confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + + if (rune_num >= MAX_RUNES) + { + msg_print("You have already learn the maximun number of runespells!"); + return; + } + + if (!get_runespell(&s_ptr)) return; + + ds_ptr->type = s_ptr.type; + ds_ptr->rune2 = s_ptr.rune2; + ds_ptr->mana = s_ptr.mana; + strcpy(ds_ptr->name, "Unnamed Runespell"); + + get_string("Name this runespell: ", ds_ptr->name, 29); + + rune_num++; + + /* Take a turn */ + energy_use = 100; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); +} + + +/* + * Carve a runespell onto a Runestone + */ +void do_cmd_rune_carve() +{ + rune_spell s_ptr; + + object_type *o_ptr; + + cptr q, s; + + int item, i; + + char out_val[80]; + + + /* Not when confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + /* Require lite */ + if (p_ptr->blind || no_lite()) + { + msg_print("You cannot see!"); + return; + } + + if (!get_check("Beware, this will destroy the involved runes, continue?")) + { + return; + } + + if (!get_runespell(&s_ptr)) return; + + /* Restrict choices to unused runes */ + item_tester_hook = item_tester_hook_runestone; + + /* Get an item */ + q = "Use which runestone? "; + s = "You have no runestone to use."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + o_ptr->pval = s_ptr.type; + o_ptr->pval2 = s_ptr.rune2; + o_ptr->pval3 = s_ptr.mana; + + /* Start with nothing */ + strcpy(out_val, ""); + + /* Use old inscription */ + if (o_ptr->note) + { + /* Start with the old inscription */ + strcpy(out_val, quark_str(o_ptr->note)); + } + + /* Get a new inscription (possibly empty) */ + if (get_string("Name this runestone: ", out_val, 80)) + { + /* Save the inscription */ + o_ptr->note = quark_add(out_val); + + /* Combine the pack */ + p_ptr->notice |= (PN_COMBINE); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + } + + /* Delete the runes */ + for (i = 0; i < INVEN_WIELD; i++) + { + o_ptr = &p_ptr->inventory[i]; + + if (o_ptr->k_idx) + { + bool_ do_del = FALSE; + + if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == s_ptr.type)) do_del = TRUE; + if ((o_ptr->tval == TV_RUNE2) && (BIT(o_ptr->sval) & s_ptr.rune2)) do_del = TRUE; + + if (do_del) + { + inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE); + } + } + } + + /* Take a turn -- Carving takes a LONG time */ + energy_use = 400; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); +} + + +/* + * Remove a runespell + */ +void do_cmd_rune_del() +{ + rune_spell *s_ptr; + + int s_idx; + + int i; + + + /* Not when confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + s_ptr = select_runespell(&s_idx); + + if (s_ptr == NULL) return; + + /* Delete and move */ + for (i = s_idx + 1; i < rune_num; i++) + { + rune_spells[i - 1].type = rune_spells[i].type; + rune_spells[i - 1].rune2 = rune_spells[i].rune2; + rune_spells[i - 1].mana = rune_spells[i].mana; + strcpy(rune_spells[i - 1].name, rune_spells[i].name); + } + rune_num--; + + /* Take a turn */ + energy_use = 100; + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + p_ptr->redraw |= (PR_MANA); +} + + +void do_cmd_rune_add() +{ + int ext = 0; + + char ch; + + + /* Select what to do */ + while (TRUE) + { + if (!get_com("Add to [M]emory(need runes to cast) or " + "Carve a [R]unestone(less mana to cast)", &ch)) + { + ext = 0; + break; + } + if ((ch == 'M') || (ch == 'm')) + { + ext = 1; + break; + } + if ((ch == 'R') || (ch == 'r')) + { + ext = 2; + break; + } + } + + switch (ext) + { + /* Create a Spell in memory */ + case 1: + { + do_cmd_rune_add_mem(); + break; + } + + /* Carve a Runestone */ + case 2: + { + do_cmd_rune_carve(); + break; + } + } +} + + +void do_cmd_runecrafter() +{ + int ext = 0; + + char ch; + + + /* Select what to do */ + while (TRUE) + { + if (!get_com("Rune Spell:[C]reate, [D]elete, C[a]st, D[i]rectly Cast " + "or Use [R]unestone", &ch)) + { + ext = 0; + break; + } + if ((ch == 'C') || (ch == 'c')) + { + ext = 1; + break; + } + if ((ch == 'D') || (ch == 'd')) + { + ext = 2; + break; + } + if ((ch == 'A') || (ch == 'a')) + { + ext = 3; + break; + } + if ((ch == 'I') || (ch == 'i')) + { + ext = 4; + break; + } + if ((ch == 'R') || (ch == 'r')) + { + ext = 5; + break; + } + } + + switch (ext) + { + /* Create a Spell */ + case 1: + { + do_cmd_rune_add(); + break; + } + + /* Delete a Spell */ + case 2: + { + do_cmd_rune_del(); + break; + } + + /* Cast a Spell */ + case 3: + { + do_cmd_rune_cast(); + break; + } + + /* Directly Cast a Spell */ + case 4: + { + do_cmd_rune(); + break; + } + + /* Cast a Runestone */ + case 5: + { + do_cmd_runestone(); + break; + } + } +} + + +void do_cmd_unbeliever_antimagic() +{ + if (get_skill(SKILL_ANTIMAGIC) < 20) + { + msg_print("You must have at least a level 20 antimagic skill " + "to be able to disrupt the magic continuum."); + return; + } + + if (p_ptr->antimagic_extra & CLASS_ANTIMAGIC) + { + p_ptr->antimagic_extra &= ~CLASS_ANTIMAGIC; + msg_print("You stop disrupting the magic continuum."); + } + else + { + p_ptr->antimagic_extra |= CLASS_ANTIMAGIC; + msg_print("You start disrupting the magic continuum."); + } + + /* Recalculate bonuses */ + p_ptr->update |= (PU_BONUS); +} + + +/* + * Detect traps + kill traps + */ +void do_cmd_unbeliever() +{ + int ext = 0; + + char ch; + + + /* Select what to do */ + while (TRUE) + { + if (!get_com("Disrupt [C]ontinuum or [D]etect Traps", &ch)) + { + ext = 0; + break; + } + if ((ch == 'C') || (ch == 'c')) + { + ext = 1; + break; + } + if ((ch == 'D') || (ch == 'd')) + { + ext = 2; + break; + } + } + + switch (ext) + { + /* Disrupt Continuum */ + case 1: + { + do_cmd_unbeliever_antimagic(); + break; + } + + /* Detect Traps */ + case 2: + { + s16b skill = get_skill(SKILL_ANTIMAGIC); + + if (skill < 25) + { + msg_print("You cannot use your detection abilities yet."); + break; + } + + detect_traps(DEFAULT_RADIUS); + + if (skill >= 35) destroy_doors_touch(); + + break; + } + } +} + +/* + * Hook to determine if an object is totemable + */ +static bool_ item_tester_hook_totemable(object_type *o_ptr) +{ + /* Only full corpse */ + if ((o_ptr->tval == TV_CORPSE) && + ((o_ptr->sval == SV_CORPSE_CORPSE) || (o_ptr->sval == SV_CORPSE_SKELETON))) + { + return (TRUE); + } + + /* Assume not */ + return (FALSE); +} + + +/* + * Summoners + */ +void do_cmd_summoner_extract() +{ + object_type *o_ptr, forge, *q_ptr; + + cptr q, s; + + int item, r; + + bool_ partial; + + + /* Not when confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + /* Require lite */ + if (p_ptr->blind || no_lite()) + { + msg_print("You cannot see!"); + return; + } + + item_tester_hook = item_tester_hook_totemable; + + /* Get an item */ + q = "Use which corpse? "; + s = "You have no corpse to use."; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Get the item */ + o_ptr = get_object(item); + + + if (r_info[o_ptr->pval2].flags1 & RF1_UNIQUE) + { + partial = FALSE; + } + else + { + partial = get_check("Do you want to create a partial totem?"); + } + + r = o_ptr->pval2; + + inc_stack_size(item, -1); + + if (magik(r_info[o_ptr->pval2].level - get_skill(SKILL_SUMMON))) + { + msg_print("You failed to extract a totem."); + energy_use += 100; + return; + } + + /* Prepare for object creation */ + q_ptr = &forge; + + /* Create the object */ + object_prep(q_ptr, lookup_kind(TV_TOTEM, partial ? 1 : 2)); + q_ptr->pval = r; + q_ptr->pval2 = 0; + q_ptr->number = 1; + q_ptr->found = OBJ_FOUND_SELFMADE; + object_aware(q_ptr); + object_known(q_ptr); + q_ptr->ident |= IDENT_MENTAL; + (void)inven_carry(q_ptr, FALSE); + + msg_print("You extract a totem from the dead corpse."); + energy_use += 100; +} + + +void summon_true(int r_idx, int item) +{ + int i, status, x = 1, y = 1, rx, ry = 0, chance; + + bool_ used; + + monster_race *r_ptr = &r_info[r_idx]; + + + /* Uniques are less likely to be nice */ + if (r_ptr->flags1 & (RF1_UNIQUE)) + { + /* Because it's unique, it will always be destroyed */ + used = TRUE; + + /* About twice as hard as non-uniques */ + chance = (get_skill(SKILL_SUMMON) * 70 / (r_ptr->level + 1)); + + if (magik(chance)) + { + status = MSTATUS_PET; + } + else + { + status = MSTATUS_ENEMY; + } + } + + /* Non-uniques are easier to handle */ + else + { + if (get_skill(SKILL_SUMMON) == 0) + { + used = TRUE; + } + else + { + /* It can be used multiple times */ + used = FALSE; + + /* But it is not 100% sure (note: skill > 0) */ + chance = (r_ptr->level * 25 / get_skill(SKILL_SUMMON)); + if (magik(chance)) used = TRUE; + } + + chance = (get_skill(SKILL_SUMMON) * 130 / (r_ptr->level + 1)); + + if (magik(chance)) + { + status = MSTATUS_PET; + } + else + { + status = MSTATUS_ENEMY; + } + } + + /* Find a grid where the monster is summoned */ + for (i = 0; i < 40; i++) + { + rx = (rand_int(8) - 4) + p_ptr->px; + ry = (rand_int(8) - 4) + p_ptr->py; + if (in_bounds(ry, rx) && cave_empty_bold(ry, rx)) + { + x = rx; + y = ry; + break; + } + } + + /* No room found */ + if (i == 40) + { + msg_print("The summoning fails due to lack of room."); + return; + } + + /* Summon the monster */ + bypass_r_ptr_max_num = TRUE; + if (!(i = place_monster_one (y, x, r_idx, 0, 0, status))) + { + msg_print("The summoning fails."); + } + else + { + m_list[i].status = status; + m_list[i].mflag |= MFLAG_NO_DROP; + } + bypass_r_ptr_max_num = FALSE; + + /* Destroy the totem if the used flag is set */ + if (used) + { + /* Eliminate the totem */ + inc_stack_size(item, -1); + } + + /* Done */ + return; +} + + +void do_cmd_summoner_summon() +{ + int item, x = 1, y = 1, rx, ry, m_idx = 0, i; + + cptr q, s; + + object_type *o_ptr; + + monster_type *m_ptr; + + + /* Which Totem? */ + item_tester_tval = TV_TOTEM; + + q = "Summon from which Totem?"; + s = "There are no totems to summon from!"; + if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR))) return; + + /* Access the item */ + o_ptr = get_object(item); + + /* Take a turn */ + energy_use = 100; + + /* True Totems have their own function. */ + if (o_ptr->sval == 2) + { + summon_true(o_ptr->pval, item); + return; + } + + /* Handle partial totems */ + + /* Find a grid where the monster is summoned */ + for (i = 0; i < 40; i++) + { + rx = (rand_int(8) - 4) + p_ptr->px; + ry = (rand_int(8) - 4) + p_ptr->py; + if (in_bounds(ry, rx) && cave_empty_bold(ry, rx)) + { + x = rx; + y = ry; + break; + } + } + + /* No room found */ + if (i == 40) + { + msg_print("The summoning fails due to lack of room."); + return; + } + + /* Summon the monster */ + bypass_r_ptr_max_num = TRUE; + place_monster_one_no_drop = TRUE; + m_idx = place_monster_one(y, x, o_ptr->pval, 0, 0, MSTATUS_PET); + bypass_r_ptr_max_num = FALSE; + + /* Failure. */ + if (!m_idx) + { + msg_print("The summoning fails."); + } + + /* Mark the monster as a "partial" ally */ + m_ptr = &m_list[m_idx]; + m_ptr->mflag |= MFLAG_PARTIAL | MFLAG_NO_DROP; +} + + +void do_cmd_summoner(void) +{ + int ext = 0; + + char ch; + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + /* not if confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + /* not if blind */ + if (p_ptr->blind || no_lite()) + { + msg_print("You cannot see!"); + return; + } + + /* Select what to do */ + while (TRUE) + { + if (!get_com("[E]xtract a totem, [S]ummon", &ch)) + { + ext = 0; + break; + } + if ((ch == 'E') || (ch == 'e')) + { + ext = 1; + break; + } + if ((ch == 's') || (ch == 'S')) + { + ext = 2; + break; + } + } + + switch (ext) + { + case 1: + { + do_cmd_summoner_extract(); + break; + } + + case 2: + { + do_cmd_summoner_summon(); + break; + } + } +} + + +/* + * Fighters may invoke The Rush. + */ +void do_cmd_blade(void) +{ + /* Are we already Rushed? */ + if (p_ptr->rush) + { + msg_format("You have %d turns of The Rush remaining", p_ptr->rush); + return; + } + + /* Are you sure? */ + if (!get_check("Are you sure you want to invoke The Rush?")) return; + + /* Let's Rush! */ + set_rush(2 + p_ptr->lev / 2 + randint(p_ptr->lev / 2)); +} + + +/* + * Dodge Chance Feedback. + */ +void use_ability_blade(void) +{ + int chance = p_ptr->dodge_chance - ((dun_level * 5) / 6); + + if (chance < 0) chance = 0; + if (wizard) + { + msg_format("You have exactly %d chances of dodging a level %d monster.", chance, dun_level); + } + + if (chance < 5) + { + msg_format("You have almost no chance of dodging a level %d monster.", dun_level); + } + else if (chance < 10) + { + msg_format("You have a slight chance of dodging a level %d monster.", dun_level); + } + else if (chance < 20) + { + msg_format("You have a significant chance of dodging a level %d monster.", dun_level); + } + else if (chance < 40) + { + msg_format("You have a large chance of dodging a level %d monster.", dun_level); + } + else if (chance < 70) + { + msg_format("You have a high chance of dodging a level %d monster.", dun_level); + } + else + { + msg_format("You will usually dodge successfully a level %d monster.", dun_level); + } + + return; +} + +/* + * Helper function to describe symbiotic powers + */ +void symbiotic_info(char *p, int power) +{ + int plev = get_skill(SKILL_SYMBIOTIC); + + strcpy(p, ""); + + switch (power) + { + case 2: + { + strnfmt(p, 80, " power %d", plev * 3); + break; + } + case 5: + { + strnfmt(p, 80, " heal %d%%", 15 + get_skill_scale(SKILL_SYMBIOTIC, 35)); + break; + } + } +} + + +/* + * Cast a symbiotic spell + */ +void do_cmd_symbiotic(void) +{ + int n = 0; + int chance; + int minfail = 0; + int plev = get_skill(SKILL_SYMBIOTIC); + magic_power spell; + + /* Get the carried monster */ + object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY]; + + /* No magic */ + if (p_ptr->antimagic) + { + msg_print("Your anti-magic field disrupts any magic attempts."); + return; + } + + /* No magic */ + if (p_ptr->anti_magic) + { + msg_print("Your anti-magic shell disrupts any magic attempts."); + return; + } + + /* not if confused */ + if (p_ptr->confused) + { + msg_print("You are too confused!"); + return; + } + + /* get power */ + if (!get_magic_power(&n, symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info, + get_skill(SKILL_SYMBIOTIC), A_INT)) return; + + spell = symbiotic_powers[n]; + + /* Verify "dangerous" spells */ + if (spell.mana_cost > p_ptr->csp) + { + /* Warning */ + msg_print("You do not have enough mana to use this power."); + + /* Verify */ + if (!get_check("Attempt it anyway? ")) return; + } + + /* Spell failure chance */ + chance = spell.fail; + + /* Reduce failure rate by "effective" level adjustment */ + chance -= 3 * (plev - spell.min_lev); + + /* Reduce failure rate by INT/WIS adjustment */ + chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1); + + /* Not enough mana to cast */ + if (spell.mana_cost > p_ptr->csp) + { + chance += 5 * (spell.mana_cost - p_ptr->csp); + } + + /* Extract the minimum failure rate */ + minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]]; + + /* Failure rate */ + chance = clamp_failure_chance(chance, minfail); + + /* Failed spell */ + if (rand_int(100) < chance) + { + if (flush_failure) flush(); + msg_format("You failed to concentrate hard enough!"); + sound(SOUND_FAIL); + } + else + { + sound(SOUND_ZAP); + + /* spell code */ + switch (n) + { + case 0: + { + int dir, x, y; + cave_type *c_ptr; + monster_type *m_ptr; + monster_race *r_ptr; + object_type *q_ptr; + object_type forge; + + msg_print("Hypnotise which pet?"); + if (!get_rep_dir(&dir)) return; + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + c_ptr = &cave[y][x]; + if (c_ptr->m_idx) + { + m_ptr = &m_list[c_ptr->m_idx]; + r_ptr = race_inf(m_ptr); + + if (!(r_ptr->flags1 & RF1_NEVER_MOVE)) + { + msg_print("You can only hypnotise monsters that cannot move."); + } + else if (m_ptr->status < MSTATUS_PET) + { + msg_print("You can only hypnotise pets and companions."); + } + else if (r_ptr->flags9 & RF9_SPECIAL_GENE) + { + msg_print("You cannot hypnotise this monster."); + } + else + { + /* TODO fix this hack hack hack hackity hack with ToME 3 flags */ + q_ptr = &forge; + object_prep(q_ptr, lookup_kind(TV_HYPNOS, 1)); + q_ptr->number = 1; + q_ptr->pval = m_ptr->r_idx; + q_ptr->pval2 = m_ptr->hp; + q_ptr->pval3 = m_ptr->maxhp; + /* overflow alert */ + q_ptr->exp = m_ptr->exp; + q_ptr->elevel = m_ptr->level; + object_aware(q_ptr); + object_known(q_ptr); + + q_ptr->ident |= IDENT_STOREB; + + drop_near(q_ptr, 0, y, x); + + delete_monster(y, x); + health_who = 0; + } + } + else + { + msg_print("There is no pet here !"); + } + + break; + } + + case 1: + { + monster_type *m_ptr; + int m_idx; + int item, x, y, d; + object_type *o_ptr; + + cptr q, s; + + /* Restrict choices to monsters */ + item_tester_tval = TV_HYPNOS; + + /* Get an item */ + q = "Awaken which monster? "; + s = "You have no monster to awaken."; + if (!get_item(&item, q, s, (USE_FLOOR))) return; + + o_ptr = &o_list[0 - item]; + + d = 2; + while (d < 100) + { + scatter(&y, &x, p_ptr->py, p_ptr->px, d); + + if (cave_floor_bold(y, x) && (!cave[y][x].m_idx)) break; + + d++; + } + + if (d >= 100) return; + + if ((m_idx = place_monster_one(y, x, o_ptr->pval, 0, FALSE, MSTATUS_PET)) == 0) return; + + /* TODO fix this hack hack hack hackity hack with ToME 3 flags */ + /* Have to be careful here; releasing the symbiote into a + * dungeon with leveled monsters will level the symbiote + * before we can get hold of it. We'll be nice and use the + * larger of the saved exp and the exp that the newly-generated + * monster starts with. */ + m_ptr = &m_list[m_idx]; + if (m_ptr->exp < o_ptr->exp) + { + m_ptr->exp = o_ptr->exp; + monster_check_experience(m_idx, TRUE); + if (m_ptr->level != o_ptr->elevel) + cmsg_format(TERM_VIOLET, "ERROR: level-%d HYPNOS becomes level-%d symbiote", o_ptr->elevel, m_ptr->level); + } + m_ptr->hp = o_ptr->pval2; + m_ptr->maxhp = o_ptr->pval3; + + floor_item_increase(0 - item, -1); + floor_item_describe(0 - item); + floor_item_optimize(0 - item); + break; + } + + /* Charm */ + case 2: + { + int dir; + + if (!get_aim_dir(&dir)) return; + + fire_bolt(GF_CHARM_UNMOVING, dir, plev * 3); + + break; + } + + /* Life Share */ + case 3: + { + s32b percent1, percent2; + + if (!o_ptr->k_idx) + { + msg_print("You are not in symbiosis."); + break; + } + + percent1 = p_ptr->chp; + percent1 = (percent1 * 100) / p_ptr->mhp; + + percent2 = o_ptr->pval2; + percent2 = (percent2 * 100) / o_ptr->pval3; + + /* Now get the average */ + percent1 = (percent1 + percent2) / 2; + + /* And set the hp of monster & player to it */ + p_ptr->chp = (percent1 * p_ptr->mhp) / 100; + o_ptr->pval2 = (percent1 * o_ptr->pval3) / 100; + + /* Redraw */ + p_ptr->redraw |= (PR_HP); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + + /* Display the monster hitpoints */ + p_ptr->redraw |= (PR_MH); + + break; + } + + /* Minor Symbiotic Powers */ + case 4: + { + if (!o_ptr->k_idx) + { + msg_print("You are not in symbiosis."); + break; + } + + if (0 > use_symbiotic_power(o_ptr->pval, FALSE, FALSE, TRUE)) + return; + + break; + } + + /* Heal Symbiote */ + case 5: + { + int hp; + + if (!o_ptr->k_idx) + { + msg_print("You are not in symbiosis."); + break; + } + + hp = o_ptr->pval3 * (15 + get_skill_scale(SKILL_SYMBIOTIC, 35)) / 100; + o_ptr->pval2 += hp; + if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3; + + msg_format("%s is healed.", symbiote_name(TRUE)); + + /* Display the monster hitpoints */ + p_ptr->redraw |= (PR_MH); + + break; + } + + + /* Major Symbiotic Powers */ + case 6: + { + if (!o_ptr->k_idx) + { + msg_print("You are not in symbiosis."); + break; + } + + if(0 > use_symbiotic_power(o_ptr->pval, TRUE, FALSE, TRUE)) + return; + + break; + } + + /* Summon never-moving pet */ + case 7: + { + summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_MINE, FALSE); + + break; + } + + /* Force Symbiosis */ + case 8: + { + int y, x; + cave_type *c_ptr; + monster_type *m_ptr; + + if (!tgt_pt(&x, &y)) return; + + c_ptr = &cave[y][x]; + + if (!c_ptr->m_idx) break; + + m_ptr = &m_list[c_ptr->m_idx]; + use_symbiotic_power(m_ptr->r_idx, TRUE, FALSE, TRUE); + + break; + } + + + default: + { + msg_print("Zap?"); + + break; + } + } + } + + /* Take a turn */ + energy_use = 100; + + /* Sufficient mana */ + if (spell.mana_cost <= p_ptr->csp) + { + /* Use some mana */ + p_ptr->csp -= spell.mana_cost; + } + + /* Over-exert the player */ + else + { + int oops = spell.mana_cost - p_ptr->csp; + + /* No mana left */ + p_ptr->csp = 0; + p_ptr->csp_frac = 0; + + /* Message */ + msg_print("You faint from the effort!"); + + /* Hack -- Bypass free action */ + (void)set_paralyzed(randint(5 * oops + 1)); + + /* Damage CON (possibly permanently) */ + if (rand_int(100) < 50) + { + bool_ perm = (rand_int(100) < 25); + + /* Message */ + msg_print("You have damaged your body!"); + + /* Reduce constitution */ + (void)dec_stat(A_CHR, 15 + randint(10), perm); + } + } + + /* Redraw mana */ + p_ptr->redraw |= (PR_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); +} + +/* + * Boulder creation .. sorry :) + */ +void do_cmd_create_boulder() +{ + int x, y, dir; + cave_type *c_ptr; + + if (!get_rep_dir(&dir)) return; + y = p_ptr->py + ddy[dir]; + x = p_ptr->px + ddx[dir]; + c_ptr = &cave[y][x]; + + /* Granite -- How about other wall types? */ + if (((c_ptr->feat >= FEAT_WALL_EXTRA) && (c_ptr->feat <= FEAT_WALL_SOLID)) || + ((c_ptr->feat >= FEAT_MAGMA_H) && (c_ptr->feat <= FEAT_QUARTZ_K)) || + ((c_ptr->feat == FEAT_MAGMA) || + (c_ptr->feat == FEAT_QUARTZ))) + { + object_type forge; + object_type *q_ptr; + + (void)wall_to_mud(dir); + + /* Get local object */ + q_ptr = &forge; + + /* Hack -- Give the player some shots */ + object_prep(q_ptr, lookup_kind(TV_JUNK, SV_BOULDER)); + q_ptr->number = (byte)rand_range(2, 5); + object_aware(q_ptr); + object_known(q_ptr); + q_ptr->ident |= IDENT_MENTAL; + q_ptr->discount = 90; + q_ptr->found = OBJ_FOUND_SELFMADE; + + (void)inven_carry(q_ptr, FALSE); + + msg_print("You make some boulders."); + + p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE); + p_ptr->window |= (PW_OVERHEAD); + + /* Take a turn */ + energy_use = 100; + } +} + +/* + * Clamp failure chance + */ +extern int clamp_failure_chance(int chance, int minfail) +{ + if (minfail < 0) minfail = 0; + + /* Minimum failure rate */ + if (chance < minfail) chance = minfail; + + /* Stunning makes spells harder */ + if (p_ptr->stun > 50) chance += 25; + else if (p_ptr->stun) chance += 15; + + /* Always a 5 percent chance of working */ + if (chance > 95) chance = 95; + + return chance; +} diff --git a/src/cmovie.c b/src/cmovie.c deleted file mode 100644 index d1459e02..00000000 --- a/src/cmovie.c +++ /dev/null @@ -1,496 +0,0 @@ -/* File: cmovie.c */ - -/* Purpose: play cmovie files -DarkGod-Improv- */ - -#include "angband.h" - -/* - * Play a given cmovie - */ -s16b do_play_cmovie(cptr cmov_file) -{ - FILE *fff; - - int y, line = 0, x; - int delay; - - char *s; - - char buf[1024]; - char cbuf[90]; - char ch; - - char mode = 0; - - - /* Cmovie files are moved to the user directory on the multiuser systems */ - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_CMOV, cmov_file); - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Read the file */ - fff = my_fopen(buf, "r"); - - /* Failure */ - if (!fff) return ( -1); - - /* Save screen */ - character_icky = TRUE; - Term_save(); - Term_clear(); - - /* Give some usefull info */ - prt("While viewing the movie you can press Escape to exit, t/Space to switch between", 0, 0); - prt("fluid more and step by step mode and any other key to step a frame in step by", 1, 0); - prt("step mode.", 2, 0); - prt("You can press D to do an html screenshot of the current frame.", 3, 0); - prt("You can also use + and - to speed up/down the playing speed.", 5, 0); - prt("Press any key when ready.", 8, 0); - - inkey(); - - Term_clear(); - - line = -1; - - delay = 1; - - /* Init to white */ - for (x = 0; x < 80; x++) - { - cbuf[x] = 'w'; - } - - /* Parse */ - while (0 == my_fgets(fff, buf, 1024)) - { - /* Do not wait */ - inkey_scan = TRUE; - ch = inkey(); - - /* Stop */ - if (ch == ESCAPE) break; - - /* Change mode */ - else if (ch == 't') - { - mode = FALSE; - } - else if (ch == ' ') - { - mode = TRUE; - } - - /* Change speed */ - else if (ch == '+') - { - delay--; - if (delay < 0) delay = 0; - } - else if (ch == '-') - { - delay++; - if (delay > 5) delay = 5; - } - else if (ch == 'D') - { - do_cmd_html_dump(); - } - - line++; - - /* Skip comments and blank lines */ - if (!buf[0] || (buf[0] == '#')) continue; - - /* Verify correct "colon" format */ - if (buf[1] != ':') break; - - /* Clean screen */ - if (buf[0] == 'C') - { - Term_clear(); - - /* Next */ - continue; - } - - /* Displays a textbox */ - if (buf[0] == 'B') - { - int len = strlen(buf + 2); - - /* Clear the line */ - Term_erase(0, 0, 255); - - /* Display the message */ - c_put_str(TERM_VIOLET, "###", 0, 0); - c_put_str(TERM_ORANGE, buf + 2, 0, 3); - c_put_str(TERM_VIOLET, "###", 0, 3 + len); - c_put_str(TERM_WHITE, "(more)", 0, 6 + len); - - /* Next */ - continue; - } - - /* Wait a key */ - if (buf[0] == 'W') - { - inkey(); - - /* Next */ - continue; - } - - /* Sleep */ - if (buf[0] == 'S') - { - long msec; - - /* Scan for the values */ - if (1 != sscanf(buf + 2, "%ld:", &msec)) - { - return ( -2); - } - - if (!mode) - { - Term_xtra(TERM_XTRA_DELAY, msec); - } - else - { - bool_ stop = FALSE; - - while (TRUE) - { - ch = inkey(); - - /* Stop */ - if (ch == ESCAPE) - { - stop = TRUE; - break; - } - /* Change mode */ - else if (ch == 't') - { - mode = FALSE; - break; - } - /* Change mode */ - else if (ch == ' ') - { - if (mode) continue; - mode = TRUE; - break; - } - /* Change speed */ - else if (ch == '+') - { - delay--; - if (delay < 0) delay = 0; - } - else if (ch == '-') - { - delay++; - if (delay > 5) delay = 5; - } - else if (ch == 'D') - { - do_cmd_html_dump(); - } - else break; - } - if (stop) break; - } - - /* Next */ - continue; - } - - /* Get color for the NEXT L line */ - if (buf[0] == 'E') - { - /* Find the colon before the name */ - s = strchr(buf + 2, ':'); - - /* Verify that colon */ - if (!s) return ( -2); - - /* Nuke the colon, advance to the name */ - *s++ = '\0'; - - /* Paranoia -- require a name */ - if (!*s) return ( -2); - - /* Get the index */ - y = atoi(buf + 2); - - C_COPY(cbuf, s, 80, char); - - /* Next... */ - continue; - } - - /* Print a line */ - if (buf[0] == 'L') - { - /* Find the colon before the name */ - s = strchr(buf + 2, ':'); - - /* Verify that colon */ - if (!s) return ( -2); - - /* Nuke the colon, advance to the name */ - *s++ = '\0'; - - /* Paranoia -- require a name */ - if (!*s) return ( -2); - - /* Get the index */ - y = atoi(buf + 2); - - for (x = 0; x < 80; x++) - { - Term_putch(x, y, color_char_to_attr(cbuf[x]), s[x]); - - /* Reinit to white */ - cbuf[x] = 'w'; - } - Term_redraw_section(0, y, 79, y); - - /* Next... */ - continue; - } - - /* Update 1 char */ - if (buf[0] == 'P') - { - int x, y, a, c; - - /* Scan for the values */ - if (4 != sscanf(buf + 2, "%d:%d:%d:%d", - &x, &y, &c, &a)) - { - a = 'w'; - if (3 != sscanf(buf + 2, "%d:%d:%d", - &x, &y, &c)) return ( -2); - } - - Term_putch(x, y, color_char_to_attr(cbuf[x]), c); - Term_redraw_section(x, y, x + 1, y + 1); - - /* Next... */ - continue; - } - } - - /* Load screen */ - Term_load(); - character_icky = FALSE; - - /* Close */ - my_fclose(fff); - - return (0); -} - - -/* - * Start the recording of a cmovie - */ -void do_record_cmovie(cptr cmovie) -{ - char buf[1024]; - int fd = -1; - int y; - - - /* Build the filename */ - path_build(buf, 1024, ANGBAND_DIR_CMOV, cmovie); - - /* File type is "TEXT" */ - FILE_TYPE(FILE_TYPE_TEXT); - - /* Check for existing file */ - fd = fd_open(buf, O_RDONLY); - - /* Existing file */ - if (fd >= 0) - { - char out_val[160]; - - /* Close the file */ - (void)fd_close(fd); - - /* Build query */ - (void)sprintf(out_val, "Replace existing file %s? ", cmovie); - - /* Ask */ - if (get_check(out_val)) fd = -1; - } - - /* Be sure */ - if (!get_check("Ready to record(Press ctrl+D to enter a textual note while recording)?")) return; - - /* Open the non-existing file */ - if (fd < 0) movfile = my_fopen(buf, "w"); - - /* Invalid file */ - if (movfile == NULL) - { - msg_format("Cmovie recording failed!"); - - return; - } - - /* First thing: Record clear screen then enable the recording */ - fprintf(movfile, "# Generated by %s\n", - get_version_string()); - fprintf(movfile, "C:\n"); - last_paused = 0; - do_movies = 1; - cmovie_init_second(); - - /* Mega Hack, get the screen */ - for (y = 0; y < Term->hgt; y++) - { - cmovie_record_line(y); - } -} - - -/* - * Stop the recording - */ -void do_stop_cmovie() -{ - if (do_movies == 1) - { - do_movies = 0; - my_fclose(movfile); - } -} - - -/* - * Start a cmovie - */ -void do_start_cmovie() -{ - char name[90], rname[94]; - - - /* Should never happen */ - if (do_movies == 1) return; - - /* Default */ - sprintf(name, "%s", player_base); - - if (get_string("Cmovie name: ", name, 80)) - { - if (name[0] && (name[0] != ' ')) - { - sprintf(rname, "%s.cmv", name); - - if (get_check("Record(y), Play(n)?")) do_record_cmovie(rname); - else do_play_cmovie(rname); - } - } -} - - -void cmovie_clean_line(int y, char *abuf, char *cbuf) -{ - const byte *ap = Term->scr->a[y]; - const char *cp = Term->scr->c[y]; - - byte a; - char c; - - int x; - int wid, hgt; - int screen_wid, screen_hgt; - - - /* Retrieve current screen size */ - Term_get_size(&wid, &hgt); - - /* Calculate the size of dungeon map area */ - screen_wid = wid - (COL_MAP + 1); - screen_hgt = hgt - (ROW_MAP + 1); - - /* For the time being, assume 80 column display XXX XXX XXX */ - for (x = 0; x < wid; x++) - { - /* Convert dungeon map into default attr/chars */ - if (!character_icky && - ((x - COL_MAP) >= 0) && - ((x - COL_MAP) < screen_wid) && - ((y - ROW_MAP) >= 0) && - ((y - ROW_MAP) < screen_hgt)) - { - /* Retrieve default attr/char */ - map_info_default(y + panel_row_prt, x + panel_col_prt, &a, &c); - - abuf[x] = conv_color[a & 0xf]; - - if (c == '\0') cbuf[x] = ' '; - else cbuf[x] = c; - } - - else - { - abuf[x] = conv_color[ap[x] & 0xf]; - cbuf[x] = cp[x]; - } - } - - /* Null-terminate the prepared strings */ - abuf[x] = '\0'; - cbuf[x] = '\0'; -} - - -/* - * Write a record of a screen row into a cmovie file - */ -void cmovie_record_line(int y) -{ - char abuf[256]; - char cbuf[256]; - - cmovie_clean_line(y, abuf, cbuf); - - /* Write a colour record */ - fprintf(movfile, "E:%d:%.80s\n", y, abuf); - - /* Write a char record */ - fprintf(movfile, "L:%d:%.80s\n", y, cbuf); -} - - -/* - * Record a "text box" - */ -void do_cmovie_insert() -{ - char buf[81] = ""; - - /* Dont record */ - do_movies = 2; - - while (get_string("Textbox(ESC to end): ", buf, 80)) - { - fprintf(movfile, "B:%s\nW:\n", buf); - buf[0] = '\0'; - } - - /* We reinit the time as to not count the time the user needed ot enter the text */ - cmovie_init_second(); - - /* Continue recording */ - do_movies = 1; -} diff --git a/src/cmovie.cc b/src/cmovie.cc new file mode 100644 index 00000000..85db2a32 --- /dev/null +++ b/src/cmovie.cc @@ -0,0 +1,500 @@ +/* File: cmovie.c */ + +/* Purpose: play cmovie files -DarkGod-Improv- */ + +#include "angband.h" + +/* + * Play a given cmovie + */ +s16b do_play_cmovie(cptr cmov_file) +{ + FILE *fff; + + int y, line = 0, x; + int delay; + + char *s; + + char buf[1024]; + char cbuf[90]; + char ch; + + char mode = 0; + + + /* Cmovie files are moved to the user directory on the multiuser systems */ + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_CMOV, cmov_file); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Read the file */ + fff = my_fopen(buf, "r"); + + /* Failure */ + if (!fff) return ( -1); + + /* Save screen */ + character_icky = TRUE; + Term_save(); + Term_clear(); + + /* Give some usefull info */ + prt("While viewing the movie you can press Escape to exit, t/Space to switch between", 0, 0); + prt("fluid more and step by step mode and any other key to step a frame in step by", 1, 0); + prt("step mode.", 2, 0); + prt("You can press D to do an html screenshot of the current frame.", 3, 0); + prt("You can also use + and - to speed up/down the playing speed.", 5, 0); + prt("Press any key when ready.", 8, 0); + + inkey(); + + Term_clear(); + + line = -1; + + delay = 1; + + /* Init to white */ + for (x = 0; x < 80; x++) + { + cbuf[x] = 'w'; + } + + /* Parse */ + while (0 == my_fgets(fff, buf, 1024)) + { + /* Do not wait */ + inkey_scan = TRUE; + ch = inkey(); + + /* Stop */ + if (ch == ESCAPE) break; + + /* Change mode */ + else if (ch == 't') + { + mode = FALSE; + } + else if (ch == ' ') + { + mode = TRUE; + } + + /* Change speed */ + else if (ch == '+') + { + delay--; + if (delay < 0) delay = 0; + } + else if (ch == '-') + { + delay++; + if (delay > 5) delay = 5; + } + else if (ch == 'D') + { + do_cmd_html_dump(); + } + + line++; + + /* Skip comments and blank lines */ + if (!buf[0] || (buf[0] == '#')) continue; + + /* Verify correct "colon" format */ + if (buf[1] != ':') break; + + /* Clean screen */ + if (buf[0] == 'C') + { + Term_clear(); + + /* Next */ + continue; + } + + /* Displays a textbox */ + if (buf[0] == 'B') + { + int len = strlen(buf + 2); + + /* Clear the line */ + Term_erase(0, 0, 255); + + /* Display the message */ + c_put_str(TERM_VIOLET, "###", 0, 0); + c_put_str(TERM_ORANGE, buf + 2, 0, 3); + c_put_str(TERM_VIOLET, "###", 0, 3 + len); + c_put_str(TERM_WHITE, "(more)", 0, 6 + len); + + /* Next */ + continue; + } + + /* Wait a key */ + if (buf[0] == 'W') + { + inkey(); + + /* Next */ + continue; + } + + /* Sleep */ + if (buf[0] == 'S') + { + long msec; + + /* Scan for the values */ + if (1 != sscanf(buf + 2, "%ld:", &msec)) + { + return ( -2); + } + + if (!mode) + { + Term_xtra(TERM_XTRA_DELAY, msec); + } + else + { + bool_ stop = FALSE; + + while (TRUE) + { + ch = inkey(); + + /* Stop */ + if (ch == ESCAPE) + { + stop = TRUE; + break; + } + /* Change mode */ + else if (ch == 't') + { + mode = FALSE; + break; + } + /* Change mode */ + else if (ch == ' ') + { + if (mode) continue; + mode = TRUE; + break; + } + /* Change speed */ + else if (ch == '+') + { + delay--; + if (delay < 0) delay = 0; + } + else if (ch == '-') + { + delay++; + if (delay > 5) delay = 5; + } + else if (ch == 'D') + { + do_cmd_html_dump(); + } + else break; + } + if (stop) break; + } + + /* Next */ + continue; + } + + /* Get color for the NEXT L line */ + if (buf[0] == 'E') + { + /* Find the colon before the name */ + s = strchr(buf + 2, ':'); + + /* Verify that colon */ + if (!s) return ( -2); + + /* Nuke the colon, advance to the name */ + *s++ = '\0'; + + /* Paranoia -- require a name */ + if (!*s) return ( -2); + + /* Get the index */ + y = atoi(buf + 2); + + /* Copy */ + for (int i = 0; i < 80; i++) + { + cbuf[i] = s[i]; + } + + /* Next... */ + continue; + } + + /* Print a line */ + if (buf[0] == 'L') + { + /* Find the colon before the name */ + s = strchr(buf + 2, ':'); + + /* Verify that colon */ + if (!s) return ( -2); + + /* Nuke the colon, advance to the name */ + *s++ = '\0'; + + /* Paranoia -- require a name */ + if (!*s) return ( -2); + + /* Get the index */ + y = atoi(buf + 2); + + for (x = 0; x < 80; x++) + { + Term_putch(x, y, color_char_to_attr(cbuf[x]), s[x]); + + /* Reinit to white */ + cbuf[x] = 'w'; + } + Term_redraw_section(0, y, 79, y); + + /* Next... */ + continue; + } + + /* Update 1 char */ + if (buf[0] == 'P') + { + int x, y, a, c; + + /* Scan for the values */ + if (4 != sscanf(buf + 2, "%d:%d:%d:%d", + &x, &y, &c, &a)) + { + a = 'w'; + if (3 != sscanf(buf + 2, "%d:%d:%d", + &x, &y, &c)) return ( -2); + } + + Term_putch(x, y, color_char_to_attr(cbuf[x]), c); + Term_redraw_section(x, y, x + 1, y + 1); + + /* Next... */ + continue; + } + } + + /* Load screen */ + Term_load(); + character_icky = FALSE; + + /* Close */ + my_fclose(fff); + + return (0); +} + + +/* + * Start the recording of a cmovie + */ +void do_record_cmovie(cptr cmovie) +{ + char buf[1024]; + int fd = -1; + int y; + + + /* Build the filename */ + path_build(buf, 1024, ANGBAND_DIR_CMOV, cmovie); + + /* File type is "TEXT" */ + FILE_TYPE(FILE_TYPE_TEXT); + + /* Check for existing file */ + fd = fd_open(buf, O_RDONLY); + + /* Existing file */ + if (fd >= 0) + { + char out_val[160]; + + /* Close the file */ + (void)fd_close(fd); + + /* Build query */ + (void)sprintf(out_val, "Replace existing file %s? ", cmovie); + + /* Ask */ + if (get_check(out_val)) fd = -1; + } + + /* Be sure */ + if (!get_check("Ready to record(Press ctrl+D to enter a textual note while recording)?")) return; + + /* Open the non-existing file */ + if (fd < 0) movfile = my_fopen(buf, "w"); + + /* Invalid file */ + if (movfile == NULL) + { + msg_format("Cmovie recording failed!"); + + return; + } + + /* First thing: Record clear screen then enable the recording */ + fprintf(movfile, "# Generated by %s\n", + get_version_string()); + fprintf(movfile, "C:\n"); + last_paused = 0; + do_movies = 1; + cmovie_init_second(); + + /* Mega Hack, get the screen */ + for (y = 0; y < Term->hgt; y++) + { + cmovie_record_line(y); + } +} + + +/* + * Stop the recording + */ +void do_stop_cmovie() +{ + if (do_movies == 1) + { + do_movies = 0; + my_fclose(movfile); + } +} + + +/* + * Start a cmovie + */ +void do_start_cmovie() +{ + char name[90], rname[94]; + + + /* Should never happen */ + if (do_movies == 1) return; + + /* Default */ + sprintf(name, "%s", player_base); + + if (get_string("Cmovie name: ", name, 80)) + { + if (name[0] && (name[0] != ' ')) + { + sprintf(rname, "%s.cmv", name); + + if (get_check("Record(y), Play(n)?")) do_record_cmovie(rname); + else do_play_cmovie(rname); + } + } +} + + +void cmovie_clean_line(int y, char *abuf, char *cbuf) +{ + const byte *ap = Term->scr->a[y]; + const char *cp = Term->scr->c[y]; + + byte a; + char c; + + int x; + int wid, hgt; + int screen_wid, screen_hgt; + + + /* Retrieve current screen size */ + Term_get_size(&wid, &hgt); + + /* Calculate the size of dungeon map area */ + screen_wid = wid - (COL_MAP + 1); + screen_hgt = hgt - (ROW_MAP + 1); + + /* For the time being, assume 80 column display XXX XXX XXX */ + for (x = 0; x < wid; x++) + { + /* Convert dungeon map into default attr/chars */ + if (!character_icky && + ((x - COL_MAP) >= 0) && + ((x - COL_MAP) < screen_wid) && + ((y - ROW_MAP) >= 0) && + ((y - ROW_MAP) < screen_hgt)) + { + /* Retrieve default attr/char */ + map_info_default(y + panel_row_prt, x + panel_col_prt, &a, &c); + + abuf[x] = conv_color[a & 0xf]; + + if (c == '\0') cbuf[x] = ' '; + else cbuf[x] = c; + } + + else + { + abuf[x] = conv_color[ap[x] & 0xf]; + cbuf[x] = cp[x]; + } + } + + /* Null-terminate the prepared strings */ + abuf[x] = '\0'; + cbuf[x] = '\0'; +} + + +/* + * Write a record of a screen row into a cmovie file + */ +void cmovie_record_line(int y) +{ + char abuf[256]; + char cbuf[256]; + + cmovie_clean_line(y, abuf, cbuf); + + /* Write a colour record */ + fprintf(movfile, "E:%d:%.80s\n", y, abuf); + + /* Write a char record */ + fprintf(movfile, "L:%d:%.80s\n", y, cbuf); +} + + +/* + * Record a "text box" + */ +void do_cmovie_insert() +{ + char buf[81] = ""; + + /* Dont record */ + do_movies = 2; + + while (get_string("Textbox(ESC to end): ", buf, 80)) + { + fprintf(movfile, "B:%s\nW:\n", buf); + buf[0] = '\0'; + } + + /* We reinit the time as to not count the time the user needed ot enter the text */ + cmovie_init_second(); + + /* Continue recording */ + do_movies = 1; +} diff --git a/src/corrupt.c b/src/corrupt.c deleted file mode 100644 index a4c579d4..00000000 --- a/src/corrupt.c +++ /dev/null @@ -1,973 +0,0 @@ -#include "angband.h" -#include - -/** - * Vampire corruption helpers - */ - -static void subrace_add_power(player_race_mod *rmp_ptr, int power) -{ - int i; - - for (i=0; i<4; i++) - { - if (rmp_ptr->powers[i] == -1) - { - rmp_ptr->powers[i] = power; - return; - } - } -} - -static void player_gain_vampire_teeth() -{ - player_race_mod *rmp_ptr = NULL; - - switch_subrace(SUBRACE_SAVE, TRUE); - - rmp_ptr = &race_mod_info[SUBRACE_SAVE]; - subrace_add_power(rmp_ptr, PWR_VAMPIRISM); - rmp_ptr->flags1 = rmp_ptr->flags1 - | PR1_VAMPIRE - | PR1_UNDEAD - | PR1_NO_SUBRACE_CHANGE; -} - -static void player_gain_vampire_strength() -{ - player_race_mod *rmp_ptr = &race_mod_info[SUBRACE_SAVE]; - /* Apply the bonuses/penalities */ - rmp_ptr->r_mhp = rmp_ptr->r_mhp + 1; - rmp_ptr->r_exp = rmp_ptr->r_exp + 100; - - rmp_ptr->r_adj[A_STR] = rmp_ptr->r_adj[A_STR] + 3; - rmp_ptr->r_adj[A_INT] = rmp_ptr->r_adj[A_INT] + 2; - rmp_ptr->r_adj[A_WIS] = rmp_ptr->r_adj[A_WIS] - 3; - rmp_ptr->r_adj[A_DEX] = rmp_ptr->r_adj[A_DEX] - 2; - rmp_ptr->r_adj[A_CON] = rmp_ptr->r_adj[A_CON] + 1; - rmp_ptr->r_adj[A_CHR] = rmp_ptr->r_adj[A_CHR] - 4; - - /* be reborn! */ - do_rebirth(); - cmsg_print(TERM_L_DARK, "You feel death slipping inside."); -} - -static void player_gain_vampire() -{ - player_race_mod *rmp_ptr = &race_mod_info[SUBRACE_SAVE]; - - /* Be a Vampire and be proud of it */ - cptr title = get_subrace_title(SUBRACE_SAVE); - if (streq(title, " ") || streq(title, "Vampire")) - { - title = "Vampire"; - rmp_ptr->place = FALSE; - set_subrace_title(SUBRACE_SAVE, title); - } - else - { - char buf[512]; - sprintf(buf, "Vampire %s", title); - set_subrace_title(SUBRACE_SAVE, buf); - } - - /* Bonus/and .. not bonus :) */ - rmp_ptr->flags1 = rmp_ptr->flags1 | PR1_HURT_LITE; - rmp_ptr->oflags2[2] = rmp_ptr->oflags2[2] - | TR2_RES_POIS - | TR2_RES_NETHER - | TR2_RES_COLD - | TR2_RES_DARK - | TR2_HOLD_LIFE; - rmp_ptr->oflags3[2] = rmp_ptr->oflags3[2] - | TR3_LITE1; -} - -/** - * Corruptions - */ -corruption_type corruptions[CORRUPTIONS_MAX] = -{ - /* - * BALROG corruptions - */ - - { /* 0 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_ORANGE, - NULL /* no group */, - "Balrog Aura", - "A corrupted wall of flames surrounds you.", - "The wall of corrupted flames abandons you.", - " Surrounds you with a fiery aura\n" - " But it can burn scrolls when you read them", - { -1 }, - { -1 }, - NULL, - -1, - }, - - { /* 1 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_ORANGE, - NULL /* no group */, - "Balrog Wings", - "Wings of shadow grow in your back.", - "The wings in your back fall apart.", - " Creates ugly, but working, wings allowing you to fly\n" - " But it reduces charisma by 4 and dexterity by 2", - { -1 }, - { -1 }, - NULL, - -1, - }, - - { /* 2 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_ORANGE, - NULL /* no group */, - "Balrog Strength", - "Your muscles get unnatural strength.", - "Your muscles get weaker again.", - " Provides 3 strength and 1 constitution\n" - " But it reduces charisma by 1 and dexterity by 3", - { -1 }, - { -1 }, - NULL, - -1, - }, - - { /* 3 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_YELLOW, - NULL /* no group */, - "Balrog Form", - "You feel the might of a Balrog inside you.", - "The presence of the Balrog seems to abandon you.", - " Allows you to turn into a Balrog at will\n" - " You need Balrog Wings, Balrog Aura and Balrog Strength to activate it", - { CORRUPT_BALROG_AURA, CORRUPT_BALROG_WINGS, CORRUPT_BALROG_STRENGTH }, - { -1 }, - NULL, - PWR_BALROG, - }, - - /* - * DEMON corruptions - */ - { /* 4 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Demon Spirit", - "Your spirit opens to corrupted thoughts.", - "Your spirit closes again to the corrupted thoughts.", - " Increases your intelligence by 1\n" - " But reduce your charisma by 2", - { -1 }, - { -1 }, - NULL, - -1, - }, - - { /* 5 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Demon Hide", - "Your skin grows into a thick hide.", - "Your skin returns to a natural state.", - " Increases your armour class by your level\n" - " Provides immunity to fire at level 40\n" - " But reduces speed by your level / 7", - { -1 }, - { -1 }, - NULL, - -1, - }, - - { /* 6 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Demon Breath", - "Your breath becomes mephitic.", - "Your breath is once again normal.", - " Provides fire breath\n" - " But gives a small chance to spoil potions when you quaff them", - { -1 }, - { -1 }, - NULL, - PWR_BR_FIRE, - }, - - { /* 7 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_L_RED, - NULL /* no group */, - "Demon Realm", - "You feel more attuned to the demon realm.", - "You lose your attunement to the demon realm.", - " Provides access to the demon school skill and the use of demonic equipment\n" - " You need Demon Spirit, Demon Hide and Demon Breath to activate it", - { CORRUPT_DEMON_SPIRIT, CORRUPT_DEMON_HIDE, CORRUPT_DEMON_BREATH }, - { -1 }, - NULL, - -1, - }, - - /* - * Teleportation corruptions - */ - - { /* 8 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_GREEN, - NULL /* no group */, - "Random teleportation", - "Space seems to fizzle around you.", - "Space solidify again around you.", - " Randomly teleports you around", - { -1 }, - { CORRUPT_ANTI_TELEPORT, -1 }, - NULL, - -1, - }, - - { /* 9 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_GREEN, - NULL /* no group */, - "Anti-teleportation", - "Space continuum freezes around you.", - "Space continuum can once more be altered around you.", - " Prevents all teleportations, be it of you or monsters", - { -1 }, - { CORRUPT_RANDOM_TELEPORT, -1 }, - NULL, - POWER_COR_SPACE_TIME, - }, - - /* - * Troll blood - */ - - { /* 10 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_GREEN, - NULL /* no group */, - "Troll Blood", - "Your blood thickens, you sense corruption in it.", - "Your blood returns to a normal state.", - " Troll blood flows in your veins, granting increased regeneration\n" - " It also enables you to feel the presence of other troll beings\n" - " But it will make your presence more noticeable and aggravating", - { -1 }, - { -1 }, - NULL, - -1, - }, - - /* - * The vampire corruption set - */ - - { /* 11 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_L_DARK, - "Vampire", - "Vampiric Teeth", - "You grow vampiric teeth!", - NULL, /* cannot lose */ - " Your teeth allow you to drain blood to feed yourself\n" - " However your stomach now only accepts blood.", - { -1 }, - { -1 }, - player_gain_vampire_teeth, - -1, - }, - - { /* 12 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_L_DARK, - "Vampire", - "Vampiric Strength", - "Your body seems more dead than alive.", - NULL, /* cannot lose */ - " Your body seems somewhat dead\n" - " In this near undead state it has improved strength, constitution and intelligence\n" - " But reduced dexterity, wisdom and charisma.", - { CORRUPT_VAMPIRE_TEETH, -1 }, - { -1 }, - player_gain_vampire_strength, - -1, - }, - - { /* 13 */ - { MODULE_TOME, MODULE_THEME, -1 }, - TERM_L_DARK, - "Vampire", - "Vampire", - "You die to be reborn in a Vampire form.", - NULL, /* cannot lose */ - " You are a Vampire. As such you resist cold, poison, darkness and nether.\n" - " Your life is sustained, but you cannot stand the light of the sun.", - { CORRUPT_VAMPIRE_STRENGTH, -1 }, - { -1 }, - player_gain_vampire, - -1, - }, - - /* - * Activatable corruptions (mutations) - */ - - { /* 14 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Ancalagon's Breath", - "You gain the ability to spit acid.", - "You lose the ability to spit acid.", - " Fires an acid ball.\n" - " Damage=level Radius 1+(level/30)\n" - " Level=9, Cost=9, Stat=DEX, Difficulty=15", - { -1 }, - { -1 }, - NULL, - PWR_SPIT_ACID, - }, - - { /* 15 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Smaug's Breath", - "You gain the ability to breathe fire.", - "You lose the ability to breathe fire.", - " Fires a fire ball.\n" - " Damage=2*level Radius 1+(level/20)\n" - " Level=20, Cost=10, Stat=CON, Difficulty=18", - { -1 }, - { -1 }, - NULL, - PWR_BR_FIRE, - }, - - { /* 16 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Glaurung's Gaze", - "Your eyes look mesmerizing...", - "Your eyes look uninteresting.", - " Tries to make a monster your pet.\n" - " Power=level\n" - " Level=12, Cost=12, Stat=CHR, Difficulty=18", - { -1 }, - { -1 }, - NULL, - PWR_HYPN_GAZE, - }, - - { /* 17 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Saruman's Power", - "You gain the ability to move objects telekinetically.", - "You lose the ability to move objects telekinetically.", - " Move an object in line of sight to you.\n" - " Max weight equal to (level) pounds\n" - " Level=9, Cost=9, Stat=WIS, Difficulty=14", - { -1 }, - { -1 }, - NULL, - PWR_TELEKINES, - }, - - { /* 18 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Teleport", - "You gain the power of teleportation at will.", - "You lose the power of teleportation at will.", - " Teleports the player at will.\n" - " Distance 10+4*level squares\n" - " Level=7, Cost=7, Stat=WIS, Difficulty=15", - { -1 }, - { -1 }, - NULL, - PWR_VTELEPORT, - }, - - { /* 19 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Glaurung's Spell", - "You gain the power of Mind Blast.", - "You lose the power of Mind Blast.", - " Fires a mind blasting bolt (psi damage).\n" - " Psi Damage (3+(level-1)/5)d3\n" - " Level=5, Cost=3, Stat=WIS, Difficulty=15", - { -1 }, - { -1 }, - NULL, - PWR_MIND_BLST, - }, - - { /* 20 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Vampiric Drain", - "You become vampiric.", - "You are no longer vampiric.", - " You can drain life from a foe like a vampire.\n" - " Drains (level+1d(level))*(level/10) hitpoints,\n" - " heals you and satiates you. Doesn't work on all monsters\n" - " Level=4, Cost=5, Stat=CON, Difficulty=9", - { -1 }, - { -1 }, - NULL, - PWR_VAMPIRISM, - }, - - { /* 21 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Carcharoth's Nose", - "You smell a metallic odour.", - "You no longer smell a metallic odour.", - " You can detect nearby precious metal (treasure).\n" - " Radius 25\n" - " Level=3, Cost=2, Stat=INT, Difficulty=12", - { -1 }, - { -1 }, - NULL, - PWR_SMELL_MET, - }, - - { /* 22 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Huan's Nose", - "You smell filthy monsters.", - "You no longer smell filthy monsters.", - " You can detect nearby monsters.\n" - " Radius 25\n" - " Level=5, Cost=4, Stat=INT, Difficulty=15", - { -1 }, - { -1 }, - NULL, - PWR_SMELL_MON, - }, - - { /* 23 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Blink", - "You gain the power of minor teleportation.", - "You lose the power of minor teleportation.", - " You can teleport yourself short distances (10 squares).\n" - " Level=3, Cost=3, Stat=WIS, Difficulty=12", - { -1 }, - { -1 }, - NULL, - PWR_BLINK, - }, - - { /* 24 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Eat Rock", - "The walls look delicious.", - "The walls look unappetizing.", - " You can consume solid rock with food benefit,\n" - " leaving an empty space behind.\n" - " Level=8, Cost=12, Stat=CON, Difficulty=18", - { -1 }, - { -1 }, - NULL, - PWR_EAT_ROCK, - }, - - { /* 25 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Swap Position", - "You feel like walking a mile in someone else's shoes.", - "You feel like staying in your own shoes.", - " You can switch locations with another being,\n" - " unless it resists teleportation.\n" - " Level=15, Cost=12, Stat=DEX, Difficulty=16", - { -1 }, - { -1 }, - NULL, - PWR_SWAP_POS, - }, - - { /* 26 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Shriek", - "Your vocal cords get much tougher.", - "Your vocal cords get much weaker.", - " Fires a sound ball and aggravates monsters.\n" - " Damage=level*4, Radius=8, centered on player\n" - " Level=4, Cost=4, Stat=CON, Difficulty=6", - { -1 }, - { -1 }, - NULL, - PWR_SHRIEK, - }, - - { /* 27 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Illuminate", - "You can light up rooms with your presence.", - "You can no longer light up rooms with your presence.", - " You can emit bright light that illuminates an area.\n" - " Damage=2d(level/2) Radius=(level/10)+1\n" - " Level=3, Cost=2, Stat=INT, Difficulty=10", - { -1 }, - { -1 }, - NULL, - PWR_ILLUMINE, - }, - - { /* 28 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Detect Curses", - "You can feel evil magics.", - "You can no longer feel evil magics.", - " You can feel the danger of evil magic.\n" - " It detects cursed items in the inventory\n" - " Level=7, Cost=14, Stat=WIS, Difficulty=14", - { -1 }, - { -1 }, - NULL, - PWR_DET_CURSE, - }, - - { /* 29 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Berserk", - "You feel a controlled rage.", - "You no longer feel a controlled rage.", - " You can drive yourself into a berserk frenzy.\n" - " It grants super-heroism. Duration=10+1d(level)\n" - " Level=8, Cost=8, Stat=STR, Difficulty=14", - { -1 }, - { -1 }, - NULL, - PWR_BERSERK, - }, - - { /* 30 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Midas touch", - "You gain the Midas touch.", - "You lose the Midas touch.", - " You can turn ordinary items to gold.\n" - " Turns a non-artifact object into 1/3 its value in gold\n" - " Level=10, Cost=5, Stat=INT, Difficulty=12", - { -1 }, - { -1 }, - NULL, - PWR_MIDAS_TCH, - }, - - { /* 31 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Grow Mold", - "You feel a sudden affinity for mold.", - "You feel a sudden dislike for mold.", - " You can cause mold to grow near you.\n" - " Summons up to 8 molds around the player\n" - " Level=1, Cost=6, Stat=CON, Difficulty=14", - { -1 }, - { -1 }, - NULL, - PWR_GROW_MOLD, - }, - - { /* 32 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Resist Elements", - "You feel like you can protect yourself.", - "You feel like you might be vulnerable.", - " You can harden yourself to the ravages of the elements.\n" - " Level-dependent chance of gaining resistances to the four \n" - " elements and poison. Duration=20 + d20\n" - " Level=10, Cost=12, Stat=CON, Difficulty=12", - { -1 }, - { -1 }, - NULL, - PWR_RESIST, - }, - - { /* 33 */ - { MODULE_THEME, -1 }, - TERM_RED, - NULL /* no group */, - "Earthquake", - "You gain the ability to wreck the dungeon.", - "You lose the ability to wreck the dungeon.", - " You can bring down the dungeon around your ears.\n" - " Radius=10, center on the player\n" - " Level=12, Cost=12, Stat=STR, Difficulty=16", - { -1 }, - { -1 }, - NULL, - PWR_EARTHQUAKE, - }, - -}; - -/** - * Initialize corruptions - */ -void init_corruptions() -{ - /* Nothing needed currently */ -} - -/* - * Corruptions - */ -bool_ player_has_corruption(int corruption_idx) -{ - if (corruption_idx < 0) - { - return FALSE; - } - - return (p_ptr->corruptions[corruption_idx]); -} - -static bool_ player_can_gain_corruption(int corruption_idx) -{ - cptr r_name = rp_ptr->title + rp_name; - bool_ allowed = TRUE; /* Allowed by default */ - - assert(corruption_idx >= 0); - - if (corruption_idx == CORRUPT_TROLL_BLOOD) - { - /* Ok trolls should not get this one. never. */ - if (streq(r_name, "Troll")) - { - allowed = FALSE; - } - } - - /* Theme module adds additional restrictions for Maiar */ - - if (game_module_idx == MODULE_THEME) - { - if (streq(r_name, "Maia")) - { - /* We use a whitelist of corruptions for Maiar */ - bool_ allow = FALSE; - if ((corruption_idx == CORRUPT_BALROG_AURA) || - (corruption_idx == CORRUPT_BALROG_WINGS) || - (corruption_idx == CORRUPT_BALROG_STRENGTH) || - (corruption_idx == CORRUPT_BALROG_FORM) || - (corruption_idx == CORRUPT_DEMON_BREATH)) - { - allow = TRUE; - }; - - /* Mix result into 'allowed' flag */ - allowed = allowed & allow; - } - } - - /* Result */ - return allowed; -} - -static bool_ player_allow_corruption(int corruption_idx) -{ - int i; - bool_ found = FALSE; - corruption_type *c_ptr = NULL; - - assert(corruption_idx < CORRUPTIONS_MAX); - c_ptr = &corruptions[corruption_idx]; - - /* Must be allowed by module */ - for (i = 0; c_ptr->modules[i] >= 0; i++) - { - if (c_ptr->modules[i] == game_module_idx) - { - found = TRUE; - } - } - - if (!found) - { - return FALSE; - } - - /* Vampire teeth is special */ - if (corruption_idx == CORRUPT_VAMPIRE_TEETH) - { - if (PRACE_FLAG(PR1_NO_SUBRACE_CHANGE)) - { - return TRUE; - } - else - { - return FALSE; - } - } - - return TRUE; -} - -static void player_set_corruption(int c, bool_ set) -{ - p_ptr->corruptions[c] = set; - p_ptr->redraw = p_ptr->redraw | PR_BASIC; - p_ptr->update = p_ptr->update | PU_BONUS | PU_TORCH | PU_BODY | PU_POWERS; - -} - -void player_gain_corruption(int corruption_idx) -{ - corruption_type *c_ptr = NULL; - assert(corruption_idx >= 0); - assert(corruption_idx < CORRUPTIONS_MAX); - c_ptr = &corruptions[corruption_idx]; - - /* Set the player's corruption flag */ - player_set_corruption(corruption_idx, TRUE); - - /* Invoke callback if necessary */ - if (c_ptr->gain_callback) - { - c_ptr->gain_callback(); - } -} - -static void player_lose_corruption(int corruption_idx) -{ - assert(corruption_idx >= 0); - assert(corruption_idx < CORRUPTIONS_MAX); - - player_set_corruption(corruption_idx, FALSE); - - /* Currently no corruptions need any special handling when lost */ -} - -/* - * Test if we have that corruption - * We must: - * 1) have it or be willing to get it - * 2) have all its dependancies - * 3) have none of its opposing corruptions - * 4) pass the possible tests - */ -static bool_ test_depend_corrupt(s16b corrupt_idx, bool_ can_gain) -{ - s16b i; - corruption_type *c_ptr = NULL; - - assert(corrupt_idx >= 0); - assert(corrupt_idx < CORRUPTIONS_MAX); - - c_ptr = &corruptions[corrupt_idx]; - - if (can_gain) - { - if (p_ptr->corruptions[corrupt_idx]) - { - return FALSE; - } - } else { - if (!p_ptr->corruptions[corrupt_idx]) - { - return FALSE; - } - } - - /* Go through all dependencies */ - for (i=0; c_ptr->depends[i] >= 0; i++) - { - if (!test_depend_corrupt(c_ptr->depends[i], FALSE)) - { - return FALSE; - } - } - - /* Go through all opposers */ - for (i=0; c_ptr->opposes[i] >= 0; i++) - { - if (test_depend_corrupt(c_ptr->opposes[i], FALSE)) - { - return FALSE; - } - } - - /* are we even allowed to get it? */ - return player_can_gain_corruption(corrupt_idx); -} - -void gain_random_corruption() -{ - s16b i, max; - s16b pos[CORRUPTIONS_MAX]; - - /* Get the list of all possible ones */ - max = 0; - for (i=0; i < CORRUPTIONS_MAX; i++) - { - if (test_depend_corrupt(i, TRUE) && - player_allow_corruption(i)) - { - pos[max] = i; - max = max + 1; - } - } - - /* Ok now get one of them */ - if (max > 0) - { - s16b ret = rand_int(max); - int c_idx = pos[ret]; - assert(c_idx < CORRUPTIONS_MAX); - - player_gain_corruption(c_idx); - cmsg_print(TERM_L_RED, corruptions[c_idx].get_text); - } -} - -static void remove_corruption(int c_idx) -{ - assert(c_idx >= 0 && c_idx < CORRUPTIONS_MAX); - assert(corruptions[c_idx].lose_text); - - player_lose_corruption(c_idx); - cmsg_print(TERM_L_RED, corruptions[c_idx].lose_text); -} - -void lose_corruption() -{ - s16b i, max; - s16b pos[CORRUPTIONS_MAX]; - - /* Get the list of all possible ones */ - max = 0; - for (i = 0; i < CORRUPTIONS_MAX; i++) - { - bool_ is_removable = (corruptions[i].lose_text != NULL); - if (test_depend_corrupt(i, FALSE) && is_removable) - { - pos[max] = i; - max = max + 1; - } - } - - /* Ok now get one of them */ - if (max > 0) - { - s16b ret = rand_int(max); - int c_idx = pos[ret]; - - /* Remove the corruption */ - remove_corruption(c_idx); - - /* Ok now lets see if it broke some dependencies */ - for (i = 0; i < max - 1; i++) - { - if (p_ptr->corruptions[pos[i]] != test_depend_corrupt(pos[i], FALSE)) - { - remove_corruption(pos[i]); - } - } - } -} - - -/* - * Dump the corruption list - */ -void dump_corruptions(FILE *fff, bool_ color, bool_ header) -{ - int i; - - assert(fff != NULL); - - for (i = 0; i < CORRUPTIONS_MAX; i++) - { - corruption_type *c_ptr = &corruptions[i]; - - if (header) - { - fprintf(fff, "\n Corruption list:\n"); - header = FALSE; - } - - if (p_ptr->corruptions[i]) - { - byte c = c_ptr->color; - - if (color) - { - fprintf(fff, "#####%c%s:\n", conv_color[c], c_ptr->name); - } - else - { - fprintf(fff, "%s:\n", c_ptr->name); - } - - fprintf(fff, "%s\n", c_ptr->desc); - } - } -} - -/* - * Get the power granted by a corruption. Returns -1 - * if the given corruption does not grant a power. - */ -s16b get_corruption_power(int corruption_idx) -{ - corruption_type *c_ptr = NULL; - - assert(corruption_idx >= 0); - assert(corruption_idx < CORRUPTIONS_MAX); - - c_ptr = &corruptions[corruption_idx]; - - if ((c_ptr->power >= 0) && (c_ptr->power < POWER_MAX)) - { - return c_ptr->power; - } - else - { - assert(c_ptr->power == -1); /* Sanity check: Should always be the case. */ - return -1; - } -} diff --git a/src/corrupt.cc b/src/corrupt.cc new file mode 100644 index 00000000..a4c579d4 --- /dev/null +++ b/src/corrupt.cc @@ -0,0 +1,973 @@ +#include "angband.h" +#include + +/** + * Vampire corruption helpers + */ + +static void subrace_add_power(player_race_mod *rmp_ptr, int power) +{ + int i; + + for (i=0; i<4; i++) + { + if (rmp_ptr->powers[i] == -1) + { + rmp_ptr->powers[i] = power; + return; + } + } +} + +static void player_gain_vampire_teeth() +{ + player_race_mod *rmp_ptr = NULL; + + switch_subrace(SUBRACE_SAVE, TRUE); + + rmp_ptr = &race_mod_info[SUBRACE_SAVE]; + subrace_add_power(rmp_ptr, PWR_VAMPIRISM); + rmp_ptr->flags1 = rmp_ptr->flags1 + | PR1_VAMPIRE + | PR1_UNDEAD + | PR1_NO_SUBRACE_CHANGE; +} + +static void player_gain_vampire_strength() +{ + player_race_mod *rmp_ptr = &race_mod_info[SUBRACE_SAVE]; + /* Apply the bonuses/penalities */ + rmp_ptr->r_mhp = rmp_ptr->r_mhp + 1; + rmp_ptr->r_exp = rmp_ptr->r_exp + 100; + + rmp_ptr->r_adj[A_STR] = rmp_ptr->r_adj[A_STR] + 3; + rmp_ptr->r_adj[A_INT] = rmp_ptr->r_adj[A_INT] + 2; + rmp_ptr->r_adj[A_WIS] = rmp_ptr->r_adj[A_WIS] - 3; + rmp_ptr->r_adj[A_DEX] = rmp_ptr->r_adj[A_DEX] - 2; + rmp_ptr->r_adj[A_CON] = rmp_ptr->r_adj[A_CON] + 1; + rmp_ptr->r_adj[A_CHR] = rmp_ptr->r_adj[A_CHR] - 4; + + /* be reborn! */ + do_rebirth(); + cmsg_print(TERM_L_DARK, "You feel death slipping inside."); +} + +static void player_gain_vampire() +{ + player_race_mod *rmp_ptr = &race_mod_info[SUBRACE_SAVE]; + + /* Be a Vampire and be proud of it */ + cptr title = get_subrace_title(SUBRACE_SAVE); + if (streq(title, " ") || streq(title, "Vampire")) + { + title = "Vampire"; + rmp_ptr->place = FALSE; + set_subrace_title(SUBRACE_SAVE, title); + } + else + { + char buf[512]; + sprintf(buf, "Vampire %s", title); + set_subrace_title(SUBRACE_SAVE, buf); + } + + /* Bonus/and .. not bonus :) */ + rmp_ptr->flags1 = rmp_ptr->flags1 | PR1_HURT_LITE; + rmp_ptr->oflags2[2] = rmp_ptr->oflags2[2] + | TR2_RES_POIS + | TR2_RES_NETHER + | TR2_RES_COLD + | TR2_RES_DARK + | TR2_HOLD_LIFE; + rmp_ptr->oflags3[2] = rmp_ptr->oflags3[2] + | TR3_LITE1; +} + +/** + * Corruptions + */ +corruption_type corruptions[CORRUPTIONS_MAX] = +{ + /* + * BALROG corruptions + */ + + { /* 0 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_ORANGE, + NULL /* no group */, + "Balrog Aura", + "A corrupted wall of flames surrounds you.", + "The wall of corrupted flames abandons you.", + " Surrounds you with a fiery aura\n" + " But it can burn scrolls when you read them", + { -1 }, + { -1 }, + NULL, + -1, + }, + + { /* 1 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_ORANGE, + NULL /* no group */, + "Balrog Wings", + "Wings of shadow grow in your back.", + "The wings in your back fall apart.", + " Creates ugly, but working, wings allowing you to fly\n" + " But it reduces charisma by 4 and dexterity by 2", + { -1 }, + { -1 }, + NULL, + -1, + }, + + { /* 2 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_ORANGE, + NULL /* no group */, + "Balrog Strength", + "Your muscles get unnatural strength.", + "Your muscles get weaker again.", + " Provides 3 strength and 1 constitution\n" + " But it reduces charisma by 1 and dexterity by 3", + { -1 }, + { -1 }, + NULL, + -1, + }, + + { /* 3 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_YELLOW, + NULL /* no group */, + "Balrog Form", + "You feel the might of a Balrog inside you.", + "The presence of the Balrog seems to abandon you.", + " Allows you to turn into a Balrog at will\n" + " You need Balrog Wings, Balrog Aura and Balrog Strength to activate it", + { CORRUPT_BALROG_AURA, CORRUPT_BALROG_WINGS, CORRUPT_BALROG_STRENGTH }, + { -1 }, + NULL, + PWR_BALROG, + }, + + /* + * DEMON corruptions + */ + { /* 4 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Demon Spirit", + "Your spirit opens to corrupted thoughts.", + "Your spirit closes again to the corrupted thoughts.", + " Increases your intelligence by 1\n" + " But reduce your charisma by 2", + { -1 }, + { -1 }, + NULL, + -1, + }, + + { /* 5 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Demon Hide", + "Your skin grows into a thick hide.", + "Your skin returns to a natural state.", + " Increases your armour class by your level\n" + " Provides immunity to fire at level 40\n" + " But reduces speed by your level / 7", + { -1 }, + { -1 }, + NULL, + -1, + }, + + { /* 6 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Demon Breath", + "Your breath becomes mephitic.", + "Your breath is once again normal.", + " Provides fire breath\n" + " But gives a small chance to spoil potions when you quaff them", + { -1 }, + { -1 }, + NULL, + PWR_BR_FIRE, + }, + + { /* 7 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_L_RED, + NULL /* no group */, + "Demon Realm", + "You feel more attuned to the demon realm.", + "You lose your attunement to the demon realm.", + " Provides access to the demon school skill and the use of demonic equipment\n" + " You need Demon Spirit, Demon Hide and Demon Breath to activate it", + { CORRUPT_DEMON_SPIRIT, CORRUPT_DEMON_HIDE, CORRUPT_DEMON_BREATH }, + { -1 }, + NULL, + -1, + }, + + /* + * Teleportation corruptions + */ + + { /* 8 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_GREEN, + NULL /* no group */, + "Random teleportation", + "Space seems to fizzle around you.", + "Space solidify again around you.", + " Randomly teleports you around", + { -1 }, + { CORRUPT_ANTI_TELEPORT, -1 }, + NULL, + -1, + }, + + { /* 9 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_GREEN, + NULL /* no group */, + "Anti-teleportation", + "Space continuum freezes around you.", + "Space continuum can once more be altered around you.", + " Prevents all teleportations, be it of you or monsters", + { -1 }, + { CORRUPT_RANDOM_TELEPORT, -1 }, + NULL, + POWER_COR_SPACE_TIME, + }, + + /* + * Troll blood + */ + + { /* 10 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_GREEN, + NULL /* no group */, + "Troll Blood", + "Your blood thickens, you sense corruption in it.", + "Your blood returns to a normal state.", + " Troll blood flows in your veins, granting increased regeneration\n" + " It also enables you to feel the presence of other troll beings\n" + " But it will make your presence more noticeable and aggravating", + { -1 }, + { -1 }, + NULL, + -1, + }, + + /* + * The vampire corruption set + */ + + { /* 11 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_L_DARK, + "Vampire", + "Vampiric Teeth", + "You grow vampiric teeth!", + NULL, /* cannot lose */ + " Your teeth allow you to drain blood to feed yourself\n" + " However your stomach now only accepts blood.", + { -1 }, + { -1 }, + player_gain_vampire_teeth, + -1, + }, + + { /* 12 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_L_DARK, + "Vampire", + "Vampiric Strength", + "Your body seems more dead than alive.", + NULL, /* cannot lose */ + " Your body seems somewhat dead\n" + " In this near undead state it has improved strength, constitution and intelligence\n" + " But reduced dexterity, wisdom and charisma.", + { CORRUPT_VAMPIRE_TEETH, -1 }, + { -1 }, + player_gain_vampire_strength, + -1, + }, + + { /* 13 */ + { MODULE_TOME, MODULE_THEME, -1 }, + TERM_L_DARK, + "Vampire", + "Vampire", + "You die to be reborn in a Vampire form.", + NULL, /* cannot lose */ + " You are a Vampire. As such you resist cold, poison, darkness and nether.\n" + " Your life is sustained, but you cannot stand the light of the sun.", + { CORRUPT_VAMPIRE_STRENGTH, -1 }, + { -1 }, + player_gain_vampire, + -1, + }, + + /* + * Activatable corruptions (mutations) + */ + + { /* 14 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Ancalagon's Breath", + "You gain the ability to spit acid.", + "You lose the ability to spit acid.", + " Fires an acid ball.\n" + " Damage=level Radius 1+(level/30)\n" + " Level=9, Cost=9, Stat=DEX, Difficulty=15", + { -1 }, + { -1 }, + NULL, + PWR_SPIT_ACID, + }, + + { /* 15 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Smaug's Breath", + "You gain the ability to breathe fire.", + "You lose the ability to breathe fire.", + " Fires a fire ball.\n" + " Damage=2*level Radius 1+(level/20)\n" + " Level=20, Cost=10, Stat=CON, Difficulty=18", + { -1 }, + { -1 }, + NULL, + PWR_BR_FIRE, + }, + + { /* 16 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Glaurung's Gaze", + "Your eyes look mesmerizing...", + "Your eyes look uninteresting.", + " Tries to make a monster your pet.\n" + " Power=level\n" + " Level=12, Cost=12, Stat=CHR, Difficulty=18", + { -1 }, + { -1 }, + NULL, + PWR_HYPN_GAZE, + }, + + { /* 17 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Saruman's Power", + "You gain the ability to move objects telekinetically.", + "You lose the ability to move objects telekinetically.", + " Move an object in line of sight to you.\n" + " Max weight equal to (level) pounds\n" + " Level=9, Cost=9, Stat=WIS, Difficulty=14", + { -1 }, + { -1 }, + NULL, + PWR_TELEKINES, + }, + + { /* 18 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Teleport", + "You gain the power of teleportation at will.", + "You lose the power of teleportation at will.", + " Teleports the player at will.\n" + " Distance 10+4*level squares\n" + " Level=7, Cost=7, Stat=WIS, Difficulty=15", + { -1 }, + { -1 }, + NULL, + PWR_VTELEPORT, + }, + + { /* 19 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Glaurung's Spell", + "You gain the power of Mind Blast.", + "You lose the power of Mind Blast.", + " Fires a mind blasting bolt (psi damage).\n" + " Psi Damage (3+(level-1)/5)d3\n" + " Level=5, Cost=3, Stat=WIS, Difficulty=15", + { -1 }, + { -1 }, + NULL, + PWR_MIND_BLST, + }, + + { /* 20 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Vampiric Drain", + "You become vampiric.", + "You are no longer vampiric.", + " You can drain life from a foe like a vampire.\n" + " Drains (level+1d(level))*(level/10) hitpoints,\n" + " heals you and satiates you. Doesn't work on all monsters\n" + " Level=4, Cost=5, Stat=CON, Difficulty=9", + { -1 }, + { -1 }, + NULL, + PWR_VAMPIRISM, + }, + + { /* 21 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Carcharoth's Nose", + "You smell a metallic odour.", + "You no longer smell a metallic odour.", + " You can detect nearby precious metal (treasure).\n" + " Radius 25\n" + " Level=3, Cost=2, Stat=INT, Difficulty=12", + { -1 }, + { -1 }, + NULL, + PWR_SMELL_MET, + }, + + { /* 22 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Huan's Nose", + "You smell filthy monsters.", + "You no longer smell filthy monsters.", + " You can detect nearby monsters.\n" + " Radius 25\n" + " Level=5, Cost=4, Stat=INT, Difficulty=15", + { -1 }, + { -1 }, + NULL, + PWR_SMELL_MON, + }, + + { /* 23 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Blink", + "You gain the power of minor teleportation.", + "You lose the power of minor teleportation.", + " You can teleport yourself short distances (10 squares).\n" + " Level=3, Cost=3, Stat=WIS, Difficulty=12", + { -1 }, + { -1 }, + NULL, + PWR_BLINK, + }, + + { /* 24 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Eat Rock", + "The walls look delicious.", + "The walls look unappetizing.", + " You can consume solid rock with food benefit,\n" + " leaving an empty space behind.\n" + " Level=8, Cost=12, Stat=CON, Difficulty=18", + { -1 }, + { -1 }, + NULL, + PWR_EAT_ROCK, + }, + + { /* 25 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Swap Position", + "You feel like walking a mile in someone else's shoes.", + "You feel like staying in your own shoes.", + " You can switch locations with another being,\n" + " unless it resists teleportation.\n" + " Level=15, Cost=12, Stat=DEX, Difficulty=16", + { -1 }, + { -1 }, + NULL, + PWR_SWAP_POS, + }, + + { /* 26 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Shriek", + "Your vocal cords get much tougher.", + "Your vocal cords get much weaker.", + " Fires a sound ball and aggravates monsters.\n" + " Damage=level*4, Radius=8, centered on player\n" + " Level=4, Cost=4, Stat=CON, Difficulty=6", + { -1 }, + { -1 }, + NULL, + PWR_SHRIEK, + }, + + { /* 27 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Illuminate", + "You can light up rooms with your presence.", + "You can no longer light up rooms with your presence.", + " You can emit bright light that illuminates an area.\n" + " Damage=2d(level/2) Radius=(level/10)+1\n" + " Level=3, Cost=2, Stat=INT, Difficulty=10", + { -1 }, + { -1 }, + NULL, + PWR_ILLUMINE, + }, + + { /* 28 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Detect Curses", + "You can feel evil magics.", + "You can no longer feel evil magics.", + " You can feel the danger of evil magic.\n" + " It detects cursed items in the inventory\n" + " Level=7, Cost=14, Stat=WIS, Difficulty=14", + { -1 }, + { -1 }, + NULL, + PWR_DET_CURSE, + }, + + { /* 29 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Berserk", + "You feel a controlled rage.", + "You no longer feel a controlled rage.", + " You can drive yourself into a berserk frenzy.\n" + " It grants super-heroism. Duration=10+1d(level)\n" + " Level=8, Cost=8, Stat=STR, Difficulty=14", + { -1 }, + { -1 }, + NULL, + PWR_BERSERK, + }, + + { /* 30 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Midas touch", + "You gain the Midas touch.", + "You lose the Midas touch.", + " You can turn ordinary items to gold.\n" + " Turns a non-artifact object into 1/3 its value in gold\n" + " Level=10, Cost=5, Stat=INT, Difficulty=12", + { -1 }, + { -1 }, + NULL, + PWR_MIDAS_TCH, + }, + + { /* 31 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Grow Mold", + "You feel a sudden affinity for mold.", + "You feel a sudden dislike for mold.", + " You can cause mold to grow near you.\n" + " Summons up to 8 molds around the player\n" + " Level=1, Cost=6, Stat=CON, Difficulty=14", + { -1 }, + { -1 }, + NULL, + PWR_GROW_MOLD, + }, + + { /* 32 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Resist Elements", + "You feel like you can protect yourself.", + "You feel like you might be vulnerable.", + " You can harden yourself to the ravages of the elements.\n" + " Level-dependent chance of gaining resistances to the four \n" + " elements and poison. Duration=20 + d20\n" + " Level=10, Cost=12, Stat=CON, Difficulty=12", + { -1 }, + { -1 }, + NULL, + PWR_RESIST, + }, + + { /* 33 */ + { MODULE_THEME, -1 }, + TERM_RED, + NULL /* no group */, + "Earthquake", + "You gain the ability to wreck the dungeon.", + "You lose the ability to wreck the dungeon.", + " You can bring down the dungeon around your ears.\n" + " Radius=10, center on the player\n" + " Level=12, Cost=12, Stat=STR, Difficulty=16", + { -1 }, + { -1 }, + NULL, + PWR_EARTHQUAKE, + }, + +}; + +/** + * Initialize corruptions + */ +void init_corruptions() +{ + /* Nothing needed currently */ +} + +/* + * Corruptions + */ +bool_ player_has_corruption(int corruption_idx) +{ + if (corruption_idx < 0) + { + return FALSE; + } + + return (p_ptr->corruptions[corruption_idx]); +} + +static bool_ player_can_gain_corruption(int corruption_idx) +{ + cptr r_name = rp_ptr->title + rp_name; + bool_ allowed = TRUE; /* Allowed by default */ + + assert(corruption_idx >= 0); + + if (corruption_idx == CORRUPT_TROLL_BLOOD) + { + /* Ok trolls should not get this one. never. */ + if (streq(r_name, "Troll")) + { + allowed = FALSE; + } + } + + /* Theme module adds additional restrictions for Maiar */ + + if (game_module_idx == MODULE_THEME) + { + if (streq(r_name, "Maia")) + { + /* We use a whitelist of corruptions for Maiar */ + bool_ allow = FALSE; + if ((corruption_idx == CORRUPT_BALROG_AURA) || + (corruption_idx == CORRUPT_BALROG_WINGS) || + (corruption_idx == CORRUPT_BALROG_STRENGTH) || + (corruption_idx == CORRUPT_BALROG_FORM) || + (corruption_idx == CORRUPT_DEMON_BREATH)) + { + allow = TRUE; + }; + + /* Mix result into 'allowed' flag */ + allowed = allowed & allow; + } + } + + /* Result */ + return allowed; +} + +static bool_ player_allow_corruption(int corruption_idx) +{ + int i; + bool_ found = FALSE; + corruption_type *c_ptr = NULL; + + assert(corruption_idx < CORRUPTIONS_MAX); + c_ptr = &corruptions[corruption_idx]; + + /* Must be allowed by module */ + for (i = 0; c_ptr->modules[i] >= 0; i++) + { + if (c_ptr->modules[i] == game_module_idx) + { + found = TRUE; + } + } + + if (!found) + { + return FALSE; + } + + /* Vampire teeth is special */ + if (corruption_idx == CORRUPT_VAMPIRE_TEETH) + { + if (PRACE_FLAG(PR1_NO_SUBRACE_CHANGE)) + { + return TRUE; + } + else + { + return FALSE; + } + } + + return TRUE; +} + +static void player_set_corruption(int c, bool_ set) +{ + p_ptr->corruptions[c] = set; + p_ptr->redraw = p_ptr->redraw | PR_BASIC; + p_ptr->update = p_ptr->update | PU_BONUS | PU_TORCH | PU_BODY | PU_POWERS; + +} + +void player_gain_corruption(int corruption_idx) +{ + corruption_type *c_ptr = NULL; + assert(corruption_idx >= 0); + assert(corruption_idx < CORRUPTIONS_MAX); + c_ptr = &corruptions[corruption_idx]; + + /* Set the player's corruption flag */ + player_set_corruption(corruption_idx, TRUE); + + /* Invoke callback if necessary */ + if (c_ptr->gain_callback) + { + c_ptr->gain_callback(); + } +} + +static void player_lose_corruption(int corruption_idx) +{ + assert(corruption_idx >= 0); + assert(corruption_idx < CORRUPTIONS_MAX); + + player_set_corruption(corruption_idx, FALSE); + + /* Currently no corruptions need any special handling when lost */ +} + +/* + * Test if we have that corruption + * We must: + * 1) have it or be willing to get it + * 2) have all its dependancies + * 3) have none of its opposing corruptions + * 4) pass the possible tests + */ +static bool_ test_depend_corrupt(s16b corrupt_idx, bool_ can_gain) +{ + s16b i; + corruption_type *c_ptr = NULL; + + assert(corrupt_idx >= 0); + assert(corrupt_idx < CORRUPTIONS_MAX); + + c_ptr = &corruptions[corrupt_idx]; + + if (can_gain) + { + if (p_ptr->corruptions[corrupt_idx]) + { + return FALSE; + } + } else { + if (!p_ptr->corruptions[corrupt_idx]) + { + return FALSE; + } + } + + /* Go through all dependencies */ + for (i=0; c_ptr->depends[i] >= 0; i++) + { + if (!test_depend_corrupt(c_ptr->depends[i], FALSE)) + { + return FALSE; + } + } + + /* Go through all opposers */ + for (i=0; c_ptr->opposes[i] >= 0; i++) + { + if (test_depend_corrupt(c_ptr->opposes[i], FALSE)) + { + return FALSE; + } + } + + /* are we even allowed to get it? */ + return player_can_gain_corruption(corrupt_idx); +} + +void gain_random_corruption() +{ + s16b i, max; + s16b pos[CORRUPTIONS_MAX]; + + /* Get the list of all possible ones */ + max = 0; + for (i=0; i < CORRUPTIONS_MAX; i++) + { + if (test_depend_corrupt(i, TRUE) && + player_allow_corruption(i)) + { + pos[max] = i; + max = max + 1; + } + } + + /* Ok now get one of them */ + if (max > 0) + { + s16b ret = rand_int(max); + int c_idx = pos[ret]; + assert(c_idx < CORRUPTIONS_MAX); + + player_gain_corruption(c_idx); + cmsg_print(TERM_L_RED, corruptions[c_idx].get_text); + } +} + +static void remove_corruption(int c_idx) +{ + assert(c_idx >= 0 && c_idx < CORRUPTIONS_MAX); + assert(corruptions[c_idx].lose_text); + + player_lose_corruption(c_idx); + cmsg_print(TERM_L_RED, corruptions[c_idx].lose_text); +} + +void lose_corruption() +{ + s16b i, max; + s16b pos[CORRUPTIONS_MAX]; + + /* Get the list of all possible ones */ + max = 0; + for (i = 0; i < CORRUPTIONS_MAX; i++) + { + bool_ is_removable = (corruptions[i].lose_text != NULL); + if (test_depend_corrupt(i, FALSE) && is_removable) + { + pos[max] = i; + max = max + 1; + } + } + + /* Ok now get one of them */ + if (max > 0) + { + s16b ret = rand_int(max); + int c_idx = pos[ret]; + + /* Remove the corruption */ + remove_corruption(c_idx); + + /* Ok now lets see if it broke some dependencies */ + for (i = 0; i < max - 1; i++) + { + if (p_ptr->corruptions[pos[i]] != test_depend_corrupt(pos[i], FALSE)) + { + remove_corruption(pos[i]); + } + } + } +} + + +/* + * Dump the corruption list + */ +void dump_corruptions(FILE *fff, bool_ color, bool_ header) +{ + int i; + + assert(fff != NULL); + + for (i = 0; i < CORRUPTIONS_MAX; i++) + { + corruption_type *c_ptr = &corruptions[i]; + + if (header) + { + fprintf(fff, "\n Corruption list:\n"); + header = FALSE; + } + + if (p_ptr->corruptions[i]) + { + byte c = c_ptr->color; + + if (color) + { + fprintf(fff, "#####%c%s:\n", conv_color[c], c_ptr->name); + } + else + { + fprintf(fff, "%s:\n", c_ptr->name); + } + + fprintf(fff, "%s\n", c_ptr->desc); + } + } +} + +/* + * Get the power granted by a corruption. Returns -1 + * if the given corruption does not grant a power. + */ +s16b get_corruption_power(int corruption_idx) +{ + corruption_type *c_ptr = NULL; + + assert(corruption_idx >= 0); + assert(corruption_idx < CORRUPTIONS_MAX); + + c_ptr = &corruptions[corruption_idx]; + + if ((c_ptr->power >= 0) && (c_ptr->power < POWER_MAX)) + { + return c_ptr->power; + } + else + { + assert(c_ptr->power == -1); /* Sanity check: Should always be the case. */ + return -1; + } +} diff --git a/src/device_allocation.c b/src/device_allocation.c deleted file mode 100644 index e64f0401..00000000 --- a/src/device_allocation.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "device_allocation.h" - -int compare_device_allocation(device_allocation *a, device_allocation *b) -{ - return SGLIB_NUMERIC_COMPARATOR(a->tval, b->tval); -} - -SGLIB_DEFINE_LIST_FUNCTIONS(device_allocation, compare_device_allocation, next); - -void device_allocation_init(device_allocation *device_allocation, byte tval) -{ - assert(device_allocation != NULL); - - device_allocation->tval = tval; - device_allocation->rarity = 0; - range_init(&device_allocation->base_level, 0, 0); - range_init(&device_allocation->max_level, 0, 0); - device_allocation->next = NULL; -} - -device_allocation *device_allocation_new(byte tval) -{ - device_allocation *d = malloc(sizeof(device_allocation)); - device_allocation_init(d, tval); - return d; -} diff --git a/src/device_allocation.cc b/src/device_allocation.cc new file mode 100644 index 00000000..f97de2ec --- /dev/null +++ b/src/device_allocation.cc @@ -0,0 +1,26 @@ +#include "device_allocation.h" + +int compare_device_allocation(device_allocation *a, device_allocation *b) +{ + return SGLIB_NUMERIC_COMPARATOR(a->tval, b->tval); +} + +SGLIB_DEFINE_LIST_FUNCTIONS(device_allocation, compare_device_allocation, next); + +void device_allocation_init(device_allocation *device_allocation, byte tval) +{ + assert(device_allocation != NULL); + + device_allocation->tval = tval; + device_allocation->rarity = 0; + range_init(&device_allocation->base_level, 0, 0); + range_init(&device_allocation->max_level, 0, 0); + device_allocation->next = NULL; +} + +device_allocation *device_allocation_new(byte tval) +{ + device_allocation *d = new device_allocation; + device_allocation_init(d, tval); + return d; +} diff --git a/src/dice.c b/src/dice.c deleted file mode 100644 index ea9b7533..00000000 --- a/src/dice.c +++ /dev/null @@ -1,94 +0,0 @@ -#include "dice.h" - -void dice_init(dice_type *dice, long base, long num, long sides) -{ - assert(dice != NULL); - - dice->base = base; - dice->num = num; - dice->sides = sides; -} - -bool_ dice_parse(dice_type *dice, cptr s) -{ - long base, num, sides; - - if (sscanf(s, "%ld+%ldd%ld", &base, &num, &sides) == 3) - { - dice_init(dice, base, num, sides); - return TRUE; - } - - if (sscanf(s, "%ld+d%ld", &base, &sides) == 2) - { - dice_init(dice, base, 1, sides); - return TRUE; - } - - if (sscanf(s, "d%ld", &sides) == 1) - { - dice_init(dice, 0, 1, sides); - return TRUE; - } - - if (sscanf(s, "%ldd%ld", &num, &sides) == 2) - { - dice_init(dice, 0, num, sides); - return TRUE; - } - - if (sscanf(s, "%ld", &base) == 1) - { - dice_init(dice, base, 0, 0); - return TRUE; - } - - return FALSE; -} - -void dice_parse_checked(dice_type *dice, cptr s) -{ - bool_ result = dice_parse(dice, s); - if (!result) - { - abort(); - } -} - -long dice_roll(dice_type *dice) -{ - assert(dice != NULL); - return dice->base + damroll(dice->num, dice->sides); -} - -void dice_print(dice_type *dice, char *output) -{ - char buf[16]; - - output[0] = '\0'; - - if (dice->base > 0) - { - sprintf(buf, "%ld", dice->base); - strcat(output, buf); - } - - if ((dice->num > 0) || (dice->sides > 0)) - { - if (dice->base > 0) - { - strcat(output, "+"); - } - - if (dice->num > 1) - { - sprintf(buf, "%ld", dice->num); - strcat(output, buf); - } - - strcat(output, "d"); - - sprintf(buf, "%ld", dice->sides); - strcat(output, buf); - } -} diff --git a/src/dice.cc b/src/dice.cc new file mode 100644 index 00000000..ea9b7533 --- /dev/null +++ b/src/dice.cc @@ -0,0 +1,94 @@ +#include "dice.h" + +void dice_init(dice_type *dice, long base, long num, long sides) +{ + assert(dice != NULL); + + dice->base = base; + dice->num = num; + dice->sides = sides; +} + +bool_ dice_parse(dice_type *dice, cptr s) +{ + long base, num, sides; + + if (sscanf(s, "%ld+%ldd%ld", &base, &num, &sides) == 3) + { + dice_init(dice, base, num, sides); + return TRUE; + } + + if (sscanf(s, "%ld+d%ld", &base, &sides) == 2) + { + dice_init(dice, base, 1, sides); + return TRUE; + } + + if (sscanf(s, "d%ld", &sides) == 1) + { + dice_init(dice, 0, 1, sides); + return TRUE; + } + + if (sscanf(s, "%ldd%ld", &num, &sides) == 2) + { + dice_init(dice, 0, num, sides); + return TRUE; + } + + if (sscanf(s, "%ld", &base) == 1) + { + dice_init(dice, base, 0, 0); + return TRUE; + } + + return FALSE; +} + +void dice_parse_checked(dice_type *dice, cptr s) +{ + bool_ result = dice_parse(dice, s); + if (!result) + { + abort(); + } +} + +long dice_roll(dice_type *dice) +{ + assert(dice != NULL); + return dice->base + damroll(dice->num, dice->sides); +} + +void dice_print(dice_type *dice, char *output) +{ + char buf[16]; + + output[0] = '\0'; + + if (dice->base > 0) + { + sprintf(buf, "%ld", dice->base); + strcat(output, buf); + } + + if ((dice->num > 0) || (dice->sides > 0)) + { + if (dice->base > 0) + { + strcat(output, "+"); + } + + if (dice->num > 1) + { + sprintf(buf, "%ld", dice->num); + strcat(output, buf); + } + + strcat(output, "d"); + + sprintf(buf, "%ld", dice->sides); + strcat(output, buf); + } +} diff --git a/src/dungeon.c b/src/dungeon.c deleted file mode 100644 index df3b41aa..00000000 --- a/src/dungeon.c +++ /dev/null @@ -1,5849 +0,0 @@ -/* File: dungeon.c */ - -/* Purpose: Angband game engine */ - -/* - * 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 "angband.h" - -#include - -#include "quark.h" -#include "hooks.h" -#include "spell_type.h" - -#define TY_CURSE_CHANCE 100 -#define DG_CURSE_CHANCE 50 -#define AUTO_CURSE_CHANCE 15 -#define CHAINSWORD_NOISE 100 - - -/* - * Return a "feeling" (or NULL) about an item. Method 1 (Heavy). - */ -byte value_check_aux1(object_type *o_ptr) -{ - /* Artifacts */ - if (artifact_p(o_ptr)) - { - /* Cursed/Broken */ - if (cursed_p(o_ptr)) return (SENSE_TERRIBLE); - - /* Normal */ - return (SENSE_SPECIAL); - } - - /* Ego-Items */ - if (ego_item_p(o_ptr)) - { - /* Cursed/Broken */ - if (cursed_p(o_ptr)) return (SENSE_WORTHLESS); - - /* Normal */ - return (SENSE_EXCELLENT); - } - - /* Cursed items */ - if (cursed_p(o_ptr)) return (SENSE_CURSED); - - /* Good "armor" bonus */ - if (o_ptr->to_a > 0) return (SENSE_GOOD_HEAVY); - - /* Good "weapon" bonus */ - if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_HEAVY); - - /* Default to "average" */ - return (SENSE_AVERAGE); -} - -byte value_check_aux1_magic(object_type *o_ptr) -{ - object_kind *k_ptr = &k_info[o_ptr->k_idx]; - - - switch (o_ptr->tval) - { - /* Scrolls, Potions, Wands, Staves and Rods */ - case TV_SCROLL: - case TV_POTION: - case TV_POTION2: - case TV_WAND: - case TV_STAFF: - case TV_ROD: - case TV_ROD_MAIN: - { - /* "Cursed" scrolls/potions have a cost of 0 */ - if (k_ptr->cost == 0) return (SENSE_TERRIBLE); - - /* Artifacts */ - if (artifact_p(o_ptr)) return (SENSE_SPECIAL); - - /* Scroll of Nothing, Apple Juice, etc. */ - if (k_ptr->cost < 3) return (SENSE_WORTHLESS); - - /* - * Identify, Phase Door, Cure Light Wounds, etc. are - * just average - */ - if (k_ptr->cost < 100) return (SENSE_AVERAGE); - - /* Enchant Armor, *Identify*, Restore Stat, etc. */ - if (k_ptr->cost < 10000) return (SENSE_GOOD_HEAVY); - - /* Acquirement, Deincarnation, Strength, Blood of Life, ... */ - if (k_ptr->cost >= 10000) return (SENSE_EXCELLENT); - - break; - } - - /* Food */ - case TV_FOOD: - { - /* "Cursed" food */ - if (k_ptr->cost == 0) return (SENSE_TERRIBLE); - - /* Artifacts */ - if (artifact_p(o_ptr)) return (SENSE_SPECIAL); - - /* Normal food (no magical properties) */ - if (k_ptr->cost <= 10) return (SENSE_AVERAGE); - - /* Everything else is good */ - if (k_ptr->cost > 10) return (SENSE_GOOD_HEAVY); - - break; - } - } - - /* No feeling */ - return (SENSE_NONE); -} - - -/* - * Return a "feeling" (or NULL) about an item. Method 2 (Light). - */ -byte value_check_aux2(object_type *o_ptr) -{ - /* Cursed items (all of them) */ - if (cursed_p(o_ptr)) return (SENSE_CURSED); - - /* Artifacts -- except cursed/broken ones */ - if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT); - - /* Ego-Items -- except cursed/broken ones */ - if (ego_item_p(o_ptr)) return (SENSE_GOOD_LIGHT); - - /* Good armor bonus */ - if (o_ptr->to_a > 0) return (SENSE_GOOD_LIGHT); - - /* Good weapon bonuses */ - if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_LIGHT); - - /* Default to "average" */ - return (SENSE_AVERAGE); -} - - -byte value_check_aux2_magic(object_type *o_ptr) -{ - object_kind *k_ptr = &k_info[o_ptr->k_idx]; - - - switch (o_ptr->tval) - { - /* Scrolls, Potions, Wands, Staves and Rods */ - case TV_SCROLL: - case TV_POTION: - case TV_POTION2: - case TV_WAND: - case TV_STAFF: - case TV_ROD: - case TV_ROD_MAIN: - { - /* "Cursed" scrolls/potions have a cost of 0 */ - if (k_ptr->cost == 0) return (SENSE_CURSED); - - /* Artifacts */ - if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT); - - /* Scroll of Nothing, Apple Juice, etc. */ - if (k_ptr->cost < 3) return (SENSE_AVERAGE); - - /* - * Identify, Phase Door, Cure Light Wounds, etc. are - * just average - */ - if (k_ptr->cost < 100) return (SENSE_AVERAGE); - - /* Enchant Armor, *Identify*, Restore Stat, etc. */ - if (k_ptr->cost < 10000) return (SENSE_GOOD_LIGHT); - - /* Acquirement, Deincarnation, Strength, Blood of Life, ... */ - if (k_ptr->cost >= 10000) return (SENSE_GOOD_LIGHT); - - break; - } - - /* Food */ - case TV_FOOD: - { - /* "Cursed" food */ - if (k_ptr->cost == 0) return (SENSE_CURSED); - - /* Artifacts */ - if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT); - - /* Normal food (no magical properties) */ - if (k_ptr->cost <= 10) return (SENSE_AVERAGE); - - /* Everything else is good */ - if (k_ptr->cost > 10) return (SENSE_GOOD_LIGHT); - - break; - } - } - - /* No feeling */ - return (SENSE_NONE); -} - - -/* - * Can a player be resurrected? - */ -static bool_ granted_resurrection(void) -{ - PRAY_GOD(GOD_ERU) - { - if (p_ptr->grace > 100000) - { - if (magik(70)) return (TRUE); - else return (FALSE); - } - } - return (FALSE); -} - -static byte select_sense(object_type *o_ptr) -{ - /* Valid "tval" codes */ - switch (o_ptr->tval) - { - case TV_SHOT: - case TV_ARROW: - case TV_BOLT: - case TV_BOW: - case TV_DIGGING: - case TV_HAFTED: - case TV_POLEARM: - case TV_SWORD: - case TV_MSTAFF: - case TV_AXE: - case TV_BOOTS: - case TV_GLOVES: - case TV_HELM: - case TV_CROWN: - case TV_SHIELD: - case TV_CLOAK: - case TV_SOFT_ARMOR: - case TV_HARD_ARMOR: - case TV_DRAG_ARMOR: - case TV_BOOMERANG: - case TV_TRAPKIT: - { - return 1; - break; - } - - case TV_POTION: - case TV_POTION2: - case TV_SCROLL: - case TV_WAND: - case TV_STAFF: - case TV_ROD: - case TV_ROD_MAIN: - { - return 2; - break; - } - - /* Dual use? */ - case TV_DAEMON_BOOK: - { - return 1; - break; - } - } - return 0; -} - -/* - * Sense the inventory - * - * Combat items (weapons and armour) - Fast, weak if combat skill < 10, strong - * otherwise. - * - * Magic items (scrolls, staffs, wands, potions etc) - Slow, weak if - * magic skill < 10, strong otherwise. - * - * It shouldn't matter a lot to discriminate against magic users, because - * they learn one form of ID or another, and because most magic items are - * easy_know. - */ -void sense_inventory(void) -{ - int i, combat_lev, magic_lev; - - bool_ heavy_combat, heavy_magic; - - byte feel; - - object_type *o_ptr; - - char o_name[80]; - - - /*** Check for "sensing" ***/ - - /* No sensing when confused */ - if (p_ptr->confused) return; - - /* - * In Angband, the chance of pseudo-id uses two different formulae: - * - * (1) Fast. 0 == rand_int(BASE / (plev * plev + 40) - * (2) Slow. 0 == rand_int(BASE / (plev + 5) - * - * Warriors: Fase with BASE == 9000 - * Magi: Slow with BASE == 240000 - * Priests: Fast with BASE == 10000 - * Rogues: Fase with BASE == 20000 - * Rangers: Slow with BASE == 120000 - * Paladins: Fast with BASE == 80000 - * - * In other words, those who have identify spells are penalised. - * The problems with Pern/Tome since it externalised player classes - * is that it uses the same and slow formula for spellcasters and - * fighters. - * - * In the following code, combat item pseudo-ID improves exponentially, - * (fast with BASE 9000) and magic one linear (slow with base 60000 -- - * twice faster than V rangers). - * - * I hope this makes it closer to the original model -- pelpel - */ - - /* The combat skill affects weapon/armour pseudo-ID */ - combat_lev = get_skill(SKILL_COMBAT); - - /* The magic skill affects magic item pseudo-ID */ - magic_lev = get_skill(SKILL_MAGIC); - - /* Higher skill levels give the player better sense of items */ - heavy_combat = (combat_lev > 10) ? TRUE : FALSE; - heavy_magic = (magic_lev > 10) ? TRUE : FALSE; - - - /*** Sense everything ***/ - - /* Check everything */ - for (i = 0; i < INVEN_TOTAL; i++) - { - byte okay = 0; - - o_ptr = &p_ptr->inventory[i]; - - /* Skip empty slots */ - if (!o_ptr->k_idx) continue; - - /* We know about it already, do not tell us again */ - if (o_ptr->ident & (IDENT_SENSE)) continue; - - /* It is fully known, no information needed */ - if (object_known_p(o_ptr)) continue; - - /* Valid "tval" codes */ - okay = select_sense(o_ptr); - - /* Skip non-sense machines */ - if (!okay) continue; - - /* Check for a feeling */ - if (okay == 1) - { - feel = (heavy_combat ? value_check_aux1(o_ptr) : value_check_aux2(o_ptr)); - } - else - { - feel = (heavy_magic ? value_check_aux1_magic(o_ptr) : value_check_aux2_magic(o_ptr)); - } - - /* Skip non-feelings */ - if (feel == SENSE_NONE) continue; - - /* Get an object description */ - object_desc(o_name, o_ptr, FALSE, 0); - - /* Message (equipment) */ - if (i >= INVEN_WIELD) - { - msg_format("You feel the %s (%c) you are %s %s %s...", - o_name, index_to_label(i), describe_use(i), - ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]); - } - - /* Message (inventory) */ - else - { - msg_format("You feel the %s (%c) in your pack %s %s...", - o_name, index_to_label(i), - ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]); - } - - /* We have "felt" it */ - o_ptr->ident |= (IDENT_SENSE); - - /* Set sense property */ - o_ptr->sense = feel; - - /* Combine / Reorder the pack (later) */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP); - } - - /* Squelch ! */ - squeltch_inventory(); -} - - -/* - * Go to any level (ripped off from wiz_jump) - */ -static void pattern_teleport(void) -{ - /* Ask for level */ - if (get_check("Teleport level? ")) - { - char ppp[80]; - - char tmp_val[160]; - - /* Prompt */ - sprintf(ppp, "Teleport to level (0-%d): ", 99); - - /* Default */ - sprintf(tmp_val, "%d", dun_level); - - /* Ask for a level */ - if (!get_string(ppp, tmp_val, 10)) return; - - /* Extract request */ - command_arg = atoi(tmp_val); - } - else if (get_check("Normal teleport? ")) - { - teleport_player(200); - return; - } - else - { - return; - } - - /* Paranoia */ - if (command_arg < 0) command_arg = 0; - - /* Paranoia */ - if (command_arg > 99) command_arg = 99; - - /* Accept request */ - msg_format("You teleport to dungeon level %d.", command_arg); - - autosave_checkpoint(); - - /* Change level */ - dun_level = command_arg; - - /* Leaving */ - p_ptr->leaving = TRUE; -} - - -/* - * Returns TRUE if we are on the Straight Road... - */ -static bool_ pattern_effect(void) -{ - if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_START) || - (cave[p_ptr->py][p_ptr->px].feat > FEAT_PATTERN_XTRA2)) return (FALSE); - - if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_END) - { - (void)set_poisoned(0); - (void)set_image(0); - (void)set_stun(0); - (void)set_cut(0); - (void)set_blind(0); - (void)set_afraid(0); - (void)do_res_stat(A_STR, TRUE); - (void)do_res_stat(A_INT, TRUE); - (void)do_res_stat(A_WIS, TRUE); - (void)do_res_stat(A_DEX, TRUE); - (void)do_res_stat(A_CON, TRUE); - (void)do_res_stat(A_CHR, TRUE); - (void)restore_level(); - (void)hp_player(1000); - cave_set_feat(p_ptr->py, p_ptr->px, FEAT_PATTERN_OLD); - msg_print("This section of the Straight Road looks less powerful."); - } - - - /* - * We could make the healing effect of the - * Pattern center one-time only to avoid various kinds - * of abuse, like luring the win monster into fighting you - * in the middle of the pattern... - */ - else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_OLD) - { - /* No effect */ - } - else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA1) - { - pattern_teleport(); - } - else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA2) - { - if (!(p_ptr->invuln)) - take_hit(200, "walking the corrupted Straight Road"); - } - - else - { - if (!(p_ptr->invuln)) - take_hit(damroll(1, 3), "walking the Straight Road"); - } - - return (TRUE); -} - - -/* - * If player has inscribed the object with "!!", let him know when it's - * recharged. -LM- - */ -static void recharged_notice(object_type *o_ptr) -{ - char o_name[80]; - - cptr s; - - - /* No inscription */ - if (!o_ptr->note) return; - - /* Find a '!' */ - s = strchr(quark_str(o_ptr->note), '!'); - - /* Process notification request. */ - while (s) - { - /* Find another '!' */ - if (s[1] == '!') - { - /* Describe (briefly) */ - object_desc(o_name, o_ptr, FALSE, 0); - - /* Notify the player */ - if (o_ptr->number > 1) - { - msg_format("Your %s are recharged.", o_name); - } - else - { - msg_format("Your %s is recharged.", o_name); - } - - /* Done. */ - return; - } - - /* Keep looking for '!'s */ - s = strchr(s + 1, '!'); - } -} - - - -/* - * Regenerate hit points -RAK- - */ -static void regenhp(int percent) -{ - s32b new_chp, new_chp_frac; - - int old_chp; - - - /* Only if alive */ - if (!(p_ptr->necro_extra & CLASS_UNDEAD)) - { - /* Save the old hitpoints */ - old_chp = p_ptr->chp; - - /* Extract the new hitpoints */ - new_chp = ((long)p_ptr->mhp) * percent + PY_REGEN_HPBASE; - - /* div 65536 */ - p_ptr->chp += new_chp >> 16; - - /* check for overflow */ - if ((p_ptr->chp < 0) && (old_chp > 0)) p_ptr->chp = MAX_SHORT; - - /* mod 65536 */ - new_chp_frac = (new_chp & 0xFFFF) + p_ptr->chp_frac; - - if (new_chp_frac >= 0x10000L) - { - p_ptr->chp_frac = new_chp_frac - 0x10000L; - p_ptr->chp++; - } - else - { - p_ptr->chp_frac = new_chp_frac; - } - - /* Fully healed */ - if (p_ptr->chp >= p_ptr->mhp) - { - p_ptr->chp = p_ptr->mhp; - p_ptr->chp_frac = 0; - } - - /* Notice changes */ - if (old_chp != p_ptr->chp) - { - /* Redraw */ - p_ptr->redraw |= (PR_HP); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - } - } -} - - -/* - * Regenerate mana points -RAK- - */ -static void regenmana(int percent) -{ - s32b new_mana, new_mana_frac; - - int old_csp; - - /* Incraese regen with int */ - percent += adj_str_blow[p_ptr->stat_ind[A_INT]] * 3; - - old_csp = p_ptr->csp; - new_mana = ((long)p_ptr->msp) * percent + PY_REGEN_MNBASE; - - /* div 65536 */ - p_ptr->csp += new_mana >> 16; - - /* check for overflow */ - if ((p_ptr->csp < 0) && (old_csp > 0)) - { - p_ptr->csp = MAX_SHORT; - } - - /* mod 65536 */ - new_mana_frac = (new_mana & 0xFFFF) + p_ptr->csp_frac; - - if (new_mana_frac >= 0x10000L) - { - p_ptr->csp_frac = new_mana_frac - 0x10000L; - p_ptr->csp++; - } - else - { - p_ptr->csp_frac = new_mana_frac; - } - - /* Must set frac to zero even if equal */ - if (p_ptr->csp >= p_ptr->msp) - { - p_ptr->csp = p_ptr->msp; - p_ptr->csp_frac = 0; - } - - /* Redraw mana */ - if (old_csp != p_ptr->csp) - { - /* Redraw */ - p_ptr->redraw |= (PR_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - } -} - - - - - - -/* - * Regenerate the monsters (once per 100 game turns) - * - * XXX XXX XXX Should probably be done during monster turns. - */ -static void regen_monsters(void) -{ - int i, frac; - - object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY]; - - - if (o_ptr->k_idx) - { - monster_race *r_ptr = &r_info[o_ptr->pval]; - - /* Allow regeneration (if needed) */ - if (o_ptr->pval2 < o_ptr->pval3) - { - /* Hack -- Base regeneration */ - frac = o_ptr->pval3 / 100; - - /* Hack -- Minimal regeneration rate */ - if (!frac) frac = 1; - - /* Hack -- Some monsters regenerate quickly */ - if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2; - - - /* Hack -- Regenerate */ - o_ptr->pval2 += frac; - - /* Do not over-regenerate */ - if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3; - - /* Redraw (later) */ - p_ptr->redraw |= (PR_MH); - } - } - - /* Regenerate everyone */ - for (i = 1; i < m_max; i++) - { - /* Check the i'th monster */ - monster_type *m_ptr = &m_list[i]; - monster_race *r_ptr = race_inf(m_ptr); - - /* Skip dead monsters */ - if (!m_ptr->r_idx) continue; - - /* Dont regen bleeding/poisonned monsters */ - if (m_ptr->bleeding || m_ptr->poisoned) continue; - - /* Allow regeneration (if needed) */ - if (m_ptr->hp < m_ptr->maxhp) - { - /* Hack -- Base regeneration */ - frac = m_ptr->maxhp / 100; - - /* Hack -- Minimal regeneration rate */ - if (!frac) frac = 1; - - /* Hack -- Some monsters regenerate quickly */ - if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2; - - - /* Hack -- Regenerate */ - m_ptr->hp += frac; - - /* Do not over-regenerate */ - if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp; - - /* Redraw (later) if needed */ - if (health_who == i) p_ptr->redraw |= (PR_HEALTH); - } - } -} - - -/* - * Does an object decay? - * - * Should belong to object1.c, renamed to object_decays() -- pelpel - */ -bool_ decays(object_type *o_ptr) -{ - u32b f1, f2, f3, f4, f5, esp; - - - /* Extract some flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - if (f3 & TR3_DECAY) return (TRUE); - - return (FALSE); -} - - -static int process_lasting_spell(s16b music) -{ - spell_type *spell = spell_at(-music); - return spell_type_produce_effect_lasting(spell); -} - -static void gere_class_special() -{ - switch (p_ptr->druid_extra2) - { - /* Lay a path of mana on the floor */ - case CLASS_MANA_PATH: - { - /* Does the player have enought mana ? */ - if (p_ptr->csp < (s32b)(p_ptr->druid_extra & 255)) - { - p_ptr->druid_extra = 0; - p_ptr->druid_extra2 = CLASS_NONE; - msg_print("You stop laying a mana path."); - } - else - { - /* Use some mana */ - p_ptr->csp -= (p_ptr->druid_extra & 255); - - if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE) - { - /* Absorb some of the mana of the grid */ - p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 50; - if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp; - - /* Set the new grid mana */ - cave[p_ptr->py][p_ptr->px].mana = p_ptr->druid_extra & 255; - } - else - { - int m = cave[p_ptr->py][p_ptr->px].mana; - - if (m + (p_ptr->druid_extra & 255) > 255) - { - cave[p_ptr->py][p_ptr->px].mana = 255; - } - else - { - cave[p_ptr->py][p_ptr->px].mana += p_ptr->druid_extra & 255; - } - } - } - - break; - } - - /* Lay a path of mana on the floor */ - case CLASS_WINDS_MANA: - { - /* Does the player have enought mana ? */ - if (p_ptr->csp < (s32b)(p_ptr->druid_extra & 255)) - { - p_ptr->druid_extra = CLASS_NONE; - msg_print("You stop expulsing mana winds."); - } - else - { - int dam = 0; - - /* Use some mana */ - p_ptr->csp -= (p_ptr->druid_extra & 255); - - if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE) - { - dam = (p_ptr->druid_extra & 255) + 256; - } - else - { - dam = (p_ptr->druid_extra & 255); - } - - fire_explosion(p_ptr->py, p_ptr->px, GF_WINDS_MANA, 2, dam); - } - - break; - } - - case CLASS_CANALIZE_MANA: - { - if (p_ptr->druid_extra & CLASS_CANALIZE_MANA_EXTRA) - { - p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 10; - } - else - { - p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 20; - } - - if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp; - - cave[p_ptr->py][p_ptr->px].mana = 0; - - break; - } - - /* CLASS_NONE, possibly others? */ - default: - { - /* No mana update */ - return; - } - } - - /* Redraw mana */ - p_ptr->update |= (PU_BONUS); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); -} - - -static void check_music() -{ - int use_mana; - - /* Music sung by player */ - if (!p_ptr->music_extra) return; - - use_mana = process_lasting_spell(p_ptr->music_extra); - - if (p_ptr->csp < use_mana) - { - msg_print("You stop your spell."); - p_ptr->music_extra = 0; - p_ptr->music_extra2 = 0; - } - else - { - p_ptr->csp -= use_mana; - - /* Redraw mana */ - p_ptr->redraw |= (PR_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - } -} - - -/* - * Generate the feature effect - */ -void apply_effect(int y, int x) -{ - cave_type *c_ptr = &cave[y][x]; - - feature_type *f_ptr = &f_info[c_ptr->feat]; - - - if (f_ptr->d_frequency[0] != 0) - { - int i; - - for (i = 0; i < 4; i++) - { - /* Check the frequency */ - if (f_ptr->d_frequency[i] == 0) continue; - - if (((turn % f_ptr->d_frequency[i]) == 0) && - ((f_ptr->d_side[i] != 0) || (f_ptr->d_dice[i] != 0))) - { - int l, dam = 0; - int d = f_ptr->d_dice[i], s = f_ptr->d_side[i]; - - if (d == -1) d = p_ptr->lev; - if (s == -1) s = p_ptr->lev; - - /* Roll damage */ - for (l = 0; l < d; l++) - { - dam += randint(s); - } - - /* Apply damage */ - project( -100, 0, y, x, dam, f_ptr->d_type[i], - PROJECT_KILL | PROJECT_HIDE); - - /* Hack -- notice death */ - if (!alive || death) return; - } - } - } -} - - - -/* XXX XXX XXX */ -bool_ is_recall = FALSE; - - -/* - * Hook for corruptions - */ -static void process_world_corruptions() -{ - if (player_has_corruption(CORRUPT_RANDOM_TELEPORT)) - { - if (rand_int(300) == 1) - { - if (magik(70)) - { - if (get_check("Teleport?")) - { - teleport_player(50); - } - else - { - disturb(0); - msg_print("Your corruption takes over you, you teleport!"); - teleport_player(50); - } - } - } - } - - if (player_has_corruption(CORRUPT_ANTI_TELEPORT)) - { - if (p_ptr->corrupt_anti_teleport_stopped) - { - int amt = p_ptr->msp + p_ptr->csp; - amt = amt / 100; - if (amt < 1) { - amt = 1; - } - increase_mana(-amt); - if (p_ptr->csp == 0) - { - p_ptr->corrupt_anti_teleport_stopped = FALSE; - msg_print("You stop controlling your corruption."); - p_ptr->update = p_ptr->update | PU_BONUS; - } - } - } -} - - -/* - * Shim for accessing Lua variable. - */ -static bool_ grace_delay_trigger() -{ - p_ptr->grace_delay++; - - if (p_ptr->grace_delay >= 15) - { - /* reset */ - p_ptr->grace_delay = 0; - /* triggered */ - return TRUE; - } - else - { - /* not triggered */ - return FALSE; - } -} - -/* - * Hook for gods - */ -static void process_world_gods() -{ - const char *race_name = rp_ptr->title + rp_name; - const char *subrace_name = rmp_ptr->title + rmp_name; - - GOD(GOD_VARDA) - { - if (grace_delay_trigger()) - { - /* Piety increases if in light. */ - if (cave[p_ptr->py][p_ptr->px].info & CAVE_GLOW) - { - inc_piety(GOD_ALL, 2); - } - - if (streq(race_name, "Orc") || - streq(race_name, "Troll") || - streq(race_name, "Dragon") || - streq(race_name, "Demon")) - { - /* Varda hates evil races */ - inc_piety(GOD_ALL, -2); - } else { - /* ... and everyone slightly less */ - inc_piety(GOD_ALL, -1); - } - - /* Prayer uses piety */ - if (p_ptr->praying) - { - inc_piety(GOD_ALL, -1); - } - } - } - - GOD(GOD_ULMO) - { - if (grace_delay_trigger()) - { - int i; - /* Ulmo likes the Edain (except Easterlings) */ - if (streq(race_name, "Human") || - streq(race_name, "Dunadan") || - streq(race_name, "Druadan") || - streq(race_name, "RohanKnight")) - { - inc_piety(GOD_ALL, 2); - } - else if (streq(race_name, "Easterling") || - streq(race_name, "Demon") || - streq(race_name, "Orc")) - { - /* hated races */ - inc_piety(GOD_ALL, -2); - } - else - { - inc_piety(GOD_ALL, 1); - } - - if (p_ptr->praying) - { - inc_piety(GOD_ALL, -1); - } - - /* Gain 1 point for each trident in inventory */ - for (i = 0; i < INVEN_TOTAL; i++) - { - if ((p_ptr->inventory[i].tval == TV_POLEARM) && - (p_ptr->inventory[i].sval == SV_TRIDENT)) - { - inc_piety(GOD_ALL, 1); - } - } - } - } - - GOD(GOD_AULE) - { - if (grace_delay_trigger()) - { - int i; - - /* Aule likes Dwarves and Dark Elves (Eol's - * influence here) */ - if (!(streq(race_name, "Dwarf") || - streq(race_name, "Petty-dwarf") || - streq(race_name, "Gnome") || - streq(race_name, "Dark-Elf"))) - { - inc_piety(GOD_ALL, -1); - } - - /* Search inventory for axe or hammer - Gain 1 - * point of grace for each hammer or axe */ - for (i = 0; i < INVEN_TOTAL; i++) - { - int tval = p_ptr->inventory[i].tval; - int sval = p_ptr->inventory[i].sval; - - switch (tval) - { - case TV_AXE: - inc_piety(GOD_ALL, 1); - break; - - case TV_HAFTED: - if ((sval == SV_WAR_HAMMER) || - (sval == SV_LUCERN_HAMMER) || - (sval == SV_GREAT_HAMMER)) - { - inc_piety(GOD_ALL, 1); - } - break; - } - } - - /* Praying may grant you a free stone skin - * once in a while */ - if (p_ptr->praying) - { - int chance; - s32b grace; - - inc_piety(GOD_ALL, -2); - grace = p_ptr->grace; /* shorthand */ - - chance = 1; - if (grace >= 50000) - { - chance = 50000; - } - else - { - chance = 50000 - grace; - } - - if (randint(100000) <= 100000 / chance) - { - s16b type = 0; - - if (grace >= 10000) - { - type = SHIELD_COUNTER; - } - - set_shield( - randint(10) + 10 + (grace / 100), - 10 + (grace / 100), - type, - 2 + (grace / 200), - 3 + (grace / 400)); - - msg_print("Aule casts Stone Skin on you."); - } - } - } - } - - GOD(GOD_MANDOS) - { - if (grace_delay_trigger()) - { - /* He loves astral beings */ - if (streq(subrace_name, "LostSoul")) - { - inc_piety(GOD_ALL, 1); - } - - /* He likes High Elves only, though, as races */ - if (!streq(race_name, "High-Elf")) - { - inc_piety(GOD_ALL, -1); - } - - /* Really hates vampires and demons */ - if (streq(subrace_name, "Vampire") || - streq(race_name, "Demon")) - { - inc_piety(GOD_ALL, -10); - } - else - { - inc_piety(GOD_ALL, 2); - } - /* he really doesn't like to be disturbed */ - if (p_ptr->praying) - { - inc_piety(GOD_ALL, -5); - } - } - } - -} - -/* - * Handle certain things once every 10 game turns - * - * Note that a single movement in the overhead wilderness mode - * consumes 132 times as much energy as a normal one... - */ -static void process_world(void) -{ - timer_type *t_ptr; - - int x, y, i, j; - - int regen_amount; - bool_ cave_no_regen = FALSE; - int upkeep_factor = 0; - - dungeon_info_type *d_ptr = &d_info[dungeon_type]; - - cave_type *c_ptr; - - object_type *o_ptr; - u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0; - - - /* - * Every 10 game turns -- which means this section is invoked once - * in a player turn under the normal speed, and 132 times in a move - * in the reduced map mode. - */ - if (turn % 10) return; - - /* - * I don't know if this is the right thing to do because I'm totally - * ignorant (yes, I must admit that...) about the scripting part of - * the game, but since there have been complaints telling us it - * runs terribly slow in the reduced map mode... -- pelpel - * - * Note to coders: if it is desirable to make this active in the - * reduced map mode, remove the if condition surrounding the line - * and move the code inside into every 1000 game turns section. - */ - if (dun_level || (!p_ptr->wild_mode)) - { - /* Handle corruptions */ - process_world_corruptions(); - - /* Handle gods */ - process_world_gods(); - - /* Handle the player song */ - check_music(); - } - - /* Handle the timers */ - for (t_ptr = gl_timers; t_ptr != NULL; t_ptr = t_ptr->next) - { - if (!t_ptr->enabled) continue; - - t_ptr->countdown--; - if (!t_ptr->countdown) - { - t_ptr->countdown = t_ptr->delay; - assert(t_ptr->callback != NULL); - t_ptr->callback(); - } - } - - /* Handle class special actions */ - gere_class_special(); - - /* Check the fate */ - if (fate_option && (p_ptr->lev > 10)) - { - /* - * WAS: == 666 against randint(50000). - * Since CPU's don't know Judeo-Christian / Cabalistic traditions, - * and since comparisons with zero is more efficient in many - * architectures... - */ - if (rand_int(50000) == 0) gain_fate(0); - } - - /*** Is the wielded monsters still hypnotised ***/ - o_ptr = &p_ptr->inventory[INVEN_CARRY]; - - if (o_ptr->k_idx) - { - monster_race *r_ptr = &r_info[o_ptr->pval]; - - if ((randint(1000) < r_ptr->level - ((p_ptr->lev * 2) + get_skill(SKILL_SYMBIOTIC)))) - { - msg_format("%s breaks free from hypnosis!", - symbiote_name(TRUE)); - carried_make_attack_normal(o_ptr->pval); - } - } - - /*** Attempt timed autosave ***/ - if (autosave_t && autosave_freq) - { - if ((turn % ((s32b)autosave_freq * 10)) == 0) - { - is_autosave = TRUE; - msg_print("Autosaving the game..."); - do_cmd_save_game(); - is_autosave = FALSE; - } - } - - - /*** Handle the wilderness/town (sunshine) ***/ - - /* While in town/wilderness and not in the overworld mode */ - if (!dun_level && !p_ptr->wild_mode) - { - /* Hack -- Daybreak/Nighfall in town */ - if ((turn % ((10L * DAY) / 2)) == 0) - { - bool_ dawn; - - /* Check for dawn */ - dawn = ((turn % (10L * DAY)) == 0); - - /* Day breaks */ - if (dawn) - { - /* Message */ - msg_print("The sun has risen."); - - /* Hack -- Scan the town */ - for (y = 0; y < cur_hgt; y++) - { - for (x = 0; x < cur_wid; x++) - { - /* Get the cave grid */ - c_ptr = &cave[y][x]; - - /* Assume lit */ - c_ptr->info |= (CAVE_GLOW); - - /* Hack -- Memorize lit grids if allowed */ - if (view_perma_grids) c_ptr->info |= (CAVE_MARK); - - /* Hack -- Notice spot */ - note_spot(y, x); - } - } - } - - /* Night falls */ - else - { - /* Message */ - msg_print("The sun has set."); - - /* Hack -- Scan the town */ - for (y = 0; y < cur_hgt; y++) - { - for (x = 0; x < cur_wid; x++) - { - /* Get the cave grid */ - c_ptr = &cave[y][x]; - - /* Darken "boring" features */ - if (cave_plain_floor_grid(c_ptr)) - { - /* Forget the grid */ - c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK); - - /* Hack -- Notice spot */ - note_spot(y, x); - } - } - } - } - - /* Update the monsters */ - p_ptr->update |= (PU_MONSTERS); - - /* Redraw map */ - p_ptr->redraw |= (PR_MAP); - - /* Window stuff */ - p_ptr->window |= (PW_OVERHEAD); - } - } - - /* Tell a day passed */ - if (((turn + (DAY_START * 10L)) % (10L * DAY)) == 0) - { - char buf[20]; - - sprintf(buf, "%s", get_day(bst(YEAR, turn) + START_YEAR)); - cmsg_format(TERM_L_GREEN, - "Today it is %s of the %s year of the third age.", - get_month_name(bst(DAY, turn), wizard, FALSE), buf); - } - - /* Set back the rewards once a day */ - if ((turn % (10L * STORE_TURNS)) == 0) - { - /* Select new bounties. */ - if (magik(20)) select_bounties(); - } - - /* Modify loan */ - if (p_ptr->loan) - { - if (p_ptr->loan_time) p_ptr->loan_time--; - - if (((turn % 5000) == 0) && !p_ptr->loan_time) - { - cmsg_print(TERM_RED, "You should pay your loan..."); - - p_ptr->loan += p_ptr->loan / 12; - - if (p_ptr->loan > PY_MAX_GOLD) p_ptr->loan = PY_MAX_GOLD; - - /* Do a nasty stuff */ - if (p_ptr->wild_mode && rand_int(2)) - { - /* Discount player items */ - int z = 0, tries = 200; - object_type *o_ptr = NULL; - - while (tries--) - { - z = rand_int(INVEN_TOTAL); - o_ptr = &p_ptr->inventory[z]; - - if (!o_ptr->k_idx) continue; - - if (o_ptr->discount >= 100) continue; - - break; - } - - if (tries) - { - o_ptr->discount += 70; - if (o_ptr->discount >= 100) o_ptr->discount = 100; - - inven_item_optimize(z); - inven_item_describe(z); - - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - } - } - - else - { - int merc = test_monster_name("Mean-looking mercenary"); - int agent = test_monster_name("Agent of the black market"); - int num = 5 + (p_ptr->lev / 3), z; - - for (z = 0; z < num; z++) - { - int yy, xx, attempts = 200, m_idx; - - /* Summon */ - while (1) - { - scatter(&yy, &xx, p_ptr->py, p_ptr->px, 6); - - /* Accept an empty grid within the boundary */ - if (in_bounds(yy, xx) && cave_floor_bold(yy, xx)) break; - - /* Max number of retries reached */ - if (--attempts == 0) break; - } - - /* All the attempts failed */ - if (attempts == 0) continue; - - /* Summon a monster */ - m_idx = place_monster_one(yy, xx, magik(80) ? merc : agent, - 0, FALSE, MSTATUS_ENEMY); - - /* Level it */ - if (m_idx) - { - monster_type *m_ptr = &m_list[m_idx]; - - m_ptr->exp = MONSTER_EXP(p_ptr->lev * 2); - monster_check_experience(m_idx, TRUE); - } - } - } - } - } - - /*** Process the monsters ***/ - - /* Check for creature generation. */ - if (!p_ptr->wild_mode && - !p_ptr->inside_arena && - !p_ptr->inside_quest && - (rand_int(d_info[(dun_level) ? dungeon_type : DUNGEON_WILDERNESS].max_m_alloc_chance) == 0)) - { - /* Make a new monster */ - if (!(dungeon_flags2 & DF2_NO_NEW_MONSTER)) - { - (void)alloc_monster(MAX_SIGHT + 5, FALSE); - } - } - - /* Hack -- Check for creature regeneration */ - if (!p_ptr->wild_mode && ((turn % 100) == 0)) regen_monsters(); - - - /*** Damage over Time ***/ - - /* Take damage from poison */ - if (p_ptr->poisoned && !p_ptr->invuln) - { - /* Take damage */ - take_hit(1, "poison"); - } - - - /* Vampires take damage from sunlight */ - if (p_ptr->sensible_lite) - { - if ((!dun_level) && (((turn / ((10L * DAY) / 2)) % 2) == 0)) - { - if (cave[p_ptr->py][p_ptr->px].info & (CAVE_GLOW)) - { - /* Take damage */ - msg_print("The sun's rays scorch your undead flesh!"); - take_hit(1, "sunlight"); - cave_no_regen = TRUE; - drop_from_wild(); - } - } - - if ((p_ptr->inventory[INVEN_LITE].tval != 0) && - (p_ptr->inventory[INVEN_LITE].sval >= SV_LITE_GALADRIEL) && - (p_ptr->inventory[INVEN_LITE].sval <= SV_STONE_LORE) && - (p_ptr->inventory[INVEN_LITE].sval != SV_LITE_UNDEATH)) - { - object_type * o_ptr = &p_ptr->inventory[INVEN_LITE]; - char o_name [80]; - char ouch [80]; - - /* Get an object description */ - object_desc(o_name, o_ptr, FALSE, 0); - - msg_format("The %s scorches your undead flesh!", o_name); - - cave_no_regen = TRUE; - - /* Get an object description */ - object_desc(o_name, o_ptr, TRUE, 0); - - sprintf(ouch, "wielding %s", o_name); - take_hit(1, ouch); - } - } - - /* Drown in deep water unless the player have levitation, water walking - water breathing, or magic breathing.*/ - if (!p_ptr->ffall && !p_ptr->walk_water && !p_ptr->magical_breath && - !p_ptr->water_breath && - (cave[p_ptr->py][p_ptr->px].feat == FEAT_DEEP_WATER)) - { - if (calc_total_weight() > ((weight_limit()) / 2)) - { - /* Take damage */ - msg_print("You are drowning!"); - take_hit(randint(p_ptr->lev), "drowning"); - cave_no_regen = TRUE; - } - } - - - /* Spectres -- take damage when moving through walls */ - - /* - * Added: ANYBODY takes damage if inside through walls - * without wraith form -- NOTE: Spectres will never be - * reduced below 0 hp by being inside a stone wall; others - * WILL BE! - */ - if (!cave_floor_bold(p_ptr->py, p_ptr->px)) - { - int feature = cave[p_ptr->py][p_ptr->px].feat; - - /* Player can walk through or fly over trees */ - if ((has_ability(AB_TREE_WALK) || p_ptr->fly) && (feature == FEAT_TREES)) - { - /* Do nothing */ - } - /* Player can climb over mountains */ - else if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB)) - { - /* Do nothing */ - } - else if (PRACE_FLAG(PR1_SEMI_WRAITH) && (!p_ptr->wraith_form) && (f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_CAN_PASS)) - { - int amt = 1 + ((p_ptr->lev) / 5); - - cave_no_regen = TRUE; - if (amt > p_ptr->chp - 1) amt = p_ptr->chp - 1; - take_hit(amt, " walls ..."); - } - } - - - /* Take damage from cuts */ - if ((p_ptr->cut) && !(p_ptr->invuln)) - { - /* Mortal wound or Deep Gash */ - if (p_ptr->cut > 200) - { - i = 3; - } - - /* Severe cut */ - else if (p_ptr->cut > 100) - { - i = 2; - } - - /* Other cuts */ - else - { - i = 1; - } - - /* Take damage */ - take_hit(i, "a fatal wound"); - } - - - /*** Check the Food, and Regenerate ***/ - - /* Digest normally */ - if (p_ptr->food < PY_FOOD_MAX) - { - /* Every 100 game turns */ - if ((turn % 100) == 0) - { - int speed_use = p_ptr->pspeed; - - /* Maximum */ - if (speed_use > 199) - { - speed_use = 199; - } - - /* Minimum */ - else if (speed_use < 0) - { - speed_use = 0; - } - - /* Basic digestion rate based on speed */ - i = extract_energy[speed_use] * 2; - - /* Regeneration takes more food */ - if (p_ptr->regenerate) i += 30; - - /* Regeneration takes more food */ - if (p_ptr->tim_regen) i += p_ptr->tim_regen_pow / 10; - - /* Invisibility consume a lot of food */ - i += p_ptr->invis / 2; - - /* Invulnerability consume a lot of food */ - if (p_ptr->invuln) i += 40; - - /* Wraith Form consume a lot of food */ - if (p_ptr->wraith_form) i += 30; - - /* Get the weapon */ - o_ptr = &p_ptr->inventory[INVEN_WIELD]; - - /* Examine the sword */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Hitpoints multiplier consume a lot of food */ - if (o_ptr->k_idx && (f2 & (TR2_LIFE))) i += o_ptr->pval * 5; - - /* Slow digestion takes less food */ - if (p_ptr->slow_digest) i -= 10; - - /* Minimal digestion */ - if (i < 1) i = 1; - - /* Digest some food */ - (void)set_food(p_ptr->food - i); - } - } - - /* Digest quickly when gorged */ - else - { - /* Digest a lot of food */ - (void)set_food(p_ptr->food - 100); - } - - /* Starve to death (slowly) */ - if (p_ptr->food < PY_FOOD_STARVE) - { - /* Calculate damage */ - i = (PY_FOOD_STARVE - p_ptr->food) / 10; - - /* Take damage */ - if (!(p_ptr->invuln)) take_hit(i, "starvation"); - } - - /* Default regeneration */ - regen_amount = PY_REGEN_NORMAL; - - /* Getting Weak */ - if (p_ptr->food < PY_FOOD_WEAK) - { - /* Lower regeneration */ - if (p_ptr->food < PY_FOOD_STARVE) - { - regen_amount = 0; - } - else if (p_ptr->food < PY_FOOD_FAINT) - { - regen_amount = PY_REGEN_FAINT; - } - else - { - regen_amount = PY_REGEN_WEAK; - } - - /* Getting Faint */ - if (p_ptr->food < PY_FOOD_FAINT) - { - /* Faint occasionally */ - if (!p_ptr->paralyzed && (rand_int(100) < 10)) - { - /* Message */ - msg_print("You faint from the lack of food."); - disturb(1); - - /* Hack -- faint (bypass free action) */ - (void)set_paralyzed(1 + rand_int(5)); - } - } - } - - /* Are we walking the pattern? */ - if (!p_ptr->wild_mode && pattern_effect()) - { - cave_no_regen = TRUE; - } - else - { - /* Regeneration ability */ - if (p_ptr->regenerate) - { - regen_amount = regen_amount * 2; - } - } - - - /* Searching or Resting */ - if (p_ptr->searching || resting) - { - regen_amount = regen_amount * 2; - } - - if (total_friends) - { - int upkeep_divider = 20; - - if (has_ability(AB_PERFECT_CASTING)) - upkeep_divider = 15; - - if (total_friends > 1 + (p_ptr->lev / (upkeep_divider))) - { - upkeep_factor = (total_friend_levels); - - if (upkeep_factor > 100) upkeep_factor = 100; - else if (upkeep_factor < 10) upkeep_factor = 10; - } - } - - /* Regenerate the mana */ - if (p_ptr->csp < p_ptr->msp) - { - if (upkeep_factor) - { - s16b upkeep_regen = (((100 - upkeep_factor) * regen_amount) / 100); - regenmana(upkeep_regen); - } - else - { - regenmana(regen_amount); - } - } - - /* Eru piety incraese with time */ - if (((turn % 100) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode)) - { - NOT_PRAY_GOD(GOD_ERU) - { - int inc = wisdom_scale(10); - - /* Increase by wisdom/4 */ - if (!inc) inc = 1; - inc_piety(GOD_ERU, inc); - } - } - /* Most gods piety decrease with time */ - if (((turn % 300) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level)) - { - GOD(GOD_MANWE) - { - int dec = 4 - wisdom_scale(3); - - PRAY_GOD(GOD_MANWE) - dec++; - if (PRACE_FLAG(PR1_ELF)) - dec -= wisdom_scale(2); - if (dec < 1) dec = 1; - inc_piety(GOD_MANWE, -dec); - } - GOD(GOD_MELKOR) - { - int dec = 8 - wisdom_scale(6); - - PRAY_GOD(GOD_MELKOR) - dec++; - if (PRACE_FLAG(PR1_ELF)) - dec += 5 - wisdom_scale(4); - if (dec < 1) dec = 1; - inc_piety(GOD_MELKOR, -dec); - } - PRAY_GOD(GOD_TULKAS) - { - int dec = 4 - wisdom_scale(3); - - if (dec < 1) dec = 1; - inc_piety(GOD_TULKAS, -dec); - } - } - /* Yavanna piety decrease with time */ - if (((turn % 400) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level)) - { - GOD(GOD_YAVANNA) - { - int dec = 5 - wisdom_scale(3); - - /* Blech what an hideous hack */ - if (!strcmp(rp_ptr->title + rp_name, "Ent")) - dec -= wisdom_scale(2); - if (dec < 1) dec = 1; - inc_piety(GOD_YAVANNA, -dec); - } - } - p_ptr->did_nothing = FALSE; - - /* Increase regen by tim regen */ - if (p_ptr->tim_regen) regen_amount += p_ptr->tim_regen_pow; - - /* Poisoned or cut yields no healing */ - if (p_ptr->poisoned) regen_amount = 0; - if (p_ptr->cut) regen_amount = 0; - - /* Special floor -- Pattern, in a wall -- yields no healing */ - if (cave_no_regen) regen_amount = 0; - - /* Being over grass allows Yavanna to regen you */ - PRAY_GOD(GOD_YAVANNA) - { - if (cave[p_ptr->py][p_ptr->px].feat == FEAT_GRASS) - { - regen_amount += 200 + wisdom_scale(800); - } - } - - /* Regenerate Hit Points if needed */ - if ((p_ptr->chp < p_ptr->mhp) && !cave_no_regen) - { - if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_END) && - (cave[p_ptr->py][p_ptr->px].feat >= FEAT_PATTERN_START)) - { - /* Hmmm. this should never happen? */ - regenhp(regen_amount / 5); - } - else - { - regenhp(regen_amount); - } - } - - - /*** Timeout Various Things ***/ - - /* Handle temporary stat drains */ - for (i = 0; i < 6; i++) - { - if (p_ptr->stat_cnt[i] > 0) - { - p_ptr->stat_cnt[i]--; - if (p_ptr->stat_cnt[i] == 0) - { - do_res_stat(i, FALSE); - } - } - } - - /* Hack -- Hallucinating */ - if (p_ptr->image) - { - (void)set_image(p_ptr->image - 1); - } - - /* Holy Aura */ - if (p_ptr->holy) - { - (void)set_holy(p_ptr->holy - 1); - } - - /* Soul absorbtion */ - if (p_ptr->absorb_soul) - { - (void)set_absorb_soul(p_ptr->absorb_soul - 1); - } - - /* Undead loose Death Points */ - if (p_ptr->necro_extra & CLASS_UNDEAD) - { - int old_chp = p_ptr->chp; - int warning = (p_ptr->mhp * hitpoint_warn / 10); - - /* Bypass invulnerability and wraithform */ - p_ptr->chp--; - - /* Display the hitpoints */ - p_ptr->redraw |= (PR_HP); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - - /* Dead player */ - if (p_ptr->chp < 0) - { - bool_ old_quick = quick_messages; - - /* Sound */ - sound(SOUND_DEATH); - - /* Hack -- Note death */ - if (!last_words) - { - msg_print("You die."); - msg_print(NULL); - } - else - { - char death_message[80]; - - (void)get_rnd_line("death.txt", death_message); - msg_print(death_message); - } - - /* Note cause of death */ - (void)strcpy(died_from, "being undead too long"); - - if (p_ptr->image) strcat(died_from, "(?)"); - - /* No longer a winner */ - total_winner = FALSE; - - /* Leaving */ - p_ptr->leaving = TRUE; - - /* Note death */ - death = TRUE; - - quick_messages = FALSE; - if (get_check("Make a last screenshot? ")) - { - do_cmd_html_dump(); - } - quick_messages = old_quick; - - /* Dead */ - return; - } - - /* Hitpoint warning */ - if (p_ptr->chp < warning) - { - /* Hack -- bell on first notice */ - if (alert_hitpoint && (old_chp > warning)) bell(); - - sound(SOUND_WARN); - - /* Message */ - msg_print("*** LOW DEATHPOINT WARNING! ***"); - msg_print(NULL); - } - } - - /* Walk water */ - if (p_ptr->walk_water) - { - (void)set_walk_water(p_ptr->walk_water - 1); - } - - /* True Strike */ - if (p_ptr->strike) - { - (void)set_strike(p_ptr->strike - 1); - } - - /* Meditation */ - if (p_ptr->meditation) - { - (void)set_meditation(p_ptr->meditation - 1); - } - - /* Timed project */ - if (p_ptr->tim_project) - { - (void)set_project(p_ptr->tim_project - 1, p_ptr->tim_project_gf, p_ptr->tim_project_dam, p_ptr->tim_project_rad, p_ptr->tim_project_flag); - } - - /* Timed roots */ - if (p_ptr->tim_roots) - { - (void)set_roots(p_ptr->tim_roots - 1, p_ptr->tim_roots_ac, p_ptr->tim_roots_dam); - } - - /* Timed breath */ - if (p_ptr->tim_water_breath) - { - (void)set_tim_breath(p_ptr->tim_water_breath - 1, FALSE); - } - if (p_ptr->tim_magic_breath) - { - (void)set_tim_breath(p_ptr->tim_magic_breath - 1, TRUE); - } - - /* Timed precognition */ - if (p_ptr->tim_precognition > 0) - { - set_tim_precognition(p_ptr->tim_precognition - 1); - } - - /* Timed regen */ - if (p_ptr->tim_regen) - { - (void)set_tim_regen(p_ptr->tim_regen - 1, p_ptr->tim_regen_pow); - } - - /* Timed Disrupt shield */ - if (p_ptr->disrupt_shield) - { - (void)set_disrupt_shield(p_ptr->disrupt_shield - 1); - } - - /* Timed Parasite */ - if (p_ptr->parasite) - { - (void)set_parasite(p_ptr->parasite - 1, p_ptr->parasite_r_idx); - } - - /* Timed Reflection */ - if (p_ptr->tim_reflect) - { - (void)set_tim_reflect(p_ptr->tim_reflect - 1); - } - - /* Timed Prob Travel */ - if (p_ptr->prob_travel) - { - (void)set_prob_travel(p_ptr->prob_travel - 1); - } - - /* Timed Time Resistance */ - if (p_ptr->tim_res_time) - { - (void)set_tim_res_time(p_ptr->tim_res_time - 1); - } - - /* Timed Levitation */ - if (p_ptr->tim_ffall) - { - (void)set_tim_ffall(p_ptr->tim_ffall - 1); - } - if (p_ptr->tim_fly) - { - (void)set_tim_fly(p_ptr->tim_fly - 1); - } - - /* Thunderstorm */ - if (p_ptr->tim_thunder) - { - int dam = damroll(p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2); - int i, tries = 600; - monster_type *m_ptr = NULL; - - while (tries) - { - /* Access the monster */ - m_ptr = &m_list[i = rand_range(1, m_max - 1)]; - - tries--; - - /* Ignore "dead" monsters */ - if (!m_ptr->r_idx) continue; - - /* Cant see ? cant hit */ - if (!los(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx)) continue; - - /* Do not hurt friends! */ - if (is_friend(m_ptr) >= 0) continue; - break; - } - - if (tries) - { - char m_name[80]; - - monster_desc(m_name, m_ptr, 0); - msg_format("Lightning strikes %s.", m_name); - project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_ELEC, - PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); - project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_LITE, - PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); - project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_SOUND, - PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); - } - - (void)set_tim_thunder(p_ptr->tim_thunder - 1, p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2); - } - - /* Poisonned hands */ - if (p_ptr->tim_poison) - { - (void)set_poison(p_ptr->tim_poison - 1); - } - - /* Timed Fire Aura */ - if (p_ptr->tim_fire_aura) - { - (void)set_tim_fire_aura(p_ptr->tim_fire_aura - 1); - } - - /* Brightness */ - if (p_ptr->tim_lite) - { - (void)set_lite(p_ptr->tim_lite - 1); - } - - /* Blindness */ - if (p_ptr->blind) - { - (void)set_blind(p_ptr->blind - 1); - } - - /* Timed no_breeds */ - if (no_breeds) - { - (void)set_no_breeders(no_breeds - 1); - } - - /* Timed mimic */ - if (p_ptr->tim_mimic) - { - (void)set_mimic(p_ptr->tim_mimic - 1, p_ptr->mimic_form, p_ptr->mimic_level); - } - - /* Timed special move commands */ - if (p_ptr->immov_cntr) - { - p_ptr->immov_cntr--; - } - - /* Timed invisibility */ - if (p_ptr->tim_invisible) - { - (void)set_invis(p_ptr->tim_invisible - 1, p_ptr->tim_inv_pow); - } - - /* Times see-invisible */ - if (p_ptr->tim_invis) - { - (void)set_tim_invis(p_ptr->tim_invis - 1); - } - - /* Timed esp */ - if (p_ptr->tim_esp) - { - (void)set_tim_esp(p_ptr->tim_esp - 1); - } - - /* Timed infra-vision */ - if (p_ptr->tim_infra) - { - (void)set_tim_infra(p_ptr->tim_infra - 1); - } - - /* Paralysis */ - if (p_ptr->paralyzed) - { - dec_paralyzed(); - } - - /* Confusion */ - if (p_ptr->confused) - { - (void)set_confused(p_ptr->confused - 1); - } - - /* Afraid */ - if (p_ptr->afraid) - { - (void)set_afraid(p_ptr->afraid - 1); - } - - /* Fast */ - if (p_ptr->fast) - { - (void)set_fast(p_ptr->fast - 1, p_ptr->speed_factor); - } - - /* Light speed */ - if (p_ptr->lightspeed) - { - (void)set_light_speed(p_ptr->lightspeed - 1); - } - - /* Slow */ - if (p_ptr->slow) - { - (void)set_slow(p_ptr->slow - 1); - } - - /* Protection from evil */ - if (p_ptr->protevil) - { - (void)set_protevil(p_ptr->protevil - 1); - } - - /* Protection from good */ - if (p_ptr->protgood) - { - (void)set_protgood(p_ptr->protgood - 1); - } - - /* Protection from undead */ - if (p_ptr->protundead) - { - (void)set_protundead(p_ptr->protundead - 1); - } - - /* Invulnerability */ - if (p_ptr->invuln) - { - (void)set_invuln(p_ptr->invuln - 1); - } - - /* Wraith form */ - if (p_ptr->tim_wraith) - { - (void)set_shadow(p_ptr->tim_wraith - 1); - } - - /* Heroism */ - if (p_ptr->hero) - { - (void)set_hero(p_ptr->hero - 1); - } - - /* Super Heroism */ - if (p_ptr->shero) - { - (void)set_shero(p_ptr->shero - 1); - } - - /* Blessed */ - if (p_ptr->blessed) - { - (void)set_blessed(p_ptr->blessed - 1); - } - - /* Shield */ - if (p_ptr->shield) - { - (void)set_shield(p_ptr->shield - 1, p_ptr->shield_power, p_ptr->shield_opt, p_ptr->shield_power_opt, p_ptr->shield_power_opt2); - } - - /* Oppose Acid */ - if (p_ptr->oppose_acid) - { - (void)set_oppose_acid(p_ptr->oppose_acid - 1); - } - - /* Oppose Lightning */ - if (p_ptr->oppose_elec) - { - (void)set_oppose_elec(p_ptr->oppose_elec - 1); - } - - /* Oppose Fire */ - if (p_ptr->oppose_fire) - { - (void)set_oppose_fire(p_ptr->oppose_fire - 1); - } - - /* Oppose Cold */ - if (p_ptr->oppose_cold) - { - (void)set_oppose_cold(p_ptr->oppose_cold - 1); - } - - /* Oppose Poison */ - if (p_ptr->oppose_pois) - { - (void)set_oppose_pois(p_ptr->oppose_pois - 1); - } - - /* Oppose Light & Dark */ - if (p_ptr->oppose_ld) - { - (void)set_oppose_ld(p_ptr->oppose_ld - 1); - } - - /* Oppose Chaos & Confusion */ - if (p_ptr->oppose_cc) - { - (void)set_oppose_cc(p_ptr->oppose_cc - 1); - } - - /* Oppose Sound & Shards */ - if (p_ptr->oppose_ss) - { - (void)set_oppose_ss(p_ptr->oppose_ss - 1); - } - - /* Oppose Nexus */ - if (p_ptr->oppose_nex) - { - (void)set_oppose_nex(p_ptr->oppose_nex - 1); - } - - /* Mental Barrier */ - if (p_ptr->tim_mental_barrier) - { - (void)set_mental_barrier(p_ptr->tim_mental_barrier - 1); - } - - /* The rush */ - if (p_ptr->rush) - { - (void)set_rush(p_ptr->rush - 1); - } - - - /* Timed mimicry */ - if (get_skill(SKILL_MIMICRY)) - { - /* Extract the value and the flags */ - u32b value = p_ptr->mimic_extra >> 16; - - u32b att = p_ptr->mimic_extra & 0xFFFF; - - if ((att & CLASS_LEGS) || (att & CLASS_WALL) || (att & CLASS_ARMS)) - { - value--; - - if (!value) - { - if (att & CLASS_LEGS) msg_print("You lose your extra pair of legs."); - if (att & CLASS_ARMS) msg_print("You lose your extra pair of arms."); - if (att & CLASS_WALL) msg_print("You lose your affinity for walls."); - - att &= ~(CLASS_ARMS); - att &= ~(CLASS_LEGS); - att &= ~(CLASS_WALL); - - if (disturb_state) disturb(0); - } - - p_ptr->update |= (PU_BODY); - p_ptr->mimic_extra = att + (value << 16); - } - } - - - /*** Poison and Stun and Cut ***/ - - /* Poison */ - if (p_ptr->poisoned) - { - int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1); - - /* Apply some healing */ - (void)set_poisoned(p_ptr->poisoned - adjust); - } - - /* Stun */ - if (p_ptr->stun) - { - int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1); - - /* Apply some healing */ - (void)set_stun(p_ptr->stun - adjust); - } - - /* Cut */ - if (p_ptr->cut) - { - int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1); - - /* Hack -- Truly "mortal" wound */ - if (p_ptr->cut > 1000) adjust = 0; - - /* Apply some healing */ - (void)set_cut(p_ptr->cut - adjust); - } - - /* Hack - damage done by the dungeon -SC- */ - if ((dun_level != 0) && (d_ptr->d_frequency[0] != 0)) - { - int i, j, k; - - /* Apply damage to every grid in the dungeon */ - for (i = 0; i < 4; i++) - { - /* Check the frequency */ - if (d_ptr->d_frequency[i] == 0) continue; - - if (((turn % d_ptr->d_frequency[i]) == 0) && - ((d_ptr->d_side[i] != 0) || (d_ptr->d_dice[i] != 0))) - { - for (j = 0; j < cur_hgt - 1; j++) - { - for (k = 0; k < cur_wid - 1; k++) - { - int l, dam = 0; - - if (!(dungeon_flags1 & DF1_DAMAGE_FEAT)) - { - /* If the grid is empty, skip it */ - if ((cave[j][k].o_idx == 0) && - ((j != p_ptr->py) && (i != p_ptr->px))) continue; - } - - /* Let's not hurt poor monsters */ - if (cave[j][k].m_idx) continue; - - /* Roll damage */ - for (l = 0; l < d_ptr->d_dice[i]; l++) - { - dam += randint(d_ptr->d_side[i]); - } - - /* Apply damage */ - project( -100, 0, j, k, dam, d_ptr->d_type[i], - PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); - } - } - } - } - } - - /* handle spell effects */ - if (!p_ptr->wild_mode) - { - /* - * I noticed significant performance degrade after the introduction - * of staying spell effects. I believe serious optimisation effort - * is required before another release. - * - * More important is to fix that display weirdness... - * - * It seems that the game never expects that monster deaths and - * terrain feature changes should happen here... Moving these - * to process_player() [before resting code, with "every 10 game turn" - * 'if'] may or may not fix the problem... -- pelpel to DG - */ - for (j = 0; j < cur_hgt - 1; j++) - { - for (i = 0; i < cur_wid - 1; i++) - { - int e = cave[j][i].effect; - - if (e) - { - effect_type *e_ptr = &effects[e]; - - if (e_ptr->time) - { - /* Apply damage */ - project(0, 0, j, i, e_ptr->dam, e_ptr->type, - PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); - } - else - { - cave[j][i].effect = 0; - } - - if ((e_ptr->flags & EFF_WAVE) && !(e_ptr->flags & EFF_LAST)) - { - if (distance(e_ptr->cy, e_ptr->cx, j, i) < e_ptr->rad - 1) - cave[j][i].effect = 0; - } - else if ((e_ptr->flags & EFF_STORM) && !(e_ptr->flags & EFF_LAST)) - { - cave[j][i].effect = 0; - } - - lite_spot(j, i); - } - } - } - - /* Reduce & handle effects */ - for (i = 0; i < MAX_EFFECTS; i++) - { - /* Skip empty slots */ - if (effects[i].time == 0) continue; - - /* Reduce duration */ - effects[i].time--; - - /* Creates a "wave" effect*/ - if (effects[i].flags & EFF_WAVE) - { - effect_type *e_ptr = &effects[i]; - int x, y, z; - - e_ptr->rad++; - - /* What a frelling ugly line of ifs ... */ - if (effects[i].flags & EFF_DIR8) - for (y = e_ptr->cy - e_ptr->rad, z = 0; y <= e_ptr->cy; y++, z++) - { - for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++) - { - if (!in_bounds(y, x)) continue; - - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) - cave[y][x].effect = i; - } - } - else if (effects[i].flags & EFF_DIR2) - for (y = e_ptr->cy, z = e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++, z--) - { - for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++) - { - if (!in_bounds(y, x)) continue; - - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) - cave[y][x].effect = i; - } - } - else if (effects[i].flags & EFF_DIR6) - for (x = e_ptr->cx, z = e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++, z--) - { - for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++) - { - if (!in_bounds(y, x)) continue; - - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) - cave[y][x].effect = i; - } - } - else if (effects[i].flags & EFF_DIR4) - for (x = e_ptr->cx - e_ptr->rad, z = 0; x <= e_ptr->cx; x++, z++) - { - for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++) - { - if (!in_bounds(y, x)) continue; - - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) - cave[y][x].effect = i; - } - } - else if (effects[i].flags & EFF_DIR9) - for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++) - { - for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++) - { - if (!in_bounds(y, x)) continue; - - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) - cave[y][x].effect = i; - } - } - else if (effects[i].flags & EFF_DIR1) - for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++) - { - for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++) - { - if (!in_bounds(y, x)) continue; - - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) - cave[y][x].effect = i; - } - } - else if (effects[i].flags & EFF_DIR7) - for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++) - { - for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++) - { - if (!in_bounds(y, x)) continue; - - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) - cave[y][x].effect = i; - } - } - else if (effects[i].flags & EFF_DIR3) - for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++) - { - for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++) - { - if (!in_bounds(y, x)) continue; - - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) - cave[y][x].effect = i; - } - } - else - for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++) - { - for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++) - { - if (!in_bounds(y, x)) continue; - - /* This is *slow* -- pelpel */ - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) - cave[y][x].effect = i; - } - } - } - /* Creates a "storm" effect*/ - else if (effects[i].flags & EFF_STORM) - { - effect_type *e_ptr = &effects[i]; - int x, y; - - e_ptr->cy = p_ptr->py; - e_ptr->cx = p_ptr->px; - for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++) - { - for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++) - { - if (!in_bounds(y, x)) continue; - - if (los(e_ptr->cy, e_ptr->cx, y, x) && - (distance(e_ptr->cy, e_ptr->cx, y, x) <= e_ptr->rad)) - { - cave[y][x].effect = i; - lite_spot(y, x); - } - } - } - } - } - - apply_effect(p_ptr->py, p_ptr->px); - } - - /* Arg cannot breath? */ - if ((dungeon_flags2 & DF2_WATER_BREATH) && (!p_ptr->water_breath)) - { - cmsg_print(TERM_L_RED, "You cannot breathe water! You suffocate!"); - take_hit(damroll(3, p_ptr->lev), "suffocating"); - } - if ((dungeon_flags2 & DF2_NO_BREATH) && (!p_ptr->magical_breath)) - { - cmsg_print(TERM_L_RED, "There is no air there! You suffocate!"); - take_hit(damroll(3, p_ptr->lev), "suffocating"); - } - - /* - * Every 1500 turns, warn about any Black Breath not gotten from - * an equipped object, and stop any resting. -LM- - * - * It's apparent that someone has halved the frequency... -- pelpel - */ - if (((turn % 3000) == 0) && p_ptr->black_breath) - { - u32b f1, f2, f3, f4, f5; - - bool_ be_silent = FALSE; - - /* check all equipment for the Black Breath flag. */ - for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) - { - o_ptr = &p_ptr->inventory[i]; - - /* Skip non-objects */ - if (!o_ptr->k_idx) continue; - - /* Extract the item flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* No messages if object has the flag, to avoid annoyance. */ - if (f4 & (TR4_BLACK_BREATH)) be_silent = TRUE; - - } - /* If we are allowed to speak, warn and disturb. */ - - if (!be_silent) - { - cmsg_print(TERM_L_DARK, "The Black Breath saps your soul!"); - disturb(0); - } - } - - - /*** Process Light ***/ - - /* Check for light being wielded */ - o_ptr = &p_ptr->inventory[INVEN_LITE]; - - /* Burn some fuel in the current lite */ - if (o_ptr->tval == TV_LITE) - { - /* Extract the item flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Hack -- Use some fuel */ - if ((f4 & TR4_FUEL_LITE) && (o_ptr->timeout > 0)) - { - /* Decrease life-span */ - o_ptr->timeout--; - - /* Hack -- notice interesting fuel steps */ - if ((o_ptr->timeout < 100) || ((o_ptr->timeout % 100) == 0)) - { - /* Window stuff */ - p_ptr->window |= (PW_EQUIP); - } - - /* Hack -- Special treatment when blind */ - if (p_ptr->blind) - { - /* Hack -- save some light for later */ - if (o_ptr->timeout == 0) o_ptr->timeout++; - } - - /* The light is now out */ - else if (o_ptr->timeout < 1) - { - disturb(0); - cmsg_print(TERM_YELLOW, "Your light has gone out!"); - } - - /* The light is getting dim */ - else if ((o_ptr->timeout < 100) && (o_ptr->timeout % 10 == 0)) - { - if (disturb_minor) disturb(0); - cmsg_print(TERM_YELLOW, "Your light is growing faint."); - } - } - } - - /* Calculate torch radius */ - p_ptr->update |= (PU_TORCH); - - - /*** Process Inventory ***/ - - /* - * Handle experience draining. In Oangband, the effect is worse, - * especially for high-level characters. As per Tolkien, hobbits - * are resistant. - */ - if (p_ptr->black_breath) - { - byte chance = 0; - int plev = p_ptr->lev; - - if (PRACE_FLAG(PR1_RESIST_BLACK_BREATH)) chance = 2; - else chance = 5; - - if ((rand_int(100) < chance) && (p_ptr->exp > 0)) - { - p_ptr->exp -= 1 + plev / 5; - p_ptr->max_exp -= 1 + plev / 5; - (void)do_dec_stat(rand_int(6), STAT_DEC_NORMAL); - check_experience(); - } - } - - /* Drain Mana */ - if (p_ptr->drain_mana && p_ptr->csp) - { - p_ptr->csp -= p_ptr->drain_mana; - if (magik(30)) p_ptr->csp -= p_ptr->drain_mana; - - if (p_ptr->csp < 0) - { - p_ptr->csp = 0; - disturb(0); - } - - /* Redraw */ - p_ptr->redraw |= (PR_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - } - - /* Partial summons drain mana */ - if (p_ptr->maintain_sum) - { - u32b oldcsp = p_ptr->csp; - p_ptr->csp -= p_ptr->maintain_sum / 10000; - - if (p_ptr->csp < 0) - { - p_ptr->csp = 0; - disturb(0); - - p_ptr->maintain_sum = 0; - } - else - { - /* Leave behind any fractional sp */ - p_ptr->maintain_sum -= (oldcsp - p_ptr->csp) * 10000; - } - - /* Redraw */ - p_ptr->redraw |= (PR_MANA); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - - } - - /* Drain Hitpoints */ - if (p_ptr->drain_life) - { - int drain = p_ptr->drain_life + rand_int(p_ptr->mhp / 100); - - p_ptr->chp -= (drain < p_ptr->chp ? drain : p_ptr->chp); - - if (p_ptr->chp == 0) - { - disturb(0); - } - - /* Redraw */ - p_ptr->redraw |= (PR_HP); - - /* Window stuff */ - p_ptr->window |= (PW_PLAYER); - - } - - /* Handle experience draining */ - if (p_ptr->exp_drain) - { - if ((rand_int(100) < 10) && (p_ptr->exp > 0)) - { - p_ptr->exp--; - p_ptr->max_exp--; - check_experience(); - } - } - - /* Process equipment */ - for (j = 0, i = INVEN_WIELD; i < INVEN_TOTAL; i++) - { - /* Get the object */ - o_ptr = &p_ptr->inventory[i]; - - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - - /* TY Curse */ - if ((f3 & TR3_TY_CURSE) && (rand_int(TY_CURSE_CHANCE) == 0)) - { - activate_ty_curse(); - } - - /* DG Curse */ - if ((f4 & TR4_DG_CURSE) && (rand_int(DG_CURSE_CHANCE) == 0)) - { - activate_dg_curse(); - - /* The object recurse itself ! */ - o_ptr->ident |= IDENT_CURSED; - } - - /* Auto Curse */ - if ((f3 & TR3_AUTO_CURSE) && (rand_int(AUTO_CURSE_CHANCE) == 0)) - { - /* The object recurse itself ! */ - o_ptr->ident |= IDENT_CURSED; - } - - /* - * Hack: Uncursed teleporting items (e.g. Dragon Weapons) - * can actually be useful! - */ - if ((f3 & TR3_TELEPORT) && (rand_int(100) < 1)) - { - if ((o_ptr->ident & IDENT_CURSED) && !p_ptr->anti_tele) - { - disturb(0); - - /* Teleport player */ - teleport_player(40); - } - else - { - if (p_ptr->wild_mode || - (o_ptr->note && strchr(quark_str(o_ptr->note), '.'))) - { - /* Do nothing */ - /* msg_print("Teleport aborted.") */; - } - else if (get_check("Teleport? ")) - { - disturb(0); - teleport_player(50); - } - } - } - - - /* Skip non-objects */ - if (!o_ptr->k_idx) continue; - - /* Hack: Skip wielded lights that need fuel (already handled above) */ - if ((i == INVEN_LITE) && (o_ptr->tval == TV_LITE) && (f4 & TR4_FUEL_LITE)) continue; - - /* Recharge activatable objects */ - if (o_ptr->timeout > 0) - { - /* Recharge */ - o_ptr->timeout--; - - /* Notice changes */ - if (o_ptr->timeout == 0) - { - recharged_notice(o_ptr); - j++; - } - } - - /* Recharge second spell in Mage Staffs of Spells */ - if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && (o_ptr->xtra2 > 0)) - { - /* Recharge */ - o_ptr->xtra2--; - - /* Notice changes */ - if (o_ptr->xtra2 == 0) j++; - } - } - - /* Notice changes */ - if (j) - { - /* Window stuff */ - p_ptr->window |= (PW_EQUIP); - } - - /* Recharge rods */ - for (j = 0, i = 0; i < INVEN_TOTAL; i++) - { - o_ptr = &p_ptr->inventory[i]; - - /* Skip non-objects */ - if (!o_ptr->k_idx) continue; - - /* Examine the rod */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Temporary items are destroyed */ - if (f5 & TR5_TEMPORARY) - { - o_ptr->timeout--; - - if (o_ptr->timeout <= 0) - { - inc_stack_size(i, -99); - - /* Combine and Reorder pack */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - } - } - - /* Examine all charging rods or stacks of charging rods. */ - if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2)) - { - /* Increase the rod's mana. */ - o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1; - - /* Always notice */ - j++; - - /* Notice changes, provide message if object is inscribed. */ - if (o_ptr->timeout >= o_ptr->pval2) - { - o_ptr->timeout = o_ptr->pval2; - recharged_notice(o_ptr); - } - } - - /* Examine all charging random artifacts */ - if ((f5 & TR5_ACTIVATE_NO_WIELD) && (o_ptr->timeout > 0)) - { - /* Charge it */ - o_ptr->timeout--; - - /* Notice changes */ - if (o_ptr->timeout == 0) - { - j++; - recharged_notice(o_ptr); - } - } - - /* Decay objects in pack */ - if (decays(o_ptr)) - { - /* Decay it */ - if (o_ptr->pval != 0) - { - if (o_ptr->timeout > 0) - { - if (dungeon_flags1 & DF1_HOT) - { - o_ptr->pval -= 2; - } - else if ((dungeon_flags1 & DF1_COLD) && rand_int(2)) - { - if (magik(50)) o_ptr->pval--; - } - else - { - o_ptr->pval--; - } - } - - if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--; - - /* Notice changes */ - if (o_ptr->pval <= 0) - { - pack_decay(i); - j++; - } - } - } - - /* Hatch eggs */ - if (o_ptr->tval == TV_EGG) - { - int mx, my; - - if (o_ptr->timeout == 0) - { - o_ptr->pval--; - - /* Notice changes */ - if (o_ptr->pval <= 0) - { - monster_type *m_ptr; - monster_race *r_ptr; - - mx = p_ptr->px; - my = p_ptr->py + 1; - get_pos_player(5, &my, &mx); - msg_print("Your egg hatches!"); - place_monster_aux(my, mx, o_ptr->pval2, FALSE, FALSE, MSTATUS_PET); - - m_ptr = &m_list[cave[my][mx].m_idx]; - r_ptr = race_inf(m_ptr); - - if ((r_ptr->flags9 & RF9_IMPRESED) && can_create_companion()) - { - msg_format("And you have given the imprint to your %s!", - r_name + r_ptr->name); - m_ptr->status = MSTATUS_COMPANION; - } - - inc_stack_size(i, -1); - - j++; - } - } - } - } - - /* Notice changes */ - if (j) - { - /* Combine pack */ - p_ptr->notice |= (PN_COMBINE); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN); - } - - /*** Process Objects ***/ - - /* Process objects */ - for (i = 1; i < o_max; i++) - { - /* Access object */ - o_ptr = &o_list[i]; - - /* Skip dead objects */ - if (!o_ptr->k_idx) continue; - - /* Examine the rod */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Temporary items are destroyed */ - if (f5 & TR5_TEMPORARY) - { - o_ptr->timeout--; - - if (o_ptr->timeout <= 0) - { - floor_item_increase(i, -99); - floor_item_optimize(i); - - /* Combine and Reorder pack */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - } - } - - /* Recharge rods on the ground. No messages. */ - if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2)) - { - /* Increase the rod's mana. */ - o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1; - - /* Do not overflow */ - if (o_ptr->timeout >= o_ptr->pval2) - { - o_ptr->timeout = o_ptr->pval2; - } - } - - /* Decay objects on the ground*/ - if (decays(o_ptr)) - { - /* Decay it */ - if (o_ptr->pval != 0) - { - if (o_ptr->timeout > 0) - { - if (dungeon_flags1 & DF1_HOT) - { - o_ptr->pval -= 2; - } - else if ((dungeon_flags1 & DF1_COLD) && rand_int(2)) - { - if (magik(50)) o_ptr->pval--; - } - else - { - o_ptr->pval--; - } - } - - if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--; - - /* Turn it into a skeleton */ - if (o_ptr->pval <= 0) - { - floor_decay(i); - } - } - } - - /* Hatch eggs */ - if (o_ptr->tval == TV_EGG) - { - int mx, my; - if (o_ptr->timeout > 0) o_ptr->pval--; - - /* Notice changes */ - if (o_ptr->pval <= 0) - { - mx = o_ptr->ix; - my = o_ptr->iy; - get_pos_player(5, &my, &mx); - msg_print("An egg hatches!"); - place_monster_one(my, mx, o_ptr->pval2, 0, FALSE, MSTATUS_ENEMY); - floor_item_increase(i, -1); - floor_item_describe(i); - floor_item_optimize(i); - } - } - } - - - /*** Involuntary Movement ***/ - - /* Delayed Word-of-Recall */ - if (p_ptr->word_recall) - { - /* Can we ? */ - if (process_hooks(HOOK_RECALL, "()", "")) - { - p_ptr->word_recall = 0; - } - - /* No recall. sorry */ - else if (dungeon_flags2 & DF2_NO_RECALL_OUT) - { - cmsg_print(TERM_L_DARK, "You cannot recall from here."); - p_ptr->word_recall = 0; - } - - /* Cannot WoR out of death fate levels */ - else if (dungeon_type == DUNGEON_DEATH) - { - cmsg_print(TERM_L_DARK, "You are fated to die here. FIGHT for your life!"); - p_ptr->word_recall = 0; - } - - /* I think the 'inside_quest' code belongs here -- pelpel */ - - /* They cannot use word of recall until reaching surface */ - else if (p_ptr->astral) - { - msg_print("As an astral being you can't recall."); - p_ptr->word_recall = 0; - } - - /* Normal WoR */ - else - { - /* - * HACK: Autosave BEFORE resetting the recall counter (rr9) - * The player is yanked up/down as soon as - * he loads the autosaved game. - */ - if (p_ptr->word_recall == 1) - { - autosave_checkpoint(); - } - - /* Make SURE that persistent levels are saved - * I don't know if this is needed, but I'm getting reports, - * so I'm adding this extra save -- Neil - */ - save_dungeon(); - - /* Count down towards recall */ - p_ptr->word_recall--; - - /* Activate the recall */ - if (p_ptr->word_recall == 0) - { - /* Disturbing! */ - disturb(0); - - /* Determine the level */ - if (p_ptr->inside_quest) - { - msg_print("The recall is cancelled by a powerful magic force!"); - } - else if (dun_level) - { - msg_print("You feel yourself yanked upwards!"); - - p_ptr->recall_dungeon = dungeon_type; - dungeon_type = DUNGEON_WILDERNESS; - dun_level = 0; - - is_recall = TRUE; - - p_ptr->inside_quest = 0; - p_ptr->leaving = TRUE; - } - else - { - msg_print("You feel yourself yanked downwards!"); - - /* New depth */ - dungeon_type = p_ptr->recall_dungeon; - dun_level = max_dlv[dungeon_type]; - if (dun_level < 1) dun_level = 1; - - /* Reset player position */ - p_ptr->oldpx = p_ptr->px; - p_ptr->oldpy = p_ptr->py; - - /* Leaving */ - is_recall = TRUE; - - p_ptr->leaving = TRUE; - p_ptr->wild_mode = FALSE; - } - - /* Sound */ - sound(SOUND_TPLEVEL); - } - } - } -} - - -/* - * Verify use of "wizard" mode - */ -static bool_ enter_wizard_mode(void) -{ - /* Ask first time, but not while loading a dead char with the -w option */ - if (!noscore && !(p_ptr->chp < 0)) - { - /* Mention effects */ - msg_print("Wizard mode is for debugging and experimenting."); - msg_print("The game will not be scored if you enter wizard mode."); - msg_print(NULL); - - /* Verify request */ - if (!get_check("Are you sure you want to enter wizard mode? ")) - { - return (FALSE); - } - - /* Mark savefile */ - noscore |= 0x0002; - } - - /* Success */ - return (TRUE); -} - - -/* - * Verify use of "debug" commands - */ -static bool_ enter_debug_mode(void) -{ - /* Ask first time */ - if (!noscore && !wizard) - { - /* Mention effects */ - msg_print("The debug commands are for debugging and experimenting."); - msg_print("The game will not be scored if you use debug commands."); - msg_print(NULL); - - /* Verify request */ - if (!get_check("Are you sure you want to use debug commands? ")) - { - return (FALSE); - } - - /* Mark savefile */ - noscore |= 0x0008; - } - - /* Success */ - return (TRUE); -} - - -/* - * Parse and execute the current command - * Give "Warning" on illegal commands. - * - * XXX XXX XXX Make some "blocks" - */ -static void process_command(void) -{ - char error_m[80]; - - /* Handle repeating the last command */ - repeat_check(); - - /* Process the appropriate hooks */ - if (process_hooks(HOOK_KEYPRESS, "(d)", command_cmd)) return; - - /* Parse the command */ - switch (command_cmd) - { - /* Ignore */ - case ESCAPE: - case ' ': - case 0: - { - break; - } - - /* Ignore return */ - case '\r': - { - break; - } - - - - /*** Wizard Commands ***/ - - /* Toggle Wizard Mode */ - case KTRL('W'): - { - if (wizard) - { - wizard = FALSE; - msg_print("Wizard mode off."); - } - else if (enter_wizard_mode()) - { - wizard = TRUE; - msg_print("Wizard mode on."); - } - - /* Update monsters */ - p_ptr->update |= (PU_MONSTERS); - - /* Redraw "title" */ - p_ptr->redraw |= (PR_TITLE); - - break; - } - - /* Special "debug" commands */ - case KTRL('A'): - { - /* Enter debug mode */ - if (enter_debug_mode()) - { - do_cmd_debug(); - } - break; - } - - - /*** Inventory Commands ***/ - - /* Wear/wield equipment */ - case 'w': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_wield(); - break; - } - - /* Take off equipment */ - case 't': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_takeoff(); - p_ptr->redraw |= (PR_MH); - break; - } - - /* Drop an item */ - case 'd': - { - if (do_control_drop()) break; - if (!p_ptr->wild_mode) do_cmd_drop(); - break; - } - - /* Destroy an item */ - case 'k': - { - if (p_ptr->control) break; - do_cmd_destroy(); - break; - } - - /* Equipment list */ - case 'e': - { - if (p_ptr->control) break; - do_cmd_equip(); - break; - } - - /* Inventory list */ - case 'i': - { - if (do_control_inven()) break; - do_cmd_inven(); - break; - } - - - /*** Various commands ***/ - - /* Identify an object */ - case 'I': - { - do_cmd_observe(); - break; - } - - /* Hack -- toggle windows */ - case KTRL('I'): - { - toggle_inven_equip(); - break; - } - - - /*** Standard "Movement" Commands ***/ - - /* Alter a grid */ - case '+': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_alter(); - break; - } - - /* Dig a tunnel */ - case 'T': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_tunnel(); - break; - } - - /* Move (usually pick up things) */ - case ';': - { - if (do_control_walk()) break; - - do_cmd_walk(always_pickup, TRUE); - - break; - } - - /* Move (usually do not pick up) */ - case '-': - { - if (do_control_walk()) break; - - do_cmd_walk(!always_pickup, TRUE); - - break; - } - - - /*** Running, Resting, Searching, Staying */ - - /* Begin Running -- Arg is Max Distance */ - case '.': - { - if (p_ptr->control || p_ptr->wild_mode) break; - do_cmd_run(); - break; - } - - /* Stay still (usually pick things up) */ - case ',': - { - if (do_control_pickup()) break; - do_cmd_stay(always_pickup); - break; - } - - /* Stay still (usually do not pick up) */ - case 'g': - { - if (p_ptr->control) break; - do_cmd_stay(!always_pickup); - break; - } - - /* Rest -- Arg is time */ - case 'R': - { - if (p_ptr->control) break; - do_cmd_rest(); - break; - } - - /* Search for traps/doors */ - case 's': - { - if (p_ptr->control) break; - do_cmd_search(); - break; - } - - /* Toggle search mode */ - case 'S': - { - if (p_ptr->control) break; - do_cmd_toggle_search(); - break; - } - - - /*** Stairs and Doors and Chests and Traps ***/ - - /* Enter store */ - case '_': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_store(); - break; - } - - /* Go up staircase */ - case '<': - { - object_type *o_ptr; - u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0; - - - /* Check for light being wielded */ - o_ptr = &p_ptr->inventory[INVEN_LITE]; - /* Burn some fuel in the current lite */ - if (o_ptr->tval == TV_LITE) - /* Extract the item flags */ - object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); - - /* Cannot move if rooted in place */ - if (p_ptr->tim_roots) break; - - if (p_ptr->control) break; - /* Normal cases */ - if (p_ptr->wild_mode || dun_level || is_quest(dun_level)) - { - do_cmd_go_up(); - } - /* Don't let the player < when he'd just drop right back down */ - else if (p_ptr->food < PY_FOOD_ALERT) - { - msg_print("You are too hungry to travel."); - } - else if (p_ptr->sensible_lite && - (((turn / ((10L * DAY) / 2)) % 2) == 0)) - { - /* Burn vampires! burn! */ - msg_print("You can't travel during the day!"); - } - else if (p_ptr->sensible_lite && - (o_ptr->tval != 0) && - (o_ptr->sval >= SV_LITE_GALADRIEL) && - (o_ptr->sval <= SV_STONE_LORE) && - (o_ptr->sval != SV_LITE_UNDEATH)) - { - msg_print("Travel with your present light would be unsafe."); - } - else if (p_ptr->cut || p_ptr->poisoned) - { - /* I actually died this way once -- neil */ - msg_print("You are too injured to travel."); - } - else if (ambush_flag) - { - msg_print("To flee the ambush you have to reach the edge of the map."); - } - /* TODO: make the above stuff use this hook */ - else if (!process_hooks(HOOK_FORBID_TRAVEL, "()")) - { - p_ptr->oldpx = p_ptr->px; - p_ptr->oldpy = p_ptr->py; - change_wild_mode(); - - /* Update the known wilderness */ - reveal_wilderness_around_player(p_ptr->wilderness_y, - p_ptr->wilderness_x, - 0, WILDERNESS_SEE_RADIUS); - } - - break; - } - - /* Go down staircase */ - case '>': - { - /* Cannot move if rooted in place */ - if (p_ptr->tim_roots) break; - - if (p_ptr->control) break; - /* Normal cases */ - if (!p_ptr->wild_mode) - { - do_cmd_go_down(); - } - - /* Special cases */ - else - { - if ((wf_info[wild_map[p_ptr->py][p_ptr->px].feat].entrance >= 1000) || - (wild_map[p_ptr->py][p_ptr->px].entrance > 1000)) - { - p_ptr->wilderness_x = p_ptr->px; - p_ptr->wilderness_y = p_ptr->py; - p_ptr->wild_mode = !p_ptr->wild_mode; - do_cmd_go_down(); - - if (dun_level == 0) - { - p_ptr->wild_mode = !p_ptr->wild_mode; - } - else - { - p_ptr->wilderness_x = p_ptr->px; - p_ptr->wilderness_y = p_ptr->py; - change_wild_mode(); - } - } - else - { - p_ptr->wilderness_x = p_ptr->px; - p_ptr->wilderness_y = p_ptr->py; - change_wild_mode(); - } - } - - break; - } - - /* Open a door or chest */ - case 'o': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_open(); - break; - } - - /* Close a door */ - case 'c': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_close(); - break; - } - - /* Give an item */ - case 'y': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_give(); - break; - } - - /* Chat */ - case 'Y': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_chat(); - break; - } - - /* Jam a door with spikes */ - case 'j': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_spike(); - break; - } - - /* Bash a door */ - case 'B': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_bash(); - break; - } - - /* Disarm a trap or chest */ - case 'D': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_disarm(); - break; - } - - - /*** Magic and Prayers ***/ - - /* Interact with skills */ - case 'G': - { - if (p_ptr->control) break; - do_cmd_skill(); - break; - } - - /* Interact with abilities */ - case 'N': - { - if (p_ptr->control) break; - do_cmd_ability(); - break; - } - - /* Browse a book */ - case 'b': - { - if (p_ptr->control) break; - do_cmd_browse(); - break; - } - - /* Cast a spell */ - case 'm': - { - if (do_control_magic()) break; - - /* No magic in the overworld map */ - if (p_ptr->wild_mode) break; - - /* Neither in the Arena */ - if (p_ptr->inside_arena) - { - msg_print("The arena absorbs all attempted magic!"); - - break; - } - do_cmd_activate_skill(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Pray a prayer */ - case 'p': - { - if (p_ptr->control || p_ptr->wild_mode) break; - do_cmd_pray(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Issue commands to pets */ - case 'P': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_pet(); - break; - } - - /* Cut up a corpse */ - case 'h': - { - if (p_ptr->control || p_ptr->wild_mode) break; - do_cmd_cut_corpse(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Cure some meat */ - case 'K': - { - if (p_ptr->control) break; - do_cmd_cure_meat(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Steal an item form a monster */ - case 'Z': - { - if (p_ptr->control || p_ptr->wild_mode) break; - do_cmd_steal(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /*** Use various objects ***/ - - /* Inscribe an object */ - case '{': - { - if (p_ptr->control) break; - do_cmd_inscribe(); - break; - } - - /* Uninscribe an object */ - case '}': - { - if (p_ptr->control) break; - do_cmd_uninscribe(); - break; - } - - /* Activate an artifact */ - case 'A': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (p_ptr->inside_arena) - { - msg_print("The arena absorbs all attempted magic!"); - msg_print(NULL); - break; - } - - do_cmd_activate(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Eat some food */ - case 'E': - { - if (p_ptr->control) break; - do_cmd_eat_food(); - break; - } - - /* Fuel your lantern/torch */ - case 'F': - { - if (p_ptr->control) break; - do_cmd_refill(); - break; - } - - /* Fire an item */ - case 'f': - { - object_type *j_ptr; - - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (p_ptr->inside_arena) - { - msg_print("You're in the arena now. This is hand-to-hand!"); - msg_print(NULL); - break; - } - - j_ptr = &p_ptr->inventory[INVEN_BOW]; - - if (process_hooks(HOOK_FIRE, "(O)", j_ptr)) - break; - - if (j_ptr->tval == TV_BOOMERANG) - { - do_cmd_boomerang(); - } - else - { - do_cmd_fire(); - } - - break; - } - - /* Throw an item */ - case 'v': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (p_ptr->inside_arena) - { - msg_print("You're in the arena now. This is hand-to-hand!"); - msg_print(NULL); - break; - } - - do_cmd_throw(); - break; - } - - /* Aim a wand */ - case 'a': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (p_ptr->inside_arena) - { - msg_print("The arena absorbs all attempted magic!"); - msg_print(NULL); - break; - } - - do_cmd_aim_wand(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Zap a rod */ - case 'z': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (p_ptr->inside_arena) - { - msg_print("The arena absorbs all attempted magic!"); - msg_print(NULL); - break; - } - - do_cmd_zap_rod(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Quaff a potion */ - case 'q': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (p_ptr->inside_arena) - { - msg_print("The arena absorbs all attempted magic!"); - msg_print(NULL); - break; - } - - do_cmd_quaff_potion(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Drink from a fountain -SC- */ - case 'H': - { - cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; - - if (p_ptr->control) break; - if ((c_ptr->feat == FEAT_FOUNTAIN) || - (c_ptr->feat == FEAT_EMPTY_FOUNTAIN)) - { - do_cmd_drink_fountain(); - squeltch_inventory(); - squeltch_grid(); - } - else - { - msg_print("You see no fountain here."); - } - - break; - } - - /* Read a scroll */ - case 'r': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (p_ptr->inside_arena) - { - msg_print("The arena absorbs all attempted magic!"); - msg_print(NULL); - break; - } - - do_cmd_read_scroll(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Use a staff */ - case 'u': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (p_ptr->inside_arena) - { - msg_print("The arena absorbs all attempted magic!"); - msg_print(NULL); - break; - } - - do_cmd_use_staff(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Use racial power */ - case 'U': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (p_ptr->inside_arena) - { - msg_print("The arena absorbs all attempted magic!"); - msg_print(NULL); - break; - } - - do_cmd_power(); - squeltch_inventory(); - squeltch_grid(); - break; - } - - /* Sacrifice at an altar */ - case 'O': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - if (PRACE_FLAG(PR1_NO_GOD)) - { - msg_print("You cannot worship gods."); - } - else - { - do_cmd_sacrifice(); - } - - break; - } - - /*** Looking at Things (nearby or on map) ***/ - - /* Full dungeon map */ - case 'M': - { - if (!p_ptr->wild_mode) do_cmd_view_map(); - break; - } - - /* Locate player on map */ - case 'L': - { - do_cmd_locate(); - break; - } - - /* Look around */ - case 'l': - { - do_cmd_look(); - break; - } - - /* Target monster or location */ - case '*': - { - if (p_ptr->control) break; - if (!p_ptr->wild_mode) do_cmd_target(); - break; - } - - /* Engrave the floor */ - case 'x': - { - if (p_ptr->control) break; - if (p_ptr->wild_mode) break; - - /* No point in engraving if there isn't any mana on this grid. */ - /* DG - actualy there is, it doesnt break macros */ - do_cmd_sense_grid_mana(); - do_cmd_engrave(); - - break; - } - - /*** Help and Such ***/ - - /* Help */ - case '?': - { - do_cmd_help(); - break; - } - - /* Identify symbol */ - case '/': - { - do_cmd_query_symbol(); - break; - } - - /* Character description */ - case 'C': - { - do_cmd_change_name(); - break; - } - - - /*** System Commands ***/ - - /* Hack -- User interface */ - case '!': - { - (void)Term_user(0); - break; - } - - /* Single line from a pref file */ - case '"': - { - do_cmd_pref(); - break; - } - - /* Interact with macros */ - case '@': - { - do_cmd_macros(); - break; - } - - /* Interact with visuals */ - case '%': - { - do_cmd_visuals(); - break; - } - - /* Interact with colors */ - case '&': - { - do_cmd_colors(); - break; - } - - /* Interact with options */ - case '=': - { - do_cmd_options(); - break; - } - - - /*** Misc Commands ***/ - - /* Take notes */ - case ':': - { - do_cmd_note(); - break; - } - - /* Version info */ - case 'V': - { - do_cmd_version(); - break; - } - - /* Repeat level feeling */ - case KTRL('F'): - { - if (!p_ptr->wild_mode) - do_cmd_feeling(); - break; - } - - /* Show previous message */ - case KTRL('O'): - { - do_cmd_message_one(); - break; - } - - /* Show previous messages */ - case KTRL('P'): - { - do_cmd_messages(); - break; - } - - /* Show quest status -KMW- */ - case KTRL('Q'): - case CMD_QUEST: -{ - do_cmd_checkquest(); - break; - } - - /* Redraw the screen */ - case KTRL('R'): - { - do_cmd_redraw(); - break; - } - - /* Hack -- Save and don't quit */ - case KTRL('S'): - { - is_autosave = FALSE; - do_cmd_save_game(); - break; - } - - case KTRL('T'): - { - do_cmd_time(); - } - break; - - /* Save and quit */ - case KTRL('X'): - { - alive = FALSE; - - /* Leaving */ - p_ptr->leaving = TRUE; - - break; - } - - /* Quit (commit suicide) */ - case 'Q': - { - do_cmd_suicide(); - break; - } - - /* Activate cmovie */ - case '|': - { - /* Stop ? */ - if (do_movies == 1) - { - do_stop_cmovie(); - msg_print("Cmovie recording stopped."); - } - else - { - do_start_cmovie(); - } - break; - } - - /* Extended command */ - case '#': - { - do_cmd_cli(); - break; - } - - /* Check artifacts, uniques, objects */ - case '~': - { - do_cmd_knowledge(); - break; - } - - /* Commands only available as extended commands: */ - - /* Extended command help. */ - case CMD_CLI_HELP: - { - do_cmd_cli_help(); - break; - } - - /* Game time. */ - case CMD_SHOW_TIME: - { - do_cmd_time(); - break; - } - - /* Check skills. */ - case CMD_SHOW_SKILL: - { - do_cmd_skill(); - break; - } - - /* Check abilities. */ - case CMD_SHOW_ABILITY: - { - do_cmd_ability(); - break; - } - - /* Save a html screenshot. */ - case CMD_DUMP_HTML: - { - do_cmd_html_dump(); - break; - } - - /* Record a macro. */ - case '$': - case CMD_MACRO: - { - do_cmd_macro_recorder(); - break; - } - case CMD_BLUNDER: - { - if (do_control_walk()) break; - do_cmd_walk(always_pickup, FALSE); - break; - } - /* Hack -- Unknown command */ - default: - { - int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane; - - /* Would like to have an option disabling this -- pelpel */ - if (rand_int(100) < insanity) - { - get_rnd_line("error.txt", error_m); - sound(SOUND_ILLEGAL); - msg_print(error_m); - } - else - { - prt("Type '?' for help.", 0, 0); - } - - break; - } - } -} - - - - -/* - * Process the player - * - * Notice the annoying code to handle "pack overflow", which - * must come first just in case somebody manages to corrupt - * the savefiles by clever use of menu commands or something. - */ -void process_player(void) -{ - int i, j; - - int speed_use; - - - /*** Apply energy ***/ - - /* Obtain current speed */ - speed_use = p_ptr->pspeed; - - /* Maximum value */ - if (speed_use > 199) - { - speed_use = 199; - } - - /* Minimum value */ - else if (speed_use < 0) - { - speed_use = 0; - } - - /* Give the player some energy */ - p_ptr->energy += extract_energy[speed_use]; - - /* No turn yet */ - if (p_ptr->energy < 100) return; - - - /*** Check for interupts ***/ - - /* Complete resting */ - if (resting < 0) - { - /* Basic resting */ - if (resting == -1) - { - /* Stop resting */ - if ((p_ptr->chp == p_ptr->mhp) && (p_ptr->csp >= p_ptr->msp)) - { - disturb(0); - } - } - - /* Complete resting */ - else if (resting == -2) - { - bool_ stop = TRUE; - object_type *o_ptr; - - /* Get the carried monster */ - o_ptr = &p_ptr->inventory[INVEN_CARRY]; - - /* Stop resting */ - if ((!p_ptr->drain_life) && (p_ptr->chp != p_ptr->mhp)) stop = FALSE; - if ((!p_ptr->drain_mana) && (p_ptr->csp != p_ptr->msp)) stop = FALSE; - if (o_ptr->pval2 < o_ptr->pval3) stop = FALSE; - if (p_ptr->blind || p_ptr->confused) stop = FALSE; - if (p_ptr->poisoned || p_ptr->afraid) stop = FALSE; - if (p_ptr->stun || p_ptr->cut) stop = FALSE; - if (p_ptr->slow || p_ptr->paralyzed) stop = FALSE; - if (p_ptr->image || p_ptr->word_recall) stop = FALSE; - if (p_ptr->immov_cntr != 0) stop = FALSE; - - for (i = 0; i < 6; i++) - { - if (p_ptr->stat_cnt[i] > 0) stop = FALSE; - } - - if (stop) - { - disturb(0); - } - p_ptr->redraw |= (PR_STATE); - } - } - - /* Handle "abort" */ - if (!avoid_abort) - { - /* Check for "player abort" (semi-efficiently for resting) */ - if (running || command_rep || (resting && !(resting & 0x0F))) - { - /* Do not wait */ - inkey_scan = TRUE; - - /* Check for a key */ - if (inkey()) - { - /* Flush input */ - flush(); - - /* Disturb */ - disturb(0); - - /* Hack -- Show a Message */ - msg_print("Cancelled."); - } - } - } - - - /*** Handle actual user input ***/ - - /* Repeat until out of energy */ - while (p_ptr->energy >= 100) - { - /* Notice stuff (if needed) */ - if (p_ptr->notice) notice_stuff(); - - /* Update stuff (if needed) */ - if (p_ptr->update) update_stuff(); - - /* Redraw stuff (if needed) */ - if (p_ptr->redraw) redraw_stuff(); - - /* Redraw stuff (if needed) */ - if (p_ptr->window) window_stuff(); - - /* Hack -- mark current wilderness location as known */ - if (!p_ptr->wild_mode && dun_level == 0) - wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].known = TRUE; - - - /* Place the cursor on the player */ - move_cursor_relative(p_ptr->py, p_ptr->px); - - /* Refresh (optional) */ - if (fresh_before) Term_fresh(); - - /* Hack -- Pack Overflow */ - if (p_ptr->inventory[INVEN_PACK].k_idx) - { - int item = INVEN_PACK; - - char o_name[80]; - - object_type *o_ptr; - - /* Access the slot to be dropped */ - o_ptr = &p_ptr->inventory[item]; - - /* Disturbing */ - disturb(0); - - /* Warning */ - msg_print("Your pack overflows!"); - - /* Describe */ - object_desc(o_name, o_ptr, TRUE, 3); - - /* Message */ - msg_format("You drop %s (%c).", o_name, index_to_label(item)); - - /* Drop it (carefully) near the player */ - drop_near(o_ptr, 0, p_ptr->py, p_ptr->px); - - /* Modify, Describe, Optimize */ - inc_stack_size(item, -255); - - /* Notice stuff (if needed) */ - if (p_ptr->notice) notice_stuff(); - - /* Update stuff (if needed) */ - if (p_ptr->update) update_stuff(); - - /* Redraw stuff (if needed) */ - if (p_ptr->redraw) redraw_stuff(); - - /* Redraw stuff (if needed) */ - if (p_ptr->window) window_stuff(); - } - - - /* Assume free turn */ - energy_use = 0; - - - /* Paralyzed or Knocked Out */ - if ((p_ptr->paralyzed) || (p_ptr->stun >= 100)) - { - /* Take a turn */ - energy_use = 100; - } - - /* Resting */ - else if (resting) - { - /* Timed rest */ - if (resting > 0) - { - /* Reduce rest count */ - resting--; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - } - - p_ptr->did_nothing = TRUE; - - /* Take a turn */ - energy_use = 100; - } - - /* Running */ - else if (running) - { - /* Take a step */ - run_step(0); - - /* - * Commented out because it doesn't make any sense - * to require a player holding down direction keys - * instead of using running commands when s/he follows - * Eru and do the opposite for the other deities -- pelpel - */ - /* p_ptr->did_nothing = TRUE; */ - } - - /* Repeated command */ - else if (command_rep) - { - /* Count this execution */ - command_rep--; - - /* Redraw the state */ - p_ptr->redraw |= (PR_STATE); - - /* Redraw stuff */ - redraw_stuff(); - - /* Hack -- Assume messages were seen */ - msg_flag = FALSE; - - /* Clear the top line */ - prt("", 0, 0); - - /* Process the command */ - process_command(); - - p_ptr->did_nothing = TRUE; - } - - /* Normal command */ - else - { - /* Place the cursor on the player */ - move_cursor_relative(p_ptr->py, p_ptr->px); - - /* Get a command (normal) */ - request_command(FALSE); - - /* Process the command */ - process_command(); - } - - - /*** Clean up ***/ - - /* Significant */ - if (energy_use) - { - /* Use some energy */ - p_ptr->energy -= energy_use; - - - /* Hack -- constant hallucination */ - if (p_ptr->image) p_ptr->redraw |= (PR_MAP); - - - /* Shimmer monsters if needed */ - if (!avoid_other && !use_graphics && shimmer_monsters) - { - /* Clear the flag */ - shimmer_monsters = FALSE; - - /* Shimmer multi-hued monsters */ - for (i = 1; i < m_max; i++) - { - monster_type *m_ptr; - monster_race *r_ptr; - - /* Access monster */ - m_ptr = &m_list[i]; - - /* Skip dead monsters */ - if (!m_ptr->r_idx) continue; - - /* Access the monster race */ - r_ptr = race_inf(m_ptr); - - /* Skip non-multi-hued monsters */ - if (!(r_ptr->flags1 & (RF1_ATTR_MULTI))) continue; - - /* Reset the flag */ - shimmer_monsters = TRUE; - - /* Redraw regardless */ - lite_spot(m_ptr->fy, m_ptr->fx); - } - } - - /* Shimmer objects if needed and requested */ - if (!avoid_other && !avoid_shimmer && !use_graphics && - shimmer_objects) - { - /* Clear the flag */ - shimmer_objects = FALSE; - - /* Shimmer multi-hued objects */ - for (i = 1; i < o_max; i++) - { - /* Acquire object -- for speed only base items are allowed to shimmer */ - object_type *o_ptr = &o_list[i]; - object_kind *k_ptr = &k_info[o_ptr->k_idx]; - - /* Skip dead or carried objects */ - if ((!o_ptr->k_idx) || (!o_ptr->ix)) continue; - - /* Skip non-multi-hued monsters */ - if (!(k_ptr->flags5 & (TR5_ATTR_MULTI))) continue; - - /* Reset the flag */ - shimmer_objects = TRUE; - - /* Redraw regardless */ - lite_spot(o_ptr->iy, o_ptr->ix); - } - } - - /* - * Shimmer features if needed and requested - * - * Note: this can be unbearably slow when a player chooses - * to use a REALLY big screen in levels filled with shallow - * water. I believe this also hurts a lot on multiuser systems. - * However fast modern processors are, I/O cannot be made that - * fast, and that's why shimmering has been limited to small - * number of monsters -- pelpel - */ - if (!avoid_other && !avoid_shimmer && !use_graphics && - !resting && !running) - { - for (j = panel_row_min; j <= panel_row_max; j++) - { - for (i = panel_col_min; i <= panel_col_max; i++) - { - cave_type *c_ptr = &cave[j][i]; - feature_type *f_ptr; - - /* Apply terrain feature mimics */ - if (c_ptr->mimic) - { - f_ptr = &f_info[c_ptr->mimic]; - } - else - { - f_ptr = &f_info[f_info[c_ptr->feat].mimic]; - } - - /* Skip normal features */ - if (!(f_ptr->flags1 & (FF1_ATTR_MULTI))) continue; - - /* Redraw a shimmering spot */ - lite_spot(j, i); - } - } - } - - - /* Handle monster detection */ - if (repair_monsters) - { - /* Reset the flag */ - repair_monsters = FALSE; - - /* Rotate detection flags */ - for (i = 1; i < m_max; i++) - { - monster_type *m_ptr; - - /* Access monster */ - m_ptr = &m_list[i]; - - /* Skip dead monsters */ - if (!m_ptr->r_idx) continue; - - /* Nice monsters get mean */ - if (m_ptr->mflag & (MFLAG_NICE)) - { - /* Nice monsters get mean */ - m_ptr->mflag &= ~(MFLAG_NICE); - } - - /* Handle memorized monsters */ - if (m_ptr->mflag & (MFLAG_MARK)) - { - /* Maintain detection */ - if (m_ptr->mflag & (MFLAG_SHOW)) - { - /* Forget flag */ - m_ptr->mflag &= ~(MFLAG_SHOW); - - /* Still need repairs */ - repair_monsters = TRUE; - } - - /* Remove detection */ - else - { - /* Forget flag */ - m_ptr->mflag &= ~(MFLAG_MARK); - - /* Assume invisible */ - m_ptr->ml = FALSE; - - /* Update the monster */ - update_mon(i, FALSE); - - /* Redraw regardless */ - lite_spot(m_ptr->fy, m_ptr->fx); - } - } - } - } - - /* - * Moved from dungeon() -- It'll get called whenever player - * spends energy, so that maze isn't incredibly easy for - * Sorcerors and alike any longer -- pelpel - * - * Forget everything when requested hehe I'm *NASTY* - */ - if (dun_level && (dungeon_flags1 & DF1_FORGET)) - { - wiz_dark(); - } - } - - - /* Hack -- notice death */ - if (!alive || death) break; - - /* Handle "leaving" */ - if (p_ptr->leaving) break; - } -} - - - -/* - * Interact with the current dungeon level. - * - * This function will not exit until the level is completed, - * the user dies, or the game is terminated. - */ -static void dungeon(void) -{ - /* Reset various flags */ - hack_mind = FALSE; - - /* Not leaving */ - p_ptr->leaving = FALSE; - - /* Reset the "command" vars */ - command_cmd = 0; - command_new = 0; - command_rep = 0; - command_arg = 0; - command_dir = 0; - - /* Make sure partial summoning counter is initialized. */ - p_ptr->maintain_sum = 0; - - /* Cancel the target */ - target_who = 0; - - /* Cancel the health bar */ - health_track(0); - - - /* Check visual effects */ - shimmer_monsters = TRUE; - shimmer_objects = TRUE; - repair_monsters = TRUE; - repair_objects = TRUE; - - - /* Disturb */ - disturb(1); - - /* Track maximum player level */ - if (p_ptr->max_plv < p_ptr->lev) - { - p_ptr->max_plv = p_ptr->lev; - } - - /* Track maximum dungeon level (if not in quest -KMW-) */ - if ((max_dlv[dungeon_type] < dun_level) && !p_ptr->inside_quest) - { - max_dlv[dungeon_type] = dun_level; - } - - /* No stairs down from Quest */ - if (is_quest(dun_level) && !p_ptr->astral) - { - create_down_stair = FALSE; - create_down_shaft = FALSE; - } - - /* Paranoia -- no stairs from town or wilderness */ - if (!dun_level) create_down_stair = create_up_stair = FALSE; - if (!dun_level) create_down_shaft = create_up_shaft = FALSE; - - /* Option -- no connected stairs */ - if (!dungeon_stair) create_down_stair = create_up_stair = FALSE; - if (!dungeon_stair) create_down_shaft = create_up_shaft = FALSE; - - /* no connecting stairs on special levels */ - if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_stair = create_up_stair = FALSE; - if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_shaft = create_up_shaft = FALSE; - - /* Make a stairway. */ - if ((create_up_stair || create_down_stair || - create_up_shaft || create_down_shaft) && - !get_fbranch()) - { - /* Place a stairway */ - if (cave_valid_bold(p_ptr->py, p_ptr->px)) - { - /* XXX XXX XXX */ - delete_object(p_ptr->py, p_ptr->px); - - /* Make stairs */ - if (create_down_stair) - { - cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_MORE); - } - else if (create_down_shaft) - { - cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_SHAFT_DOWN); - } - else if (create_up_shaft) - { - cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_SHAFT_UP); - } - else - { - cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_LESS); - } - } - - /* Cancel the stair request */ - create_down_stair = create_up_stair = FALSE; - create_down_shaft = create_up_shaft = FALSE; - } - - /* Hack - Assume invalid panel */ - panel_row_min = cur_hgt; - panel_row_max = 0; - panel_col_min = cur_wid; - panel_col_max = 0; - - /* Center the panel */ - verify_panel(); - - /* Flush messages */ - msg_print(NULL); - - - /* Enter "xtra" mode */ - character_xtra = TRUE; - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - /* Window stuff */ - p_ptr->window |= (PW_MONSTER); - - /* Redraw dungeon */ - p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA); - - /* Redraw map */ - p_ptr->redraw |= (PR_MAP); - - /* Window stuff */ - p_ptr->window |= (PW_OVERHEAD); - - /* Update stuff */ - p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_SANITY | PU_BODY); - - /* Calculate torch radius */ - p_ptr->update |= (PU_TORCH); - - /* Update stuff */ - update_stuff(); - - /* Redraw stuff */ - redraw_stuff(); - - /* Redraw stuff */ - window_stuff(); - - /* Update stuff */ - p_ptr->update |= (PU_VIEW | PU_FLOW | PU_DISTANCE | PU_MON_LITE); - - /* Update stuff */ - update_stuff(); - - /* Redraw stuff */ - redraw_stuff(); - - /* Leave "xtra" mode */ - character_xtra = FALSE; - - /* Update stuff */ - p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_BODY); - - /* Combine / Reorder the pack */ - p_ptr->notice |= (PN_COMBINE | PN_REORDER); - - /* Notice stuff */ - notice_stuff(); - - /* Update stuff */ - update_stuff(); - - /* Redraw stuff */ - redraw_stuff(); - - /* Window stuff */ - window_stuff(); - - /* Refresh */ - Term_fresh(); - - - /* Announce (or repeat) the feeling */ - if (dun_level) do_cmd_feeling(); - - - /* Hack -- notice death or departure */ - if (!alive || death) return; - - /*** Process this dungeon level ***/ - - /* Reset the monster generation level */ - monster_level = dun_level; - - /* Reset the object generation level */ - object_level = dun_level; - - hack_mind = TRUE; - - /* Mega Hack, if needed wipe all stairs */ - if (dungeon_type == DUNGEON_DEATH) - { - int i, j; - - for (i = 0; i < cur_wid; i++) - { - for (j = 0; j < cur_hgt; j++) - { - cave_type *c_ptr = &cave[j][i]; - - switch (c_ptr->feat) - { - case FEAT_MORE: - case FEAT_LESS: - case FEAT_SHAFT_UP: - case FEAT_SHAFT_DOWN: - { - cave_set_feat(j, i, FEAT_FLOOR); - break; - } - } - } - } - - /* Reset the monster generation level */ - monster_level = 127; - - /* Reset the object generation level */ - object_level = 0; - } - - /* Main loop */ - while (TRUE) - { - /* Hack -- Compact the monster list occasionally */ - if (m_cnt + 32 > max_m_idx) compact_monsters(64); - - /* Hack -- Compress the monster list occasionally */ - if (m_cnt + 32 < m_max) compact_monsters(0); - - - /* Hack -- Compact the object list occasionally */ - if (o_cnt + 32 > max_o_idx) compact_objects(64); - - /* Hack -- Compress the object list occasionally */ - if (o_cnt + 32 < o_max) compact_objects(0); - - - - /* Process the player */ - process_player(); - - /* Notice stuff */ - if (p_ptr->notice) notice_stuff(); - - /* Update stuff */ - if (p_ptr->update) update_stuff(); - - /* Redraw stuff */ - if (p_ptr->redraw) redraw_stuff(); - - /* Redraw stuff */ - if (p_ptr->window) window_stuff(); - - /* Hack -- Hilite the player */ - move_cursor_relative(p_ptr->py, p_ptr->px); - - /* Optional fresh */ - if (fresh_after) Term_fresh(); - - /* Hack -- Notice death or departure */ - if (!alive || death) break; - - - total_friends = 0; - total_friend_levels = 0; - - /* Process all of the monsters */ - process_monsters(); - - /* Notice stuff */ - if (p_ptr->notice) notice_stuff(); - - /* Update stuff */ - if (p_ptr->update) update_stuff(); - - /* Redraw stuff */ - if (p_ptr->redraw) redraw_stuff(); - - /* Redraw stuff */ - if (p_ptr->window) window_stuff(); - - /* Hack -- Hilite the player */ - move_cursor_relative(p_ptr->py, p_ptr->px); - - /* Optional fresh */ - if (fresh_after) Term_fresh(); - - /* Hack -- Notice death or departure */ - if (!alive || death) break; - - - /* Process the world */ - process_world(); - - /* Process the appropriate hooks */ - process_hooks(HOOK_END_TURN, "(d)", is_quest(dun_level)); - - /* Make it pulsate and live !!!! */ - if ((dungeon_flags1 & DF1_EVOLVE) && dun_level) - { - if (!(turn % 10)) evolve_level(TRUE); - } - - /* Notice stuff */ - if (p_ptr->notice) notice_stuff(); - - /* Update stuff */ - if (p_ptr->update) update_stuff(); - - /* Redraw stuff */ - if (p_ptr->redraw) redraw_stuff(); - - /* Window stuff */ - if (p_ptr->window) window_stuff(); - - /* Hack -- Hilite the player */ - move_cursor_relative(p_ptr->py, p_ptr->px); - - /* Optional fresh */ - if (fresh_after) Term_fresh(); - - /* Hack -- Notice death or departure */ - if (!alive || death) break; - - /* Handle "leaving" */ - if (p_ptr->leaving) break; - - /* Count game turns */ - turn++; - } - - /* Did we leave a dungeon ? */ - if ((dun_level < d_info[dungeon_type].mindepth) && !is_recall) - { - dun_level = 0; - - if (d_info[dungeon_type].ix > -1) - { - p_ptr->wilderness_x = d_info[dungeon_type].ix; - p_ptr->wilderness_y = d_info[dungeon_type].iy; - } - - dungeon_type = DUNGEON_WILDERNESS; - } - - if (dun_level > d_info[dungeon_type].maxdepth) - { - dun_level = 0; - - if (d_info[dungeon_type].ox > -1) - { - p_ptr->wilderness_x = d_info[dungeon_type].ox; - p_ptr->wilderness_y = d_info[dungeon_type].oy; - } - - dungeon_type = DUNGEON_WILDERNESS; - } - - is_recall = FALSE; -} - - - - -/* - * Load some "user pref files" - */ -static void load_all_pref_files(void) -{ - char buf[1024]; - - - /* Access the "race" pref file */ - sprintf(buf, "%s.prf", rp_ptr->title + rp_name); - - /* Process that file */ - process_pref_file(buf); - - /* Access the "class" pref file */ - sprintf(buf, "%s.prf", spp_ptr->title + c_name); - - /* Process that file */ - process_pref_file(buf); - - /* Access the "character" pref file */ - sprintf(buf, "%s.prf", player_name); - - /* Process that file */ - process_pref_file(buf); - - /* Process player specific automatizer sets */ - /* TODO: Disabled temporarily because it causes duplicate - * rules on save and subsequent game load. */ - /* sprintf(buf2, "%s.atm", player_name); */ - /* path_build(buf, sizeof(buf), ANGBAND_DIR_USER, buf2); */ - /* automatizer_load(buf); */ -} - -/* - * Actually play a game - * - * If the "new_game" parameter is true, then, after loading the - * savefile, we will commit suicide, if necessary, to allow the - * player to start a new game. - */ -void play_game(bool_ new_game) -{ - int i, tmp_dun; - - bool_ cheat_death = FALSE; - - /* Hack -- Character is "icky" */ - character_icky = TRUE; - - - /* Make sure main term is active */ - Term_activate(angband_term[0]); - - /* Initialise the resize hook XXX XXX XXX */ - angband_term[0]->resize_hook = resize_map; - - /* XXX XXX XXX hardcoded number of terms */ - for (i = 1; i < 8; i++) - { - if (angband_term[i]) - { - /* Add redraw hook */ - angband_term[i]->resize_hook = resize_window; - } - } - - - /* Hack -- turn off the cursor */ - (void)Term_set_cursor(0); - - /* Character list */ - if (!new_game && !no_begin_screen) new_game = begin_screen(); - no_begin_screen = FALSE; - - /* Attempt to load */ - if (!load_player()) - { - /* Oops */ - quit("broken savefile"); - } - - /* Nothing loaded */ - if (!character_loaded) - { - /* Make new player */ - new_game = TRUE; - - /* The dungeon is not ready */ - character_dungeon = FALSE; - } - else - { - int i; - - /* Init new skills to their defaults */ - for (i = old_max_s_idx; i < max_s_idx; i++) - { - s32b value = 0, mod = 0; - - compute_skills(&value, &mod, i); - - init_skill(value, mod, i); - } - } - - /* Process old character */ - if (!new_game) - { - /* Process the player name */ - process_player_name(FALSE); - } - - /* Init the RNG */ - if (Rand_quick) - { - u32b seed; - - /* Basic seed */ - seed = (time(NULL)); - -#ifdef SET_UID - - /* Mutate the seed on Unix machines */ - seed = ((seed >> 3) * (getpid() << 1)); - -#endif - - /* Use the complex RNG */ - Rand_quick = FALSE; - - /* Seed the "complex" RNG */ - Rand_state_init(seed); - } - - /* Extract the options */ - for (i = 0; option_info[i].o_desc; i++) - { - int os = option_info[i].o_page; - int ob = option_info[i].o_bit; - - /* Set the "default" options */ - if (option_info[i].o_var) - { - /* Set */ - if (option_flag[os] & (1L << ob)) - { - /* Set */ - (*option_info[i].o_var) = TRUE; - } - - /* Clear */ - else - { - /* Clear */ - (*option_info[i].o_var) = FALSE; - } - } - } - - /* Roll new character */ - if (new_game) - { - /* Show intro */ - modules[game_module_idx].intro(); - - /* The dungeon is not ready */ - character_dungeon = FALSE; - - /* Hack -- seed for flavors */ - seed_flavor = rand_int(0x10000000); - - /* Roll up a new character */ - player_birth(); - - /* Start in town, or not */ - if (p_ptr->astral) dun_level = 98; - else dun_level = 0; - p_ptr->inside_quest = 0; - p_ptr->inside_arena = 0; - - /* Hack -- enter the world */ - /* Mega-hack Vampires and Spectres start in the dungeon */ - if (PRACE_FLAG(PR1_UNDEAD)) - { - turn = (10L * DAY / 2) + (START_DAY * 10) + 1; - } - else - { - turn = (START_DAY * 10) + 1; - } - } - - /* Flash a message */ - prt("Please wait...", 0, 0); - - /* Flush the message */ - Term_fresh(); - - /* Be sure to not bother the player */ - calc_powers_silent = TRUE; - - /* Hack -- Enter wizard mode */ - if (arg_wizard && enter_wizard_mode()) wizard = TRUE; - - /* Flavor the objects */ - flavor_init(); - - /* Reset the visual mappings */ - reset_visuals(); - - /* Window stuff */ - p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); - - /* Window stuff */ - p_ptr->window |= (PW_MONSTER); - - /* Window stuff */ - window_stuff(); - - /* load user file */ - process_pref_file("user.prf"); - - /* Load the "pref" files */ - load_all_pref_files(); - - /* Set or clear "rogue_like_commands" if requested */ - if (arg_force_original) rogue_like_commands = FALSE; - if (arg_force_roguelike) rogue_like_commands = TRUE; - - /* Initialize vault info */ - if (init_v_info()) quit("Cannot initialize vaults"); - - /* Initialize hooks */ - init_hooks(); - init_hooks_help(); - init_hooks_module(); - - /* React to changes */ - Term_xtra(TERM_XTRA_REACT, 0); - - /* Mega hack, prevent lots of bugs */ - if ((p_ptr->px == 0) || (p_ptr->py == 0)) - { - p_ptr->px = 1; - p_ptr->py = 1; - }; - - /* Hack - if note file exists, load it */ - if (!new_game) - { - add_note_type(NOTE_ENTER_DUNGEON); - } - - /* Generate a dungeon level if needed */ - if (!character_dungeon) generate_cave(); - - /* Ok tell the scripts that the game is about to start */ - process_hooks(HOOK_GAME_START, "()"); - process_hooks_new(HOOK_GAME_START, NULL, NULL); - - /* Character is now "complete" */ - character_generated = TRUE; - - - /* Hack -- Character is no longer "icky" */ - character_icky = FALSE; - - - /* Start game */ - alive = TRUE; - - /* Hack -- Enforce "delayed death" */ - if (p_ptr->chp < 0) death = TRUE; - - /* Process */ - while (TRUE) - { - /* Save the level */ - old_dun_level = dun_level; - p_ptr->old_wild_mode = p_ptr->wild_mode; - - /* We reached surface ? good, lets go down again !! */ - if (p_ptr->astral && !dun_level) - { - p_ptr->astral = FALSE; - cmsg_print(TERM_L_GREEN, - "Well done ! You reached the town ! " - "You can now go down again."); - } - - /* Update monster list window */ - p_ptr->window |= (PW_M_LIST); - - /* Process the level */ - dungeon(); - - /* Save the current level if in a persistent level */ - tmp_dun = dun_level; - dun_level = old_dun_level; - save_dungeon(); - dun_level = tmp_dun; - - /* A death fate affects level generation */ - for (i = 0; i < MAX_FATES; i++) - { - /* Ignore empty slots */ - if (!fates[i].fate) continue; - - /* Ignore non-applicable fates */ - if (fates[i].level != dun_level) continue; - - /* Non-serious fate fails to fire 50% of time */ - if (!fates[i].serious && (rand_int(2) == 0)) continue; - - /* Analyse fate */ - switch (fates[i].fate) - { - /* You are doomed */ - case FATE_DIE: - { - cmsg_print(TERM_L_DARK, "You were fated to die here. DIE!"); - - /* You shall perish there */ - dungeon_type = DUNGEON_DEATH; - dun_level = d_info[dungeon_type].mindepth; /* was 1 */ - - fates[i].fate = FATE_NONE; - break; - } - } - } - - /* Notice stuff */ - if (p_ptr->notice) notice_stuff(); - - /* Update stuff */ - if (p_ptr->update) update_stuff(); - - /* Redraw stuff */ - if (p_ptr->redraw) redraw_stuff(); - - /* Window stuff */ - if (p_ptr->window) window_stuff(); - - /* Cancel the target */ - target_who = 0; - - /* Cancel the health bar */ - health_track(0); - - - /* Forget the lite */ - forget_mon_lite(); - - /* Forget the view */ - forget_view(); - - /* Handle "quit and save" */ - if (!alive && !death) break; - - - /* Erase the old cave */ - wipe_o_list(); - - - /* XXX XXX XXX */ - msg_print(NULL); - - /* Accidental Death */ - if (alive && death) - { - cheat_death = FALSE; - - /* Can we die ? please let us die ! */ - if (process_hooks(HOOK_DIE, "()")) - { - cheat_death = TRUE; - } - - /* Deus ex machina */ - else if (granted_resurrection()) - { - cheat_death = TRUE; - p_ptr->grace = -200000; - cmsg_format(TERM_L_GREEN, - "The power of %s raises you back from the grave!", - deity_info[p_ptr->pgod].name); - msg_print(NULL); - } - - /* Blood of life */ - else if (p_ptr->allow_one_death > 0) - { - cheat_death = TRUE; - - /* Lose one extra life */ - p_ptr->allow_one_death--; - - cmsg_print(TERM_L_GREEN, - "You have been saved by the Blood of Life!"); - msg_print(NULL); - } - - /* Cheat death option */ - else if ((wizard || cheat_live) && !get_check("Die? ")) - { - cheat_death = TRUE; - - /* Mark social class, reset age, if needed */ - if (p_ptr->sc) p_ptr->sc = p_ptr->age = 0; - - /* Increase age */ - p_ptr->age++; - - /* Mark savefile */ - noscore |= 0x0001; - msg_print("You invoke wizard mode and cheat death."); - msg_print(NULL); - } - - if (cheat_death) - { - /* Restore the winner status */ - total_winner = has_won; - - /* One more life spent */ - p_ptr->lives++; - - /* Restore hit points */ - p_ptr->chp = p_ptr->mhp; - p_ptr->chp_frac = 0; - - /* Heal sanity */ - p_ptr->csane = p_ptr->msane; - p_ptr->csane_frac = 0; - - /* Restore spell points */ - p_ptr->csp = p_ptr->msp; - p_ptr->csp_frac = 0; - - /* Hack -- Healing */ - (void)set_blind(0); - (void)set_confused(0); - (void)set_poisoned(0); - (void)set_afraid(0); - (void)set_paralyzed(0); - (void)set_image(0); - (void)set_stun(0); - (void)set_cut(0); - - /* accounting for a new ailment. -LM- */ - p_ptr->black_breath = FALSE; - - /* Hack -- don't go to undead form */ - p_ptr->necro_extra &= ~CLASS_UNDEAD; - - /* Hack -- Prevent starvation */ - (void)set_food(PY_FOOD_MAX - 1); - - /* Hack -- cancel recall */ - if (p_ptr->word_recall) - { - /* Message */ - msg_print("A tension leaves the air around you..."); - msg_print(NULL); - - /* Hack -- Prevent recall */ - p_ptr->word_recall = 0; - } - - /* Note cause of death XXX XXX XXX */ - (void)strcpy(died_from, "Cheating death"); - - /* Do not die */ - death = FALSE; - - /* New depth -KMW- */ - /* dun_level = 0; */ - p_ptr->inside_arena = 0; - leaving_quest = 0; - p_ptr->inside_quest = 0; - - /* Leaving */ - p_ptr->leaving = TRUE; - } - } - - /* Handle "death" */ - if (death) - { - break; - } - - /* Mega hack */ - if (dun_level) p_ptr->wild_mode = FALSE; - - /* Make a new level */ - process_hooks(HOOK_NEW_LEVEL, "(d)", is_quest(dun_level)); - generate_cave(); - } - - /* Close stuff */ - close_game(); - - /* Quit */ - quit(NULL); -} - diff --git a/src/dungeon.cc b/src/dungeon.cc new file mode 100644 index 00000000..df3b41aa --- /dev/null +++ b/src/dungeon.cc @@ -0,0 +1,5849 @@ +/* File: dungeon.c */ + +/* Purpose: Angband game engine */ + +/* + * 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 "angband.h" + +#include + +#include "quark.h" +#include "hooks.h" +#include "spell_type.h" + +#define TY_CURSE_CHANCE 100 +#define DG_CURSE_CHANCE 50 +#define AUTO_CURSE_CHANCE 15 +#define CHAINSWORD_NOISE 100 + + +/* + * Return a "feeling" (or NULL) about an item. Method 1 (Heavy). + */ +byte value_check_aux1(object_type *o_ptr) +{ + /* Artifacts */ + if (artifact_p(o_ptr)) + { + /* Cursed/Broken */ + if (cursed_p(o_ptr)) return (SENSE_TERRIBLE); + + /* Normal */ + return (SENSE_SPECIAL); + } + + /* Ego-Items */ + if (ego_item_p(o_ptr)) + { + /* Cursed/Broken */ + if (cursed_p(o_ptr)) return (SENSE_WORTHLESS); + + /* Normal */ + return (SENSE_EXCELLENT); + } + + /* Cursed items */ + if (cursed_p(o_ptr)) return (SENSE_CURSED); + + /* Good "armor" bonus */ + if (o_ptr->to_a > 0) return (SENSE_GOOD_HEAVY); + + /* Good "weapon" bonus */ + if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_HEAVY); + + /* Default to "average" */ + return (SENSE_AVERAGE); +} + +byte value_check_aux1_magic(object_type *o_ptr) +{ + object_kind *k_ptr = &k_info[o_ptr->k_idx]; + + + switch (o_ptr->tval) + { + /* Scrolls, Potions, Wands, Staves and Rods */ + case TV_SCROLL: + case TV_POTION: + case TV_POTION2: + case TV_WAND: + case TV_STAFF: + case TV_ROD: + case TV_ROD_MAIN: + { + /* "Cursed" scrolls/potions have a cost of 0 */ + if (k_ptr->cost == 0) return (SENSE_TERRIBLE); + + /* Artifacts */ + if (artifact_p(o_ptr)) return (SENSE_SPECIAL); + + /* Scroll of Nothing, Apple Juice, etc. */ + if (k_ptr->cost < 3) return (SENSE_WORTHLESS); + + /* + * Identify, Phase Door, Cure Light Wounds, etc. are + * just average + */ + if (k_ptr->cost < 100) return (SENSE_AVERAGE); + + /* Enchant Armor, *Identify*, Restore Stat, etc. */ + if (k_ptr->cost < 10000) return (SENSE_GOOD_HEAVY); + + /* Acquirement, Deincarnation, Strength, Blood of Life, ... */ + if (k_ptr->cost >= 10000) return (SENSE_EXCELLENT); + + break; + } + + /* Food */ + case TV_FOOD: + { + /* "Cursed" food */ + if (k_ptr->cost == 0) return (SENSE_TERRIBLE); + + /* Artifacts */ + if (artifact_p(o_ptr)) return (SENSE_SPECIAL); + + /* Normal food (no magical properties) */ + if (k_ptr->cost <= 10) return (SENSE_AVERAGE); + + /* Everything else is good */ + if (k_ptr->cost > 10) return (SENSE_GOOD_HEAVY); + + break; + } + } + + /* No feeling */ + return (SENSE_NONE); +} + + +/* + * Return a "feeling" (or NULL) about an item. Method 2 (Light). + */ +byte value_check_aux2(object_type *o_ptr) +{ + /* Cursed items (all of them) */ + if (cursed_p(o_ptr)) return (SENSE_CURSED); + + /* Artifacts -- except cursed/broken ones */ + if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT); + + /* Ego-Items -- except cursed/broken ones */ + if (ego_item_p(o_ptr)) return (SENSE_GOOD_LIGHT); + + /* Good armor bonus */ + if (o_ptr->to_a > 0) return (SENSE_GOOD_LIGHT); + + /* Good weapon bonuses */ + if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_LIGHT); + + /* Default to "average" */ + return (SENSE_AVERAGE); +} + + +byte value_check_aux2_magic(object_type *o_ptr) +{ + object_kind *k_ptr = &k_info[o_ptr->k_idx]; + + + switch (o_ptr->tval) + { + /* Scrolls, Potions, Wands, Staves and Rods */ + case TV_SCROLL: + case TV_POTION: + case TV_POTION2: + case TV_WAND: + case TV_STAFF: + case TV_ROD: + case TV_ROD_MAIN: + { + /* "Cursed" scrolls/potions have a cost of 0 */ + if (k_ptr->cost == 0) return (SENSE_CURSED); + + /* Artifacts */ + if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT); + + /* Scroll of Nothing, Apple Juice, etc. */ + if (k_ptr->cost < 3) return (SENSE_AVERAGE); + + /* + * Identify, Phase Door, Cure Light Wounds, etc. are + * just average + */ + if (k_ptr->cost < 100) return (SENSE_AVERAGE); + + /* Enchant Armor, *Identify*, Restore Stat, etc. */ + if (k_ptr->cost < 10000) return (SENSE_GOOD_LIGHT); + + /* Acquirement, Deincarnation, Strength, Blood of Life, ... */ + if (k_ptr->cost >= 10000) return (SENSE_GOOD_LIGHT); + + break; + } + + /* Food */ + case TV_FOOD: + { + /* "Cursed" food */ + if (k_ptr->cost == 0) return (SENSE_CURSED); + + /* Artifacts */ + if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT); + + /* Normal food (no magical properties) */ + if (k_ptr->cost <= 10) return (SENSE_AVERAGE); + + /* Everything else is good */ + if (k_ptr->cost > 10) return (SENSE_GOOD_LIGHT); + + break; + } + } + + /* No feeling */ + return (SENSE_NONE); +} + + +/* + * Can a player be resurrected? + */ +static bool_ granted_resurrection(void) +{ + PRAY_GOD(GOD_ERU) + { + if (p_ptr->grace > 100000) + { + if (magik(70)) return (TRUE); + else return (FALSE); + } + } + return (FALSE); +} + +static byte select_sense(object_type *o_ptr) +{ + /* Valid "tval" codes */ + switch (o_ptr->tval) + { + case TV_SHOT: + case TV_ARROW: + case TV_BOLT: + case TV_BOW: + case TV_DIGGING: + case TV_HAFTED: + case TV_POLEARM: + case TV_SWORD: + case TV_MSTAFF: + case TV_AXE: + case TV_BOOTS: + case TV_GLOVES: + case TV_HELM: + case TV_CROWN: + case TV_SHIELD: + case TV_CLOAK: + case TV_SOFT_ARMOR: + case TV_HARD_ARMOR: + case TV_DRAG_ARMOR: + case TV_BOOMERANG: + case TV_TRAPKIT: + { + return 1; + break; + } + + case TV_POTION: + case TV_POTION2: + case TV_SCROLL: + case TV_WAND: + case TV_STAFF: + case TV_ROD: + case TV_ROD_MAIN: + { + return 2; + break; + } + + /* Dual use? */ + case TV_DAEMON_BOOK: + { + return 1; + break; + } + } + return 0; +} + +/* + * Sense the inventory + * + * Combat items (weapons and armour) - Fast, weak if combat skill < 10, strong + * otherwise. + * + * Magic items (scrolls, staffs, wands, potions etc) - Slow, weak if + * magic skill < 10, strong otherwise. + * + * It shouldn't matter a lot to discriminate against magic users, because + * they learn one form of ID or another, and because most magic items are + * easy_know. + */ +void sense_inventory(void) +{ + int i, combat_lev, magic_lev; + + bool_ heavy_combat, heavy_magic; + + byte feel; + + object_type *o_ptr; + + char o_name[80]; + + + /*** Check for "sensing" ***/ + + /* No sensing when confused */ + if (p_ptr->confused) return; + + /* + * In Angband, the chance of pseudo-id uses two different formulae: + * + * (1) Fast. 0 == rand_int(BASE / (plev * plev + 40) + * (2) Slow. 0 == rand_int(BASE / (plev + 5) + * + * Warriors: Fase with BASE == 9000 + * Magi: Slow with BASE == 240000 + * Priests: Fast with BASE == 10000 + * Rogues: Fase with BASE == 20000 + * Rangers: Slow with BASE == 120000 + * Paladins: Fast with BASE == 80000 + * + * In other words, those who have identify spells are penalised. + * The problems with Pern/Tome since it externalised player classes + * is that it uses the same and slow formula for spellcasters and + * fighters. + * + * In the following code, combat item pseudo-ID improves exponentially, + * (fast with BASE 9000) and magic one linear (slow with base 60000 -- + * twice faster than V rangers). + * + * I hope this makes it closer to the original model -- pelpel + */ + + /* The combat skill affects weapon/armour pseudo-ID */ + combat_lev = get_skill(SKILL_COMBAT); + + /* The magic skill affects magic item pseudo-ID */ + magic_lev = get_skill(SKILL_MAGIC); + + /* Higher skill levels give the player better sense of items */ + heavy_combat = (combat_lev > 10) ? TRUE : FALSE; + heavy_magic = (magic_lev > 10) ? TRUE : FALSE; + + + /*** Sense everything ***/ + + /* Check everything */ + for (i = 0; i < INVEN_TOTAL; i++) + { + byte okay = 0; + + o_ptr = &p_ptr->inventory[i]; + + /* Skip empty slots */ + if (!o_ptr->k_idx) continue; + + /* We know about it already, do not tell us again */ + if (o_ptr->ident & (IDENT_SENSE)) continue; + + /* It is fully known, no information needed */ + if (object_known_p(o_ptr)) continue; + + /* Valid "tval" codes */ + okay = select_sense(o_ptr); + + /* Skip non-sense machines */ + if (!okay) continue; + + /* Check for a feeling */ + if (okay == 1) + { + feel = (heavy_combat ? value_check_aux1(o_ptr) : value_check_aux2(o_ptr)); + } + else + { + feel = (heavy_magic ? value_check_aux1_magic(o_ptr) : value_check_aux2_magic(o_ptr)); + } + + /* Skip non-feelings */ + if (feel == SENSE_NONE) continue; + + /* Get an object description */ + object_desc(o_name, o_ptr, FALSE, 0); + + /* Message (equipment) */ + if (i >= INVEN_WIELD) + { + msg_format("You feel the %s (%c) you are %s %s %s...", + o_name, index_to_label(i), describe_use(i), + ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]); + } + + /* Message (inventory) */ + else + { + msg_format("You feel the %s (%c) in your pack %s %s...", + o_name, index_to_label(i), + ((o_ptr->number == 1) ? "is" : "are"), sense_desc[feel]); + } + + /* We have "felt" it */ + o_ptr->ident |= (IDENT_SENSE); + + /* Set sense property */ + o_ptr->sense = feel; + + /* Combine / Reorder the pack (later) */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP); + } + + /* Squelch ! */ + squeltch_inventory(); +} + + +/* + * Go to any level (ripped off from wiz_jump) + */ +static void pattern_teleport(void) +{ + /* Ask for level */ + if (get_check("Teleport level? ")) + { + char ppp[80]; + + char tmp_val[160]; + + /* Prompt */ + sprintf(ppp, "Teleport to level (0-%d): ", 99); + + /* Default */ + sprintf(tmp_val, "%d", dun_level); + + /* Ask for a level */ + if (!get_string(ppp, tmp_val, 10)) return; + + /* Extract request */ + command_arg = atoi(tmp_val); + } + else if (get_check("Normal teleport? ")) + { + teleport_player(200); + return; + } + else + { + return; + } + + /* Paranoia */ + if (command_arg < 0) command_arg = 0; + + /* Paranoia */ + if (command_arg > 99) command_arg = 99; + + /* Accept request */ + msg_format("You teleport to dungeon level %d.", command_arg); + + autosave_checkpoint(); + + /* Change level */ + dun_level = command_arg; + + /* Leaving */ + p_ptr->leaving = TRUE; +} + + +/* + * Returns TRUE if we are on the Straight Road... + */ +static bool_ pattern_effect(void) +{ + if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_START) || + (cave[p_ptr->py][p_ptr->px].feat > FEAT_PATTERN_XTRA2)) return (FALSE); + + if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_END) + { + (void)set_poisoned(0); + (void)set_image(0); + (void)set_stun(0); + (void)set_cut(0); + (void)set_blind(0); + (void)set_afraid(0); + (void)do_res_stat(A_STR, TRUE); + (void)do_res_stat(A_INT, TRUE); + (void)do_res_stat(A_WIS, TRUE); + (void)do_res_stat(A_DEX, TRUE); + (void)do_res_stat(A_CON, TRUE); + (void)do_res_stat(A_CHR, TRUE); + (void)restore_level(); + (void)hp_player(1000); + cave_set_feat(p_ptr->py, p_ptr->px, FEAT_PATTERN_OLD); + msg_print("This section of the Straight Road looks less powerful."); + } + + + /* + * We could make the healing effect of the + * Pattern center one-time only to avoid various kinds + * of abuse, like luring the win monster into fighting you + * in the middle of the pattern... + */ + else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_OLD) + { + /* No effect */ + } + else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA1) + { + pattern_teleport(); + } + else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA2) + { + if (!(p_ptr->invuln)) + take_hit(200, "walking the corrupted Straight Road"); + } + + else + { + if (!(p_ptr->invuln)) + take_hit(damroll(1, 3), "walking the Straight Road"); + } + + return (TRUE); +} + + +/* + * If player has inscribed the object with "!!", let him know when it's + * recharged. -LM- + */ +static void recharged_notice(object_type *o_ptr) +{ + char o_name[80]; + + cptr s; + + + /* No inscription */ + if (!o_ptr->note) return; + + /* Find a '!' */ + s = strchr(quark_str(o_ptr->note), '!'); + + /* Process notification request. */ + while (s) + { + /* Find another '!' */ + if (s[1] == '!') + { + /* Describe (briefly) */ + object_desc(o_name, o_ptr, FALSE, 0); + + /* Notify the player */ + if (o_ptr->number > 1) + { + msg_format("Your %s are recharged.", o_name); + } + else + { + msg_format("Your %s is recharged.", o_name); + } + + /* Done. */ + return; + } + + /* Keep looking for '!'s */ + s = strchr(s + 1, '!'); + } +} + + + +/* + * Regenerate hit points -RAK- + */ +static void regenhp(int percent) +{ + s32b new_chp, new_chp_frac; + + int old_chp; + + + /* Only if alive */ + if (!(p_ptr->necro_extra & CLASS_UNDEAD)) + { + /* Save the old hitpoints */ + old_chp = p_ptr->chp; + + /* Extract the new hitpoints */ + new_chp = ((long)p_ptr->mhp) * percent + PY_REGEN_HPBASE; + + /* div 65536 */ + p_ptr->chp += new_chp >> 16; + + /* check for overflow */ + if ((p_ptr->chp < 0) && (old_chp > 0)) p_ptr->chp = MAX_SHORT; + + /* mod 65536 */ + new_chp_frac = (new_chp & 0xFFFF) + p_ptr->chp_frac; + + if (new_chp_frac >= 0x10000L) + { + p_ptr->chp_frac = new_chp_frac - 0x10000L; + p_ptr->chp++; + } + else + { + p_ptr->chp_frac = new_chp_frac; + } + + /* Fully healed */ + if (p_ptr->chp >= p_ptr->mhp) + { + p_ptr->chp = p_ptr->mhp; + p_ptr->chp_frac = 0; + } + + /* Notice changes */ + if (old_chp != p_ptr->chp) + { + /* Redraw */ + p_ptr->redraw |= (PR_HP); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + } + } +} + + +/* + * Regenerate mana points -RAK- + */ +static void regenmana(int percent) +{ + s32b new_mana, new_mana_frac; + + int old_csp; + + /* Incraese regen with int */ + percent += adj_str_blow[p_ptr->stat_ind[A_INT]] * 3; + + old_csp = p_ptr->csp; + new_mana = ((long)p_ptr->msp) * percent + PY_REGEN_MNBASE; + + /* div 65536 */ + p_ptr->csp += new_mana >> 16; + + /* check for overflow */ + if ((p_ptr->csp < 0) && (old_csp > 0)) + { + p_ptr->csp = MAX_SHORT; + } + + /* mod 65536 */ + new_mana_frac = (new_mana & 0xFFFF) + p_ptr->csp_frac; + + if (new_mana_frac >= 0x10000L) + { + p_ptr->csp_frac = new_mana_frac - 0x10000L; + p_ptr->csp++; + } + else + { + p_ptr->csp_frac = new_mana_frac; + } + + /* Must set frac to zero even if equal */ + if (p_ptr->csp >= p_ptr->msp) + { + p_ptr->csp = p_ptr->msp; + p_ptr->csp_frac = 0; + } + + /* Redraw mana */ + if (old_csp != p_ptr->csp) + { + /* Redraw */ + p_ptr->redraw |= (PR_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + } +} + + + + + + +/* + * Regenerate the monsters (once per 100 game turns) + * + * XXX XXX XXX Should probably be done during monster turns. + */ +static void regen_monsters(void) +{ + int i, frac; + + object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY]; + + + if (o_ptr->k_idx) + { + monster_race *r_ptr = &r_info[o_ptr->pval]; + + /* Allow regeneration (if needed) */ + if (o_ptr->pval2 < o_ptr->pval3) + { + /* Hack -- Base regeneration */ + frac = o_ptr->pval3 / 100; + + /* Hack -- Minimal regeneration rate */ + if (!frac) frac = 1; + + /* Hack -- Some monsters regenerate quickly */ + if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2; + + + /* Hack -- Regenerate */ + o_ptr->pval2 += frac; + + /* Do not over-regenerate */ + if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3; + + /* Redraw (later) */ + p_ptr->redraw |= (PR_MH); + } + } + + /* Regenerate everyone */ + for (i = 1; i < m_max; i++) + { + /* Check the i'th monster */ + monster_type *m_ptr = &m_list[i]; + monster_race *r_ptr = race_inf(m_ptr); + + /* Skip dead monsters */ + if (!m_ptr->r_idx) continue; + + /* Dont regen bleeding/poisonned monsters */ + if (m_ptr->bleeding || m_ptr->poisoned) continue; + + /* Allow regeneration (if needed) */ + if (m_ptr->hp < m_ptr->maxhp) + { + /* Hack -- Base regeneration */ + frac = m_ptr->maxhp / 100; + + /* Hack -- Minimal regeneration rate */ + if (!frac) frac = 1; + + /* Hack -- Some monsters regenerate quickly */ + if (r_ptr->flags2 & (RF2_REGENERATE)) frac *= 2; + + + /* Hack -- Regenerate */ + m_ptr->hp += frac; + + /* Do not over-regenerate */ + if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp; + + /* Redraw (later) if needed */ + if (health_who == i) p_ptr->redraw |= (PR_HEALTH); + } + } +} + + +/* + * Does an object decay? + * + * Should belong to object1.c, renamed to object_decays() -- pelpel + */ +bool_ decays(object_type *o_ptr) +{ + u32b f1, f2, f3, f4, f5, esp; + + + /* Extract some flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + if (f3 & TR3_DECAY) return (TRUE); + + return (FALSE); +} + + +static int process_lasting_spell(s16b music) +{ + spell_type *spell = spell_at(-music); + return spell_type_produce_effect_lasting(spell); +} + +static void gere_class_special() +{ + switch (p_ptr->druid_extra2) + { + /* Lay a path of mana on the floor */ + case CLASS_MANA_PATH: + { + /* Does the player have enought mana ? */ + if (p_ptr->csp < (s32b)(p_ptr->druid_extra & 255)) + { + p_ptr->druid_extra = 0; + p_ptr->druid_extra2 = CLASS_NONE; + msg_print("You stop laying a mana path."); + } + else + { + /* Use some mana */ + p_ptr->csp -= (p_ptr->druid_extra & 255); + + if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE) + { + /* Absorb some of the mana of the grid */ + p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 50; + if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp; + + /* Set the new grid mana */ + cave[p_ptr->py][p_ptr->px].mana = p_ptr->druid_extra & 255; + } + else + { + int m = cave[p_ptr->py][p_ptr->px].mana; + + if (m + (p_ptr->druid_extra & 255) > 255) + { + cave[p_ptr->py][p_ptr->px].mana = 255; + } + else + { + cave[p_ptr->py][p_ptr->px].mana += p_ptr->druid_extra & 255; + } + } + } + + break; + } + + /* Lay a path of mana on the floor */ + case CLASS_WINDS_MANA: + { + /* Does the player have enought mana ? */ + if (p_ptr->csp < (s32b)(p_ptr->druid_extra & 255)) + { + p_ptr->druid_extra = CLASS_NONE; + msg_print("You stop expulsing mana winds."); + } + else + { + int dam = 0; + + /* Use some mana */ + p_ptr->csp -= (p_ptr->druid_extra & 255); + + if ((p_ptr->druid_extra >> 8) & CLASS_MANA_PATH_ERASE) + { + dam = (p_ptr->druid_extra & 255) + 256; + } + else + { + dam = (p_ptr->druid_extra & 255); + } + + fire_explosion(p_ptr->py, p_ptr->px, GF_WINDS_MANA, 2, dam); + } + + break; + } + + case CLASS_CANALIZE_MANA: + { + if (p_ptr->druid_extra & CLASS_CANALIZE_MANA_EXTRA) + { + p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 10; + } + else + { + p_ptr->csp += cave[p_ptr->py][p_ptr->px].mana / 20; + } + + if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp; + + cave[p_ptr->py][p_ptr->px].mana = 0; + + break; + } + + /* CLASS_NONE, possibly others? */ + default: + { + /* No mana update */ + return; + } + } + + /* Redraw mana */ + p_ptr->update |= (PU_BONUS); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); +} + + +static void check_music() +{ + int use_mana; + + /* Music sung by player */ + if (!p_ptr->music_extra) return; + + use_mana = process_lasting_spell(p_ptr->music_extra); + + if (p_ptr->csp < use_mana) + { + msg_print("You stop your spell."); + p_ptr->music_extra = 0; + p_ptr->music_extra2 = 0; + } + else + { + p_ptr->csp -= use_mana; + + /* Redraw mana */ + p_ptr->redraw |= (PR_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + } +} + + +/* + * Generate the feature effect + */ +void apply_effect(int y, int x) +{ + cave_type *c_ptr = &cave[y][x]; + + feature_type *f_ptr = &f_info[c_ptr->feat]; + + + if (f_ptr->d_frequency[0] != 0) + { + int i; + + for (i = 0; i < 4; i++) + { + /* Check the frequency */ + if (f_ptr->d_frequency[i] == 0) continue; + + if (((turn % f_ptr->d_frequency[i]) == 0) && + ((f_ptr->d_side[i] != 0) || (f_ptr->d_dice[i] != 0))) + { + int l, dam = 0; + int d = f_ptr->d_dice[i], s = f_ptr->d_side[i]; + + if (d == -1) d = p_ptr->lev; + if (s == -1) s = p_ptr->lev; + + /* Roll damage */ + for (l = 0; l < d; l++) + { + dam += randint(s); + } + + /* Apply damage */ + project( -100, 0, y, x, dam, f_ptr->d_type[i], + PROJECT_KILL | PROJECT_HIDE); + + /* Hack -- notice death */ + if (!alive || death) return; + } + } + } +} + + + +/* XXX XXX XXX */ +bool_ is_recall = FALSE; + + +/* + * Hook for corruptions + */ +static void process_world_corruptions() +{ + if (player_has_corruption(CORRUPT_RANDOM_TELEPORT)) + { + if (rand_int(300) == 1) + { + if (magik(70)) + { + if (get_check("Teleport?")) + { + teleport_player(50); + } + else + { + disturb(0); + msg_print("Your corruption takes over you, you teleport!"); + teleport_player(50); + } + } + } + } + + if (player_has_corruption(CORRUPT_ANTI_TELEPORT)) + { + if (p_ptr->corrupt_anti_teleport_stopped) + { + int amt = p_ptr->msp + p_ptr->csp; + amt = amt / 100; + if (amt < 1) { + amt = 1; + } + increase_mana(-amt); + if (p_ptr->csp == 0) + { + p_ptr->corrupt_anti_teleport_stopped = FALSE; + msg_print("You stop controlling your corruption."); + p_ptr->update = p_ptr->update | PU_BONUS; + } + } + } +} + + +/* + * Shim for accessing Lua variable. + */ +static bool_ grace_delay_trigger() +{ + p_ptr->grace_delay++; + + if (p_ptr->grace_delay >= 15) + { + /* reset */ + p_ptr->grace_delay = 0; + /* triggered */ + return TRUE; + } + else + { + /* not triggered */ + return FALSE; + } +} + +/* + * Hook for gods + */ +static void process_world_gods() +{ + const char *race_name = rp_ptr->title + rp_name; + const char *subrace_name = rmp_ptr->title + rmp_name; + + GOD(GOD_VARDA) + { + if (grace_delay_trigger()) + { + /* Piety increases if in light. */ + if (cave[p_ptr->py][p_ptr->px].info & CAVE_GLOW) + { + inc_piety(GOD_ALL, 2); + } + + if (streq(race_name, "Orc") || + streq(race_name, "Troll") || + streq(race_name, "Dragon") || + streq(race_name, "Demon")) + { + /* Varda hates evil races */ + inc_piety(GOD_ALL, -2); + } else { + /* ... and everyone slightly less */ + inc_piety(GOD_ALL, -1); + } + + /* Prayer uses piety */ + if (p_ptr->praying) + { + inc_piety(GOD_ALL, -1); + } + } + } + + GOD(GOD_ULMO) + { + if (grace_delay_trigger()) + { + int i; + /* Ulmo likes the Edain (except Easterlings) */ + if (streq(race_name, "Human") || + streq(race_name, "Dunadan") || + streq(race_name, "Druadan") || + streq(race_name, "RohanKnight")) + { + inc_piety(GOD_ALL, 2); + } + else if (streq(race_name, "Easterling") || + streq(race_name, "Demon") || + streq(race_name, "Orc")) + { + /* hated races */ + inc_piety(GOD_ALL, -2); + } + else + { + inc_piety(GOD_ALL, 1); + } + + if (p_ptr->praying) + { + inc_piety(GOD_ALL, -1); + } + + /* Gain 1 point for each trident in inventory */ + for (i = 0; i < INVEN_TOTAL; i++) + { + if ((p_ptr->inventory[i].tval == TV_POLEARM) && + (p_ptr->inventory[i].sval == SV_TRIDENT)) + { + inc_piety(GOD_ALL, 1); + } + } + } + } + + GOD(GOD_AULE) + { + if (grace_delay_trigger()) + { + int i; + + /* Aule likes Dwarves and Dark Elves (Eol's + * influence here) */ + if (!(streq(race_name, "Dwarf") || + streq(race_name, "Petty-dwarf") || + streq(race_name, "Gnome") || + streq(race_name, "Dark-Elf"))) + { + inc_piety(GOD_ALL, -1); + } + + /* Search inventory for axe or hammer - Gain 1 + * point of grace for each hammer or axe */ + for (i = 0; i < INVEN_TOTAL; i++) + { + int tval = p_ptr->inventory[i].tval; + int sval = p_ptr->inventory[i].sval; + + switch (tval) + { + case TV_AXE: + inc_piety(GOD_ALL, 1); + break; + + case TV_HAFTED: + if ((sval == SV_WAR_HAMMER) || + (sval == SV_LUCERN_HAMMER) || + (sval == SV_GREAT_HAMMER)) + { + inc_piety(GOD_ALL, 1); + } + break; + } + } + + /* Praying may grant you a free stone skin + * once in a while */ + if (p_ptr->praying) + { + int chance; + s32b grace; + + inc_piety(GOD_ALL, -2); + grace = p_ptr->grace; /* shorthand */ + + chance = 1; + if (grace >= 50000) + { + chance = 50000; + } + else + { + chance = 50000 - grace; + } + + if (randint(100000) <= 100000 / chance) + { + s16b type = 0; + + if (grace >= 10000) + { + type = SHIELD_COUNTER; + } + + set_shield( + randint(10) + 10 + (grace / 100), + 10 + (grace / 100), + type, + 2 + (grace / 200), + 3 + (grace / 400)); + + msg_print("Aule casts Stone Skin on you."); + } + } + } + } + + GOD(GOD_MANDOS) + { + if (grace_delay_trigger()) + { + /* He loves astral beings */ + if (streq(subrace_name, "LostSoul")) + { + inc_piety(GOD_ALL, 1); + } + + /* He likes High Elves only, though, as races */ + if (!streq(race_name, "High-Elf")) + { + inc_piety(GOD_ALL, -1); + } + + /* Really hates vampires and demons */ + if (streq(subrace_name, "Vampire") || + streq(race_name, "Demon")) + { + inc_piety(GOD_ALL, -10); + } + else + { + inc_piety(GOD_ALL, 2); + } + /* he really doesn't like to be disturbed */ + if (p_ptr->praying) + { + inc_piety(GOD_ALL, -5); + } + } + } + +} + +/* + * Handle certain things once every 10 game turns + * + * Note that a single movement in the overhead wilderness mode + * consumes 132 times as much energy as a normal one... + */ +static void process_world(void) +{ + timer_type *t_ptr; + + int x, y, i, j; + + int regen_amount; + bool_ cave_no_regen = FALSE; + int upkeep_factor = 0; + + dungeon_info_type *d_ptr = &d_info[dungeon_type]; + + cave_type *c_ptr; + + object_type *o_ptr; + u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0; + + + /* + * Every 10 game turns -- which means this section is invoked once + * in a player turn under the normal speed, and 132 times in a move + * in the reduced map mode. + */ + if (turn % 10) return; + + /* + * I don't know if this is the right thing to do because I'm totally + * ignorant (yes, I must admit that...) about the scripting part of + * the game, but since there have been complaints telling us it + * runs terribly slow in the reduced map mode... -- pelpel + * + * Note to coders: if it is desirable to make this active in the + * reduced map mode, remove the if condition surrounding the line + * and move the code inside into every 1000 game turns section. + */ + if (dun_level || (!p_ptr->wild_mode)) + { + /* Handle corruptions */ + process_world_corruptions(); + + /* Handle gods */ + process_world_gods(); + + /* Handle the player song */ + check_music(); + } + + /* Handle the timers */ + for (t_ptr = gl_timers; t_ptr != NULL; t_ptr = t_ptr->next) + { + if (!t_ptr->enabled) continue; + + t_ptr->countdown--; + if (!t_ptr->countdown) + { + t_ptr->countdown = t_ptr->delay; + assert(t_ptr->callback != NULL); + t_ptr->callback(); + } + } + + /* Handle class special actions */ + gere_class_special(); + + /* Check the fate */ + if (fate_option && (p_ptr->lev > 10)) + { + /* + * WAS: == 666 against randint(50000). + * Since CPU's don't know Judeo-Christian / Cabalistic traditions, + * and since comparisons with zero is more efficient in many + * architectures... + */ + if (rand_int(50000) == 0) gain_fate(0); + } + + /*** Is the wielded monsters still hypnotised ***/ + o_ptr = &p_ptr->inventory[INVEN_CARRY]; + + if (o_ptr->k_idx) + { + monster_race *r_ptr = &r_info[o_ptr->pval]; + + if ((randint(1000) < r_ptr->level - ((p_ptr->lev * 2) + get_skill(SKILL_SYMBIOTIC)))) + { + msg_format("%s breaks free from hypnosis!", + symbiote_name(TRUE)); + carried_make_attack_normal(o_ptr->pval); + } + } + + /*** Attempt timed autosave ***/ + if (autosave_t && autosave_freq) + { + if ((turn % ((s32b)autosave_freq * 10)) == 0) + { + is_autosave = TRUE; + msg_print("Autosaving the game..."); + do_cmd_save_game(); + is_autosave = FALSE; + } + } + + + /*** Handle the wilderness/town (sunshine) ***/ + + /* While in town/wilderness and not in the overworld mode */ + if (!dun_level && !p_ptr->wild_mode) + { + /* Hack -- Daybreak/Nighfall in town */ + if ((turn % ((10L * DAY) / 2)) == 0) + { + bool_ dawn; + + /* Check for dawn */ + dawn = ((turn % (10L * DAY)) == 0); + + /* Day breaks */ + if (dawn) + { + /* Message */ + msg_print("The sun has risen."); + + /* Hack -- Scan the town */ + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + /* Get the cave grid */ + c_ptr = &cave[y][x]; + + /* Assume lit */ + c_ptr->info |= (CAVE_GLOW); + + /* Hack -- Memorize lit grids if allowed */ + if (view_perma_grids) c_ptr->info |= (CAVE_MARK); + + /* Hack -- Notice spot */ + note_spot(y, x); + } + } + } + + /* Night falls */ + else + { + /* Message */ + msg_print("The sun has set."); + + /* Hack -- Scan the town */ + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + /* Get the cave grid */ + c_ptr = &cave[y][x]; + + /* Darken "boring" features */ + if (cave_plain_floor_grid(c_ptr)) + { + /* Forget the grid */ + c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK); + + /* Hack -- Notice spot */ + note_spot(y, x); + } + } + } + } + + /* Update the monsters */ + p_ptr->update |= (PU_MONSTERS); + + /* Redraw map */ + p_ptr->redraw |= (PR_MAP); + + /* Window stuff */ + p_ptr->window |= (PW_OVERHEAD); + } + } + + /* Tell a day passed */ + if (((turn + (DAY_START * 10L)) % (10L * DAY)) == 0) + { + char buf[20]; + + sprintf(buf, "%s", get_day(bst(YEAR, turn) + START_YEAR)); + cmsg_format(TERM_L_GREEN, + "Today it is %s of the %s year of the third age.", + get_month_name(bst(DAY, turn), wizard, FALSE), buf); + } + + /* Set back the rewards once a day */ + if ((turn % (10L * STORE_TURNS)) == 0) + { + /* Select new bounties. */ + if (magik(20)) select_bounties(); + } + + /* Modify loan */ + if (p_ptr->loan) + { + if (p_ptr->loan_time) p_ptr->loan_time--; + + if (((turn % 5000) == 0) && !p_ptr->loan_time) + { + cmsg_print(TERM_RED, "You should pay your loan..."); + + p_ptr->loan += p_ptr->loan / 12; + + if (p_ptr->loan > PY_MAX_GOLD) p_ptr->loan = PY_MAX_GOLD; + + /* Do a nasty stuff */ + if (p_ptr->wild_mode && rand_int(2)) + { + /* Discount player items */ + int z = 0, tries = 200; + object_type *o_ptr = NULL; + + while (tries--) + { + z = rand_int(INVEN_TOTAL); + o_ptr = &p_ptr->inventory[z]; + + if (!o_ptr->k_idx) continue; + + if (o_ptr->discount >= 100) continue; + + break; + } + + if (tries) + { + o_ptr->discount += 70; + if (o_ptr->discount >= 100) o_ptr->discount = 100; + + inven_item_optimize(z); + inven_item_describe(z); + + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + } + } + + else + { + int merc = test_monster_name("Mean-looking mercenary"); + int agent = test_monster_name("Agent of the black market"); + int num = 5 + (p_ptr->lev / 3), z; + + for (z = 0; z < num; z++) + { + int yy, xx, attempts = 200, m_idx; + + /* Summon */ + while (1) + { + scatter(&yy, &xx, p_ptr->py, p_ptr->px, 6); + + /* Accept an empty grid within the boundary */ + if (in_bounds(yy, xx) && cave_floor_bold(yy, xx)) break; + + /* Max number of retries reached */ + if (--attempts == 0) break; + } + + /* All the attempts failed */ + if (attempts == 0) continue; + + /* Summon a monster */ + m_idx = place_monster_one(yy, xx, magik(80) ? merc : agent, + 0, FALSE, MSTATUS_ENEMY); + + /* Level it */ + if (m_idx) + { + monster_type *m_ptr = &m_list[m_idx]; + + m_ptr->exp = MONSTER_EXP(p_ptr->lev * 2); + monster_check_experience(m_idx, TRUE); + } + } + } + } + } + + /*** Process the monsters ***/ + + /* Check for creature generation. */ + if (!p_ptr->wild_mode && + !p_ptr->inside_arena && + !p_ptr->inside_quest && + (rand_int(d_info[(dun_level) ? dungeon_type : DUNGEON_WILDERNESS].max_m_alloc_chance) == 0)) + { + /* Make a new monster */ + if (!(dungeon_flags2 & DF2_NO_NEW_MONSTER)) + { + (void)alloc_monster(MAX_SIGHT + 5, FALSE); + } + } + + /* Hack -- Check for creature regeneration */ + if (!p_ptr->wild_mode && ((turn % 100) == 0)) regen_monsters(); + + + /*** Damage over Time ***/ + + /* Take damage from poison */ + if (p_ptr->poisoned && !p_ptr->invuln) + { + /* Take damage */ + take_hit(1, "poison"); + } + + + /* Vampires take damage from sunlight */ + if (p_ptr->sensible_lite) + { + if ((!dun_level) && (((turn / ((10L * DAY) / 2)) % 2) == 0)) + { + if (cave[p_ptr->py][p_ptr->px].info & (CAVE_GLOW)) + { + /* Take damage */ + msg_print("The sun's rays scorch your undead flesh!"); + take_hit(1, "sunlight"); + cave_no_regen = TRUE; + drop_from_wild(); + } + } + + if ((p_ptr->inventory[INVEN_LITE].tval != 0) && + (p_ptr->inventory[INVEN_LITE].sval >= SV_LITE_GALADRIEL) && + (p_ptr->inventory[INVEN_LITE].sval <= SV_STONE_LORE) && + (p_ptr->inventory[INVEN_LITE].sval != SV_LITE_UNDEATH)) + { + object_type * o_ptr = &p_ptr->inventory[INVEN_LITE]; + char o_name [80]; + char ouch [80]; + + /* Get an object description */ + object_desc(o_name, o_ptr, FALSE, 0); + + msg_format("The %s scorches your undead flesh!", o_name); + + cave_no_regen = TRUE; + + /* Get an object description */ + object_desc(o_name, o_ptr, TRUE, 0); + + sprintf(ouch, "wielding %s", o_name); + take_hit(1, ouch); + } + } + + /* Drown in deep water unless the player have levitation, water walking + water breathing, or magic breathing.*/ + if (!p_ptr->ffall && !p_ptr->walk_water && !p_ptr->magical_breath && + !p_ptr->water_breath && + (cave[p_ptr->py][p_ptr->px].feat == FEAT_DEEP_WATER)) + { + if (calc_total_weight() > ((weight_limit()) / 2)) + { + /* Take damage */ + msg_print("You are drowning!"); + take_hit(randint(p_ptr->lev), "drowning"); + cave_no_regen = TRUE; + } + } + + + /* Spectres -- take damage when moving through walls */ + + /* + * Added: ANYBODY takes damage if inside through walls + * without wraith form -- NOTE: Spectres will never be + * reduced below 0 hp by being inside a stone wall; others + * WILL BE! + */ + if (!cave_floor_bold(p_ptr->py, p_ptr->px)) + { + int feature = cave[p_ptr->py][p_ptr->px].feat; + + /* Player can walk through or fly over trees */ + if ((has_ability(AB_TREE_WALK) || p_ptr->fly) && (feature == FEAT_TREES)) + { + /* Do nothing */ + } + /* Player can climb over mountains */ + else if ((p_ptr->climb) && (f_info[feature].flags1 & FF1_CAN_CLIMB)) + { + /* Do nothing */ + } + else if (PRACE_FLAG(PR1_SEMI_WRAITH) && (!p_ptr->wraith_form) && (f_info[cave[p_ptr->py][p_ptr->px].feat].flags1 & FF1_CAN_PASS)) + { + int amt = 1 + ((p_ptr->lev) / 5); + + cave_no_regen = TRUE; + if (amt > p_ptr->chp - 1) amt = p_ptr->chp - 1; + take_hit(amt, " walls ..."); + } + } + + + /* Take damage from cuts */ + if ((p_ptr->cut) && !(p_ptr->invuln)) + { + /* Mortal wound or Deep Gash */ + if (p_ptr->cut > 200) + { + i = 3; + } + + /* Severe cut */ + else if (p_ptr->cut > 100) + { + i = 2; + } + + /* Other cuts */ + else + { + i = 1; + } + + /* Take damage */ + take_hit(i, "a fatal wound"); + } + + + /*** Check the Food, and Regenerate ***/ + + /* Digest normally */ + if (p_ptr->food < PY_FOOD_MAX) + { + /* Every 100 game turns */ + if ((turn % 100) == 0) + { + int speed_use = p_ptr->pspeed; + + /* Maximum */ + if (speed_use > 199) + { + speed_use = 199; + } + + /* Minimum */ + else if (speed_use < 0) + { + speed_use = 0; + } + + /* Basic digestion rate based on speed */ + i = extract_energy[speed_use] * 2; + + /* Regeneration takes more food */ + if (p_ptr->regenerate) i += 30; + + /* Regeneration takes more food */ + if (p_ptr->tim_regen) i += p_ptr->tim_regen_pow / 10; + + /* Invisibility consume a lot of food */ + i += p_ptr->invis / 2; + + /* Invulnerability consume a lot of food */ + if (p_ptr->invuln) i += 40; + + /* Wraith Form consume a lot of food */ + if (p_ptr->wraith_form) i += 30; + + /* Get the weapon */ + o_ptr = &p_ptr->inventory[INVEN_WIELD]; + + /* Examine the sword */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Hitpoints multiplier consume a lot of food */ + if (o_ptr->k_idx && (f2 & (TR2_LIFE))) i += o_ptr->pval * 5; + + /* Slow digestion takes less food */ + if (p_ptr->slow_digest) i -= 10; + + /* Minimal digestion */ + if (i < 1) i = 1; + + /* Digest some food */ + (void)set_food(p_ptr->food - i); + } + } + + /* Digest quickly when gorged */ + else + { + /* Digest a lot of food */ + (void)set_food(p_ptr->food - 100); + } + + /* Starve to death (slowly) */ + if (p_ptr->food < PY_FOOD_STARVE) + { + /* Calculate damage */ + i = (PY_FOOD_STARVE - p_ptr->food) / 10; + + /* Take damage */ + if (!(p_ptr->invuln)) take_hit(i, "starvation"); + } + + /* Default regeneration */ + regen_amount = PY_REGEN_NORMAL; + + /* Getting Weak */ + if (p_ptr->food < PY_FOOD_WEAK) + { + /* Lower regeneration */ + if (p_ptr->food < PY_FOOD_STARVE) + { + regen_amount = 0; + } + else if (p_ptr->food < PY_FOOD_FAINT) + { + regen_amount = PY_REGEN_FAINT; + } + else + { + regen_amount = PY_REGEN_WEAK; + } + + /* Getting Faint */ + if (p_ptr->food < PY_FOOD_FAINT) + { + /* Faint occasionally */ + if (!p_ptr->paralyzed && (rand_int(100) < 10)) + { + /* Message */ + msg_print("You faint from the lack of food."); + disturb(1); + + /* Hack -- faint (bypass free action) */ + (void)set_paralyzed(1 + rand_int(5)); + } + } + } + + /* Are we walking the pattern? */ + if (!p_ptr->wild_mode && pattern_effect()) + { + cave_no_regen = TRUE; + } + else + { + /* Regeneration ability */ + if (p_ptr->regenerate) + { + regen_amount = regen_amount * 2; + } + } + + + /* Searching or Resting */ + if (p_ptr->searching || resting) + { + regen_amount = regen_amount * 2; + } + + if (total_friends) + { + int upkeep_divider = 20; + + if (has_ability(AB_PERFECT_CASTING)) + upkeep_divider = 15; + + if (total_friends > 1 + (p_ptr->lev / (upkeep_divider))) + { + upkeep_factor = (total_friend_levels); + + if (upkeep_factor > 100) upkeep_factor = 100; + else if (upkeep_factor < 10) upkeep_factor = 10; + } + } + + /* Regenerate the mana */ + if (p_ptr->csp < p_ptr->msp) + { + if (upkeep_factor) + { + s16b upkeep_regen = (((100 - upkeep_factor) * regen_amount) / 100); + regenmana(upkeep_regen); + } + else + { + regenmana(regen_amount); + } + } + + /* Eru piety incraese with time */ + if (((turn % 100) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode)) + { + NOT_PRAY_GOD(GOD_ERU) + { + int inc = wisdom_scale(10); + + /* Increase by wisdom/4 */ + if (!inc) inc = 1; + inc_piety(GOD_ERU, inc); + } + } + /* Most gods piety decrease with time */ + if (((turn % 300) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level)) + { + GOD(GOD_MANWE) + { + int dec = 4 - wisdom_scale(3); + + PRAY_GOD(GOD_MANWE) + dec++; + if (PRACE_FLAG(PR1_ELF)) + dec -= wisdom_scale(2); + if (dec < 1) dec = 1; + inc_piety(GOD_MANWE, -dec); + } + GOD(GOD_MELKOR) + { + int dec = 8 - wisdom_scale(6); + + PRAY_GOD(GOD_MELKOR) + dec++; + if (PRACE_FLAG(PR1_ELF)) + dec += 5 - wisdom_scale(4); + if (dec < 1) dec = 1; + inc_piety(GOD_MELKOR, -dec); + } + PRAY_GOD(GOD_TULKAS) + { + int dec = 4 - wisdom_scale(3); + + if (dec < 1) dec = 1; + inc_piety(GOD_TULKAS, -dec); + } + } + /* Yavanna piety decrease with time */ + if (((turn % 400) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level)) + { + GOD(GOD_YAVANNA) + { + int dec = 5 - wisdom_scale(3); + + /* Blech what an hideous hack */ + if (!strcmp(rp_ptr->title + rp_name, "Ent")) + dec -= wisdom_scale(2); + if (dec < 1) dec = 1; + inc_piety(GOD_YAVANNA, -dec); + } + } + p_ptr->did_nothing = FALSE; + + /* Increase regen by tim regen */ + if (p_ptr->tim_regen) regen_amount += p_ptr->tim_regen_pow; + + /* Poisoned or cut yields no healing */ + if (p_ptr->poisoned) regen_amount = 0; + if (p_ptr->cut) regen_amount = 0; + + /* Special floor -- Pattern, in a wall -- yields no healing */ + if (cave_no_regen) regen_amount = 0; + + /* Being over grass allows Yavanna to regen you */ + PRAY_GOD(GOD_YAVANNA) + { + if (cave[p_ptr->py][p_ptr->px].feat == FEAT_GRASS) + { + regen_amount += 200 + wisdom_scale(800); + } + } + + /* Regenerate Hit Points if needed */ + if ((p_ptr->chp < p_ptr->mhp) && !cave_no_regen) + { + if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_END) && + (cave[p_ptr->py][p_ptr->px].feat >= FEAT_PATTERN_START)) + { + /* Hmmm. this should never happen? */ + regenhp(regen_amount / 5); + } + else + { + regenhp(regen_amount); + } + } + + + /*** Timeout Various Things ***/ + + /* Handle temporary stat drains */ + for (i = 0; i < 6; i++) + { + if (p_ptr->stat_cnt[i] > 0) + { + p_ptr->stat_cnt[i]--; + if (p_ptr->stat_cnt[i] == 0) + { + do_res_stat(i, FALSE); + } + } + } + + /* Hack -- Hallucinating */ + if (p_ptr->image) + { + (void)set_image(p_ptr->image - 1); + } + + /* Holy Aura */ + if (p_ptr->holy) + { + (void)set_holy(p_ptr->holy - 1); + } + + /* Soul absorbtion */ + if (p_ptr->absorb_soul) + { + (void)set_absorb_soul(p_ptr->absorb_soul - 1); + } + + /* Undead loose Death Points */ + if (p_ptr->necro_extra & CLASS_UNDEAD) + { + int old_chp = p_ptr->chp; + int warning = (p_ptr->mhp * hitpoint_warn / 10); + + /* Bypass invulnerability and wraithform */ + p_ptr->chp--; + + /* Display the hitpoints */ + p_ptr->redraw |= (PR_HP); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + + /* Dead player */ + if (p_ptr->chp < 0) + { + bool_ old_quick = quick_messages; + + /* Sound */ + sound(SOUND_DEATH); + + /* Hack -- Note death */ + if (!last_words) + { + msg_print("You die."); + msg_print(NULL); + } + else + { + char death_message[80]; + + (void)get_rnd_line("death.txt", death_message); + msg_print(death_message); + } + + /* Note cause of death */ + (void)strcpy(died_from, "being undead too long"); + + if (p_ptr->image) strcat(died_from, "(?)"); + + /* No longer a winner */ + total_winner = FALSE; + + /* Leaving */ + p_ptr->leaving = TRUE; + + /* Note death */ + death = TRUE; + + quick_messages = FALSE; + if (get_check("Make a last screenshot? ")) + { + do_cmd_html_dump(); + } + quick_messages = old_quick; + + /* Dead */ + return; + } + + /* Hitpoint warning */ + if (p_ptr->chp < warning) + { + /* Hack -- bell on first notice */ + if (alert_hitpoint && (old_chp > warning)) bell(); + + sound(SOUND_WARN); + + /* Message */ + msg_print("*** LOW DEATHPOINT WARNING! ***"); + msg_print(NULL); + } + } + + /* Walk water */ + if (p_ptr->walk_water) + { + (void)set_walk_water(p_ptr->walk_water - 1); + } + + /* True Strike */ + if (p_ptr->strike) + { + (void)set_strike(p_ptr->strike - 1); + } + + /* Meditation */ + if (p_ptr->meditation) + { + (void)set_meditation(p_ptr->meditation - 1); + } + + /* Timed project */ + if (p_ptr->tim_project) + { + (void)set_project(p_ptr->tim_project - 1, p_ptr->tim_project_gf, p_ptr->tim_project_dam, p_ptr->tim_project_rad, p_ptr->tim_project_flag); + } + + /* Timed roots */ + if (p_ptr->tim_roots) + { + (void)set_roots(p_ptr->tim_roots - 1, p_ptr->tim_roots_ac, p_ptr->tim_roots_dam); + } + + /* Timed breath */ + if (p_ptr->tim_water_breath) + { + (void)set_tim_breath(p_ptr->tim_water_breath - 1, FALSE); + } + if (p_ptr->tim_magic_breath) + { + (void)set_tim_breath(p_ptr->tim_magic_breath - 1, TRUE); + } + + /* Timed precognition */ + if (p_ptr->tim_precognition > 0) + { + set_tim_precognition(p_ptr->tim_precognition - 1); + } + + /* Timed regen */ + if (p_ptr->tim_regen) + { + (void)set_tim_regen(p_ptr->tim_regen - 1, p_ptr->tim_regen_pow); + } + + /* Timed Disrupt shield */ + if (p_ptr->disrupt_shield) + { + (void)set_disrupt_shield(p_ptr->disrupt_shield - 1); + } + + /* Timed Parasite */ + if (p_ptr->parasite) + { + (void)set_parasite(p_ptr->parasite - 1, p_ptr->parasite_r_idx); + } + + /* Timed Reflection */ + if (p_ptr->tim_reflect) + { + (void)set_tim_reflect(p_ptr->tim_reflect - 1); + } + + /* Timed Prob Travel */ + if (p_ptr->prob_travel) + { + (void)set_prob_travel(p_ptr->prob_travel - 1); + } + + /* Timed Time Resistance */ + if (p_ptr->tim_res_time) + { + (void)set_tim_res_time(p_ptr->tim_res_time - 1); + } + + /* Timed Levitation */ + if (p_ptr->tim_ffall) + { + (void)set_tim_ffall(p_ptr->tim_ffall - 1); + } + if (p_ptr->tim_fly) + { + (void)set_tim_fly(p_ptr->tim_fly - 1); + } + + /* Thunderstorm */ + if (p_ptr->tim_thunder) + { + int dam = damroll(p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2); + int i, tries = 600; + monster_type *m_ptr = NULL; + + while (tries) + { + /* Access the monster */ + m_ptr = &m_list[i = rand_range(1, m_max - 1)]; + + tries--; + + /* Ignore "dead" monsters */ + if (!m_ptr->r_idx) continue; + + /* Cant see ? cant hit */ + if (!los(p_ptr->py, p_ptr->px, m_ptr->fy, m_ptr->fx)) continue; + + /* Do not hurt friends! */ + if (is_friend(m_ptr) >= 0) continue; + break; + } + + if (tries) + { + char m_name[80]; + + monster_desc(m_name, m_ptr, 0); + msg_format("Lightning strikes %s.", m_name); + project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_ELEC, + PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); + project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_LITE, + PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); + project(0, 0, m_ptr->fy, m_ptr->fx, dam / 3, GF_SOUND, + PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); + } + + (void)set_tim_thunder(p_ptr->tim_thunder - 1, p_ptr->tim_thunder_p1, p_ptr->tim_thunder_p2); + } + + /* Poisonned hands */ + if (p_ptr->tim_poison) + { + (void)set_poison(p_ptr->tim_poison - 1); + } + + /* Timed Fire Aura */ + if (p_ptr->tim_fire_aura) + { + (void)set_tim_fire_aura(p_ptr->tim_fire_aura - 1); + } + + /* Brightness */ + if (p_ptr->tim_lite) + { + (void)set_lite(p_ptr->tim_lite - 1); + } + + /* Blindness */ + if (p_ptr->blind) + { + (void)set_blind(p_ptr->blind - 1); + } + + /* Timed no_breeds */ + if (no_breeds) + { + (void)set_no_breeders(no_breeds - 1); + } + + /* Timed mimic */ + if (p_ptr->tim_mimic) + { + (void)set_mimic(p_ptr->tim_mimic - 1, p_ptr->mimic_form, p_ptr->mimic_level); + } + + /* Timed special move commands */ + if (p_ptr->immov_cntr) + { + p_ptr->immov_cntr--; + } + + /* Timed invisibility */ + if (p_ptr->tim_invisible) + { + (void)set_invis(p_ptr->tim_invisible - 1, p_ptr->tim_inv_pow); + } + + /* Times see-invisible */ + if (p_ptr->tim_invis) + { + (void)set_tim_invis(p_ptr->tim_invis - 1); + } + + /* Timed esp */ + if (p_ptr->tim_esp) + { + (void)set_tim_esp(p_ptr->tim_esp - 1); + } + + /* Timed infra-vision */ + if (p_ptr->tim_infra) + { + (void)set_tim_infra(p_ptr->tim_infra - 1); + } + + /* Paralysis */ + if (p_ptr->paralyzed) + { + dec_paralyzed(); + } + + /* Confusion */ + if (p_ptr->confused) + { + (void)set_confused(p_ptr->confused - 1); + } + + /* Afraid */ + if (p_ptr->afraid) + { + (void)set_afraid(p_ptr->afraid - 1); + } + + /* Fast */ + if (p_ptr->fast) + { + (void)set_fast(p_ptr->fast - 1, p_ptr->speed_factor); + } + + /* Light speed */ + if (p_ptr->lightspeed) + { + (void)set_light_speed(p_ptr->lightspeed - 1); + } + + /* Slow */ + if (p_ptr->slow) + { + (void)set_slow(p_ptr->slow - 1); + } + + /* Protection from evil */ + if (p_ptr->protevil) + { + (void)set_protevil(p_ptr->protevil - 1); + } + + /* Protection from good */ + if (p_ptr->protgood) + { + (void)set_protgood(p_ptr->protgood - 1); + } + + /* Protection from undead */ + if (p_ptr->protundead) + { + (void)set_protundead(p_ptr->protundead - 1); + } + + /* Invulnerability */ + if (p_ptr->invuln) + { + (void)set_invuln(p_ptr->invuln - 1); + } + + /* Wraith form */ + if (p_ptr->tim_wraith) + { + (void)set_shadow(p_ptr->tim_wraith - 1); + } + + /* Heroism */ + if (p_ptr->hero) + { + (void)set_hero(p_ptr->hero - 1); + } + + /* Super Heroism */ + if (p_ptr->shero) + { + (void)set_shero(p_ptr->shero - 1); + } + + /* Blessed */ + if (p_ptr->blessed) + { + (void)set_blessed(p_ptr->blessed - 1); + } + + /* Shield */ + if (p_ptr->shield) + { + (void)set_shield(p_ptr->shield - 1, p_ptr->shield_power, p_ptr->shield_opt, p_ptr->shield_power_opt, p_ptr->shield_power_opt2); + } + + /* Oppose Acid */ + if (p_ptr->oppose_acid) + { + (void)set_oppose_acid(p_ptr->oppose_acid - 1); + } + + /* Oppose Lightning */ + if (p_ptr->oppose_elec) + { + (void)set_oppose_elec(p_ptr->oppose_elec - 1); + } + + /* Oppose Fire */ + if (p_ptr->oppose_fire) + { + (void)set_oppose_fire(p_ptr->oppose_fire - 1); + } + + /* Oppose Cold */ + if (p_ptr->oppose_cold) + { + (void)set_oppose_cold(p_ptr->oppose_cold - 1); + } + + /* Oppose Poison */ + if (p_ptr->oppose_pois) + { + (void)set_oppose_pois(p_ptr->oppose_pois - 1); + } + + /* Oppose Light & Dark */ + if (p_ptr->oppose_ld) + { + (void)set_oppose_ld(p_ptr->oppose_ld - 1); + } + + /* Oppose Chaos & Confusion */ + if (p_ptr->oppose_cc) + { + (void)set_oppose_cc(p_ptr->oppose_cc - 1); + } + + /* Oppose Sound & Shards */ + if (p_ptr->oppose_ss) + { + (void)set_oppose_ss(p_ptr->oppose_ss - 1); + } + + /* Oppose Nexus */ + if (p_ptr->oppose_nex) + { + (void)set_oppose_nex(p_ptr->oppose_nex - 1); + } + + /* Mental Barrier */ + if (p_ptr->tim_mental_barrier) + { + (void)set_mental_barrier(p_ptr->tim_mental_barrier - 1); + } + + /* The rush */ + if (p_ptr->rush) + { + (void)set_rush(p_ptr->rush - 1); + } + + + /* Timed mimicry */ + if (get_skill(SKILL_MIMICRY)) + { + /* Extract the value and the flags */ + u32b value = p_ptr->mimic_extra >> 16; + + u32b att = p_ptr->mimic_extra & 0xFFFF; + + if ((att & CLASS_LEGS) || (att & CLASS_WALL) || (att & CLASS_ARMS)) + { + value--; + + if (!value) + { + if (att & CLASS_LEGS) msg_print("You lose your extra pair of legs."); + if (att & CLASS_ARMS) msg_print("You lose your extra pair of arms."); + if (att & CLASS_WALL) msg_print("You lose your affinity for walls."); + + att &= ~(CLASS_ARMS); + att &= ~(CLASS_LEGS); + att &= ~(CLASS_WALL); + + if (disturb_state) disturb(0); + } + + p_ptr->update |= (PU_BODY); + p_ptr->mimic_extra = att + (value << 16); + } + } + + + /*** Poison and Stun and Cut ***/ + + /* Poison */ + if (p_ptr->poisoned) + { + int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1); + + /* Apply some healing */ + (void)set_poisoned(p_ptr->poisoned - adjust); + } + + /* Stun */ + if (p_ptr->stun) + { + int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1); + + /* Apply some healing */ + (void)set_stun(p_ptr->stun - adjust); + } + + /* Cut */ + if (p_ptr->cut) + { + int adjust = (adj_con_fix[p_ptr->stat_ind[A_CON]] + 1); + + /* Hack -- Truly "mortal" wound */ + if (p_ptr->cut > 1000) adjust = 0; + + /* Apply some healing */ + (void)set_cut(p_ptr->cut - adjust); + } + + /* Hack - damage done by the dungeon -SC- */ + if ((dun_level != 0) && (d_ptr->d_frequency[0] != 0)) + { + int i, j, k; + + /* Apply damage to every grid in the dungeon */ + for (i = 0; i < 4; i++) + { + /* Check the frequency */ + if (d_ptr->d_frequency[i] == 0) continue; + + if (((turn % d_ptr->d_frequency[i]) == 0) && + ((d_ptr->d_side[i] != 0) || (d_ptr->d_dice[i] != 0))) + { + for (j = 0; j < cur_hgt - 1; j++) + { + for (k = 0; k < cur_wid - 1; k++) + { + int l, dam = 0; + + if (!(dungeon_flags1 & DF1_DAMAGE_FEAT)) + { + /* If the grid is empty, skip it */ + if ((cave[j][k].o_idx == 0) && + ((j != p_ptr->py) && (i != p_ptr->px))) continue; + } + + /* Let's not hurt poor monsters */ + if (cave[j][k].m_idx) continue; + + /* Roll damage */ + for (l = 0; l < d_ptr->d_dice[i]; l++) + { + dam += randint(d_ptr->d_side[i]); + } + + /* Apply damage */ + project( -100, 0, j, k, dam, d_ptr->d_type[i], + PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); + } + } + } + } + } + + /* handle spell effects */ + if (!p_ptr->wild_mode) + { + /* + * I noticed significant performance degrade after the introduction + * of staying spell effects. I believe serious optimisation effort + * is required before another release. + * + * More important is to fix that display weirdness... + * + * It seems that the game never expects that monster deaths and + * terrain feature changes should happen here... Moving these + * to process_player() [before resting code, with "every 10 game turn" + * 'if'] may or may not fix the problem... -- pelpel to DG + */ + for (j = 0; j < cur_hgt - 1; j++) + { + for (i = 0; i < cur_wid - 1; i++) + { + int e = cave[j][i].effect; + + if (e) + { + effect_type *e_ptr = &effects[e]; + + if (e_ptr->time) + { + /* Apply damage */ + project(0, 0, j, i, e_ptr->dam, e_ptr->type, + PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE); + } + else + { + cave[j][i].effect = 0; + } + + if ((e_ptr->flags & EFF_WAVE) && !(e_ptr->flags & EFF_LAST)) + { + if (distance(e_ptr->cy, e_ptr->cx, j, i) < e_ptr->rad - 1) + cave[j][i].effect = 0; + } + else if ((e_ptr->flags & EFF_STORM) && !(e_ptr->flags & EFF_LAST)) + { + cave[j][i].effect = 0; + } + + lite_spot(j, i); + } + } + } + + /* Reduce & handle effects */ + for (i = 0; i < MAX_EFFECTS; i++) + { + /* Skip empty slots */ + if (effects[i].time == 0) continue; + + /* Reduce duration */ + effects[i].time--; + + /* Creates a "wave" effect*/ + if (effects[i].flags & EFF_WAVE) + { + effect_type *e_ptr = &effects[i]; + int x, y, z; + + e_ptr->rad++; + + /* What a frelling ugly line of ifs ... */ + if (effects[i].flags & EFF_DIR8) + for (y = e_ptr->cy - e_ptr->rad, z = 0; y <= e_ptr->cy; y++, z++) + { + for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++) + { + if (!in_bounds(y, x)) continue; + + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) + cave[y][x].effect = i; + } + } + else if (effects[i].flags & EFF_DIR2) + for (y = e_ptr->cy, z = e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++, z--) + { + for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++) + { + if (!in_bounds(y, x)) continue; + + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) + cave[y][x].effect = i; + } + } + else if (effects[i].flags & EFF_DIR6) + for (x = e_ptr->cx, z = e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++, z--) + { + for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++) + { + if (!in_bounds(y, x)) continue; + + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) + cave[y][x].effect = i; + } + } + else if (effects[i].flags & EFF_DIR4) + for (x = e_ptr->cx - e_ptr->rad, z = 0; x <= e_ptr->cx; x++, z++) + { + for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++) + { + if (!in_bounds(y, x)) continue; + + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) + cave[y][x].effect = i; + } + } + else if (effects[i].flags & EFF_DIR9) + for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++) + { + for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++) + { + if (!in_bounds(y, x)) continue; + + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) + cave[y][x].effect = i; + } + } + else if (effects[i].flags & EFF_DIR1) + for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++) + { + for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++) + { + if (!in_bounds(y, x)) continue; + + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) + cave[y][x].effect = i; + } + } + else if (effects[i].flags & EFF_DIR7) + for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++) + { + for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++) + { + if (!in_bounds(y, x)) continue; + + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) + cave[y][x].effect = i; + } + } + else if (effects[i].flags & EFF_DIR3) + for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++) + { + for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++) + { + if (!in_bounds(y, x)) continue; + + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) + cave[y][x].effect = i; + } + } + else + for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++) + { + for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++) + { + if (!in_bounds(y, x)) continue; + + /* This is *slow* -- pelpel */ + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad)) + cave[y][x].effect = i; + } + } + } + /* Creates a "storm" effect*/ + else if (effects[i].flags & EFF_STORM) + { + effect_type *e_ptr = &effects[i]; + int x, y; + + e_ptr->cy = p_ptr->py; + e_ptr->cx = p_ptr->px; + for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++) + { + for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++) + { + if (!in_bounds(y, x)) continue; + + if (los(e_ptr->cy, e_ptr->cx, y, x) && + (distance(e_ptr->cy, e_ptr->cx, y, x) <= e_ptr->rad)) + { + cave[y][x].effect = i; + lite_spot(y, x); + } + } + } + } + } + + apply_effect(p_ptr->py, p_ptr->px); + } + + /* Arg cannot breath? */ + if ((dungeon_flags2 & DF2_WATER_BREATH) && (!p_ptr->water_breath)) + { + cmsg_print(TERM_L_RED, "You cannot breathe water! You suffocate!"); + take_hit(damroll(3, p_ptr->lev), "suffocating"); + } + if ((dungeon_flags2 & DF2_NO_BREATH) && (!p_ptr->magical_breath)) + { + cmsg_print(TERM_L_RED, "There is no air there! You suffocate!"); + take_hit(damroll(3, p_ptr->lev), "suffocating"); + } + + /* + * Every 1500 turns, warn about any Black Breath not gotten from + * an equipped object, and stop any resting. -LM- + * + * It's apparent that someone has halved the frequency... -- pelpel + */ + if (((turn % 3000) == 0) && p_ptr->black_breath) + { + u32b f1, f2, f3, f4, f5; + + bool_ be_silent = FALSE; + + /* check all equipment for the Black Breath flag. */ + for (i = INVEN_WIELD; i < INVEN_TOTAL; i++) + { + o_ptr = &p_ptr->inventory[i]; + + /* Skip non-objects */ + if (!o_ptr->k_idx) continue; + + /* Extract the item flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* No messages if object has the flag, to avoid annoyance. */ + if (f4 & (TR4_BLACK_BREATH)) be_silent = TRUE; + + } + /* If we are allowed to speak, warn and disturb. */ + + if (!be_silent) + { + cmsg_print(TERM_L_DARK, "The Black Breath saps your soul!"); + disturb(0); + } + } + + + /*** Process Light ***/ + + /* Check for light being wielded */ + o_ptr = &p_ptr->inventory[INVEN_LITE]; + + /* Burn some fuel in the current lite */ + if (o_ptr->tval == TV_LITE) + { + /* Extract the item flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Hack -- Use some fuel */ + if ((f4 & TR4_FUEL_LITE) && (o_ptr->timeout > 0)) + { + /* Decrease life-span */ + o_ptr->timeout--; + + /* Hack -- notice interesting fuel steps */ + if ((o_ptr->timeout < 100) || ((o_ptr->timeout % 100) == 0)) + { + /* Window stuff */ + p_ptr->window |= (PW_EQUIP); + } + + /* Hack -- Special treatment when blind */ + if (p_ptr->blind) + { + /* Hack -- save some light for later */ + if (o_ptr->timeout == 0) o_ptr->timeout++; + } + + /* The light is now out */ + else if (o_ptr->timeout < 1) + { + disturb(0); + cmsg_print(TERM_YELLOW, "Your light has gone out!"); + } + + /* The light is getting dim */ + else if ((o_ptr->timeout < 100) && (o_ptr->timeout % 10 == 0)) + { + if (disturb_minor) disturb(0); + cmsg_print(TERM_YELLOW, "Your light is growing faint."); + } + } + } + + /* Calculate torch radius */ + p_ptr->update |= (PU_TORCH); + + + /*** Process Inventory ***/ + + /* + * Handle experience draining. In Oangband, the effect is worse, + * especially for high-level characters. As per Tolkien, hobbits + * are resistant. + */ + if (p_ptr->black_breath) + { + byte chance = 0; + int plev = p_ptr->lev; + + if (PRACE_FLAG(PR1_RESIST_BLACK_BREATH)) chance = 2; + else chance = 5; + + if ((rand_int(100) < chance) && (p_ptr->exp > 0)) + { + p_ptr->exp -= 1 + plev / 5; + p_ptr->max_exp -= 1 + plev / 5; + (void)do_dec_stat(rand_int(6), STAT_DEC_NORMAL); + check_experience(); + } + } + + /* Drain Mana */ + if (p_ptr->drain_mana && p_ptr->csp) + { + p_ptr->csp -= p_ptr->drain_mana; + if (magik(30)) p_ptr->csp -= p_ptr->drain_mana; + + if (p_ptr->csp < 0) + { + p_ptr->csp = 0; + disturb(0); + } + + /* Redraw */ + p_ptr->redraw |= (PR_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + } + + /* Partial summons drain mana */ + if (p_ptr->maintain_sum) + { + u32b oldcsp = p_ptr->csp; + p_ptr->csp -= p_ptr->maintain_sum / 10000; + + if (p_ptr->csp < 0) + { + p_ptr->csp = 0; + disturb(0); + + p_ptr->maintain_sum = 0; + } + else + { + /* Leave behind any fractional sp */ + p_ptr->maintain_sum -= (oldcsp - p_ptr->csp) * 10000; + } + + /* Redraw */ + p_ptr->redraw |= (PR_MANA); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + + } + + /* Drain Hitpoints */ + if (p_ptr->drain_life) + { + int drain = p_ptr->drain_life + rand_int(p_ptr->mhp / 100); + + p_ptr->chp -= (drain < p_ptr->chp ? drain : p_ptr->chp); + + if (p_ptr->chp == 0) + { + disturb(0); + } + + /* Redraw */ + p_ptr->redraw |= (PR_HP); + + /* Window stuff */ + p_ptr->window |= (PW_PLAYER); + + } + + /* Handle experience draining */ + if (p_ptr->exp_drain) + { + if ((rand_int(100) < 10) && (p_ptr->exp > 0)) + { + p_ptr->exp--; + p_ptr->max_exp--; + check_experience(); + } + } + + /* Process equipment */ + for (j = 0, i = INVEN_WIELD; i < INVEN_TOTAL; i++) + { + /* Get the object */ + o_ptr = &p_ptr->inventory[i]; + + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + + /* TY Curse */ + if ((f3 & TR3_TY_CURSE) && (rand_int(TY_CURSE_CHANCE) == 0)) + { + activate_ty_curse(); + } + + /* DG Curse */ + if ((f4 & TR4_DG_CURSE) && (rand_int(DG_CURSE_CHANCE) == 0)) + { + activate_dg_curse(); + + /* The object recurse itself ! */ + o_ptr->ident |= IDENT_CURSED; + } + + /* Auto Curse */ + if ((f3 & TR3_AUTO_CURSE) && (rand_int(AUTO_CURSE_CHANCE) == 0)) + { + /* The object recurse itself ! */ + o_ptr->ident |= IDENT_CURSED; + } + + /* + * Hack: Uncursed teleporting items (e.g. Dragon Weapons) + * can actually be useful! + */ + if ((f3 & TR3_TELEPORT) && (rand_int(100) < 1)) + { + if ((o_ptr->ident & IDENT_CURSED) && !p_ptr->anti_tele) + { + disturb(0); + + /* Teleport player */ + teleport_player(40); + } + else + { + if (p_ptr->wild_mode || + (o_ptr->note && strchr(quark_str(o_ptr->note), '.'))) + { + /* Do nothing */ + /* msg_print("Teleport aborted.") */; + } + else if (get_check("Teleport? ")) + { + disturb(0); + teleport_player(50); + } + } + } + + + /* Skip non-objects */ + if (!o_ptr->k_idx) continue; + + /* Hack: Skip wielded lights that need fuel (already handled above) */ + if ((i == INVEN_LITE) && (o_ptr->tval == TV_LITE) && (f4 & TR4_FUEL_LITE)) continue; + + /* Recharge activatable objects */ + if (o_ptr->timeout > 0) + { + /* Recharge */ + o_ptr->timeout--; + + /* Notice changes */ + if (o_ptr->timeout == 0) + { + recharged_notice(o_ptr); + j++; + } + } + + /* Recharge second spell in Mage Staffs of Spells */ + if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && (o_ptr->xtra2 > 0)) + { + /* Recharge */ + o_ptr->xtra2--; + + /* Notice changes */ + if (o_ptr->xtra2 == 0) j++; + } + } + + /* Notice changes */ + if (j) + { + /* Window stuff */ + p_ptr->window |= (PW_EQUIP); + } + + /* Recharge rods */ + for (j = 0, i = 0; i < INVEN_TOTAL; i++) + { + o_ptr = &p_ptr->inventory[i]; + + /* Skip non-objects */ + if (!o_ptr->k_idx) continue; + + /* Examine the rod */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Temporary items are destroyed */ + if (f5 & TR5_TEMPORARY) + { + o_ptr->timeout--; + + if (o_ptr->timeout <= 0) + { + inc_stack_size(i, -99); + + /* Combine and Reorder pack */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + } + } + + /* Examine all charging rods or stacks of charging rods. */ + if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2)) + { + /* Increase the rod's mana. */ + o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1; + + /* Always notice */ + j++; + + /* Notice changes, provide message if object is inscribed. */ + if (o_ptr->timeout >= o_ptr->pval2) + { + o_ptr->timeout = o_ptr->pval2; + recharged_notice(o_ptr); + } + } + + /* Examine all charging random artifacts */ + if ((f5 & TR5_ACTIVATE_NO_WIELD) && (o_ptr->timeout > 0)) + { + /* Charge it */ + o_ptr->timeout--; + + /* Notice changes */ + if (o_ptr->timeout == 0) + { + j++; + recharged_notice(o_ptr); + } + } + + /* Decay objects in pack */ + if (decays(o_ptr)) + { + /* Decay it */ + if (o_ptr->pval != 0) + { + if (o_ptr->timeout > 0) + { + if (dungeon_flags1 & DF1_HOT) + { + o_ptr->pval -= 2; + } + else if ((dungeon_flags1 & DF1_COLD) && rand_int(2)) + { + if (magik(50)) o_ptr->pval--; + } + else + { + o_ptr->pval--; + } + } + + if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--; + + /* Notice changes */ + if (o_ptr->pval <= 0) + { + pack_decay(i); + j++; + } + } + } + + /* Hatch eggs */ + if (o_ptr->tval == TV_EGG) + { + int mx, my; + + if (o_ptr->timeout == 0) + { + o_ptr->pval--; + + /* Notice changes */ + if (o_ptr->pval <= 0) + { + monster_type *m_ptr; + monster_race *r_ptr; + + mx = p_ptr->px; + my = p_ptr->py + 1; + get_pos_player(5, &my, &mx); + msg_print("Your egg hatches!"); + place_monster_aux(my, mx, o_ptr->pval2, FALSE, FALSE, MSTATUS_PET); + + m_ptr = &m_list[cave[my][mx].m_idx]; + r_ptr = race_inf(m_ptr); + + if ((r_ptr->flags9 & RF9_IMPRESED) && can_create_companion()) + { + msg_format("And you have given the imprint to your %s!", + r_name + r_ptr->name); + m_ptr->status = MSTATUS_COMPANION; + } + + inc_stack_size(i, -1); + + j++; + } + } + } + } + + /* Notice changes */ + if (j) + { + /* Combine pack */ + p_ptr->notice |= (PN_COMBINE); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN); + } + + /*** Process Objects ***/ + + /* Process objects */ + for (i = 1; i < o_max; i++) + { + /* Access object */ + o_ptr = &o_list[i]; + + /* Skip dead objects */ + if (!o_ptr->k_idx) continue; + + /* Examine the rod */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Temporary items are destroyed */ + if (f5 & TR5_TEMPORARY) + { + o_ptr->timeout--; + + if (o_ptr->timeout <= 0) + { + floor_item_increase(i, -99); + floor_item_optimize(i); + + /* Combine and Reorder pack */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + } + } + + /* Recharge rods on the ground. No messages. */ + if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2)) + { + /* Increase the rod's mana. */ + o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1; + + /* Do not overflow */ + if (o_ptr->timeout >= o_ptr->pval2) + { + o_ptr->timeout = o_ptr->pval2; + } + } + + /* Decay objects on the ground*/ + if (decays(o_ptr)) + { + /* Decay it */ + if (o_ptr->pval != 0) + { + if (o_ptr->timeout > 0) + { + if (dungeon_flags1 & DF1_HOT) + { + o_ptr->pval -= 2; + } + else if ((dungeon_flags1 & DF1_COLD) && rand_int(2)) + { + if (magik(50)) o_ptr->pval--; + } + else + { + o_ptr->pval--; + } + } + + if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--; + + /* Turn it into a skeleton */ + if (o_ptr->pval <= 0) + { + floor_decay(i); + } + } + } + + /* Hatch eggs */ + if (o_ptr->tval == TV_EGG) + { + int mx, my; + if (o_ptr->timeout > 0) o_ptr->pval--; + + /* Notice changes */ + if (o_ptr->pval <= 0) + { + mx = o_ptr->ix; + my = o_ptr->iy; + get_pos_player(5, &my, &mx); + msg_print("An egg hatches!"); + place_monster_one(my, mx, o_ptr->pval2, 0, FALSE, MSTATUS_ENEMY); + floor_item_increase(i, -1); + floor_item_describe(i); + floor_item_optimize(i); + } + } + } + + + /*** Involuntary Movement ***/ + + /* Delayed Word-of-Recall */ + if (p_ptr->word_recall) + { + /* Can we ? */ + if (process_hooks(HOOK_RECALL, "()", "")) + { + p_ptr->word_recall = 0; + } + + /* No recall. sorry */ + else if (dungeon_flags2 & DF2_NO_RECALL_OUT) + { + cmsg_print(TERM_L_DARK, "You cannot recall from here."); + p_ptr->word_recall = 0; + } + + /* Cannot WoR out of death fate levels */ + else if (dungeon_type == DUNGEON_DEATH) + { + cmsg_print(TERM_L_DARK, "You are fated to die here. FIGHT for your life!"); + p_ptr->word_recall = 0; + } + + /* I think the 'inside_quest' code belongs here -- pelpel */ + + /* They cannot use word of recall until reaching surface */ + else if (p_ptr->astral) + { + msg_print("As an astral being you can't recall."); + p_ptr->word_recall = 0; + } + + /* Normal WoR */ + else + { + /* + * HACK: Autosave BEFORE resetting the recall counter (rr9) + * The player is yanked up/down as soon as + * he loads the autosaved game. + */ + if (p_ptr->word_recall == 1) + { + autosave_checkpoint(); + } + + /* Make SURE that persistent levels are saved + * I don't know if this is needed, but I'm getting reports, + * so I'm adding this extra save -- Neil + */ + save_dungeon(); + + /* Count down towards recall */ + p_ptr->word_recall--; + + /* Activate the recall */ + if (p_ptr->word_recall == 0) + { + /* Disturbing! */ + disturb(0); + + /* Determine the level */ + if (p_ptr->inside_quest) + { + msg_print("The recall is cancelled by a powerful magic force!"); + } + else if (dun_level) + { + msg_print("You feel yourself yanked upwards!"); + + p_ptr->recall_dungeon = dungeon_type; + dungeon_type = DUNGEON_WILDERNESS; + dun_level = 0; + + is_recall = TRUE; + + p_ptr->inside_quest = 0; + p_ptr->leaving = TRUE; + } + else + { + msg_print("You feel yourself yanked downwards!"); + + /* New depth */ + dungeon_type = p_ptr->recall_dungeon; + dun_level = max_dlv[dungeon_type]; + if (dun_level < 1) dun_level = 1; + + /* Reset player position */ + p_ptr->oldpx = p_ptr->px; + p_ptr->oldpy = p_ptr->py; + + /* Leaving */ + is_recall = TRUE; + + p_ptr->leaving = TRUE; + p_ptr->wild_mode = FALSE; + } + + /* Sound */ + sound(SOUND_TPLEVEL); + } + } + } +} + + +/* + * Verify use of "wizard" mode + */ +static bool_ enter_wizard_mode(void) +{ + /* Ask first time, but not while loading a dead char with the -w option */ + if (!noscore && !(p_ptr->chp < 0)) + { + /* Mention effects */ + msg_print("Wizard mode is for debugging and experimenting."); + msg_print("The game will not be scored if you enter wizard mode."); + msg_print(NULL); + + /* Verify request */ + if (!get_check("Are you sure you want to enter wizard mode? ")) + { + return (FALSE); + } + + /* Mark savefile */ + noscore |= 0x0002; + } + + /* Success */ + return (TRUE); +} + + +/* + * Verify use of "debug" commands + */ +static bool_ enter_debug_mode(void) +{ + /* Ask first time */ + if (!noscore && !wizard) + { + /* Mention effects */ + msg_print("The debug commands are for debugging and experimenting."); + msg_print("The game will not be scored if you use debug commands."); + msg_print(NULL); + + /* Verify request */ + if (!get_check("Are you sure you want to use debug commands? ")) + { + return (FALSE); + } + + /* Mark savefile */ + noscore |= 0x0008; + } + + /* Success */ + return (TRUE); +} + + +/* + * Parse and execute the current command + * Give "Warning" on illegal commands. + * + * XXX XXX XXX Make some "blocks" + */ +static void process_command(void) +{ + char error_m[80]; + + /* Handle repeating the last command */ + repeat_check(); + + /* Process the appropriate hooks */ + if (process_hooks(HOOK_KEYPRESS, "(d)", command_cmd)) return; + + /* Parse the command */ + switch (command_cmd) + { + /* Ignore */ + case ESCAPE: + case ' ': + case 0: + { + break; + } + + /* Ignore return */ + case '\r': + { + break; + } + + + + /*** Wizard Commands ***/ + + /* Toggle Wizard Mode */ + case KTRL('W'): + { + if (wizard) + { + wizard = FALSE; + msg_print("Wizard mode off."); + } + else if (enter_wizard_mode()) + { + wizard = TRUE; + msg_print("Wizard mode on."); + } + + /* Update monsters */ + p_ptr->update |= (PU_MONSTERS); + + /* Redraw "title" */ + p_ptr->redraw |= (PR_TITLE); + + break; + } + + /* Special "debug" commands */ + case KTRL('A'): + { + /* Enter debug mode */ + if (enter_debug_mode()) + { + do_cmd_debug(); + } + break; + } + + + /*** Inventory Commands ***/ + + /* Wear/wield equipment */ + case 'w': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_wield(); + break; + } + + /* Take off equipment */ + case 't': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_takeoff(); + p_ptr->redraw |= (PR_MH); + break; + } + + /* Drop an item */ + case 'd': + { + if (do_control_drop()) break; + if (!p_ptr->wild_mode) do_cmd_drop(); + break; + } + + /* Destroy an item */ + case 'k': + { + if (p_ptr->control) break; + do_cmd_destroy(); + break; + } + + /* Equipment list */ + case 'e': + { + if (p_ptr->control) break; + do_cmd_equip(); + break; + } + + /* Inventory list */ + case 'i': + { + if (do_control_inven()) break; + do_cmd_inven(); + break; + } + + + /*** Various commands ***/ + + /* Identify an object */ + case 'I': + { + do_cmd_observe(); + break; + } + + /* Hack -- toggle windows */ + case KTRL('I'): + { + toggle_inven_equip(); + break; + } + + + /*** Standard "Movement" Commands ***/ + + /* Alter a grid */ + case '+': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_alter(); + break; + } + + /* Dig a tunnel */ + case 'T': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_tunnel(); + break; + } + + /* Move (usually pick up things) */ + case ';': + { + if (do_control_walk()) break; + + do_cmd_walk(always_pickup, TRUE); + + break; + } + + /* Move (usually do not pick up) */ + case '-': + { + if (do_control_walk()) break; + + do_cmd_walk(!always_pickup, TRUE); + + break; + } + + + /*** Running, Resting, Searching, Staying */ + + /* Begin Running -- Arg is Max Distance */ + case '.': + { + if (p_ptr->control || p_ptr->wild_mode) break; + do_cmd_run(); + break; + } + + /* Stay still (usually pick things up) */ + case ',': + { + if (do_control_pickup()) break; + do_cmd_stay(always_pickup); + break; + } + + /* Stay still (usually do not pick up) */ + case 'g': + { + if (p_ptr->control) break; + do_cmd_stay(!always_pickup); + break; + } + + /* Rest -- Arg is time */ + case 'R': + { + if (p_ptr->control) break; + do_cmd_rest(); + break; + } + + /* Search for traps/doors */ + case 's': + { + if (p_ptr->control) break; + do_cmd_search(); + break; + } + + /* Toggle search mode */ + case 'S': + { + if (p_ptr->control) break; + do_cmd_toggle_search(); + break; + } + + + /*** Stairs and Doors and Chests and Traps ***/ + + /* Enter store */ + case '_': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_store(); + break; + } + + /* Go up staircase */ + case '<': + { + object_type *o_ptr; + u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0; + + + /* Check for light being wielded */ + o_ptr = &p_ptr->inventory[INVEN_LITE]; + /* Burn some fuel in the current lite */ + if (o_ptr->tval == TV_LITE) + /* Extract the item flags */ + object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp); + + /* Cannot move if rooted in place */ + if (p_ptr->tim_roots) break; + + if (p_ptr->control) break; + /* Normal cases */ + if (p_ptr->wild_mode || dun_level || is_quest(dun_level)) + { + do_cmd_go_up(); + } + /* Don't let the player < when he'd just drop right back down */ + else if (p_ptr->food < PY_FOOD_ALERT) + { + msg_print("You are too hungry to travel."); + } + else if (p_ptr->sensible_lite && + (((turn / ((10L * DAY) / 2)) % 2) == 0)) + { + /* Burn vampires! burn! */ + msg_print("You can't travel during the day!"); + } + else if (p_ptr->sensible_lite && + (o_ptr->tval != 0) && + (o_ptr->sval >= SV_LITE_GALADRIEL) && + (o_ptr->sval <= SV_STONE_LORE) && + (o_ptr->sval != SV_LITE_UNDEATH)) + { + msg_print("Travel with your present light would be unsafe."); + } + else if (p_ptr->cut || p_ptr->poisoned) + { + /* I actually died this way once -- neil */ + msg_print("You are too injured to travel."); + } + else if (ambush_flag) + { + msg_print("To flee the ambush you have to reach the edge of the map."); + } + /* TODO: make the above stuff use this hook */ + else if (!process_hooks(HOOK_FORBID_TRAVEL, "()")) + { + p_ptr->oldpx = p_ptr->px; + p_ptr->oldpy = p_ptr->py; + change_wild_mode(); + + /* Update the known wilderness */ + reveal_wilderness_around_player(p_ptr->wilderness_y, + p_ptr->wilderness_x, + 0, WILDERNESS_SEE_RADIUS); + } + + break; + } + + /* Go down staircase */ + case '>': + { + /* Cannot move if rooted in place */ + if (p_ptr->tim_roots) break; + + if (p_ptr->control) break; + /* Normal cases */ + if (!p_ptr->wild_mode) + { + do_cmd_go_down(); + } + + /* Special cases */ + else + { + if ((wf_info[wild_map[p_ptr->py][p_ptr->px].feat].entrance >= 1000) || + (wild_map[p_ptr->py][p_ptr->px].entrance > 1000)) + { + p_ptr->wilderness_x = p_ptr->px; + p_ptr->wilderness_y = p_ptr->py; + p_ptr->wild_mode = !p_ptr->wild_mode; + do_cmd_go_down(); + + if (dun_level == 0) + { + p_ptr->wild_mode = !p_ptr->wild_mode; + } + else + { + p_ptr->wilderness_x = p_ptr->px; + p_ptr->wilderness_y = p_ptr->py; + change_wild_mode(); + } + } + else + { + p_ptr->wilderness_x = p_ptr->px; + p_ptr->wilderness_y = p_ptr->py; + change_wild_mode(); + } + } + + break; + } + + /* Open a door or chest */ + case 'o': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_open(); + break; + } + + /* Close a door */ + case 'c': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_close(); + break; + } + + /* Give an item */ + case 'y': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_give(); + break; + } + + /* Chat */ + case 'Y': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_chat(); + break; + } + + /* Jam a door with spikes */ + case 'j': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_spike(); + break; + } + + /* Bash a door */ + case 'B': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_bash(); + break; + } + + /* Disarm a trap or chest */ + case 'D': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_disarm(); + break; + } + + + /*** Magic and Prayers ***/ + + /* Interact with skills */ + case 'G': + { + if (p_ptr->control) break; + do_cmd_skill(); + break; + } + + /* Interact with abilities */ + case 'N': + { + if (p_ptr->control) break; + do_cmd_ability(); + break; + } + + /* Browse a book */ + case 'b': + { + if (p_ptr->control) break; + do_cmd_browse(); + break; + } + + /* Cast a spell */ + case 'm': + { + if (do_control_magic()) break; + + /* No magic in the overworld map */ + if (p_ptr->wild_mode) break; + + /* Neither in the Arena */ + if (p_ptr->inside_arena) + { + msg_print("The arena absorbs all attempted magic!"); + + break; + } + do_cmd_activate_skill(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Pray a prayer */ + case 'p': + { + if (p_ptr->control || p_ptr->wild_mode) break; + do_cmd_pray(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Issue commands to pets */ + case 'P': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_pet(); + break; + } + + /* Cut up a corpse */ + case 'h': + { + if (p_ptr->control || p_ptr->wild_mode) break; + do_cmd_cut_corpse(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Cure some meat */ + case 'K': + { + if (p_ptr->control) break; + do_cmd_cure_meat(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Steal an item form a monster */ + case 'Z': + { + if (p_ptr->control || p_ptr->wild_mode) break; + do_cmd_steal(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /*** Use various objects ***/ + + /* Inscribe an object */ + case '{': + { + if (p_ptr->control) break; + do_cmd_inscribe(); + break; + } + + /* Uninscribe an object */ + case '}': + { + if (p_ptr->control) break; + do_cmd_uninscribe(); + break; + } + + /* Activate an artifact */ + case 'A': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (p_ptr->inside_arena) + { + msg_print("The arena absorbs all attempted magic!"); + msg_print(NULL); + break; + } + + do_cmd_activate(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Eat some food */ + case 'E': + { + if (p_ptr->control) break; + do_cmd_eat_food(); + break; + } + + /* Fuel your lantern/torch */ + case 'F': + { + if (p_ptr->control) break; + do_cmd_refill(); + break; + } + + /* Fire an item */ + case 'f': + { + object_type *j_ptr; + + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (p_ptr->inside_arena) + { + msg_print("You're in the arena now. This is hand-to-hand!"); + msg_print(NULL); + break; + } + + j_ptr = &p_ptr->inventory[INVEN_BOW]; + + if (process_hooks(HOOK_FIRE, "(O)", j_ptr)) + break; + + if (j_ptr->tval == TV_BOOMERANG) + { + do_cmd_boomerang(); + } + else + { + do_cmd_fire(); + } + + break; + } + + /* Throw an item */ + case 'v': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (p_ptr->inside_arena) + { + msg_print("You're in the arena now. This is hand-to-hand!"); + msg_print(NULL); + break; + } + + do_cmd_throw(); + break; + } + + /* Aim a wand */ + case 'a': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (p_ptr->inside_arena) + { + msg_print("The arena absorbs all attempted magic!"); + msg_print(NULL); + break; + } + + do_cmd_aim_wand(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Zap a rod */ + case 'z': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (p_ptr->inside_arena) + { + msg_print("The arena absorbs all attempted magic!"); + msg_print(NULL); + break; + } + + do_cmd_zap_rod(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Quaff a potion */ + case 'q': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (p_ptr->inside_arena) + { + msg_print("The arena absorbs all attempted magic!"); + msg_print(NULL); + break; + } + + do_cmd_quaff_potion(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Drink from a fountain -SC- */ + case 'H': + { + cave_type *c_ptr = &cave[p_ptr->py][p_ptr->px]; + + if (p_ptr->control) break; + if ((c_ptr->feat == FEAT_FOUNTAIN) || + (c_ptr->feat == FEAT_EMPTY_FOUNTAIN)) + { + do_cmd_drink_fountain(); + squeltch_inventory(); + squeltch_grid(); + } + else + { + msg_print("You see no fountain here."); + } + + break; + } + + /* Read a scroll */ + case 'r': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (p_ptr->inside_arena) + { + msg_print("The arena absorbs all attempted magic!"); + msg_print(NULL); + break; + } + + do_cmd_read_scroll(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Use a staff */ + case 'u': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (p_ptr->inside_arena) + { + msg_print("The arena absorbs all attempted magic!"); + msg_print(NULL); + break; + } + + do_cmd_use_staff(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Use racial power */ + case 'U': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (p_ptr->inside_arena) + { + msg_print("The arena absorbs all attempted magic!"); + msg_print(NULL); + break; + } + + do_cmd_power(); + squeltch_inventory(); + squeltch_grid(); + break; + } + + /* Sacrifice at an altar */ + case 'O': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + if (PRACE_FLAG(PR1_NO_GOD)) + { + msg_print("You cannot worship gods."); + } + else + { + do_cmd_sacrifice(); + } + + break; + } + + /*** Looking at Things (nearby or on map) ***/ + + /* Full dungeon map */ + case 'M': + { + if (!p_ptr->wild_mode) do_cmd_view_map(); + break; + } + + /* Locate player on map */ + case 'L': + { + do_cmd_locate(); + break; + } + + /* Look around */ + case 'l': + { + do_cmd_look(); + break; + } + + /* Target monster or location */ + case '*': + { + if (p_ptr->control) break; + if (!p_ptr->wild_mode) do_cmd_target(); + break; + } + + /* Engrave the floor */ + case 'x': + { + if (p_ptr->control) break; + if (p_ptr->wild_mode) break; + + /* No point in engraving if there isn't any mana on this grid. */ + /* DG - actualy there is, it doesnt break macros */ + do_cmd_sense_grid_mana(); + do_cmd_engrave(); + + break; + } + + /*** Help and Such ***/ + + /* Help */ + case '?': + { + do_cmd_help(); + break; + } + + /* Identify symbol */ + case '/': + { + do_cmd_query_symbol(); + break; + } + + /* Character description */ + case 'C': + { + do_cmd_change_name(); + break; + } + + + /*** System Commands ***/ + + /* Hack -- User interface */ + case '!': + { + (void)Term_user(0); + break; + } + + /* Single line from a pref file */ + case '"': + { + do_cmd_pref(); + break; + } + + /* Interact with macros */ + case '@': + { + do_cmd_macros(); + break; + } + + /* Interact with visuals */ + case '%': + { + do_cmd_visuals(); + break; + } + + /* Interact with colors */ + case '&': + { + do_cmd_colors(); + break; + } + + /* Interact with options */ + case '=': + { + do_cmd_options(); + break; + } + + + /*** Misc Commands ***/ + + /* Take notes */ + case ':': + { + do_cmd_note(); + break; + } + + /* Version info */ + case 'V': + { + do_cmd_version(); + break; + } + + /* Repeat level feeling */ + case KTRL('F'): + { + if (!p_ptr->wild_mode) + do_cmd_feeling(); + break; + } + + /* Show previous message */ + case KTRL('O'): + { + do_cmd_message_one(); + break; + } + + /* Show previous messages */ + case KTRL('P'): + { + do_cmd_messages(); + break; + } + + /* Show quest status -KMW- */ + case KTRL('Q'): + case CMD_QUEST: +{ + do_cmd_checkquest(); + break; + } + + /* Redraw the screen */ + case KTRL('R'): + { + do_cmd_redraw(); + break; + } + + /* Hack -- Save and don't quit */ + case KTRL('S'): + { + is_autosave = FALSE; + do_cmd_save_game(); + break; + } + + case KTRL('T'): + { + do_cmd_time(); + } + break; + + /* Save and quit */ + case KTRL('X'): + { + alive = FALSE; + + /* Leaving */ + p_ptr->leaving = TRUE; + + break; + } + + /* Quit (commit suicide) */ + case 'Q': + { + do_cmd_suicide(); + break; + } + + /* Activate cmovie */ + case '|': + { + /* Stop ? */ + if (do_movies == 1) + { + do_stop_cmovie(); + msg_print("Cmovie recording stopped."); + } + else + { + do_start_cmovie(); + } + break; + } + + /* Extended command */ + case '#': + { + do_cmd_cli(); + break; + } + + /* Check artifacts, uniques, objects */ + case '~': + { + do_cmd_knowledge(); + break; + } + + /* Commands only available as extended commands: */ + + /* Extended command help. */ + case CMD_CLI_HELP: + { + do_cmd_cli_help(); + break; + } + + /* Game time. */ + case CMD_SHOW_TIME: + { + do_cmd_time(); + break; + } + + /* Check skills. */ + case CMD_SHOW_SKILL: + { + do_cmd_skill(); + break; + } + + /* Check abilities. */ + case CMD_SHOW_ABILITY: + { + do_cmd_ability(); + break; + } + + /* Save a html screenshot. */ + case CMD_DUMP_HTML: + { + do_cmd_html_dump(); + break; + } + + /* Record a macro. */ + case '$': + case CMD_MACRO: + { + do_cmd_macro_recorder(); + break; + } + case CMD_BLUNDER: + { + if (do_control_walk()) break; + do_cmd_walk(always_pickup, FALSE); + break; + } + /* Hack -- Unknown command */ + default: + { + int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane; + + /* Would like to have an option disabling this -- pelpel */ + if (rand_int(100) < insanity) + { + get_rnd_line("error.txt", error_m); + sound(SOUND_ILLEGAL); + msg_print(error_m); + } + else + { + prt("Type '?' for help.", 0, 0); + } + + break; + } + } +} + + + + +/* + * Process the player + * + * Notice the annoying code to handle "pack overflow", which + * must come first just in case somebody manages to corrupt + * the savefiles by clever use of menu commands or something. + */ +void process_player(void) +{ + int i, j; + + int speed_use; + + + /*** Apply energy ***/ + + /* Obtain current speed */ + speed_use = p_ptr->pspeed; + + /* Maximum value */ + if (speed_use > 199) + { + speed_use = 199; + } + + /* Minimum value */ + else if (speed_use < 0) + { + speed_use = 0; + } + + /* Give the player some energy */ + p_ptr->energy += extract_energy[speed_use]; + + /* No turn yet */ + if (p_ptr->energy < 100) return; + + + /*** Check for interupts ***/ + + /* Complete resting */ + if (resting < 0) + { + /* Basic resting */ + if (resting == -1) + { + /* Stop resting */ + if ((p_ptr->chp == p_ptr->mhp) && (p_ptr->csp >= p_ptr->msp)) + { + disturb(0); + } + } + + /* Complete resting */ + else if (resting == -2) + { + bool_ stop = TRUE; + object_type *o_ptr; + + /* Get the carried monster */ + o_ptr = &p_ptr->inventory[INVEN_CARRY]; + + /* Stop resting */ + if ((!p_ptr->drain_life) && (p_ptr->chp != p_ptr->mhp)) stop = FALSE; + if ((!p_ptr->drain_mana) && (p_ptr->csp != p_ptr->msp)) stop = FALSE; + if (o_ptr->pval2 < o_ptr->pval3) stop = FALSE; + if (p_ptr->blind || p_ptr->confused) stop = FALSE; + if (p_ptr->poisoned || p_ptr->afraid) stop = FALSE; + if (p_ptr->stun || p_ptr->cut) stop = FALSE; + if (p_ptr->slow || p_ptr->paralyzed) stop = FALSE; + if (p_ptr->image || p_ptr->word_recall) stop = FALSE; + if (p_ptr->immov_cntr != 0) stop = FALSE; + + for (i = 0; i < 6; i++) + { + if (p_ptr->stat_cnt[i] > 0) stop = FALSE; + } + + if (stop) + { + disturb(0); + } + p_ptr->redraw |= (PR_STATE); + } + } + + /* Handle "abort" */ + if (!avoid_abort) + { + /* Check for "player abort" (semi-efficiently for resting) */ + if (running || command_rep || (resting && !(resting & 0x0F))) + { + /* Do not wait */ + inkey_scan = TRUE; + + /* Check for a key */ + if (inkey()) + { + /* Flush input */ + flush(); + + /* Disturb */ + disturb(0); + + /* Hack -- Show a Message */ + msg_print("Cancelled."); + } + } + } + + + /*** Handle actual user input ***/ + + /* Repeat until out of energy */ + while (p_ptr->energy >= 100) + { + /* Notice stuff (if needed) */ + if (p_ptr->notice) notice_stuff(); + + /* Update stuff (if needed) */ + if (p_ptr->update) update_stuff(); + + /* Redraw stuff (if needed) */ + if (p_ptr->redraw) redraw_stuff(); + + /* Redraw stuff (if needed) */ + if (p_ptr->window) window_stuff(); + + /* Hack -- mark current wilderness location as known */ + if (!p_ptr->wild_mode && dun_level == 0) + wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].known = TRUE; + + + /* Place the cursor on the player */ + move_cursor_relative(p_ptr->py, p_ptr->px); + + /* Refresh (optional) */ + if (fresh_before) Term_fresh(); + + /* Hack -- Pack Overflow */ + if (p_ptr->inventory[INVEN_PACK].k_idx) + { + int item = INVEN_PACK; + + char o_name[80]; + + object_type *o_ptr; + + /* Access the slot to be dropped */ + o_ptr = &p_ptr->inventory[item]; + + /* Disturbing */ + disturb(0); + + /* Warning */ + msg_print("Your pack overflows!"); + + /* Describe */ + object_desc(o_name, o_ptr, TRUE, 3); + + /* Message */ + msg_format("You drop %s (%c).", o_name, index_to_label(item)); + + /* Drop it (carefully) near the player */ + drop_near(o_ptr, 0, p_ptr->py, p_ptr->px); + + /* Modify, Describe, Optimize */ + inc_stack_size(item, -255); + + /* Notice stuff (if needed) */ + if (p_ptr->notice) notice_stuff(); + + /* Update stuff (if needed) */ + if (p_ptr->update) update_stuff(); + + /* Redraw stuff (if needed) */ + if (p_ptr->redraw) redraw_stuff(); + + /* Redraw stuff (if needed) */ + if (p_ptr->window) window_stuff(); + } + + + /* Assume free turn */ + energy_use = 0; + + + /* Paralyzed or Knocked Out */ + if ((p_ptr->paralyzed) || (p_ptr->stun >= 100)) + { + /* Take a turn */ + energy_use = 100; + } + + /* Resting */ + else if (resting) + { + /* Timed rest */ + if (resting > 0) + { + /* Reduce rest count */ + resting--; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + } + + p_ptr->did_nothing = TRUE; + + /* Take a turn */ + energy_use = 100; + } + + /* Running */ + else if (running) + { + /* Take a step */ + run_step(0); + + /* + * Commented out because it doesn't make any sense + * to require a player holding down direction keys + * instead of using running commands when s/he follows + * Eru and do the opposite for the other deities -- pelpel + */ + /* p_ptr->did_nothing = TRUE; */ + } + + /* Repeated command */ + else if (command_rep) + { + /* Count this execution */ + command_rep--; + + /* Redraw the state */ + p_ptr->redraw |= (PR_STATE); + + /* Redraw stuff */ + redraw_stuff(); + + /* Hack -- Assume messages were seen */ + msg_flag = FALSE; + + /* Clear the top line */ + prt("", 0, 0); + + /* Process the command */ + process_command(); + + p_ptr->did_nothing = TRUE; + } + + /* Normal command */ + else + { + /* Place the cursor on the player */ + move_cursor_relative(p_ptr->py, p_ptr->px); + + /* Get a command (normal) */ + request_command(FALSE); + + /* Process the command */ + process_command(); + } + + + /*** Clean up ***/ + + /* Significant */ + if (energy_use) + { + /* Use some energy */ + p_ptr->energy -= energy_use; + + + /* Hack -- constant hallucination */ + if (p_ptr->image) p_ptr->redraw |= (PR_MAP); + + + /* Shimmer monsters if needed */ + if (!avoid_other && !use_graphics && shimmer_monsters) + { + /* Clear the flag */ + shimmer_monsters = FALSE; + + /* Shimmer multi-hued monsters */ + for (i = 1; i < m_max; i++) + { + monster_type *m_ptr; + monster_race *r_ptr; + + /* Access monster */ + m_ptr = &m_list[i]; + + /* Skip dead monsters */ + if (!m_ptr->r_idx) continue; + + /* Access the monster race */ + r_ptr = race_inf(m_ptr); + + /* Skip non-multi-hued monsters */ + if (!(r_ptr->flags1 & (RF1_ATTR_MULTI))) continue; + + /* Reset the flag */ + shimmer_monsters = TRUE; + + /* Redraw regardless */ + lite_spot(m_ptr->fy, m_ptr->fx); + } + } + + /* Shimmer objects if needed and requested */ + if (!avoid_other && !avoid_shimmer && !use_graphics && + shimmer_objects) + { + /* Clear the flag */ + shimmer_objects = FALSE; + + /* Shimmer multi-hued objects */ + for (i = 1; i < o_max; i++) + { + /* Acquire object -- for speed only base items are allowed to shimmer */ + object_type *o_ptr = &o_list[i]; + object_kind *k_ptr = &k_info[o_ptr->k_idx]; + + /* Skip dead or carried objects */ + if ((!o_ptr->k_idx) || (!o_ptr->ix)) continue; + + /* Skip non-multi-hued monsters */ + if (!(k_ptr->flags5 & (TR5_ATTR_MULTI))) continue; + + /* Reset the flag */ + shimmer_objects = TRUE; + + /* Redraw regardless */ + lite_spot(o_ptr->iy, o_ptr->ix); + } + } + + /* + * Shimmer features if needed and requested + * + * Note: this can be unbearably slow when a player chooses + * to use a REALLY big screen in levels filled with shallow + * water. I believe this also hurts a lot on multiuser systems. + * However fast modern processors are, I/O cannot be made that + * fast, and that's why shimmering has been limited to small + * number of monsters -- pelpel + */ + if (!avoid_other && !avoid_shimmer && !use_graphics && + !resting && !running) + { + for (j = panel_row_min; j <= panel_row_max; j++) + { + for (i = panel_col_min; i <= panel_col_max; i++) + { + cave_type *c_ptr = &cave[j][i]; + feature_type *f_ptr; + + /* Apply terrain feature mimics */ + if (c_ptr->mimic) + { + f_ptr = &f_info[c_ptr->mimic]; + } + else + { + f_ptr = &f_info[f_info[c_ptr->feat].mimic]; + } + + /* Skip normal features */ + if (!(f_ptr->flags1 & (FF1_ATTR_MULTI))) continue; + + /* Redraw a shimmering spot */ + lite_spot(j, i); + } + } + } + + + /* Handle monster detection */ + if (repair_monsters) + { + /* Reset the flag */ + repair_monsters = FALSE; + + /* Rotate detection flags */ + for (i = 1; i < m_max; i++) + { + monster_type *m_ptr; + + /* Access monster */ + m_ptr = &m_list[i]; + + /* Skip dead monsters */ + if (!m_ptr->r_idx) continue; + + /* Nice monsters get mean */ + if (m_ptr->mflag & (MFLAG_NICE)) + { + /* Nice monsters get mean */ + m_ptr->mflag &= ~(MFLAG_NICE); + } + + /* Handle memorized monsters */ + if (m_ptr->mflag & (MFLAG_MARK)) + { + /* Maintain detection */ + if (m_ptr->mflag & (MFLAG_SHOW)) + { + /* Forget flag */ + m_ptr->mflag &= ~(MFLAG_SHOW); + + /* Still need repairs */ + repair_monsters = TRUE; + } + + /* Remove detection */ + else + { + /* Forget flag */ + m_ptr->mflag &= ~(MFLAG_MARK); + + /* Assume invisible */ + m_ptr->ml = FALSE; + + /* Update the monster */ + update_mon(i, FALSE); + + /* Redraw regardless */ + lite_spot(m_ptr->fy, m_ptr->fx); + } + } + } + } + + /* + * Moved from dungeon() -- It'll get called whenever player + * spends energy, so that maze isn't incredibly easy for + * Sorcerors and alike any longer -- pelpel + * + * Forget everything when requested hehe I'm *NASTY* + */ + if (dun_level && (dungeon_flags1 & DF1_FORGET)) + { + wiz_dark(); + } + } + + + /* Hack -- notice death */ + if (!alive || death) break; + + /* Handle "leaving" */ + if (p_ptr->leaving) break; + } +} + + + +/* + * Interact with the current dungeon level. + * + * This function will not exit until the level is completed, + * the user dies, or the game is terminated. + */ +static void dungeon(void) +{ + /* Reset various flags */ + hack_mind = FALSE; + + /* Not leaving */ + p_ptr->leaving = FALSE; + + /* Reset the "command" vars */ + command_cmd = 0; + command_new = 0; + command_rep = 0; + command_arg = 0; + command_dir = 0; + + /* Make sure partial summoning counter is initialized. */ + p_ptr->maintain_sum = 0; + + /* Cancel the target */ + target_who = 0; + + /* Cancel the health bar */ + health_track(0); + + + /* Check visual effects */ + shimmer_monsters = TRUE; + shimmer_objects = TRUE; + repair_monsters = TRUE; + repair_objects = TRUE; + + + /* Disturb */ + disturb(1); + + /* Track maximum player level */ + if (p_ptr->max_plv < p_ptr->lev) + { + p_ptr->max_plv = p_ptr->lev; + } + + /* Track maximum dungeon level (if not in quest -KMW-) */ + if ((max_dlv[dungeon_type] < dun_level) && !p_ptr->inside_quest) + { + max_dlv[dungeon_type] = dun_level; + } + + /* No stairs down from Quest */ + if (is_quest(dun_level) && !p_ptr->astral) + { + create_down_stair = FALSE; + create_down_shaft = FALSE; + } + + /* Paranoia -- no stairs from town or wilderness */ + if (!dun_level) create_down_stair = create_up_stair = FALSE; + if (!dun_level) create_down_shaft = create_up_shaft = FALSE; + + /* Option -- no connected stairs */ + if (!dungeon_stair) create_down_stair = create_up_stair = FALSE; + if (!dungeon_stair) create_down_shaft = create_up_shaft = FALSE; + + /* no connecting stairs on special levels */ + if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_stair = create_up_stair = FALSE; + if (!(dungeon_flags2 & DF2_NO_STAIR)) create_down_shaft = create_up_shaft = FALSE; + + /* Make a stairway. */ + if ((create_up_stair || create_down_stair || + create_up_shaft || create_down_shaft) && + !get_fbranch()) + { + /* Place a stairway */ + if (cave_valid_bold(p_ptr->py, p_ptr->px)) + { + /* XXX XXX XXX */ + delete_object(p_ptr->py, p_ptr->px); + + /* Make stairs */ + if (create_down_stair) + { + cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_MORE); + } + else if (create_down_shaft) + { + cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_SHAFT_DOWN); + } + else if (create_up_shaft) + { + cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_SHAFT_UP); + } + else + { + cave_set_feat(p_ptr->py, p_ptr->px, (dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_LESS); + } + } + + /* Cancel the stair request */ + create_down_stair = create_up_stair = FALSE; + create_down_shaft = create_up_shaft = FALSE; + } + + /* Hack - Assume invalid panel */ + panel_row_min = cur_hgt; + panel_row_max = 0; + panel_col_min = cur_wid; + panel_col_max = 0; + + /* Center the panel */ + verify_panel(); + + /* Flush messages */ + msg_print(NULL); + + + /* Enter "xtra" mode */ + character_xtra = TRUE; + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + /* Window stuff */ + p_ptr->window |= (PW_MONSTER); + + /* Redraw dungeon */ + p_ptr->redraw |= (PR_WIPE | PR_BASIC | PR_EXTRA); + + /* Redraw map */ + p_ptr->redraw |= (PR_MAP); + + /* Window stuff */ + p_ptr->window |= (PW_OVERHEAD); + + /* Update stuff */ + p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_SANITY | PU_BODY); + + /* Calculate torch radius */ + p_ptr->update |= (PU_TORCH); + + /* Update stuff */ + update_stuff(); + + /* Redraw stuff */ + redraw_stuff(); + + /* Redraw stuff */ + window_stuff(); + + /* Update stuff */ + p_ptr->update |= (PU_VIEW | PU_FLOW | PU_DISTANCE | PU_MON_LITE); + + /* Update stuff */ + update_stuff(); + + /* Redraw stuff */ + redraw_stuff(); + + /* Leave "xtra" mode */ + character_xtra = FALSE; + + /* Update stuff */ + p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_BODY); + + /* Combine / Reorder the pack */ + p_ptr->notice |= (PN_COMBINE | PN_REORDER); + + /* Notice stuff */ + notice_stuff(); + + /* Update stuff */ + update_stuff(); + + /* Redraw stuff */ + redraw_stuff(); + + /* Window stuff */ + window_stuff(); + + /* Refresh */ + Term_fresh(); + + + /* Announce (or repeat) the feeling */ + if (dun_level) do_cmd_feeling(); + + + /* Hack -- notice death or departure */ + if (!alive || death) return; + + /*** Process this dungeon level ***/ + + /* Reset the monster generation level */ + monster_level = dun_level; + + /* Reset the object generation level */ + object_level = dun_level; + + hack_mind = TRUE; + + /* Mega Hack, if needed wipe all stairs */ + if (dungeon_type == DUNGEON_DEATH) + { + int i, j; + + for (i = 0; i < cur_wid; i++) + { + for (j = 0; j < cur_hgt; j++) + { + cave_type *c_ptr = &cave[j][i]; + + switch (c_ptr->feat) + { + case FEAT_MORE: + case FEAT_LESS: + case FEAT_SHAFT_UP: + case FEAT_SHAFT_DOWN: + { + cave_set_feat(j, i, FEAT_FLOOR); + break; + } + } + } + } + + /* Reset the monster generation level */ + monster_level = 127; + + /* Reset the object generation level */ + object_level = 0; + } + + /* Main loop */ + while (TRUE) + { + /* Hack -- Compact the monster list occasionally */ + if (m_cnt + 32 > max_m_idx) compact_monsters(64); + + /* Hack -- Compress the monster list occasionally */ + if (m_cnt + 32 < m_max) compact_monsters(0); + + + /* Hack -- Compact the object list occasionally */ + if (o_cnt + 32 > max_o_idx) compact_objects(64); + + /* Hack -- Compress the object list occasionally */ + if (o_cnt + 32 < o_max) compact_objects(0); + + + + /* Process the player */ + process_player(); + + /* Notice stuff */ + if (p_ptr->notice) notice_stuff(); + + /* Update stuff */ + if (p_ptr->update) update_stuff(); + + /* Redraw stuff */ + if (p_ptr->redraw) redraw_stuff(); + + /* Redraw stuff */ + if (p_ptr->window) window_stuff(); + + /* Hack -- Hilite the player */ + move_cursor_relative(p_ptr->py, p_ptr->px); + + /* Optional fresh */ + if (fresh_after) Term_fresh(); + + /* Hack -- Notice death or departure */ + if (!alive || death) break; + + + total_friends = 0; + total_friend_levels = 0; + + /* Process all of the monsters */ + process_monsters(); + + /* Notice stuff */ + if (p_ptr->notice) notice_stuff(); + + /* Update stuff */ + if (p_ptr->update) update_stuff(); + + /* Redraw stuff */ + if (p_ptr->redraw) redraw_stuff(); + + /* Redraw stuff */ + if (p_ptr->window) window_stuff(); + + /* Hack -- Hilite the player */ + move_cursor_relative(p_ptr->py, p_ptr->px); + + /* Optional fresh */ + if (fresh_after) Term_fresh(); + + /* Hack -- Notice death or departure */ + if (!alive || death) break; + + + /* Process the world */ + process_world(); + + /* Process the appropriate hooks */ + process_hooks(HOOK_END_TURN, "(d)", is_quest(dun_level)); + + /* Make it pulsate and live !!!! */ + if ((dungeon_flags1 & DF1_EVOLVE) && dun_level) + { + if (!(turn % 10)) evolve_level(TRUE); + } + + /* Notice stuff */ + if (p_ptr->notice) notice_stuff(); + + /* Update stuff */ + if (p_ptr->update) update_stuff(); + + /* Redraw stuff */ + if (p_ptr->redraw) redraw_stuff(); + + /* Window stuff */ + if (p_ptr->window) window_stuff(); + + /* Hack -- Hilite the player */ + move_cursor_relative(p_ptr->py, p_ptr->px); + + /* Optional fresh */ + if (fresh_after) Term_fresh(); + + /* Hack -- Notice death or departure */ + if (!alive || death) break; + + /* Handle "leaving" */ + if (p_ptr->leaving) break; + + /* Count game turns */ + turn++; + } + + /* Did we leave a dungeon ? */ + if ((dun_level < d_info[dungeon_type].mindepth) && !is_recall) + { + dun_level = 0; + + if (d_info[dungeon_type].ix > -1) + { + p_ptr->wilderness_x = d_info[dungeon_type].ix; + p_ptr->wilderness_y = d_info[dungeon_type].iy; + } + + dungeon_type = DUNGEON_WILDERNESS; + } + + if (dun_level > d_info[dungeon_type].maxdepth) + { + dun_level = 0; + + if (d_info[dungeon_type].ox > -1) + { + p_ptr->wilderness_x = d_info[dungeon_type].ox; + p_ptr->wilderness_y = d_info[dungeon_type].oy; + } + + dungeon_type = DUNGEON_WILDERNESS; + } + + is_recall = FALSE; +} + + + + +/* + * Load some "user pref files" + */ +static void load_all_pref_files(void) +{ + char buf[1024]; + + + /* Access the "race" pref file */ + sprintf(buf, "%s.prf", rp_ptr->title + rp_name); + + /* Process that file */ + process_pref_file(buf); + + /* Access the "class" pref file */ + sprintf(buf, "%s.prf", spp_ptr->title + c_name); + + /* Process that file */ + process_pref_file(buf); + + /* Access the "character" pref file */ + sprintf(buf, "%s.prf", player_name); + + /* Process that file */ + process_pref_file(buf); + + /* Process player specific automatizer sets */ + /* TODO: Disabled temporarily because it causes duplicate + * rules on save and subsequent game load. */ + /* sprintf(buf2, "%s.atm", player_name); */ + /* path_build(buf, sizeof(buf), ANGBAND_DIR_USER, buf2); */ + /* automatizer_load(buf); */ +} + +/* + * Actually play a game + * + * If the "new_game" parameter is true, then, after loading the + * savefile, we will commit suicide, if necessary, to allow the + * player to start a new game. + */ +void play_game(bool_ new_game) +{ + int i, tmp_dun; + + bool_ cheat_death = FALSE; + + /* Hack -- Character is "icky" */ + character_icky = TRUE; + + + /* Make sure main term is active */ + Term_activate(angband_term[0]); + + /* Initialise the resize hook XXX XXX XXX */ + angband_term[0]->resize_hook = resize_map; + + /* XXX XXX XXX hardcoded number of terms */ + for (i = 1; i < 8; i++) + { + if (angband_term[i]) + { + /* Add redraw hook */ + angband_term[i]->resize_hook = resize_window; + } + } + + + /* Hack -- turn off the cursor */ + (void)Term_set_cursor(0); + + /* Character list */ + if (!new_game && !no_begin_screen) new_game = begin_screen(); + no_begin_screen = FALSE; + + /* Attempt to load */ + if (!load_player()) + { + /* Oops */ + quit("broken savefile"); + } + + /* Nothing loaded */ + if (!character_loaded) + { + /* Make new player */ + new_game = TRUE; + + /* The dungeon is not ready */ + character_dungeon = FALSE; + } + else + { + int i; + + /* Init new skills to their defaults */ + for (i = old_max_s_idx; i < max_s_idx; i++) + { + s32b value = 0, mod = 0; + + compute_skills(&value, &mod, i); + + init_skill(value, mod, i); + } + } + + /* Process old character */ + if (!new_game) + { + /* Process the player name */ + process_player_name(FALSE); + } + + /* Init the RNG */ + if (Rand_quick) + { + u32b seed; + + /* Basic seed */ + seed = (time(NULL)); + +#ifdef SET_UID + + /* Mutate the seed on Unix machines */ + seed = ((seed >> 3) * (getpid() << 1)); + +#endif + + /* Use the complex RNG */ + Rand_quick = FALSE; + + /* Seed the "complex" RNG */ + Rand_state_init(seed); + } + + /* Extract the options */ + for (i = 0; option_info[i].o_desc; i++) + { + int os = option_info[i].o_page; + int ob = option_info[i].o_bit; + + /* Set the "default" options */ + if (option_info[i].o_var) + { + /* Set */ + if (option_flag[os] & (1L << ob)) + { + /* Set */ + (*option_info[i].o_var) = TRUE; + } + + /* Clear */ + else + { + /* Clear */ + (*option_info[i].o_var) = FALSE; + } + } + } + + /* Roll new character */ + if (new_game) + { + /* Show intro */ + modules[game_module_idx].intro(); + + /* The dungeon is not ready */ + character_dungeon = FALSE; + + /* Hack -- seed for flavors */ + seed_flavor = rand_int(0x10000000); + + /* Roll up a new character */ + player_birth(); + + /* Start in town, or not */ + if (p_ptr->astral) dun_level = 98; + else dun_level = 0; + p_ptr->inside_quest = 0; + p_ptr->inside_arena = 0; + + /* Hack -- enter the world */ + /* Mega-hack Vampires and Spectres start in the dungeon */ + if (PRACE_FLAG(PR1_UNDEAD)) + { + turn = (10L * DAY / 2) + (START_DAY * 10) + 1; + } + else + { + turn = (START_DAY * 10) + 1; + } + } + + /* Flash a message */ + prt("Please wait...", 0, 0); + + /* Flush the message */ + Term_fresh(); + + /* Be sure to not bother the player */ + calc_powers_silent = TRUE; + + /* Hack -- Enter wizard mode */ + if (arg_wizard && enter_wizard_mode()) wizard = TRUE; + + /* Flavor the objects */ + flavor_init(); + + /* Reset the visual mappings */ + reset_visuals(); + + /* Window stuff */ + p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER); + + /* Window stuff */ + p_ptr->window |= (PW_MONSTER); + + /* Window stuff */ + window_stuff(); + + /* load user file */ + process_pref_file("user.prf"); + + /* Load the "pref" files */ + load_all_pref_files(); + + /* Set or clear "rogue_like_commands" if requested */ + if (arg_force_original) rogue_like_commands = FALSE; + if (arg_force_roguelike) rogue_like_commands = TRUE; + + /* Initialize vault info */ + if (init_v_info()) quit("Cannot initialize vaults"); + + /* Initialize hooks */ + init_hooks(); + init_hooks_help(); + init_hooks_module(); + + /* React to changes */ + Term_xtra(TERM_XTRA_REACT, 0); + + /* Mega hack, prevent lots of bugs */ + if ((p_ptr->px == 0) || (p_ptr->py == 0)) + { + p_ptr->px = 1; + p_ptr->py = 1; + }; + + /* Hack - if note file exists, load it */ + if (!new_game) + { + add_note_type(NOTE_ENTER_DUNGEON); + } + + /* Generate a dungeon level if needed */ + if (!character_dungeon) generate_cave(); + + /* Ok tell the scripts that the game is about to start */ + process_hooks(HOOK_GAME_START, "()"); + process_hooks_new(HOOK_GAME_START, NULL, NULL); + + /* Character is now "complete" */ + character_generated = TRUE; + + + /* Hack -- Character is no longer "icky" */ + character_icky = FALSE; + + + /* Start game */ + alive = TRUE; + + /* Hack -- Enforce "delayed death" */ + if (p_ptr->chp < 0) death = TRUE; + + /* Process */ + while (TRUE) + { + /* Save the level */ + old_dun_level = dun_level; + p_ptr->old_wild_mode = p_ptr->wild_mode; + + /* We reached surface ? good, lets go down again !! */ + if (p_ptr->astral && !dun_level) + { + p_ptr->astral = FALSE; + cmsg_print(TERM_L_GREEN, + "Well done ! You reached the town ! " + "You can now go down again."); + } + + /* Update monster list window */ + p_ptr->window |= (PW_M_LIST); + + /* Process the level */ + dungeon(); + + /* Save the current level if in a persistent level */ + tmp_dun = dun_level; + dun_level = old_dun_level; + save_dungeon(); + dun_level = tmp_dun; + + /* A death fate affects level generation */ + for (i = 0; i < MAX_FATES; i++) + { + /* Ignore empty slots */ + if (!fates[i].fate) continue; + + /* Ignore non-applicable fates */ + if (fates[i].level != dun_level) continue; + + /* Non-serious fate fails to fire 50% of time */ + if (!fates[i].serious && (rand_int(2) == 0)) continue; + + /* Analyse fate */ + switch (fates[i].fate) + { + /* You are doomed */ + case FATE_DIE: + { + cmsg_print(TERM_L_DARK, "You were fated to die here. DIE!"); + + /* You shall perish there */ + dungeon_type = DUNGEON_DEATH; + dun_level = d_info[dungeon_type].mindepth; /* was 1 */ + + fates[i].fate = FATE_NONE; + break; + } + } + } + + /* Notice stuff */ + if (p_ptr->notice) notice_stuff(); + + /* Update stuff */ + if (p_ptr->update) update_stuff(); + + /* Redraw stuff */ + if (p_ptr->redraw) redraw_stuff(); + + /* Window stuff */ + if (p_ptr->window) window_stuff(); + + /* Cancel the target */ + target_who = 0; + + /* Cancel the health bar */ + health_track(0); + + + /* Forget the lite */ + forget_mon_lite(); + + /* Forget the view */ + forget_view(); + + /* Handle "quit and save" */ + if (!alive && !death) break; + + + /* Erase the old cave */ + wipe_o_list(); + + + /* XXX XXX XXX */ + msg_print(NULL); + + /* Accidental Death */ + if (alive && death) + { + cheat_death = FALSE; + + /* Can we die ? please let us die ! */ + if (process_hooks(HOOK_DIE, "()")) + { + cheat_death = TRUE; + } + + /* Deus ex machina */ + else if (granted_resurrection()) + { + cheat_death = TRUE; + p_ptr->grace = -200000; + cmsg_format(TERM_L_GREEN, + "The power of %s raises you back from the grave!", + deity_info[p_ptr->pgod].name); + msg_print(NULL); + } + + /* Blood of life */ + else if (p_ptr->allow_one_death > 0) + { + cheat_death = TRUE; + + /* Lose one extra life */ + p_ptr->allow_one_death--; + + cmsg_print(TERM_L_GREEN, + "You have been saved by the Blood of Life!"); + msg_print(NULL); + } + + /* Cheat death option */ + else if ((wizard || cheat_live) && !get_check("Die? ")) + { + cheat_death = TRUE; + + /* Mark social class, reset age, if needed */ + if (p_ptr->sc) p_ptr->sc = p_ptr->age = 0; + + /* Increase age */ + p_ptr->age++; + + /* Mark savefile */ + noscore |= 0x0001; + msg_print("You invoke wizard mode and cheat death."); + msg_print(NULL); + } + + if (cheat_death) + { + /* Restore the winner status */ + total_winner = has_won; + + /* One more life spent */ + p_ptr->lives++; + + /* Restore hit points */ + p_ptr->chp = p_ptr->mhp; + p_ptr->chp_frac = 0; + + /* Heal sanity */ + p_ptr->csane = p_ptr->msane; + p_ptr->csane_frac = 0; + + /* Restore spell points */ + p_ptr->csp = p_ptr->msp; + p_ptr->csp_frac = 0; + + /* Hack -- Healing */ + (void)set_blind(0); + (void)set_confused(0); + (void)set_poisoned(0); + (void)set_afraid(0); + (void)set_paralyzed(0); + (void)set_image(0); + (void)set_stun(0); + (void)set_cut(0); + + /* accounting for a new ailment. -LM- */ + p_ptr->black_breath = FALSE; + + /* Hack -- don't go to undead form */ + p_ptr->necro_extra &= ~CLASS_UNDEAD; + + /* Hack -- Prevent starvation */ + (void)set_food(PY_FOOD_MAX - 1); + + /* Hack -- cancel recall */ + if (p_ptr->word_recall) + { + /* Message */ + msg_print("A tension leaves the air around you..."); + msg_print(NULL); + + /* Hack -- Prevent recall */ + p_ptr->word_recall = 0; + } + + /* Note cause of death XXX XXX XXX */ + (void)strcpy(died_from, "Cheating death"); + + /* Do not die */ + death = FALSE; + + /* New depth -KMW- */ + /* dun_level = 0; */ + p_ptr->inside_arena = 0; + leaving_quest = 0; + p_ptr->inside_quest = 0; + + /* Leaving */ + p_ptr->leaving = TRUE; + } + } + + /* Handle "death" */ + if (death) + { + break; + } + + /* Mega hack */ + if (dun_level) p_ptr->wild_mode = FALSE; + + /* Make a new level */ + process_hooks(HOOK_NEW_LEVEL, "(d)", is_quest(dun_level)); + generate_cave(); + } + + /* Close stuff */ + close_game(); + + /* Quit */ + quit(NULL); +} + diff --git a/src/externs.h b/src/externs.h index 20f094dd..29a64267 100644 --- a/src/externs.h +++ b/src/externs.h @@ -316,7 +316,7 @@ extern term *angband_term[ANGBAND_TERM_MAX]; extern char angband_term_name[ANGBAND_TERM_MAX][80]; extern byte angband_color_table[256][4]; extern char angband_sound_name[SOUND_MAX][16]; -extern cave_type *cave[MAX_HGT]; +extern cave_type **cave; extern object_type *o_list; extern monster_type *m_list; extern monster_type *km_list; @@ -1902,8 +1902,6 @@ s16b get_random_spell(s16b random_type, int lev); /* spells6.c */ -SGLIB_DEFINE_LIST_PROTOTYPES(school_provider, compare_school_provider, next); - void schools_init(); school_type *school_at(int index); @@ -1945,7 +1943,6 @@ extern s32b rescale(s32b x, s32b max, s32b new_max); extern bool_ input_box(cptr text, int y, int x, char *buf, int max); extern void draw_box(int y, int x, int h, int w); extern void display_list(int y, int x, int h, int w, cptr title, cptr *list, int max, int begin, int sel, byte sel_color); -extern int ask_menu(cptr ask, char **items, int max); extern cptr get_player_race_name(int pr, int ps); extern cptr get_month_name(int month, bool_ full, bool_ compact); extern cptr get_day(int day); @@ -2324,7 +2321,7 @@ extern s16b get_skill_scale(int skill, u32b scale); extern void do_cmd_skill(void); extern void do_cmd_activate_skill(void); extern s16b melee_skills[MAX_MELEE]; -extern char *melee_names[MAX_MELEE]; +extern const char *melee_names[MAX_MELEE]; extern s16b get_melee_skills(void); extern s16b get_melee_skill(void); extern bool_ forbid_gloves(void); diff --git a/src/files.c b/src/files.c deleted file mode 100644 index a599af23..00000000 --- a/src/files.c +++ /dev/null @@ -1,5842 +0,0 @@ -/* File: files.c */ - -/* Purpose: code dealing with files (and death) */ - -/* - * 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 "angband.h" - -#include "hiscore.h" -#include "hooks.h" - - -/* - * Extract the first few "tokens" from a buffer - * - * This function uses "colon" and "slash" and delim arg as the delimeter characters. - * - * We never extract more than "num" tokens. The "last" token may include - * "delimeter" characters, allowing the buffer to include a "string" token. - * - * We save pointers to the tokens in "tokens", and return the number found. - * - * Hack -- Attempt to handle the 'c' character formalism - * - * Hack -- An empty buffer, or a final delimeter, yields an "empty" token. - * - * Hack -- We will always extract at least one token - */ -s16b tokenize(char *buf, s16b num, char **tokens, char delim1, char delim2) -{ - int i = 0; - - char *s = buf; - - - /* Process */ - while (i < num - 1) - { - char *t; - - /* Scan the string */ - for (t = s; *t; t++) - { - /* Found a delimiter */ - if ((*t == delim1) || (*t == delim2)) break; - - /* Handle single quotes */ - if (*t == '\'') - { - /* Advance */ - t++; - - /* Handle backslash */ - if (*t == '\\') t++; - - /* Require a character */ - if (!*t) break; - - /* Advance */ - t++; - - /* Hack -- Require a close quote */ - if (*t != '\'') *t = '\''; - } - - /* Handle back-slash */ - if (*t == '\\') t++; - } - - /* Nothing left */ - if (!*t) break; - - /* Nuke and advance */ - *t++ = '\0'; - - /* Save the token */ - tokens[i++] = s; - - /* Advance */ - s = t; - } - - /* Save the token */ - tokens[i++] = s; - - /* Number found */ - return (i); -} - - - -/* - * Parse a sub-file of the "extra info" (format shown below) - * - * Each "action" line has an "action symbol" in the first column, - * followed by a colon, followed by some command specific info, - * usually in the form of "tokens" separated by colons or slashes. - * - * Blank lines, lines starting with white space, and lines starting - * with pound signs ("#") are ignored (as comments). - * - * Note the use of "tokenize()" to allow the use of both colons and - * slashes as delimeters, while still allowing final tokens which - * may contain any characters including "delimiters". - * - * Note the use of "strtol()" to allow all "integers" to be encoded - * in decimal, hexidecimal, or octal form. - * - * Note that "monster zero" is used for the "player" attr/char, "object - * zero" will be used for the "stack" attr/char, and "feature zero" is - * used for the "nothing" attr/char. - * - * Parse another file recursively, see below for details - * %: - * - * Specify the attr/char values for "monsters" by race index - * R::: - * - * Specify the attr/char values for "objects" by kind index - * K::: - * - * Specify the attr/char values for "features" by feature index - * F::: - * - * Specify the attr/char values for "stores" by store index - * B::: - * - * Specify the attr/char values for unaware "objects" by kind tval - * U::: - * - * Specify the attr/char values for inventory "objects" by kind tval - * E::: - * - * Define a macro action, given an encoded macro action - * A: - * - * Create a normal macro, given an encoded macro trigger - * P: - * - * Create a command macro, given an encoded macro trigger - * C: - * - * Create a keyset mapping - * S::: - * - * Turn an option off, given its name - * X: - * - * Turn an option on, given its name - * Y: - * - * Specify visual information, given an index, and some data - * V::::: - * - * Specify squelch settings - * Q:: - */ -errr process_pref_file_aux(char *buf) -{ - int i, j, n1, n2; - - char *zz[16]; - - - /* Skip "empty" lines */ - if (!buf[0]) return (0); - - /* Skip "blank" lines */ - if (isspace(buf[0])) return (0); - - /* Skip comments */ - if (buf[0] == '#') return (0); - - /* Require "?:*" format */ - if (buf[1] != ':') return (1); - - - /* Process "%:" */ - if (buf[0] == '%') - { - /* Attempt to Process the given file */ - return (process_pref_file(buf + 2)); - } - - - /* Process "R::/" -- attr/char for monster races */ - if (buf[0] == 'R') - { - if (tokenize(buf + 2, 3, zz, ':', '/') == 3) - { - monster_race *r_ptr; - i = (huge)strtol(zz[0], NULL, 0); - n1 = strtol(zz[1], NULL, 0); - n2 = strtol(zz[2], NULL, 0); - if (i >= max_r_idx) return (1); - r_ptr = &r_info[i]; - if (n1) r_ptr->x_attr = n1; - if (n2) - { - r_ptr->x_char = n2; - } - return (0); - } - } - - - /* Process "G:::/" -- attr/char for overlay graphics */ - if (buf[0] == 'G') - { - /* Process "G:M::/" -- attr/char for ego monsters */ - if (buf[2] == 'M') - { - if (tokenize(buf + 4, 3, zz, ':', '/') == 3) - { - monster_ego *re_ptr; - i = (huge)strtol(zz[0], NULL, 0); - n1 = strtol(zz[1], NULL, 0); - n2 = strtol(zz[2], NULL, 0); - if (i >= max_re_idx) return (1); - re_ptr = &re_info[i]; - if (n1) re_ptr->g_attr = n1; - if (n2) - { - re_ptr->g_char = n2; - } - return (0); - } - } - - /* Process "G:P::/" -- attr/char for race modifiers */ - if (buf[2] == 'P') - { - if (tokenize(buf + 4, 3, zz, ':', '/') == 3) - { - player_race_mod *rmp_ptr; - i = (huge)strtol(zz[0], NULL, 0); - n1 = strtol(zz[1], NULL, 0); - n2 = strtol(zz[2], NULL, 0); - if (i >= max_rmp_idx) return (1); - rmp_ptr = &race_mod_info[i]; - if (n1) rmp_ptr->g_attr = n1; - if (n2) - { - rmp_ptr->g_char = n2; - } - return (0); - } - } - - /* Process "G:T::/" -- attr/char for traps */ - if (buf[2] == 'T') - { - if (tokenize(buf + 4, 3, zz, ':', '/') == 3) - { - trap_type *t_ptr; - i = (huge)strtol(zz[0], NULL, 0); - n1 = strtol(zz[1], NULL, 0); - n2 = strtol(zz[2], NULL, 0); - if (i >= max_t_idx) return (1); - t_ptr = &t_info[i]; - if (n1) t_ptr->g_attr = n1; - if (n2) - { - t_ptr->g_char = n2; - } - return (0); - } - } - } - - - /* Process "K::/" -- attr/char for object kinds */ - else if (buf[0] == 'K') - { - if (tokenize(buf + 2, 3, zz, ':', '/') == 3) - { - object_kind *k_ptr; - i = (huge)strtol(zz[0], NULL, 0); - n1 = strtol(zz[1], NULL, 0); - n2 = strtol(zz[2], NULL, 0); - if (i >= max_k_idx) return (1); - k_ptr = &k_info[i]; - if (n1) k_ptr->x_attr = n1; - if (n2) - { - k_ptr->x_char = n2; - } - return (0); - } - } - - - /* Process "F::/" -- attr/char for terrain features */ - else if (buf[0] == 'F') - { - if (tokenize(buf + 2, 3, zz, ':', '/') == 3) - { - feature_type *f_ptr; - i = (huge)strtol(zz[0], NULL, 0); - n1 = strtol(zz[1], NULL, 0); - n2 = strtol(zz[2], NULL, 0); - if (i >= max_f_idx) return (1); - f_ptr = &f_info[i]; - if (n1) f_ptr->x_attr = n1; - if (n2) - { - f_ptr->x_char = n2; - } - return (0); - } - } - - /* Process "B::/" -- attr/char for stores */ - else if (buf[0] == 'B') - { - if (tokenize(buf + 2, 3, zz, ':', '/') == 3) - { - store_info_type *st_ptr; - i = (huge)strtol(zz[0], NULL, 0); - n1 = strtol(zz[1], NULL, 0); - n2 = strtol(zz[2], NULL, 0); - if (i >= max_st_idx) return (1); - st_ptr = &st_info[i]; - if (n1) st_ptr->x_attr = n1; - if (n2) st_ptr->x_char = n2; - return (0); - } - } - - /* Process "S::/" -- attr/char for special things */ - else if (buf[0] == 'S') - { - if (tokenize(buf + 2, 3, zz, ':', '/') == 3) - { - j = (byte)strtol(zz[0], NULL, 0); - n1 = strtol(zz[1], NULL, 0); - n2 = strtol(zz[2], NULL, 0); - misc_to_attr[j] = n1; - misc_to_char[j] = n2; - return (0); - } - } - - /* Process "U::/" -- attr/char for unaware items */ - else if (buf[0] == 'U') - { - if (tokenize(buf + 2, 3, zz, ':', '/') == 3) - { - j = (huge)strtol(zz[0], NULL, 0); - n1 = strtol(zz[1], NULL, 0); - n2 = strtol(zz[2], NULL, 0); - for (i = 1; i < max_k_idx; i++) - { - object_kind *k_ptr = &k_info[i]; - if (k_ptr->tval == j) - { - if (n1) k_ptr->d_attr = n1; - if (n2) k_ptr->d_char = n2; - } - } - return (0); - } - } - - - /* Process "E::" -- attribute for inventory objects */ - else if (buf[0] == 'E') - { - if (tokenize(buf + 2, 2, zz, ':', '/') == 2) - { - j = (byte)strtol(zz[0], NULL, 0) % 128; - n1 = strtol(zz[1], NULL, 0); - if (n1) tval_to_attr[j] = n1; - return (0); - } - } - - - /* Process "A:" -- save an "action" for later */ - else if (buf[0] == 'A') - { - text_to_ascii(macro__buf, buf + 2); - return (0); - } - - /* Process "P:" -- normal macro */ - else if (buf[0] == 'P') - { - char tmp[1024]; - text_to_ascii(tmp, buf + 2); - macro_add(tmp, macro__buf); - return (0); - } - - /* Process "L::: -- extended command macro */ - else if (buf[0] == 'L') - { - switch (tokenize(buf + 2, 3, zz, ':', 0)) - { - case 3: - cli_add(zz[0], zz[1], zz[2]); - return 0; - case 2: - cli_add(zz[0], zz[1], 0); - return 0; - default: - return 1; - } - } - - /* Process "C:" -- create keymap */ - else if (buf[0] == 'C') - { - int mode; - - char tmp[1024]; - - if (tokenize(buf + 2, 2, zz, ':', '/') != 2) return (1); - - mode = strtol(zz[0], NULL, 0); - if ((mode < 0) || (mode >= KEYMAP_MODES)) return (1); - - text_to_ascii(tmp, zz[1]); - if (!tmp[0] || tmp[1]) return (1); - i = (byte)(tmp[0]); - - string_free(keymap_act[mode][i]); - - keymap_act[mode][i] = string_make(macro__buf); - - return (0); - } - - - /* Process "V:::::" -- visual info */ - else if (buf[0] == 'V') - { - if (tokenize(buf + 2, 5, zz, ':', '/') == 5) - { - i = (byte)strtol(zz[0], NULL, 0); - angband_color_table[i][0] = (byte)strtol(zz[1], NULL, 0); - angband_color_table[i][1] = (byte)strtol(zz[2], NULL, 0); - angband_color_table[i][2] = (byte)strtol(zz[3], NULL, 0); - angband_color_table[i][3] = (byte)strtol(zz[4], NULL, 0); - return (0); - } - } - /* set macro trigger names and a template */ - /* Process "T:::" */ - /* Process "T: