refactor: abstract some common fedhas code
authoradvil <rawlins@gmail.com>
Thu, 29 Apr 2021 20:43:44 +0000 (16:43 -0400)
committeradvil <rawlins@gmail.com>
Thu, 29 Apr 2021 22:13:46 +0000 (18:13 -0400)
The code to check whether Fedhas protects an ally is repeated in a
number of places in a way that is a bit error-prone (in fact, most cases
seem to not be handling all the attitude possibilities). This commit
abstracts this into a common location with some relatively simple way to
call it. This could be even more abstracted into something that covers
non-god cases that often show up nearby fedhas checks (e.g. demonic
guardian). I actually started to do this and it's trickier than it
seems, though.

crawl-ref/source/actor.cc
crawl-ref/source/beam.cc
crawl-ref/source/cloud.cc
crawl-ref/source/evoke.cc
crawl-ref/source/melee-attack.cc
crawl-ref/source/religion.cc
crawl-ref/source/religion.h
crawl-ref/source/spl-damage.cc
crawl-ref/source/spl-transloc.cc

index 50335b7..a78de5e 100644 (file)
@@ -930,11 +930,10 @@ string actor::resist_margin_phrase(int margin) const
 void actor::collide(coord_def newpos, const actor *agent, int pow)
 {
     actor *other = actor_at(newpos);
-    const bool fedhas_prot = agent && agent->deity() == GOD_FEDHAS
-                             && is_monster() && fedhas_protects(as_monster());
-    const bool fedhas_prot_other = agent && agent->deity() == GOD_FEDHAS
-                                   && other && other->is_monster()
-                                   && fedhas_protects(other->as_monster());
+    // TODO: should the first of these check agent?
+    const bool god_prot = god_protects(agent, as_monster());
+    const bool god_prot_other = other && god_protects(agent, other->as_monster());
+
     ASSERT(this != other);
     ASSERT(alive());
 
@@ -945,7 +944,7 @@ void actor::collide(coord_def newpos, const actor *agent, int pow)
         return;
     }
 
-    if (is_monster() && !fedhas_prot)
+    if (is_monster() && !god_prot)
         behaviour_event(as_monster(), ME_WHACK, agent);
 
     dice_def damage(2, 1 + pow / 10);
@@ -958,29 +957,27 @@ void actor::collide(coord_def newpos, const actor *agent, int pow)
                  name(DESC_THE).c_str(),
                  conj_verb("collide").c_str(),
                  other->name(DESC_THE).c_str());
-            if (fedhas_prot || fedhas_prot_other)
+            if (god_prot || god_prot_other)
             {
-                const bool both = fedhas_prot && fedhas_prot_other;
-                simple_god_message(
-                    make_stringf(" protects %s plant%s from harm.",
-                        agent->is_player() ? "your" :
-                        both ? "some" : "a",
-                        both ? "s" : "").c_str(), GOD_FEDHAS);
+                // do messaging at the right time.
+                // TODO: a bit ugly
+                god_protects(agent, as_monster(), false);
+                god_protects(agent, other->as_monster(), false);
             }
         }
 
-        if (other->is_monster() && !fedhas_prot_other)
+        if (other->is_monster() && !god_prot_other)
             behaviour_event(other->as_monster(), ME_WHACK, agent);
 
         const string thisname = name(DESC_A, true);
         const string othername = other->name(DESC_A, true);
-        if (other->alive() && !fedhas_prot_other)
+        if (other->alive() && !god_prot_other)
         {
             other->hurt(agent, other->apply_ac(damage.roll()),
                         BEAM_MISSILE, KILLED_BY_COLLISION,
                         othername, thisname);
         }
-        if (alive() && !fedhas_prot)
+        if (alive() && !god_prot)
         {
             hurt(agent, apply_ac(damage.roll()), BEAM_MISSILE,
                  KILLED_BY_COLLISION, thisname, othername);
@@ -1005,15 +1002,11 @@ void actor::collide(coord_def newpos, const actor *agent, int pow)
                  name(DESC_THE).c_str(), conj_verb("stop").c_str());
         }
 
