From 92c09265f1980b2a6d744d2dc147bb7e7776706d Mon Sep 17 00:00:00 2001 From: "Edgar A. Bering IV" Date: Mon, 20 Dec 2021 18:08:15 +0200 Subject: [PATCH] feat: orbs of guile The guile ego is a will-check analog of harm: it reduces the willpower of any user's target by 3 * WL_PIP, in exchange for reducing the willpower of the user by 2 * WL_PIP. This is more interesting than a straight hex enhancer: the tradeoff is stiffer and it works with all willpower checks, not just hexes. --- crawl-ref/source/actor.cc | 7 +++++-- crawl-ref/source/actor.h | 2 +- crawl-ref/source/beam.cc | 6 +++--- crawl-ref/source/describe-spells.cc | 2 +- crawl-ref/source/describe.cc | 14 ++++++++++---- crawl-ref/source/describe.h | 2 +- crawl-ref/source/evoke.cc | 2 +- crawl-ref/source/item-name.cc | 2 ++ crawl-ref/source/item-prop-enum.h | 1 + crawl-ref/source/item-prop.cc | 8 ++++++++ crawl-ref/source/item-prop.h | 2 ++ crawl-ref/source/makeitem.cc | 4 +++- crawl-ref/source/mapdef.cc | 1 + crawl-ref/source/melee-attack.cc | 2 +- crawl-ref/source/mon-cast.cc | 14 +++++++------- crawl-ref/source/player.cc | 2 ++ crawl-ref/source/rltiles/dc-item.txt | 3 ++- .../source/rltiles/item/armour/brands/i-guile.png | Bin 0 -> 191 bytes crawl-ref/source/shopping.cc | 1 + crawl-ref/source/spl-cast.cc | 6 ++++-- crawl-ref/source/spl-monench.cc | 2 +- crawl-ref/source/spl-transloc.cc | 4 ++-- 22 files changed, 59 insertions(+), 28 deletions(-) create mode 100644 crawl-ref/source/rltiles/item/armour/brands/i-guile.png diff --git a/crawl-ref/source/actor.cc b/crawl-ref/source/actor.cc index d75233bbe51..86c5804785a 100644 --- a/crawl-ref/source/actor.cc +++ b/crawl-ref/source/actor.cc @@ -117,13 +117,16 @@ int actor::skill_rdiv(skill_type sk, int mult, int div) const return div_rand_round(skill(sk, mult * 256), div * 256); } -int actor::check_willpower(int power) +int actor::check_willpower(const actor* source, int power) { - const int wl = willpower(); + int wl = willpower(); if (wl == WILL_INVULN) return 100; + if (source && source->wearing_ego(EQ_ALL_ARMOUR, SPARM_GUILE)) + wl = guile_adjust_willpower(wl); + const int adj_pow = ench_power_stepdown(power); const int wlchance = (100 + wl) - adj_pow; diff --git a/crawl-ref/source/actor.h b/crawl-ref/source/actor.h index 13a104a2b5b..f327c611704 100644 --- a/crawl-ref/source/actor.h +++ b/crawl-ref/source/actor.h @@ -288,7 +288,7 @@ class actor virtual bool res_petrify(bool temp = true) const = 0; virtual int res_constrict() const = 0; virtual int willpower() const = 0; - virtual int check_willpower(int power); + virtual int check_willpower(const actor* source, int power); virtual bool no_tele(bool blink = false) const = 0; virtual int inaccuracy() const; virtual bool antimagic_susceptible() const = 0; diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index 4a60cbaa73a..f96d4fc12df 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -1702,7 +1702,7 @@ static bool _monster_resists_mass_enchantment(monster* mons, return true; } - int res_margin = mons->check_willpower(pow); + int res_margin = mons->check_willpower(&you, pow); if (res_margin > 0) { if (simple_monster_message(*mons, @@ -3187,7 +3187,7 @@ void bolt::affect_player_enchantment(bool resistible) { if (resistible && has_saving_throw() - && you.check_willpower(ench_power) > 0) + && you.check_willpower(agent(true), ench_power) > 0) { // You resisted it. @@ -5317,7 +5317,7 @@ mon_resist_type bolt::try_enchant_monster(monster* mon, int &res_margin) // Chaos effects don't get a resistance check to match melee chaos. else if (real_flavour != BEAM_CHAOS) { - if (mon->check_willpower(ench_power) > 0) + if (mon->check_willpower(agent(true), ench_power) > 0) { // Note only actually used by messages in this case. res_margin = mon->willpower() - ench_power_stepdown(ench_power); diff --git a/crawl-ref/source/describe-spells.cc b/crawl-ref/source/describe-spells.cc index 71fd74ee766..c786b65712f 100644 --- a/crawl-ref/source/describe-spells.cc +++ b/crawl-ref/source/describe-spells.cc @@ -486,7 +486,7 @@ static string _effect_string(spell_type spell, const monster_info *mon_owner) } if (you.immune_to_hex(spell)) return "(immune)"; - return make_stringf("(%d%%)", hex_chance(spell, hd)); + return make_stringf("(%d%%)", hex_chance(spell, mon_owner)); } if (spell == SPELL_SMITING) diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index 9999a5ab732..b86027fb3f2 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -1803,6 +1803,8 @@ static const char* _item_ego_desc(special_armour_type ego) return "it berserks the wearer when making melee attacks (20% chance)."; case SPARM_MAYHEM: return "it causes witnesses of the wearer's kills to go into a frenzy."; + case SPARM_GUILE: + return "it weakens the willpower of the wielder and everyone they hex."; default: return "it makes the wearer crave the taste of eggplant."; } @@ -3563,10 +3565,14 @@ static int _hex_pow(const spell_type spell, const int hd) * What are the odds of the given spell, cast by a monster with the given * spell_hd, affecting the player? */ -int hex_chance(const spell_type spell, const int hd) +int hex_chance(const spell_type spell, const monster_info* mi) { - const int capped_pow = _hex_pow(spell, hd); - const int chance = hex_success_chance(you.willpower(), capped_pow, + const int capped_pow = _hex_pow(spell, mi->spell_hd()); + const bool guile = mi->inv[MSLOT_SHIELD] + && get_armour_ego_type(*mi->inv[MSLOT_SHIELD]) == SPARM_GUILE; + const int will = guile ? guile_adjust_willpower(you.willpower()) + : you.willpower(); + const int chance = hex_success_chance(will, capped_pow, 100, true); if (spell == SPELL_STRIP_WILLPOWER) return chance + (100 - chance) / 3; // ignores wl 1/3rd of the time @@ -3810,7 +3816,7 @@ static void _get_spell_description(const spell_type spell, "spell right now. %s\n", wiz_info.c_str()) : make_stringf("Chance to defeat your Will: %d%%%s\n", - hex_chance(spell, hd), + hex_chance(spell, mon_owner), wiz_info.c_str()); } diff --git a/crawl-ref/source/describe.h b/crawl-ref/source/describe.h index e30a67f93ff..f530c441b38 100644 --- a/crawl-ref/source/describe.h +++ b/crawl-ref/source/describe.h @@ -85,7 +85,7 @@ string get_skill_description(skill_type skill, bool need_title = false); void describe_skill(skill_type skill); -int hex_chance(const spell_type spell, const int hd); +int hex_chance(const spell_type spell, const monster_info* mon_owner); void describe_to_hit(const monster_info& mi, ostringstream &result, bool parenthesize = false, const item_def* weapon = nullptr); diff --git a/crawl-ref/source/evoke.cc b/crawl-ref/source/evoke.cc index 772f75546f1..981925351d3 100644 --- a/crawl-ref/source/evoke.cc +++ b/crawl-ref/source/evoke.cc @@ -684,7 +684,7 @@ static spret _phantom_mirror(dist *target) int dur = min(6, max(1, player_adjust_evoc_power( you.skill(SK_EVOCATIONS, 1) / 4 + 1) - * (100 - victim->check_willpower(power)) / 100)); + * (100 - victim->check_willpower(&you, power)) / 100)); mon->mark_summoned(dur, true, SPELL_PHANTOM_MIRROR); diff --git a/crawl-ref/source/item-name.cc b/crawl-ref/source/item-name.cc index c8bce0d3162..917e9bca9b3 100644 --- a/crawl-ref/source/item-name.cc +++ b/crawl-ref/source/item-name.cc @@ -570,6 +570,7 @@ const char* armour_ego_name(const item_def& item, bool terse) case SPARM_LIGHT: return "light"; case SPARM_RAGE: return "wrath"; case SPARM_MAYHEM: return "mayhem"; + case SPARM_GUILE: return "guile"; default: return "bugginess"; } } @@ -615,6 +616,7 @@ const char* armour_ego_name(const item_def& item, bool terse) case SPARM_LIGHT: return "light"; case SPARM_RAGE: return "*Rage"; case SPARM_MAYHEM: return "mayhem"; + case SPARM_GUILE: return "guile"; default: return "buggy"; } } diff --git a/crawl-ref/source/item-prop-enum.h b/crawl-ref/source/item-prop-enum.h index cda26c5024c..ba1889fb386 100644 --- a/crawl-ref/source/item-prop-enum.h +++ b/crawl-ref/source/item-prop-enum.h @@ -488,6 +488,7 @@ enum special_armour_type SPARM_LIGHT, SPARM_RAGE, SPARM_MAYHEM, + SPARM_GUILE, NUM_REAL_SPECIAL_ARMOURS, NUM_SPECIAL_ARMOURS, }; diff --git a/crawl-ref/source/item-prop.cc b/crawl-ref/source/item-prop.cc index 5bd8ed71490..b16ed218c47 100644 --- a/crawl-ref/source/item-prop.cc +++ b/crawl-ref/source/item-prop.cc @@ -2300,6 +2300,9 @@ int get_armour_willpower(const item_def &arm, bool check_artp) if (get_armour_ego_type(arm) == SPARM_WILLPOWER) res += WL_PIP; + if (get_armour_ego_type(arm) == SPARM_GUILE) + res -= 2 * WL_PIP; + if (check_artp && is_artefact(arm)) res += WL_PIP * artefact_property(arm, ARTP_WILLPOWER); @@ -2756,6 +2759,11 @@ bool shield_reflects(const item_def &shield) return is_shield(shield) && get_armour_ego_type(shield) == SPARM_REFLECTION; } +int guile_adjust_willpower(int wl) +{ + return max(0, wl - 3 * WL_PIP); +} + string item_base_name(const item_def &item) { return item_base_name(item.base_type, item.sub_type); diff --git a/crawl-ref/source/item-prop.h b/crawl-ref/source/item-prop.h index 6b29cf05e97..977af0b6690 100644 --- a/crawl-ref/source/item-prop.h +++ b/crawl-ref/source/item-prop.h @@ -128,6 +128,8 @@ bool is_shield_incompatible(const item_def &weapon, const item_def *shield = nullptr) PURE; bool shield_reflects(const item_def &shield) PURE; +int guile_adjust_willpower(int wl) PURE; + // Only works for armour/weapons/missiles // weapon functions: int weapon_rarity(int w_type) IMMUTABLE; diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc index 3c260cf94d0..7ad5036377e 100644 --- a/crawl-ref/source/makeitem.cc +++ b/crawl-ref/source/makeitem.cc @@ -780,7 +780,8 @@ static special_armour_type _generate_armour_type_ego(armour_type type) return random_choose_weighted(1, SPARM_PONDEROUSNESS, 1, SPARM_LIGHT, 1, SPARM_RAGE, - 1, SPARM_MAYHEM); + 1, SPARM_MAYHEM, + 1, SPARM_GUILE); case ARM_SCARF: return random_choose_weighted(1, SPARM_RESISTANCE, @@ -986,6 +987,7 @@ bool is_armour_brand_ok(int type, int brand, bool strict) case SPARM_LIGHT: case SPARM_RAGE: case SPARM_MAYHEM: + case SPARM_GUILE: return type == ARM_ORB; case NUM_SPECIAL_ARMOURS: diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc index 342d9445e68..52f0b4ba899 100644 --- a/crawl-ref/source/mapdef.cc +++ b/crawl-ref/source/mapdef.cc @@ -4949,6 +4949,7 @@ int str_to_ego(object_class_type item_type, string ego_str) "light", "wrath", "mayhem", + "guile", nullptr }; COMPILE_CHECK(ARRAYSZ(armour_egos) == NUM_REAL_SPECIAL_ARMOURS); diff --git a/crawl-ref/source/melee-attack.cc b/crawl-ref/source/melee-attack.cc index bba1a82f0f1..7965ecb2dc6 100644 --- a/crawl-ref/source/melee-attack.cc +++ b/crawl-ref/source/melee-attack.cc @@ -3110,7 +3110,7 @@ void melee_attack::mons_do_eyeball_confusion() const int ench_pow = you.get_mutation_level(MUT_EYEBALLS) * 30; monster* mon = attacker->as_monster(); - if (mon->check_willpower(ench_pow) <= 0) + if (mon->check_willpower(&you, ench_pow) <= 0) { mprf("The eyeballs on your body gaze at %s.", mon->name(DESC_THE).c_str()); diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc index fc6cd91339d..6a89a569915 100644 --- a/crawl-ref/source/mon-cast.cc +++ b/crawl-ref/source/mon-cast.cc @@ -4443,7 +4443,7 @@ static int _mons_mesmerise(monster* mons, bool actual) } const int pow = _ench_power(SPELL_MESMERISE, *mons); - const int will_check = you.check_willpower(pow); + const int will_check = you.check_willpower(mons, pow); // Don't mesmerise if you pass an WL check or have clarity. // If you're already mesmerised, you cannot resist further. @@ -4530,7 +4530,7 @@ static int _mons_cause_fear(monster* mons, bool actual) retval = 0; else { - const int res_margin = you.check_willpower(pow); + const int res_margin = you.check_willpower(mons, pow); if (!you.can_feel_fear(true)) canned_msg(MSG_YOU_UNAFFECTED); else if (res_margin > 0) @@ -4568,7 +4568,7 @@ static int _mons_cause_fear(monster* mons, bool actual) // It's possible to scare this monster. If its magic // resistance fails, do so. - int res_margin = mi->check_willpower(pow); + int res_margin = mi->check_willpower(mons, pow); if (res_margin > 0) { simple_monster_message(**mi, @@ -4607,7 +4607,7 @@ static int _mons_mass_confuse(monster* mons, bool actual) if (actual) { - const int willpower = you.check_willpower(pow); + const int willpower = you.check_willpower(mons, pow); if (willpower > 0) mprf("You%s", you.resist_margin_phrase(willpower).c_str()); else @@ -4633,7 +4633,7 @@ static int _mons_mass_confuse(monster* mons, bool actual) retval = max(retval, 0); - int res_margin = mi->check_willpower(pow); + int res_margin = mi->check_willpower(mons, pow); if (res_margin > 0) { if (actual) @@ -5569,7 +5569,7 @@ void mons_cast(monster* mons, bolt pbolt, spell_type spell_cast, case SPELL_CONFUSION_GAZE: { ASSERT(foe); - const int res_margin = foe->check_willpower(splpow / ENCH_POW_FACTOR); + const int res_margin = foe->check_willpower(mons, splpow / ENCH_POW_FACTOR); if (res_margin > 0) { if (you.can_see(*foe)) @@ -7324,7 +7324,7 @@ static void _siren_sing(monster* mons, bool avatar) // power is the same for siren & avatar song, so just use siren const int pow = _ench_power(SPELL_SIREN_SONG, *mons); - const int willpower = you.check_willpower(pow); + const int willpower = you.check_willpower(mons, pow); // Once mesmerised by a particular monster, you cannot resist anymore. if (you.duration[DUR_MESMERISE_IMMUNE] diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index 26602f46d3e..2e257fb89c3 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -6325,6 +6325,8 @@ int player_willpower(bool temp) // ego armours rm += WL_PIP * you.wearing_ego(EQ_ALL_ARMOUR, SPARM_WILLPOWER); + rm -= 2 * WL_PIP * you.wearing_ego(EQ_ALL_ARMOUR, SPARM_GUILE); + // rings of willpower rm += WL_PIP * you.wearing(EQ_RINGS, RING_WILLPOWER); diff --git a/crawl-ref/source/rltiles/dc-item.txt b/crawl-ref/source/rltiles/dc-item.txt index 08660bb5be9..9f333c4bcda 100644 --- a/crawl-ref/source/rltiles/dc-item.txt +++ b/crawl-ref/source/rltiles/dc-item.txt @@ -540,7 +540,8 @@ i-lunging BRAND_ARM_LUNGING i-infusion BRAND_ARM_INFUSION i-light BRAND_ARM_LIGHT i-rage BRAND_ARM_RAGE -i-mayhem BRAND_ARM_MAYHEM BRAND_ARM_LAST +i-mayhem BRAND_ARM_MAYHEM +i-guile BRAND_ARM_GUILE BRAND_ARM_LAST %rim 1 ###########OBJ_WANDS diff --git a/crawl-ref/source/rltiles/item/armour/brands/i-guile.png b/crawl-ref/source/rltiles/item/armour/brands/i-guile.png new file mode 100644 index 0000000000000000000000000000000000000000..009475d07e4a935305743492fa6c2e074338a8bd GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy`~f~8u0R?H)Fz9zo$+}2@@2x$ zbQd6>u_VYZn8D%MjWi%9&eO#)q~g}w^M-r}6a<(ZbMF7YKWFm-=HR7AG)%0WPE}O@ zPW+HJQ%TY@JWk((8Zy_lJKxedcx%OjX&mJZJ%Mw id4yf(Lgj1g;{Ocho{VnuwDP(@?(lT=b6Mw<&;$UA(Lvn+ literal 0 HcmV?d00001 diff --git a/crawl-ref/source/shopping.cc b/crawl-ref/source/shopping.cc index 4d6be58c3c3..bd2106a6d6f 100644 --- a/crawl-ref/source/shopping.cc +++ b/crawl-ref/source/shopping.cc @@ -366,6 +366,7 @@ unsigned int item_value(item_def item, bool ident) case SPARM_HARM: case SPARM_RAGE: case SPARM_MAYHEM: + case SPARM_GUILE: valued += 20; break; diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index ed2a6c15904..d9f40b99c86 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -1648,9 +1648,11 @@ vector desc_wl_success_chance(const monster_info& mi, int pow, targeter* hitfunc) { targeter_beam* beam_hitf = dynamic_cast(hitfunc); - const int wl = mi.willpower(); + int wl = mi.willpower(); if (wl == WILL_INVULN) return vector{"infinite will"}; + if (you.wearing_ego(EQ_ALL_ARMOUR, SPARM_GUILE)) + wl = guile_adjust_willpower(wl); if (hitfunc && !hitfunc->affects_monster(mi)) return vector{"not susceptible"}; vector descs; @@ -2899,7 +2901,7 @@ void do_demonic_magic(int pow, int rank) if (!mons || mons->wont_attack() || !mons_is_threatening(*mons)) continue; - if (mons->check_willpower(pow) <= 0) + if (mons->check_willpower(&you, pow) <= 0) mons->paralyse(&you, 1 + roll_dice(1,4)); } } diff --git a/crawl-ref/source/spl-monench.cc b/crawl-ref/source/spl-monench.cc index aca573cb2e5..146982ccbf4 100644 --- a/crawl-ref/source/spl-monench.cc +++ b/crawl-ref/source/spl-monench.cc @@ -114,7 +114,7 @@ bool do_slow_monster(monster& mon, const actor* agent, int dur) bool enfeeble_monster(monster &mon, int pow) { - const int res_margin = mon.check_willpower(pow); + const int res_margin = mon.check_willpower(&you, pow); vector hexes; if (mons_has_attacks(mon)) diff --git a/crawl-ref/source/spl-transloc.cc b/crawl-ref/source/spl-transloc.cc index 02f3647fa29..6bfd8a136c6 100644 --- a/crawl-ref/source/spl-transloc.cc +++ b/crawl-ref/source/spl-transloc.cc @@ -1370,13 +1370,13 @@ static int _disperse_monster(monster& mon, int pow) if (mon.no_tele()) return false; - if (mon.check_willpower(pow) > 0) + if (mon.check_willpower(&you, pow) > 0) monster_blink(&mon); else monster_teleport(&mon, true); // Moving the monster may have killed it in apply_location_effects. - if (mon.alive() && mon.check_willpower(pow) <= 0) + if (mon.alive() && mon.check_willpower(&you, pow) <= 0) mon.confuse(&you, 1 + random2avg(pow / 10, 2)); return true;