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