-        if (fedhas_prot)
-        {
-            simple_god_message(
-                make_stringf(" protects %s plant from harm.",
-                    agent->is_player() ? "your" : "a").c_str(), GOD_FEDHAS);
-        }
+        if (god_prot)
+            god_protects(agent, as_monster(), false); // messaging
     }
 
-    if (!fedhas_prot)
+    if (!god_prot)
     {
         hurt(agent, apply_ac(damage.roll()), BEAM_MISSILE,
              KILLED_BY_COLLISION, "", feature_description_at(newpos));
index 55fd182..371c25e 100644 (file)
@@ -4772,12 +4772,7 @@ void bolt::affect_monster(monster* mon)
             else if (mons_is_hepliaklqana_ancestor(mon->type))
                 mprf("%s avoids your attack.", mon->name(DESC_THE).c_str());
             else if (!bush_immune(*mon))
-            {
-                simple_god_message(
-                    make_stringf(" protects %s plant from harm.",
-                        attitude == ATT_FRIENDLY ? "your" : "a").c_str(),
-                    GOD_FEDHAS);
-            }
+                god_protects(agent(), mon, false); // messaging
         }
     }
 
@@ -5041,8 +5036,7 @@ void bolt::affect_monster(monster* mon)
         //
         // FIXME: Should be a better way of doing this. For now, we are
         // just falsifying the death report... -cao
