1 #include "AppHdr.h"
2 
3 #include "actor.h"
4 
5 #include <sstream>
6 
7 #include "act-iter.h"
8 #include "areas.h"
9 #include "art-enum.h"
10 #include "attack.h"
11 #include "chardump.h"
12 #include "directn.h"
13 #include "env.h"
14 #include "fight.h" // apply_chunked_ac
15 #include "fprop.h"
16 #include "god-passive.h"
17 #include "item-prop.h"
18 #include "los.h"
19 #include "message.h"
20 #include "mon-behv.h"
21 #include "mon-death.h"
22 #include "religion.h"
23 #include "stepdown.h"
24 #include "stringutil.h"
25 #include "terrain.h"
26 #include "transform.h"
27 #include "traps.h"
28 
~actor()29 actor::~actor()
30 {
31     if (constricting)
32         delete constricting;
33 }
34 
will_trigger_shaft() const35 bool actor::will_trigger_shaft() const
36 {
37     return is_valid_shaft_level()
38            // let's pretend that they always make their saving roll
39            && !(is_monster()
40                 && mons_is_elven_twin(static_cast<const monster* >(this)));
41 }
42 
shaft_dest() const43 level_id actor::shaft_dest() const
44 {
45     return generic_shaft_dest(level_id::current());
46 }
47 
48 /**
49  * Check if the actor is on the ground (or in water).
50  */
ground_level() const51 bool actor::ground_level() const
52 {
53     return !airborne();
54 }
55 
56 // Give hands required to wield weapon.
hands_reqd(const item_def & item,bool base) const57 hands_reqd_type actor::hands_reqd(const item_def &item, bool base) const
58 {
59     return basic_hands_reqd(item, body_size(PSIZE_TORSO, base));
60 }
61 
62 /**
63  * Wrapper around the virtual actor::can_wield(const item_def&,bool,bool,bool,bool) const overload.
64  * @param item May be nullptr, in which case a dummy item will be passed in.
65  */
can_wield(const item_def * item,bool ignore_curse,bool ignore_brand,bool ignore_shield,bool ignore_transform) const66 bool actor::can_wield(const item_def* item, bool ignore_curse,
67                       bool ignore_brand, bool ignore_shield,
68                       bool ignore_transform) const
69 {
70     if (item == nullptr)
71     {
72         // Unarmed combat.
73         item_def fake;
74         fake.base_type = OBJ_UNASSIGNED;
75         return can_wield(fake, ignore_curse, ignore_brand, ignore_shield, ignore_transform);
76     }
77     else
78         return can_wield(*item, ignore_curse, ignore_brand, ignore_shield, ignore_transform);
79 }
80 
can_pass_through(int x,int y) const81 bool actor::can_pass_through(int x, int y) const
82 {
83     return can_pass_through_feat(env.grid[x][y]);
84 }
85 
can_pass_through(const coord_def & c) const86 bool actor::can_pass_through(const coord_def &c) const
87 {
88     return can_pass_through_feat(env.grid(c));
89 }
90 
is_habitable(const coord_def & _pos) const91 bool actor::is_habitable(const coord_def &_pos) const
92 {
93     return is_habitable_feat(env.grid(_pos));
94 }
95 
is_dragonkind() const96 bool actor::is_dragonkind() const {
97     return mons_class_is_draconic(mons_species());
98 }
99 
dragon_level() const100 int actor::dragon_level() const {
101     if (!is_dragonkind())
102         return 0;
103     return min(get_experience_level(), 18);
104 }
105 
handle_trap()106 bool actor::handle_trap()
107 {
108     trap_def* trap = trap_at(pos());
109     if (trap)
110         trap->trigger(*this);
111     return trap != nullptr;
112 }
113 
skill_rdiv(skill_type sk,int mult,int div) const114 int actor::skill_rdiv(skill_type sk, int mult, int div) const
115 {
116     return div_rand_round(skill(sk, mult * 256), div * 256);
117 }
118 
check_willpower(int power)119 int actor::check_willpower(int power)
120 {
121     const int wl = willpower();
122 
123     if (wl == WILL_INVULN)
124         return 100;
125 
126     const int adj_pow = ench_power_stepdown(power);
127 
128     const int wlchance = (100 + wl) - adj_pow;
129     int wlch2 = random2(100);
130     wlch2 += random2(101);
131 
132     dprf("Power: %d (%d pre-stepdown), WL: %d, target: %d, roll: %d",
133          adj_pow, power, wl, wlchance, wlch2);
134 
135     return wlchance - wlch2;
136 }
137 
set_position(const coord_def & c)138 void actor::set_position(const coord_def &c)
139 {
140     const coord_def oldpos = position;
141     position = c;
142     los_actor_moved(this, oldpos);
143     areas_actor_moved(this, oldpos);
144 }
145 
can_hibernate(bool holi_only,bool intrinsic_only) const146 bool actor::can_hibernate(bool holi_only, bool intrinsic_only) const
147 {
148     // Undead, nonliving, and plants don't sleep. If the monster is
149     // berserk or already asleep, it doesn't sleep either.
150     if (!can_sleep(holi_only))
151         return false;
152 
153     if (!holi_only)
154     {
155         // The monster is cold-resistant and can't be hibernated.
156         if (intrinsic_only && is_monster()
157                 ? get_mons_resist(*as_monster(), MR_RES_COLD) > 0
158                 : res_cold() > 0)
159         {
160             return false;
161         }
162 
163         // The monster has slept recently.
164         if (is_monster() && !intrinsic_only
165             && static_cast<const monster* >(this)->has_ench(ENCH_SLEEP_WARY))
166         {
167             return false;
168         }
169     }
170 
171     return true;
172 }
173 
can_sleep(bool holi_only) const174 bool actor::can_sleep(bool holi_only) const
175 {
176     const mon_holy_type holi = holiness();
177     if (holi & (MH_UNDEAD | MH_NONLIVING | MH_PLANT))
178         return false;
179 
180     if (!holi_only)
181         return !(berserk() || clarity() || asleep());
182 
183     return true;
184 }
185 
shield_block_succeeded()186 void actor::shield_block_succeeded()
187 {
188     item_def *sh = shield();
189     const unrandart_entry *unrand_entry;
190 
191     if (sh
192         && sh->base_type == OBJ_ARMOUR
193         && get_armour_slot(*sh) == EQ_SHIELD
194         && is_artefact(*sh)
195         && is_unrandom_artefact(*sh)
196         && (unrand_entry = get_unrand_entry(sh->unrand_idx))
197         && unrand_entry->melee_effects)
198     {
199         unrand_entry->melee_effects(sh, this, nullptr, false, 0);
200     }
201 }
202 
inaccuracy() const203 int actor::inaccuracy() const
204 {
205     const item_def *amu = slot_item(EQ_AMULET);
206     return amu && is_unrandom_artefact(*amu, UNRAND_AIR);
207 }
208 
res_corr(bool calc_unid,bool temp) const209 bool actor::res_corr(bool calc_unid, bool temp) const
210 {
211     return temp && (wearing(EQ_RINGS, RING_RESIST_CORROSION, calc_unid)
212                     || wearing(EQ_BODY_ARMOUR, ARM_ACID_DRAGON_ARMOUR, calc_unid)
213                     || scan_artefacts(ARTP_RCORR, calc_unid)
214                     || wearing_ego(EQ_ALL_ARMOUR, SPARM_PRESERVATION, calc_unid));
215 }
216 
cloud_immune(bool,bool items) const217 bool actor::cloud_immune(bool /*calc_unid*/, bool items) const
218 {
219     const item_def *body_armour = slot_item(EQ_BODY_ARMOUR);
220     return items && body_armour
221            && is_unrandom_artefact(*body_armour, UNRAND_RCLOUDS);
222 }
223 
holy_wrath_susceptible() const224 bool actor::holy_wrath_susceptible() const
225 {
226     return res_holy_energy() < 0;
227 }
228 
229 // This is a bit confusing. This is not the function that determines whether or
230 // not an actor is capable of teleporting, only whether they are specifically
231 // under the influence of the "notele" effect. See actor::no_tele() for a
232 // superset of this function.
has_notele_item(bool calc_unid,vector<const item_def * > * matches) const233 bool actor::has_notele_item(bool calc_unid, vector<const item_def *> *matches) const
234 {
235     return scan_artefacts(ARTP_PREVENT_TELEPORTATION, calc_unid, matches);
236 }
237 
angry(bool calc_unid,bool items) const238 bool actor::angry(bool calc_unid, bool items) const
239 {
240     return items && scan_artefacts(ARTP_ANGRY, calc_unid);
241 }
242 
clarity(bool calc_unid,bool items) const243 bool actor::clarity(bool calc_unid, bool items) const
244 {
245     return items && scan_artefacts(ARTP_CLARITY, calc_unid);
246 }
247 
faith(bool calc_unid,bool items) const248 bool actor::faith(bool calc_unid, bool items) const
249 {
250     return items && wearing(EQ_AMULET, AMU_FAITH, calc_unid);
251 }
252 
archmagi(bool calc_unid,bool items) const253 int actor::archmagi(bool calc_unid, bool items) const
254 {
255     return items && (wearing_ego(EQ_ALL_ARMOUR, SPARM_ARCHMAGI, calc_unid)
256                      || scan_artefacts(ARTP_ARCHMAGI, calc_unid));
257 }
258 
259 /**
260  * Indicates if the actor has an active evocations enhancer.
261  *
262  * @param calc_unid Whether to identify unknown items that enhance evocations.
263  * @param items Whether to count item powers.
264  * @return The number of levels of evocations enhancement this actor has.
265  */
spec_evoke(bool calc_unid,bool items) const266 int actor::spec_evoke(bool calc_unid, bool items) const
267 {
268     UNUSED(calc_unid);
269     UNUSED(items);
270     return 0;
271 }
272 
no_cast(bool calc_unid,bool items) const273 bool actor::no_cast(bool calc_unid, bool items) const
274 {
275     return items && scan_artefacts(ARTP_PREVENT_SPELLCASTING, calc_unid);
276 }
277 
reflection(bool calc_unid,bool items) const278 bool actor::reflection(bool calc_unid, bool items) const
279 {
280     return items && wearing(EQ_AMULET, AMU_REFLECTION, calc_unid);
281 }
282 
extra_harm(bool calc_unid,bool items) const283 bool actor::extra_harm(bool calc_unid, bool items) const
284 {
285     return items &&
286            (wearing_ego(EQ_CLOAK, SPARM_HARM, calc_unid)
287             || scan_artefacts(ARTP_HARM, calc_unid));
288 }
289 
rmut_from_item(bool calc_unid) const290 bool actor::rmut_from_item(bool calc_unid) const
291 {
292     return scan_artefacts(ARTP_RMUT, calc_unid);
293 }
294 
evokable_berserk(bool calc_unid) const295 bool actor::evokable_berserk(bool calc_unid) const
296 {
297     return scan_artefacts(ARTP_BERSERK, calc_unid);
298 }
299 
evokable_invis(bool calc_unid) const300 bool actor::evokable_invis(bool calc_unid) const
301 {
302     return wearing_ego(EQ_CLOAK, SPARM_INVISIBILITY, calc_unid)
303            || scan_artefacts(ARTP_INVISIBLE, calc_unid);
304 }
305 
306 // Return an int so we know whether an item is the sole source.
equip_flight(bool calc_unid) const307 int actor::equip_flight(bool calc_unid) const
308 {
309     if (is_player() && get_form()->forbids_flight())
310         return 0;
311 
312     // For the player, this is cached on ATTR_PERM_FLIGHT
313     return wearing(EQ_RINGS, RING_FLIGHT, calc_unid)
314            + wearing_ego(EQ_ALL_ARMOUR, SPARM_FLYING, calc_unid)
315            + scan_artefacts(ARTP_FLY, calc_unid);
316 }
317 
spirit_shield(bool calc_unid,bool items) const318 int actor::spirit_shield(bool calc_unid, bool items) const
319 {
320     int ss = 0;
321 
322     if (items)
323     {
324         ss += wearing_ego(EQ_ALL_ARMOUR, SPARM_SPIRIT_SHIELD, calc_unid);
325         ss += wearing(EQ_AMULET, AMU_GUARDIAN_SPIRIT, calc_unid);
326     }
327 
328     if (is_player())
329         ss += you.get_mutation_level(MUT_MANA_SHIELD);
330 
331     return ss;
332 }
333 
rampaging(bool calc_unid,bool items) const334 bool actor::rampaging(bool calc_unid, bool items) const
335 {
336     return items &&
337            (wearing_ego(EQ_ALL_ARMOUR, SPARM_RAMPAGING, calc_unid)
338             || scan_artefacts(ARTP_RAMPAGING, calc_unid)
339             || is_player() && player_equip_unrand(UNRAND_SEVEN_LEAGUE_BOOTS));
340 }
341 
apply_ac(int damage,int max_damage,ac_type ac_rule,bool for_real) const342 int actor::apply_ac(int damage, int max_damage, ac_type ac_rule, bool for_real) const
343 {
344     int ac = max(armour_class(), 0);
345     int gdr = gdr_perc();
346     int saved = 0;
347     switch (ac_rule)
348     {
349     case ac_type::none:
350         return damage; // no GDR, too
351     case ac_type::proportional:
352         saved = damage - apply_chunked_AC(damage, ac);
353         saved = max(saved, div_rand_round(max_damage * gdr, 100));
354         return max(damage - saved, 0);
355 
356     case ac_type::normal:
357         saved = random2(1 + ac);
358         break;
359     case ac_type::half:
360         saved = random2(1 + ac) / 2;
361         ac /= 2;
362         gdr /= 2;
363         break;
364     case ac_type::triple:
365         saved = random2(1 + ac);
366         saved += random2(1 + ac);
367         saved += random2(1 + ac);
368         ac *= 3;
369         // apply GDR only twice rather than thrice, that's probably still waaay
370         // too good. 50% gives 75% rather than 100%, too.
371         gdr = 100 - gdr * gdr / 100;
372         break;
373     default:
374         die("invalid AC rule");
375     }
376 
377     saved = max(saved, min(gdr * max_damage / 100, div_rand_round(ac, 2)));
378     if (for_real && (damage > 0) && (saved >= damage) && is_player())
379     {
380         const item_def *body_armour = slot_item(EQ_BODY_ARMOUR);
381         if (body_armour)
382             count_action(CACT_ARMOUR, body_armour->sub_type);
383         else
384             count_action(CACT_ARMOUR, -1); // unarmoured subtype
385     }
386 
387     return max(damage - saved, 0);
388 }
389 
actor_slime_wall_immune(const actor * act)390 bool actor_slime_wall_immune(const actor *act)
391 {
392     return act->is_player() && have_passive(passive_t::slime_wall_immune)
393         || act->res_acid() == 3
394         || act->is_monster() && mons_is_slime(*act->as_monster());
395 }
396 
clear_constricted()397 void actor::clear_constricted()
398 {
399     constricted_by = 0;
400     escape_attempts = 0;
401 }
402 
403 // End my constriction of i->first, but don't yet update my constricting map,
404 // so as not to invalidate i.
end_constriction(mid_t whom,bool intentional,bool quiet)405 void actor::end_constriction(mid_t whom, bool intentional, bool quiet)
406 {
407     actor *const constrictee = actor_by_mid(whom);
408 
409     if (!constrictee)
410         return;
411 
412     constrictee->clear_constricted();
413 
414     monster * const mons = monster_by_mid(whom);
415     bool roots = constrictee->is_player() && you.duration[DUR_GRASPING_ROOTS]
416         || mons && mons->has_ench(ENCH_GRASPING_ROOTS);
417     bool vile_clutch = mons && mons->has_ench(ENCH_VILE_CLUTCH);
418 
419     if (!quiet && alive() && constrictee->alive()
420         && (you.see_cell(pos()) || you.see_cell(constrictee->pos())))
421     {
422         string attacker_desc;
423         const string verb = intentional ? "release" : "lose";
424         bool force_plural = false;
425 
426         if (vile_clutch)
427         {
428             attacker_desc = "The zombie hands";
429             force_plural = true;
430         }
431         else if (roots)
432         {
433             attacker_desc = "The roots";
434             force_plural = true;
435         }
436         else
437             attacker_desc = name(DESC_THE);
438 
439         mprf("%s %s %s grip on %s.",
440              attacker_desc.c_str(),
441              force_plural ? verb.c_str()
442                           : conj_verb(verb).c_str(),
443              force_plural ? "their" : pronoun(PRONOUN_POSSESSIVE).c_str(),
444              constrictee->name(DESC_THE).c_str());
445     }
446 
447     if (vile_clutch)
448         mons->del_ench(ENCH_VILE_CLUTCH);
449     else if (roots)
450     {
451         if (mons)
452             mons->del_ench(ENCH_GRASPING_ROOTS);
453         else
454             you.duration[DUR_GRASPING_ROOTS] = 0;
455     }
456 
457     if (constrictee->is_player())
458         you.redraw_evasion = true;
459 }
460 
stop_constricting(mid_t whom,bool intentional,bool quiet)461 void actor::stop_constricting(mid_t whom, bool intentional, bool quiet)
462 {
463     if (!constricting)
464         return;
465 
466     auto i = constricting->find(whom);
467 
468     if (i != constricting->end())
469     {
470         end_constriction(whom, intentional, quiet);
471         constricting->erase(i);
472 
473         if (constricting->empty())
474         {
475             delete constricting;
476             constricting = nullptr;
477         }
478     }
479 }
480 
481 /**
482  * Stop constricting all defenders, regardless of type of constriction.
483  *
484  * @param intentional True if this was intentional, which affects the language
485  *                    in any message.
486  * @param quiet       If True, don't display a message.
487  */
stop_constricting_all(bool intentional,bool quiet)488 void actor::stop_constricting_all(bool intentional, bool quiet)
489 {
490     if (!constricting)
491         return;
492 
493     for (const auto &entry : *constricting)
494         end_constriction(entry.first, intentional, quiet);
495 
496     delete constricting;
497     constricting = nullptr;
498 }
499 
_invalid_constricting_map_entry(const actor * constrictee)500 static bool _invalid_constricting_map_entry(const actor *constrictee)
501 {
502     return !constrictee
503         || !constrictee->alive()
504         || !constrictee->is_constricted();
505 }
506 
507 /**
508  * Stop directly constricting all defenders.
509  *
510  * @param intentional True if this was intentional, which affects the language
511  *                    in any message.
512  * @param quiet       If True, don't display a message.
513  */
stop_directly_constricting_all(bool intentional,bool quiet)514 void actor::stop_directly_constricting_all(bool intentional, bool quiet)
515 {
516     if (!constricting)
517         return;
518 
519     vector<mid_t> need_cleared;
520     for (const auto &entry : *constricting)
521     {
522         const actor * const constrictee = actor_by_mid(entry.first);
523         if (_invalid_constricting_map_entry(constrictee)
524             || constrictee->is_directly_constricted())
525         {
526             need_cleared.push_back(entry.first);
527         }
528     }
529 
530     for (auto whom : need_cleared)
531     {
532         end_constriction(whom, intentional, quiet);
533         constricting->erase(whom);
534     }
535 
536     if (constricting->empty())
537     {
538         delete constricting;
539         constricting = nullptr;
540     }
541 }
542 
stop_being_constricted(bool quiet)543 void actor::stop_being_constricted(bool quiet)
544 {
545     // Make sure we are actually being constricted.
546     actor* const constrictor = actor_by_mid(constricted_by);
547 
548     if (constrictor)
549         constrictor->stop_constricting(mid, false, quiet);
550 
551     // In case the actor no longer exists.
552     clear_constricted();
553 }
554 
555 /**
556  * Does the actor have an constrictor that's now invalid? Checks validity based
557  * on the type of constriction being done to the actor.
558  *
559  * @param move True if we are checking after the actor has moved.
560  * @returns    True if the constrictor is defined yet invalid, false
561  *             otherwise.
562  */
has_invalid_constrictor(bool move) const563 bool actor::has_invalid_constrictor(bool move) const
564 {
565     if (!is_constricted())
566         return false;
567 
568     const actor* const attacker = actor_by_mid(constricted_by, true);
569     if (!attacker || !attacker->alive())
570         return true;
571 
572     // When the player is at the origin, they don't have the normal
573     // considerations, since they're just here to avoid messages or LOS
574     // effects. Cheibriados' time step abilities are an exception to this as
575     // they have the player "leave the normal flow of time" and so should break
576     // constriction.
577     const bool ignoring_player = attacker->is_player()
578         && attacker->pos().origin()
579         && !you.duration[DUR_TIME_STEP];
580 
581     // Direct constriction (e.g. by nagas and octopode players or AT_CONSTRICT)
582     // must happen between adjacent squares.
583     if (is_directly_constricted())
584         return !ignoring_player && !adjacent(attacker->pos(), pos());
585 
586     // Indirect constriction requires the defender not to move.
587     return move
588         // Indirect constriction requires reachable ground.
589         || !feat_has_solid_floor(env.grid(pos()))
590         // Constriction doesn't work out of LOS.
591         || !ignoring_player && !attacker->see_cell(pos());
592 }
593 
594 /**
595  * Clear any constrictions that are no longer valid.
596  *
597  * @param movement True if we are clearing invalid constrictions after
598  *                 the actor has moved, false otherwise.
599  */
clear_invalid_constrictions(bool move)600 void actor::clear_invalid_constrictions(bool move)
601 {
602     if (has_invalid_constrictor(move))
603         stop_being_constricted();
604 
605     if (!constricting)
606         return;
607 
608     vector<mid_t> need_cleared;
609     for (const auto &entry : *constricting)
610     {
611         const actor * const constrictee = actor_by_mid(entry.first, true);
612         if (_invalid_constricting_map_entry(constrictee)
613             || constrictee->has_invalid_constrictor())
614         {
615             need_cleared.push_back(entry.first);
616         }
617     }
618 
619     for (auto whom : need_cleared)
620         stop_constricting(whom, false, false);
621 }
622 
start_constricting(actor & whom,int dur)623 void actor::start_constricting(actor &whom, int dur)
624 {
625     if (!constricting)
626         constricting = new constricting_t();
627 
628     ASSERT(constricting->find(whom.mid) == constricting->end());
629 
630     (*constricting)[whom.mid] = dur;
631     whom.constricted_by = mid;
632 
633     if (whom.is_player())
634         you.redraw_evasion = true;
635 }
636 
num_constricting() const637 int actor::num_constricting() const
638 {
639     return constricting ? constricting->size() : 0;
640 }
641 
is_constricting() const642 bool actor::is_constricting() const
643 {
644     return constricting && !constricting->empty();
645 }
646 
is_constricted() const647 bool actor::is_constricted() const
648 {
649     return constricted_by;
650 }
651 
is_directly_constricted() const652 bool actor::is_directly_constricted() const
653 {
654     return is_constricted()
655         && (is_player() && !you.duration[DUR_GRASPING_ROOTS]
656             || is_monster()
657                && !as_monster()->has_ench(ENCH_VILE_CLUTCH)
658                && !as_monster()->has_ench(ENCH_GRASPING_ROOTS));
659 }
660 
accum_has_constricted()661 void actor::accum_has_constricted()
662 {
663     if (!constricting)
664         return;
665 
666     for (auto &entry : *constricting)
667         entry.second += you.time_taken;
668 }
669 
can_constrict(const actor * defender,bool direct,bool engulf) const670 bool actor::can_constrict(const actor* defender, bool direct, bool engulf) const
671 {
672     ASSERT(defender); // XXX: change to actor &defender
673 
674     if (direct)
675     {
676         return (!is_constricting() || has_usable_tentacle() || engulf)
677                && (!defender->is_constricted() || engulf)
678                && can_see(*defender)
679                && !confused()
680                && body_size(PSIZE_BODY) >= defender->body_size(PSIZE_BODY)
681                && defender->res_constrict() < 3
682                && adjacent(pos(), defender->pos());
683     }
684 
685     return can_see(*defender)
686         && !defender->is_constricted()
687         && defender->res_constrict() < 3
688         // All current indrect forms of constriction require reachable ground.
689         && feat_has_solid_floor(env.grid(defender->pos()));
690 }
691 
692 #ifdef DEBUG_DIAGNOSTICS
693 # define DIAG_ONLY(x) x
694 #else
695 # define DIAG_ONLY(x) (void)0
696 #endif
697 
698 /*
699  * Damage the defender with constriction damage. Longer duration gives more
700  * damage, but with a 50 aut step-down. Direct constriction uses strength-based
701  * base damage that is modified by XL, whereas indirect, spell-based
702  * constriction uses spellpower.
703  *
704  * @param defender The defender being constricted.
705  * @param duration How long the defender has been constricted in AUT.
706  */
constriction_damage_defender(actor & defender,int duration)707 void actor::constriction_damage_defender(actor &defender, int duration)
708 {
709     const bool direct = defender.is_directly_constricted();
710     const bool vile_clutch = !direct && defender.as_monster()
711         && defender.as_monster()->has_ench(ENCH_VILE_CLUTCH);
712     int damage = constriction_damage(direct);
713 
714     DIAG_ONLY(const int basedam = damage);
715     damage += div_rand_round(damage * stepdown((float)duration, 50.0),
716                              BASELINE_DELAY * 5);
717     if (is_player() && direct)
718         damage = div_rand_round(damage * (27 + 2 * you.experience_level), 81);
719 
720     DIAG_ONLY(const int durdam = damage);
721     damage -= random2(1 + (defender.armour_class() / 2));
722     DIAG_ONLY(const int acdam = damage);
723     damage = timescale_damage(this, damage);
724     DIAG_ONLY(const int timescale_dam = damage);
725 
726     string exclamations;
727     if (damage <= 0 && is_player()
728         && you.can_see(defender))
729     {
730         exclamations = ", but do no damage.";
731     }
732     else
733         exclamations = attack_strength_punctuation(damage);
734 
735     if (is_player() || you.can_see(*this))
736     {
737         string attacker_desc;
738         bool force_plural = false;
739         if (vile_clutch)
740         {
741             attacker_desc = "The zombie hands";
742             force_plural = true;
743         }
744         else if (!direct)
745         {
746             attacker_desc = "The grasping roots";
747             force_plural = true;
748         }
749         else if (is_player())
750             attacker_desc = "You";
751         else
752             attacker_desc = name(DESC_THE);
753 
754         mprf("%s %s %s%s%s", attacker_desc.c_str(),
755              force_plural ? "constrict"
756                           : conj_verb("constrict").c_str(),
757              defender.name(DESC_THE).c_str(),
758 #ifdef DEBUG_DIAGNOSTICS
759              make_stringf(" for %d", damage).c_str(),
760 #else
761              "",
762 #endif
763              exclamations.c_str());
764     }
765     else if (you.can_see(defender) || defender.is_player())
766     {
767         mprf("%s %s constricted%s%s",
768              defender.name(DESC_THE).c_str(),
769              defender.conj_verb("are").c_str(),
770 #ifdef DEBUG_DIAGNOSTICS
771              make_stringf(" for %d", damage).c_str(),
772 #else
773              "",
774 #endif
775              exclamations.c_str());
776     }
777 
778     damage = defender.hurt(this, damage, BEAM_MISSILE, KILLED_BY_CONSTRICTION, "",
779                            "", false);
780     DIAG_ONLY(const int infdam = damage);
781 
782     dprf("constrict at: %s df: %s base %d dur %d ac %d tsc %d inf %d",
783          name(DESC_PLAIN, true).c_str(),
784          defender.name(DESC_PLAIN, true).c_str(),
785          basedam, durdam, acdam, timescale_dam, infdam);
786 
787     if (defender.is_monster() && defender.as_monster()->hit_points < 1)
788         monster_die(*defender.as_monster(), this);
789 }
790 
791 // Deal damage over time
handle_constriction()792 void actor::handle_constriction()
793 {
794     if (is_sanctuary(pos()))
795         stop_constricting_all(true);
796 
797     // Constriction should have stopped the moment the actors became
798     // non-adjacent; but disabling constriction by hand in every single place
799     // is too error-prone.
800     clear_invalid_constrictions();
801 
802     if (!constricting)
803         return;
804 
805     // need to make a copy, since constriction_damage_defender can
806     // unpredictably invalidate constricting
807     constricting_t tmp_constricting = *constricting;
808     for (auto &i : tmp_constricting)
809     {
810         actor* const defender = actor_by_mid(i.first);
811         const int duration = i.second;
812         if (defender && defender->alive())
813             constriction_damage_defender(*defender, duration);
814     }
815 
816     clear_invalid_constrictions();
817 }
818 
describe_props() const819 string actor::describe_props() const
820 {
821     ostringstream oss;
822 
823     if (props.size() == 0)
824         return "";
825 
826     for (auto i = props.begin(); i != props.end(); ++i)
827     {
828         if (i != props.begin())
829             oss <<  ", ";
830         oss << string(i->first) << ": ";
831 
832         CrawlStoreValue val = i->second;
833 
834         switch (val.get_type())
835         {
836             case SV_BOOL:
837                 oss << val.get_bool();
838                 break;
839             case SV_BYTE:
840                 oss << val.get_byte();
841                 break;
842             case SV_SHORT:
843                 oss << val.get_short();
844                 break;
845             case SV_INT:
846                 oss << val.get_int();
847                 break;
848             case SV_FLOAT:
849                 oss << val.get_float();
850                 break;
851             case SV_STR:
852                 oss << val.get_string();
853                 break;
854             case SV_COORD:
855             {
856                 coord_def coord = val.get_coord();
857                 oss << "(" << coord.x << ", " << coord.y << ")";
858                 break;
859             }
860             case SV_MONST:
861             {
862                 monster mon = val.get_monster();
863                 oss << mon.name(DESC_PLAIN) << "(" << mon.mid << ")";
864                 break;
865             }
866             case SV_INT64:
867                 oss << val.get_int64();
868                 break;
869 
870             default:
871                 oss << "???";
872                 break;
873         }
874     }
875     return oss.str();
876 }
877 
878 /**
879  * Is the actor currently being slowed by a torpor snail?
880  */
torpor_slowed() const881 bool actor::torpor_slowed() const
882 {
883     if (!props.exists(TORPOR_SLOWED_KEY) || is_sanctuary(pos())
884         || is_stationary()
885         || stasis())
886     {
887         return false;
888     }
889 
890     for (monster_near_iterator ri(pos(), LOS_SOLID_SEE); ri; ++ri)
891     {
892         const monster *mons = *ri;
893         if (mons && mons->type == MONS_TORPOR_SNAIL
894             && !is_sanctuary(mons->pos())
895             && !mons_aligned(mons, this))
896         {
897             return true;
898         }
899     }
900 
901     return false;
902 }
903 
resist_margin_phrase(int margin) const904 string actor::resist_margin_phrase(int margin) const
905 {
906     if (willpower() == WILL_INVULN)
907         return " " + conj_verb("are") + " unaffected.";
908 
909     static const string resist_messages[][2] =
910     {
911       { " barely %s.",                  "resist" },
912       { " %s to resist.",               "struggle" },
913       { " %s with significant effort.", "resist" },
914       { " %s with some effort.",        "resist" },
915       { " easily %s.",                  "resist" },
916       { " %s with almost no effort.",   "resist" },
917     };
918 
919     const int index = max(0, min((int)ARRAYSZ(resist_messages) - 1,
920                                  ((margin + 45) / 15)));
921 
922     return make_stringf(resist_messages[index][0].c_str(),
923                         conj_verb(resist_messages[index][1]).c_str());
924 }
925 
collide(coord_def newpos,const actor * agent,int pow)926 void actor::collide(coord_def newpos, const actor *agent, int pow)
927 {
928     actor *other = actor_at(newpos);
929     // TODO: should the first of these check agent?
930     const bool god_prot = god_protects(agent, as_monster());
931     const bool god_prot_other = other && god_protects(agent, other->as_monster());
932 
933     ASSERT(this != other);
934     ASSERT(alive());
935 
936     if (is_insubstantial()
937         || mons_is_projectile(type)
938         || other && mons_is_projectile(other->type))
939     {
940         return;
941     }
942 
943     if (is_monster() && !god_prot)
944         behaviour_event(as_monster(), ME_WHACK, agent);
945 
946     dice_def damage(2, 1 + pow / 10);
947 
948     if (other && other->alive())
949     {
950         if (you.can_see(*this) || you.can_see(*other))
951         {
952             mprf("%s %s with %s!",
953                  name(DESC_THE).c_str(),
954                  conj_verb("collide").c_str(),
955                  other->name(DESC_THE).c_str());
956             if (god_prot || god_prot_other)
957             {
958                 // do messaging at the right time.
959                 // TODO: a bit ugly
960                 god_protects(agent, as_monster(), false);
961                 god_protects(agent, other->as_monster(), false);
962             }
963         }
964 
965         if (other->is_monster() && !god_prot_other)
966             behaviour_event(other->as_monster(), ME_WHACK, agent);
967 
968         const string thisname = name(DESC_A, true);
969         const string othername = other->name(DESC_A, true);
970         if (other->alive() && !god_prot_other)
971         {
972             other->hurt(agent, other->apply_ac(damage.roll()),
973                         BEAM_MISSILE, KILLED_BY_COLLISION,
974                         othername, thisname);
975         }
976         if (alive() && !god_prot)
977         {
978             hurt(agent, apply_ac(damage.roll()), BEAM_MISSILE,
979                  KILLED_BY_COLLISION, thisname, othername);
980         }
981         return;
982     }
983 
984     if (you.can_see(*this))
985     {
986         if (!can_pass_through_feat(env.grid(newpos)))
987         {
988             mprf("%s %s into %s!",
989                  name(DESC_THE).c_str(), conj_verb("slam").c_str(),
990                  env.map_knowledge(newpos).known()
991                  ? feature_description_at(newpos, false, DESC_THE)
992                        .c_str()
993                  : "something");
994         }
995         else
996         {
997             mprf("%s violently %s moving!",
998                  name(DESC_THE).c_str(), conj_verb("stop").c_str());
999         }
1000 
1001         if (god_prot)
1002             god_protects(agent, as_monster(), false); // messaging
1003     }
1004 
1005     if (!god_prot)
1006     {
1007         hurt(agent, apply_ac(damage.roll()), BEAM_MISSILE,
1008              KILLED_BY_COLLISION, "", feature_description_at(newpos));
1009     }
1010 }
1011 
1012 /// Is this creature despised by the so-called 'good gods'?
evil() const1013 bool actor::evil() const
1014 {
1015     return bool(holiness() & (MH_UNDEAD | MH_DEMONIC | MH_EVIL));
1016 }
1017 
1018 /**
1019  * Ensures that `act` is valid if possible. If this isn't possible,
1020  * return nullptr. This will convert YOU_FAULTLESS into `you`.
1021  *
1022  * @param act the actor to validate.
1023  *
1024  * @return an actor that is either the player or passes `!invalid_monster`, or
1025  *         otherwise `nullptr`.
1026  */
ensure_valid_actor(const actor * act)1027 /* static */ const actor *actor::ensure_valid_actor(const actor *act)
1028 {
1029     if (!act)
1030         return nullptr;
1031     if (act->is_player())
1032         return act;
1033     const monster *mon = act->as_monster();
1034     if (mon->mid == MID_YOU_FAULTLESS)
1035         return &you;
1036     if (invalid_monster(mon))
1037         return nullptr;
1038     return mon;
1039 }
1040 
1041 /// @copydoc actor::ensure_valid_actor(const actor *act)
ensure_valid_actor(actor * act)1042 /* static */ actor *actor::ensure_valid_actor(actor *act)
1043 {
1044     // Defer to the other function. Since it returns only act, nullptr, or you,
1045     // none of which points to a const object, the const_cast here is safe.
1046     return const_cast<actor *>(ensure_valid_actor(static_cast<const actor *>(act)));
1047 }
1048