From a28741dd2b29142210f64325cb2cb87c806d3409 Mon Sep 17 00:00:00 2001 From: j-p-e-g Date: Mon, 20 Jul 2009 21:18:13 +0000 Subject: Fix 2822832: temporary brands not being removed when thrown with LRET_FUMBLED plus: thrown weapon permabrand being removed when DUR_WEAPON_BRAND is active plus: double messaging of "You are now empty-handed." Fix 2810517: Added a pre-ability requirements check that handles Lugonu's enter/exit abyss, berserking, recite, breath attacks and some more. If the requirements aren't met, the check fails before the success check, thus no turn is wasted. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@10360 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/abl-show.cc | 315 +++++++++++++++++++++++++------------------ crawl-ref/source/abyss.cc | 4 +- crawl-ref/source/abyss.h | 1 + crawl-ref/source/it_use2.cc | 2 +- crawl-ref/source/item_use.cc | 14 +- crawl-ref/source/item_use.h | 5 +- crawl-ref/source/spells3.cc | 5 +- 7 files changed, 205 insertions(+), 141 deletions(-) diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc index 88fd7e5641..aa6d1604eb 100644 --- a/crawl-ref/source/abl-show.cc +++ b/crawl-ref/source/abl-show.cc @@ -963,6 +963,182 @@ bool activate_ability() return _activate_talent(talents[selected]); } +// Check prerequisites for a number of abilities. +// Abort any attempt if these cannot be met, without losing the turn. +// TODO: Many more cases need to be added! +static bool _check_ability_possible(const ability_def& abil, + bool hungerCheck = true) +{ + // Don't insta-starve the player. + // (Happens at 100, losing consciousness possible from 500 downward.) + if (hungerCheck && you.species != SP_VAMPIRE) + { + const int expected_hunger = you.hunger - abil.food_cost * 2; +#ifdef DEBUG_DIAGNOSTICS + mprf(MSGCH_DIAGNOSTICS, + "hunger: %d, max. food_cost: %d, expected hunger: %d", + you.hunger, abil.food_cost * 2, expected_hunger); +#endif + // Safety margin for natural hunger, mutations etc. + if (expected_hunger <= 150) + { + mpr("You're too hungry."); + return (false); + } + } + + switch (abil.ability) + { + case ABIL_ZIN_RECITE: + { + const int result = check_recital_audience(); + if (result < 0) + { + mpr("There's no appreciative audience!"); + return (false); + } + else if (result < 1) + { + mpr("There's no-one here to preach to!"); + return (false); + } + return (true); + } + case ABIL_ZIN_CURE_ALL_MUTATIONS: + return (how_mutated()); + + case ABIL_ZIN_SANCTUARY: + if (env.sanctuary_time) + { + mpr("There's already a sanctuary in place on this level."); + return (false); + } + return (true); + + case ABIL_ELYVILON_PURIFICATION: + if (!you.disease && !you.rotting && !you.duration[DUR_POISONING] + && !you.duration[DUR_CONF] && !you.duration[DUR_SLOW] + && !you.duration[DUR_PARALYSIS] && !you.duration[DUR_PETRIFIED]) + { + mpr("Nothing ails you!"); + return (false); + } + return (true); + + case ABIL_ELYVILON_RESTORATION: + case ABIL_MUMMY_RESTORATION: + if (you.strength == you.max_strength + && you.intel == you.max_intel + && you.dex == you.max_dex + && (abil.ability == ABIL_MUMMY_RESTORATION || !player_rotted())) + { + mprf("You don't need to restore your stats%s!", + abil.ability == ABIL_ELYVILON_RESTORATION ? " or hit points" + : ""); + return (false); + } + return (true); + + case ABIL_LUGONU_ABYSS_EXIT: + if (you.level_type != LEVEL_ABYSS) + { + mpr("You aren't in the Abyss!"); + return (false); + } + return (true); + + case ABIL_LUGONU_CORRUPT: + return (!is_level_incorruptible()); + + case ABIL_LUGONU_ABYSS_ENTER: + if (you.level_type == LEVEL_ABYSS) + { + mpr("You're already here!"); + return (false); + } + else if (you.level_type == LEVEL_PANDEMONIUM) + { + mpr("That doesn't work from Pandemonium."); + return (false); + } + return (true); + + case ABIL_SIF_MUNA_FORGET_SPELL: + if (you.spell_no == 0) + { + mpr("You don't know any spells."); + return (false); + } + return (true); + + case ABIL_SPIT_POISON: + case ABIL_BREATHE_FIRE: + case ABIL_BREATHE_FROST: + case ABIL_BREATHE_POISON: + case ABIL_BREATHE_LIGHTNING: + case ABIL_BREATHE_POWER: + case ABIL_BREATHE_STICKY_FLAME: + case ABIL_BREATHE_STEAM: + if (you.duration[DUR_BREATH_WEAPON]) + { + canned_msg(MSG_CANNOT_DO_YET); + return (false); + } + return (true); + + case ABIL_EVOKE_BERSERK: + case ABIL_TROG_BERSERK: + if (you.hunger_state < HS_SATIATED) + { + mpr("You're too hungry to berserk."); + return (false); + } + return (you.can_go_berserk(true) && berserk_check_wielded_weapon()); + + case ABIL_MAPPING: + if (player_mutation_level(MUT_MAPPING) < 3 + && (you.level_type == LEVEL_PANDEMONIUM + || !player_in_mappable_area())) + { + mpr("You feel momentarily disoriented."); + return (false); + } + return (true); + + case ABIL_FLY_II: + if (you.duration[DUR_EXHAUSTED]) + { + mpr("You're too exhausted to fly."); + return (false); + } + else if (you.burden_state != BS_UNENCUMBERED) + { + mpr("You're carrying too much weight to fly."); + return (false); + } + return (true); + + case ABIL_TORMENT: + if (you.is_undead) + { + mpr("The unliving cannot use this ability."); + return (false); + } + return (true); + + case ABIL_EVOKE_TURN_INVISIBLE: // ring, randarts, darkness items + if (you.hunger_state < HS_SATIATED) + { + mpr("You're too hungry to turn invisible."); + return (false); + } + return (true); + + default: + return (true); + } +} + static bool _activate_talent(const talent& tal) { // Doing these would outright kill the player due to stat drain. @@ -1029,40 +1205,13 @@ static bool _activate_talent(const talent& tal) return (false); } - if (!enough_hp( abil.hp_cost, false )) + if (!enough_hp(abil.hp_cost, false)) { crawl_state.zero_turns_taken(); return (false); } - if (tal.which == ABIL_ZIN_SANCTUARY && env.sanctuary_time) - { - mpr("There's already a sanctuary in place on this level."); - crawl_state.zero_turns_taken(); - return (false); - } - - // Don't insta-starve the player. - // (Happens at 100, losing consciousness possible from 500 downward.) - if (hungerCheck && you.species != SP_VAMPIRE) - { - const int expected_hunger = you.hunger - abil.food_cost * 2; -#ifdef DEBUG_DIAGNOSTICS - mprf(MSGCH_DIAGNOSTICS, - "hunger: %d, max. food_cost: %d, expected hunger: %d", - you.hunger, abil.food_cost * 2, expected_hunger); -#endif - // Safety margin for natural hunger, mutations etc. - if (expected_hunger <= 150) - { - mpr("You're too hungry."); - crawl_state.zero_turns_taken(); - return (false); - } - } - - if ((tal.which == ABIL_EVOKE_BERSERK || tal.which == ABIL_TROG_BERSERK) - && !berserk_check_wielded_weapon()) + if (!_check_ability_possible(abil, hungerCheck)) { crawl_state.zero_turns_taken(); return (false); @@ -1159,12 +1308,6 @@ static bool _do_ability(const ability_def& abil) case ABIL_SPIT_POISON: // Naga + spit poison mutation { - if (you.duration[DUR_BREATH_WEAPON]) - { - canned_msg(MSG_CANNOT_DO_YET); - return (false); - } - const int pow = you.experience_level + player_mutation_level(MUT_SPIT_POISON) * 5 + (you.species == SP_NAGA) * 10; @@ -1184,15 +1327,6 @@ static bool _do_ability(const ability_def& abil) } case ABIL_EVOKE_MAPPING: // Randarts case ABIL_MAPPING: // Sense surroundings mutation - if (abil.ability == ABIL_MAPPING - && player_mutation_level(MUT_MAPPING) < 3 - && (you.level_type == LEVEL_PANDEMONIUM - || !player_in_mappable_area())) - { - mpr("You feel momentarily disoriented."); - return (false); - } - power = (abil.ability == ABIL_EVOKE_MAPPING) ? you.skills[SK_EVOCATIONS] : you.experience_level; @@ -1229,17 +1363,9 @@ static bool _do_ability(const ability_def& abil) case ABIL_BREATHE_POWER: case ABIL_BREATHE_STICKY_FLAME: case ABIL_BREATHE_STEAM: - if (you.duration[DUR_BREATH_WEAPON] && abil.ability != ABIL_SPIT_ACID) - { - canned_msg(MSG_CANNOT_DO_YET); + beam.range = _calc_breath_ability_range(abil.ability); + if (!spell_direction(abild, beam)) return (false); - } - else - { - beam.range = _calc_breath_ability_range(abil.ability); - if (!spell_direction(abild, beam)) - return (false); - } switch (abil.ability) { @@ -1336,20 +1462,8 @@ static bool _do_ability(const ability_def& abil) break; case ABIL_EVOKE_BERSERK: // amulet of rage, randarts - // FIXME: This is not the best way to do stuff, because - // you have a chance of failing the evocation test, in - // which case you'll lose MP, etc. - // We should add a pre-failure-test check in general. - if (you.hunger_state < HS_SATIATED) - { - mpr("You're too hungry to berserk."); - return (false); - } - else if (!you.can_go_berserk(true)) - return (false); - - // only exercise if berserk succeeds - // because of the test above, this should always happen, + // Only exercise if berserk succeeds. + // Because of the test above, this should always happen, // but I'm leaving it in - haranp if (go_berserk(true)) exercise(SK_EVOCATIONS, 1); @@ -1365,18 +1479,7 @@ static bool _do_ability(const ability_def& abil) // Fly (Draconians, or anything else with wings). case ABIL_FLY_II: - if (you.duration[DUR_EXHAUSTED]) - { - mpr("You're too exhausted to fly."); - return (false); - } - else if (you.burden_state != BS_UNENCUMBERED) - { - mpr("You're carrying too much weight to fly."); - return (false); - } - else - cast_fly(you.experience_level * 2); + cast_fly(you.experience_level * 2); break; // DEMONIC POWERS: @@ -1395,12 +1498,6 @@ static bool _do_ability(const ability_def& abil) break; case ABIL_TORMENT: - if (you.is_undead) - { - mpr("The unliving cannot use this ability."); - return (false); - } - torment(TORMENT_GENERIC, you.pos()); break; @@ -1448,12 +1545,6 @@ static bool _do_ability(const ability_def& abil) break; case ABIL_EVOKE_TURN_INVISIBLE: // ring, randarts, darkness items - if (you.hunger_state < HS_SATIATED) - { - mpr("You're too hungry to turn invisible."); - return (false); - } - potion_effect(POT_INVISIBILITY, 2 * you.skills[SK_EVOCATIONS] + 5); contaminate_player( 1 + random2(3), true ); exercise(SK_EVOCATIONS, 1); @@ -1487,17 +1578,6 @@ static bool _do_ability(const ability_def& abil) case ABIL_ZIN_RECITE: { - int result = check_recital_audience(); - if (result < 0) - { - mpr("There's no appreciative audience!"); - return (false); - } - else if (result < 1) - { - mpr("There's no-one here to preach to!"); - return (false); - } // up to (60 + 40)/2 = 50 const int pow = (2*skill_bump(SK_INVOCATIONS) + you.piety / 5) / 2; start_delay(DELAY_RECITE, 3, pow, you.hp); @@ -1707,12 +1787,6 @@ static bool _do_ability(const ability_def& abil) case ABIL_TROG_BERSERK: // Trog abilities don't use or train invocations. - if (you.hunger_state < HS_SATIATED) - { - mpr("You're too hungry to berserk."); - return (false); - } - go_berserk(true); break; @@ -1729,7 +1803,8 @@ static bool _do_ability(const ability_def& abil) break; case ABIL_SIF_MUNA_FORGET_SPELL: - cast_selective_amnesia(true); + if (!cast_selective_amnesia(true)) + return (false); break; case ABIL_ELYVILON_DESTROY_WEAPONS: @@ -1787,12 +1862,6 @@ static bool _do_ability(const ability_def& abil) break; case ABIL_LUGONU_ABYSS_EXIT: - if (you.level_type != LEVEL_ABYSS) - { - mpr("You aren't in the Abyss!"); - return (false); // Don't incur costs. - } - banished(DNGN_EXIT_ABYSS); exercise(SK_INVOCATIONS, 8 + random2(10)); break; @@ -1830,17 +1899,6 @@ static bool _do_ability(const ability_def& abil) case ABIL_LUGONU_ABYSS_ENTER: { - if (you.level_type == LEVEL_ABYSS) - { - mpr("You're already here."); - return (false); - } - else if (you.level_type == LEVEL_PANDEMONIUM) - { - mpr("That doesn't work from Pandemonium."); - return (false); - } - // Move permanent hp/mp loss from leaving to entering the Abyss. (jpeg) const int maxloss = std::max(2, div_rand_round(you.hp_max, 30)); // Lose permanent HP @@ -1927,7 +1985,6 @@ static bool _do_ability(const ability_def& abil) break; } case ABIL_FEAWN_SUNLIGHT: - if (!sunlight()) return (false); @@ -1942,7 +1999,7 @@ static bool _do_ability(const ability_def& abil) break; case ABIL_FEAWN_RAIN: - rain(you.pos() ); + rain(you.pos()); exercise(SK_INVOCATIONS, 2 + random2(3)); break; diff --git a/crawl-ref/source/abyss.cc b/crawl-ref/source/abyss.cc index 50e8d2a2f4..255f9c0bc0 100644 --- a/crawl-ref/source/abyss.cc +++ b/crawl-ref/source/abyss.cc @@ -904,7 +904,7 @@ static bool _is_level_corrupted() return (!!env.markers.find(MAT_CORRUPTION_NEXUS)); } -static bool _is_level_incorruptible() +bool is_level_incorruptible() { if (_is_level_corrupted()) { @@ -932,7 +932,7 @@ static void _corrupt_choose_colours() bool lugonu_corrupt_level(int power) { - if (_is_level_incorruptible()) + if (is_level_incorruptible()) return (false); mprf(MSGCH_GOD, "Lugonu's Hand of Corruption reaches out!"); diff --git a/crawl-ref/source/abyss.h b/crawl-ref/source/abyss.h index 7ecebd9e82..6fad89e955 100644 --- a/crawl-ref/source/abyss.h +++ b/crawl-ref/source/abyss.h @@ -14,6 +14,7 @@ void generate_abyss(); void area_shift(); void abyss_teleport(bool new_area); void save_abyss_uniques(); +bool is_level_incorruptible(); bool lugonu_corrupt_level(int power); void run_corruption_effects(int duration); diff --git a/crawl-ref/source/it_use2.cc b/crawl-ref/source/it_use2.cc index ca82d654e9..62e8a6ab26 100644 --- a/crawl-ref/source/it_use2.cc +++ b/crawl-ref/source/it_use2.cc @@ -486,7 +486,7 @@ bool unwield_item(bool showMsgs) if (you.duration[DUR_WEAPON_BRAND]) { you.duration[DUR_WEAPON_BRAND] = 0; - set_item_ego_type( item, OBJ_WEAPONS, SPWPN_NORMAL ); + set_item_ego_type(item, OBJ_WEAPONS, SPWPN_NORMAL); // We're letting this through even if hiding messages. mpr("Your branding evaporates."); diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index 9266f445cd..5a0ed0af1e 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -224,7 +224,7 @@ static bool _valid_weapon_swap(const item_def &item) // If force is true, don't check weapon inscriptions. // (Assuming the player was already prompted for that.) bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages, - bool force) + bool force, bool show_unwield_msg) { if (inv_count() < 1) { @@ -304,7 +304,8 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages, if (!unwield_item(show_weff_messages)) return (false); - canned_msg(MSG_EMPTY_HANDED); + if (show_unwield_msg) + canned_msg(MSG_EMPTY_HANDED); // Switching to bare hands is extra fast. you.turn_is_over = true; @@ -2220,8 +2221,11 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus, // Items that get a temporary brand from a player spell lose the // brand as soon as the player lets go of the item. Can't call // unwield_item() yet since the beam might get canceled. - if (you.duration[DUR_WEAPON_BRAND] && projected == LRET_THROWN) - set_item_ego_type( item, OBJ_WEAPONS, SPWPN_NORMAL ); + if (you.duration[DUR_WEAPON_BRAND] && projected != LRET_LAUNCHED + && throw_2 == you.equip[EQ_WEAPON]) + { + set_item_ego_type(item, OBJ_WEAPONS, SPWPN_NORMAL); + } std::string ammo_name; setup_missile_beam(&you, pbolt, item, ammo_name, returning); @@ -2307,7 +2311,7 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus, bool unwielded = false; if (throw_2 == you.equip[EQ_WEAPON] && thrown.quantity == 1) { - if (!wield_weapon(true, PROMPT_GOT_SPECIAL)) + if (!wield_weapon(true, PROMPT_GOT_SPECIAL, true, false, false)) return (false); unwielded = true; diff --git a/crawl-ref/source/item_use.h b/crawl-ref/source/item_use.h index 59fbbc944c..24972511a8 100644 --- a/crawl-ref/source/item_use.h +++ b/crawl-ref/source/item_use.h @@ -135,8 +135,9 @@ bool can_wield(item_def *weapon, bool say_why = false, /* *********************************************************************** * called from: acr * *********************************************************************** */ -bool wield_weapon(bool auto_wield, int slot = -1, bool show_we_messages = true, - bool force = false); +bool wield_weapon(bool auto_wield, int slot = -1, + bool show_weff_messages = true, bool force = false, + bool show_unwield_msg = true); // last updated 12may2000 {dlb} diff --git a/crawl-ref/source/spells3.cc b/crawl-ref/source/spells3.cc index 0a960b525f..6f238654ad 100644 --- a/crawl-ref/source/spells3.cc +++ b/crawl-ref/source/spells3.cc @@ -93,8 +93,9 @@ bool cast_selective_amnesia(bool force) return (false); } - if (!force && - random2(you.skills[SK_SPELLCASTING]) < random2(spell_difficulty(spell))) + if (!force + && random2(you.skills[SK_SPELLCASTING]) + < random2(spell_difficulty(spell))) { mpr("Oops! This spell sure is a blunt instrument."); forget_map(20 + random2(50)); -- cgit v1.2.3-54-g00ecf