-        else if (you_worship(GOD_FEDHAS) && flavour == BEAM_SPORE
-            && fedhas_protects(mon))
+        else if (flavour == BEAM_SPORE && god_protects(mon))
         {
             if (mon->attitude == ATT_FRIENDLY)
                 mon->attitude = ATT_HOSTILE;
@@ -6684,33 +6678,7 @@ bool shoot_through_monster(const bolt& beam, const monster* victim)
     actor *originator = beam.agent();
     if (!victim || !originator)
         return false;
-
-    bool origin_worships_fedhas;
-    mon_attitude_type origin_attitude;
-    if (originator->is_player())
-    {
-        origin_worships_fedhas = have_passive(passive_t::shoot_through_plants);
-        origin_attitude = ATT_FRIENDLY;
-    }
-    else
-    {
-        monster* temp = originator->as_monster();
-        if (!temp)
-            return false;
-        origin_worships_fedhas = (temp->god == GOD_FEDHAS
-            || (temp->friendly()
-                && have_passive(passive_t::shoot_through_plants)));
-        origin_attitude = temp->attitude;
-    }
-
-    // Fedhas logic: the alignment check is to allow a penanced player
-    // to continue to fight hostile plants, in case what they angered can
-    // fight back
-    return (origin_worships_fedhas
-            && fedhas_protects(victim)
-            && (mons_atts_aligned(victim->attitude, origin_attitude)
-               || victim->neutral()))
-           // Player guardians
+    return god_protects(originator, victim)
            || (originator->is_player()
                && (testbits(victim->flags, MF_DEMONIC_GUARDIAN)
                    || mons_is_hepliaklqana_ancestor(victim->type)));
index ae12535..c419532 100644 (file)
@@ -788,10 +788,10 @@ void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range,
 
     const monster * const mons = monster_at(ctarget);
 
-    // Fedhas protects plants from damaging clouds placed by the player.
-    if (agent
-        && agent->deity() == GOD_FEDHAS
-        && fedhas_protects(mons)
+    // Fedhas protects plants from damaging clouds.
+    // XX demonic guardians? This logic mostly doesn't apply because protected
+    // monsters are also cloud immune, mostly
+    if (god_protects(agent, mons)
         && !actor_cloud_immune(*mons, cl_type))
     {
         return;
@@ -996,11 +996,11 @@ bool actor_cloud_immune(const actor &act, const cloud_struct &cloud)
     const bool player = act.is_player();
 
     if (!player
-        && (you_worship(GOD_FEDHAS)
-            && fedhas_protects(act.as_monster())
+        && (god_protects(act.as_monster())
             || testbits(act.as_monster()->flags, MF_DEMONIC_GUARDIAN))
         && (cloud.whose == KC_YOU || cloud.whose == KC_FRIENDLY)
-        && (act.as_monster()->friendly() || act.as_monster()->neutral()))
+        && (act.as_monster()->friendly() || act.as_monster()->neutral())
+        && (cloud.whose == KC_YOU || cloud.whose == KC_FRIENDLY))
     {
         return true;
     }
index b7f763d..b7c5bc4 100644 (file)
@@ -761,8 +761,7 @@ static spret _phantom_mirror(dist *target)
 static bool _valid_tremorstone_target(const monster &m)
 {
     return !mons_is_firewood(m)
-        && !(have_passive(passive_t::shoot_through_plants)
-                                && fedhas_protects(&m))
+        && !god_protects(&m)
         && !always_shoot_through_monster(&you, m);
 }
 
index 99bb53e..9808ccb 100644 (file)
@@ -132,8 +132,7 @@ bool melee_attack::handle_phase_attempted()
             if (stop_attack_prompt(hitfunc, "attack",
                                    [](const actor *act)
                                    {
-                                       return !(you.deity() == GOD_FEDHAS
-                                       && fedhas_protects(act->as_monster()));
+                                       return !god_protects(act->as_monster());
                                    }, nullptr, defender->as_monster()))
             {
                 cancel_attack = true;
@@ -176,8 +175,7 @@ bool melee_attack::handle_phase_attempted()
             if (stop_attack_prompt(hitfunc, "attack",
                                    [](const actor *act)
                                    {
-                                       return !(you.deity() == GOD_FEDHAS
-                                       && fedhas_protects(act->as_monster()));
+                                       return !god_protects(act->as_monster());
                                    }, nullptr, defender->as_monster()))
             {
                 cancel_attack = true;
index 658acc6..673d5fe 100644 (file)
@@ -2655,9 +2655,44 @@ static bool _fedhas_protects_species(monster_type mc)
 
 bool fedhas_protects(const monster *target)
 {
-    return target
-        ? _fedhas_protects_species(mons_base_type(*target))
-        : false;
+    return target && _fedhas_protects_species(mons_base_type(*target));
+}
+
+bool god_protects(const actor *agent, const monster *target, bool quiet)
+{
+    // The alignment check is to allow a penanced player to continue to fight 
+    // hostiles that would otherwise be protected, in case what they angered can
+    // fight back
+
+    const bool aligned = agent && target
+        && ((agent->is_player()
+                ? target->friendly()
+                : mons_atts_aligned(target->attitude,
+                                                agent->as_monster()->attitude))
+            || target->neutral());
+    // XX does it matter whether this just checks fedhas vs.
+    // the shoot_through_plants passive
+    if (aligned
+        && ((agent->is_player() || agent->as_monster()->friendly())
+                        && have_passive(passive_t::shoot_through_plants)
+            || agent->is_monster() && agent->deity() == GOD_FEDHAS) // purely theoretical?
+        && fedhas_protects(target))
+    {
+        if (!quiet && you.can_see(*target))
+        {
+            simple_god_message(
+                        make_stringf(" protects %s plant from harm.",
+                            agent->is_player() ? "your" : "a").c_str(),
+                        GOD_FEDHAS);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool god_protects(const monster *target, bool quiet)
+{
+    return god_protects(&you, target, quiet);
 }
 
 // Fedhas neutralises most plants and fungi
index c089a9a..cfca4c3 100644 (file)
@@ -117,6 +117,8 @@ bool god_protects_from_harm();
 bool jiyva_is_dead();
 void set_penance_xp_timeout();
 bool fedhas_protects(const monster* target);
+bool god_protects(const actor *agent, const monster *target, bool quiet=true);
+bool god_protects(const monster *target, bool quiet=true);
 bool fedhas_neutralises(const monster& target);
 void nemelex_death_message();
 
index 782521d..899bdbe 100644 (file)
@@ -394,11 +394,8 @@ static void _player_hurt_monster(monster &mon, int damage, beam_type flavour,
 {
     ASSERT(mon.alive() || !god_conducts);
 
-    if (god_conducts && you.deity() == GOD_FEDHAS && fedhas_neutralises(mon))
-    {
-        simple_god_message(" protects your plant from harm.", GOD_FEDHAS);
+    if (god_conducts && god_protects(&mon, false))
         return;
-    }
 
     god_conduct_trigger conducts[3];
     if (god_conducts)
@@ -578,9 +575,8 @@ static spret _cast_los_attack_spell(spell_type spell, int pow,
             verb = "frozen";
             prompt_verb = "refrigerate";
             vulnerable = [](const actor *caster, const actor *act) {
-                return act->is_player() || act->res_cold() < 3
-                       && !(caster->deity() == GOD_FEDHAS
-                            && fedhas_protects(act->as_monster()));
+                return (act != caster || act->res_cold() < 3)
+                       && !god_protects(caster, act->as_monster());
             };
             break;
 
@@ -604,8 +600,7 @@ static spret _cast_los_attack_spell(spell_type spell, int pow,
             // prompt_verb = "sing" The singing sword prompts in melee-attack
             vulnerable = [](const actor *caster, const actor *act) {
                 return act != caster
-                       && !(caster->deity() == GOD_FEDHAS
-                            && fedhas_protects(act->as_monster()));
+                       && !god_protects(caster, act->as_monster());
             };
             break;
 
@@ -806,8 +801,7 @@ spret cast_airstrike(int pow, const dist &beam, bool fail)
         return spret::success; // still losing a turn
     }
 
-    if (!(have_passive(passive_t::shoot_through_plants)
-          && fedhas_protects(mons))
+    if (!god_protects(mons)
         && stop_attack_prompt(mons, false, you.pos()))
     {
         return spret::abort;
@@ -1349,8 +1343,7 @@ spret cast_shatter(int pow, bool fail)
     auto vulnerable = [](const actor *act) -> bool
     {
         return !act->is_player()
-               && !(have_passive(passive_t::shoot_through_plants)
-                    && fedhas_protects(act->as_monster()))
+               && !god_protects(act->as_monster())
                && _shatterable(act);
     };
     if (stop_attack_prompt(hitfunc, "attack", vulnerable))
@@ -1549,14 +1542,8 @@ static int _irradiate_cell(coord_def where, int pow, actor *agent)
          mons->name(DESC_THE).c_str(),
          attack_strength_punctuation(dam).c_str());
 
-    if (agent->deity() == GOD_FEDHAS && fedhas_protects(mons))
-    {
-        simple_god_message(
-                    make_stringf(" protects %s plant from harm.",
-                        agent->is_player() ? "your" : "a").c_str(),
-                    GOD_FEDHAS);
+    if (god_protects(mons, false))
         return 0;
-    }
 
     if (agent->is_player())
         _player_hurt_monster(*mons, dam, BEAM_MMISSILE);
@@ -1586,8 +1573,7 @@ spret cast_irradiate(int powc, actor* who, bool fail)
     auto vulnerable = [who](const actor *act) -> bool
     {
         return !act->is_player()
-               && !(who->deity() == GOD_FEDHAS
-                    && fedhas_protects(act->as_monster()));
+               && !god_protects(who, act->as_monster());
     };
 
     if (stop_attack_prompt(hitfunc, "irradiate", vulnerable))
@@ -1640,11 +1626,8 @@ static int _ignite_tracer_cloud_value(coord_def where, actor *agent)
                         ? 0
                         : resist_adjust_damage(act, BEAM_FIRE, 40);
 
-        if (agent->deity() == GOD_FEDHAS && agent->is_player()
-            && fedhas_protects(act->as_monster()))
-        {
+        if (god_protects(agent, act->as_monster()))
             return 0;
-        }
 
         return mons_aligned(act, agent) ? -dam : dam;
     }
@@ -1773,17 +1756,8 @@ static int _ignite_poison_monsters(coord_def where, int pow, actor *agent)
     if (damage <= 0)
         return 0;
 
-    if (agent && agent->deity() == GOD_FEDHAS && fedhas_protects(mon))
-    {
-        if (!tracer)
-        {
-            simple_god_message(
-                        make_stringf(" protects %s plant from harm.",
-                            agent->is_player() ? "your" : "a").c_str(),
-                        GOD_FEDHAS);
-        }
+    if (god_protects(agent, mon, tracer))
         return 0;
-    }
 
     mon->expose_to_element(BEAM_FIRE, damage);
 
@@ -2191,15 +2165,8 @@ static int _discharge_monsters(const coord_def &where, int pow,
     // rEelec monsters don't allow arcs to continue.
     else if (victim->res_elec() > 0)
         return 0;
-    else if (agent.deity() == GOD_FEDHAS
-             && fedhas_protects(victim->as_monster()))
-    {
-        simple_god_message(
-                    make_stringf(" protects %s plant from harm.",
-                        agent.is_player() ? "your" : "a").c_str(),
-                    GOD_FEDHAS);
+    else if (god_protects(&agent, victim->as_monster(), false))
         return 0;
-    }
     else
     {
         monster* mons = victim->as_monster();
@@ -2256,8 +2223,7 @@ bool safe_discharge(coord_def where, vector<const actor *> &exclude)
             {
                 // Harmless to these monsters, so don't prompt about them.
                 if (act->res_elec() > 0
-                    || you.deity() == GOD_FEDHAS
-                       && fedhas_protects(act->as_monster()))
+                    || god_protects(act->as_monster()))
                 {
                     continue;
                 }
@@ -2355,8 +2321,7 @@ spret cast_sandblast(int pow, bolt &beam, bool fail)
 
 static bool _elec_not_immune(const actor *act)
 {
-    return act->res_elec() < 3 && !(you_worship(GOD_FEDHAS)
-                                    && fedhas_protects(act->as_monster()));
+    return act->res_elec() < 3 && !god_protects(act->as_monster());
 }
 
 coord_def get_thunderbolt_last_aim(actor *caster)
@@ -3131,7 +3096,7 @@ spret cast_hailstorm(int pow, bool fail, bool tracer)
       // but we'll verify it as a matter of good hygiene.
         const monster* mon = act->as_monster();
         return mon && !mons_is_firewood(*mon)
-            && !(you_worship(GOD_FEDHAS) && fedhas_protects(mon))
+            && !god_protects(mon)
             && !mons_is_projectile(*mon)
             && !(mons_is_avatar(mon->type) && mons_aligned(&you, mon))
             && !testbits(mon->flags, MF_DEMONIC_GUARDIAN);
@@ -3208,8 +3173,7 @@ spret cast_imb(int pow, bool fail)
     {
         return !(act->is_monster()
                  && (mons_is_conjured(act->as_monster()->type)
-                     || (have_passive(passive_t::shoot_through_plants)
-                         && fedhas_protects(act->as_monster())))) ;
+                     || god_protects(act->as_monster())));
     };
 
     if (stop_attack_prompt(*hitfunc, "blast", vulnerable))
index 846b268..2d62843 100644 (file)
@@ -1374,9 +1374,8 @@ static void _attract_actor(const actor* agent, actor* victim,
                            const coord_def pos, int pow, int strength)
 {
     ASSERT(victim); // XXX: change to actor &victim
-    const bool fedhas_prot = agent->deity() == GOD_FEDHAS
-                             && victim->is_monster()
-                             && fedhas_protects(victim->as_monster());
+    const bool fedhas_prot = victim->is_monster()
+                                && god_protects(agent, victim->as_monster());
 
     ray_def ray;
     if (!find_ray(victim->pos(), pos, ray, opc_solid))