Skip to content

Commit

Permalink
feat: orbs of guile
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ebering committed Dec 27, 2021
1 parent ecb9636 commit 92c0926
Show file tree
Hide file tree
Showing 22 changed files with 59 additions and 28 deletions.
7 changes: 5 additions & 2 deletions crawl-ref/source/actor.cc
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion crawl-ref/source/actor.h
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions crawl-ref/source/beam.cc
Expand Up @@ -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,
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion crawl-ref/source/describe-spells.cc
Expand Up @@ -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)
Expand Down
14 changes: 10 additions & 4 deletions crawl-ref/source/describe.cc
Expand Up @@ -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.";
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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());
}

Expand Down
2 changes: 1 addition & 1 deletion crawl-ref/source/describe.h
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion crawl-ref/source/evoke.cc
Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions crawl-ref/source/item-name.cc
Expand Up @@ -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";
}
}
Expand Down Expand Up @@ -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";
}
}
Expand Down
1 change: 1 addition & 0 deletions crawl-ref/source/item-prop-enum.h
Expand Up @@ -488,6 +488,7 @@ enum special_armour_type
SPARM_LIGHT,
SPARM_RAGE,
SPARM_MAYHEM,
SPARM_GUILE,
NUM_REAL_SPECIAL_ARMOURS,
NUM_SPECIAL_ARMOURS,
};
Expand Down
8 changes: 8 additions & 0 deletions crawl-ref/source/item-prop.cc
Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions crawl-ref/source/item-prop.h
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion crawl-ref/source/makeitem.cc
Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions crawl-ref/source/mapdef.cc
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion crawl-ref/source/melee-attack.cc
Expand Up @@ -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());
Expand Down
14 changes: 7 additions & 7 deletions crawl-ref/source/mon-cast.cc
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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]
Expand Down
2 changes: 2 additions & 0 deletions crawl-ref/source/player.cc
Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion crawl-ref/source/rltiles/dc-item.txt
Expand Up @@ -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
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions crawl-ref/source/shopping.cc
Expand Up @@ -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;

Expand Down
6 changes: 4 additions & 2 deletions crawl-ref/source/spl-cast.cc
Expand Up @@ -1648,9 +1648,11 @@ vector<string> desc_wl_success_chance(const monster_info& mi, int pow,
targeter* hitfunc)
{
targeter_beam* beam_hitf = dynamic_cast<targeter_beam*>(hitfunc);
const int wl = mi.willpower();
int wl = mi.willpower();
if (wl == WILL_INVULN)
return vector<string>{"infinite will"};
if (you.wearing_ego(EQ_ALL_ARMOUR, SPARM_GUILE))
wl = guile_adjust_willpower(wl);
if (hitfunc && !hitfunc->affects_monster(mi))
return vector<string>{"not susceptible"};
vector<string> descs;
Expand Down Expand Up @@ -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));
}
}
2 changes: 1 addition & 1 deletion crawl-ref/source/spl-monench.cc
Expand Up @@ -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<enchant_type> hexes;

if (mons_has_attacks(mon))
Expand Down
4 changes: 2 additions & 2 deletions crawl-ref/source/spl-transloc.cc
Expand Up @@ -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;
Expand Down

0 comments on commit 92c0926

Please sign in to comment.