1 /**
2 * @file
3 * @brief Contains monster death functionality, including unique code.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "mon-death.h"
9
10 #include "act-iter.h"
11 #include "areas.h"
12 #include "arena.h"
13 #include "artefact.h"
14 #include "art-enum.h"
15 #include "attitude-change.h"
16 #include "bloodspatter.h"
17 #include "cloud.h"
18 #include "cluautil.h"
19 #include "colour.h"
20 #include "coordit.h"
21 #include "corpse.h"
22 #include "dactions.h"
23 #include "database.h"
24 #include "delay.h"
25 #include "describe.h"
26 #include "dgn-overview.h"
27 #include "english.h"
28 #include "env.h"
29 #include "fineff.h"
30 #include "god-abil.h"
31 #include "god-blessing.h"
32 #include "god-companions.h"
33 #include "god-conduct.h"
34 #include "god-passive.h" // passive_t::bless_followers, share_exp, convert_orcs
35 #include "hints.h"
36 #include "hiscores.h"
37 #include "item-name.h"
38 #include "item-prop.h"
39 #include "item-status-flag-type.h"
40 #include "items.h"
41 #include "kills.h"
42 #include "libutil.h"
43 #include "mapdef.h"
44 #include "mapmark.h"
45 #include "message.h"
46 #include "mon-abil.h"
47 #include "mon-behv.h"
48 #include "mon-explode.h"
49 #include "mon-gear.h"
50 #include "mon-place.h"
51 #include "mon-poly.h"
52 #include "mon-speak.h"
53 #include "mon-tentacle.h"
54 #include "mutation.h"
55 #include "nearby-danger.h"
56 #include "notes.h"
57 #include "religion.h"
58 #include "shout.h"
59 #include "spl-damage.h"
60 #include "spl-summoning.h"
61 #include "sprint.h" // SPRINT_MULTIPLIER
62 #include "state.h"
63 #include "stepdown.h"
64 #include "stringutil.h"
65 #include "tag-version.h"
66 #include "target.h"
67 #include "terrain.h"
68 #include "tilepick.h"
69 #include "timed-effects.h"
70 #include "traps.h"
71 #include "unwind.h"
72 #include "viewchar.h"
73 #include "view.h"
74
75 /**
76 * Initialise a corpse item.
77 *
78 * @param mons The monster to use as a template.
79 * @param corpse[out] The item that's filled in; no properties it already has
80 * are checked.
81 * @returns whether a corpse could be created.
82 */
_fill_out_corpse(const monster & mons,item_def & corpse)83 static bool _fill_out_corpse(const monster& mons, item_def& corpse)
84 {
85 corpse.clear();
86
87 monster_type mtype = mons.type;
88 monster_type corpse_class = mons_species(mtype);
89
90 ASSERT(!invalid_monster_type(mtype));
91 ASSERT(!invalid_monster_type(corpse_class));
92
93 if (mons_genus(mtype) == MONS_DRACONIAN
94 || mons_genus(mtype) == MONS_DEMONSPAWN)
95 {
96 if (mons.type == MONS_TIAMAT)
97 corpse_class = MONS_DRACONIAN;
98 else
99 corpse_class = draco_or_demonspawn_subspecies(mons);
100 }
101
102 if (mons.props.exists(ORIGINAL_TYPE_KEY))
103 {
104 // Shapeshifters too.
105 mtype = (monster_type) mons.props[ORIGINAL_TYPE_KEY].get_int();
106 corpse_class = mons_species(mtype);
107 }
108
109 if (!mons_class_can_leave_corpse(corpse_class))
110 return false;
111
112 corpse.base_type = OBJ_CORPSES;
113 corpse.mon_type = corpse_class;
114 corpse.sub_type = CORPSE_BODY;
115 corpse.freshness = FRESHEST_CORPSE; // rot time
116 corpse.quantity = 1;
117 corpse.rnd = 1 + random2(255);
118 corpse.orig_monnum = mtype;
119
120 corpse.props[MONSTER_HIT_DICE] = short(mons.get_experience_level());
121 if (mons.mons_species() == MONS_HYDRA)
122 corpse.props[CORPSE_HEADS] = short(mons.heads());
123 if (mons.props.exists("old_heads"))
124 corpse.props[CORPSE_HEADS] = short(mons.props["old_heads"].get_int());
125 COMPILE_CHECK(sizeof(mid_t) == sizeof(int));
126 corpse.props[MONSTER_MID] = int(mons.mid);
127
128 monster_info minfo(corpse_class);
129 int col = int(minfo.colour());
130 if (col == COLOUR_UNDEF)
131 {
132 // XXX hack to avoid crashing in wiz mode.
133 if (mons_is_ghost_demon(mons.type) && !mons.ghost)
134 col = LIGHTRED;
135 else
136 {
137 minfo = monster_info(&mons);
138 col = int(minfo.colour());
139 }
140 }
141 if (col == COLOUR_UNDEF)
142 col = int(random_colour());
143 corpse.props[FORCED_ITEM_COLOUR_KEY] = col;
144
145 if (!mons.mname.empty() && !(mons.flags & MF_NAME_NOCORPSE))
146 {
147 corpse.props[CORPSE_NAME_KEY] = mons.mname;
148 corpse.props[CORPSE_NAME_TYPE_KEY].get_int64() = mons.flags.flags;
149 }
150 else if (mons_is_unique(mtype))
151 {
152 corpse.props[CORPSE_NAME_KEY] = mons_type_name(mtype, DESC_PLAIN);
153 corpse.props[CORPSE_NAME_TYPE_KEY].get_int64() = 0;
154 }
155
156 // 0 mid indicates this is a dummy monster, such as for kiku corpse drop
157 if (mons_genus(mons.type) == MONS_ORC && mons.mid != 0)
158 {
159 auto &saved_mon = corpse.props[ORC_CORPSE_KEY].get_monster();
160 saved_mon = mons;
161
162 // Ensure that saved_mon is alive, lest it be cleared on marshall.
163 if (saved_mon.max_hit_points <= 0)
164 saved_mon.max_hit_points = 1;
165 saved_mon.hit_points = saved_mon.max_hit_points;
166 }
167
168 return true;
169 }
170
_explode_corpse(item_def & corpse,const coord_def & where)171 static bool _explode_corpse(item_def& corpse, const coord_def& where)
172 {
173 // Don't want results to show up behind the player.
174 los_def ld(where, opc_no_actor);
175
176 if (mons_class_leaves_hide(corpse.mon_type)
177 && mons_genus(corpse.mon_type) == MONS_DRAGON)
178 {
179 // Uh... dragon hide is tough stuff and it keeps the monster in
180 // one piece? More importantly, it prevents a flavour feature
181 // from becoming a trap for the unwary.
182
183 return false;
184 }
185
186 ld.update();
187
188 const int max_chunks = max_corpse_chunks(corpse.mon_type);
189 const int nchunks = stepdown_value(1 + random2(max_chunks), 4, 4, 12, 12);
190
191 if (corpse.base_type != OBJ_GOLD)
192 {
193 blood_spray(where, corpse.mon_type, nchunks * 3); // spray some blood
194 return true;
195 }
196
197 const int total_gold = corpse.quantity;
198
199 // spray gold everywhere!
200 for (int ntries = 0, chunks_made = 0;
201 chunks_made < nchunks && ntries < 10000; ++ntries)
202 {
203 coord_def cp = where;
204 cp.x += random_range(-LOS_DEFAULT_RANGE, LOS_DEFAULT_RANGE);
205 cp.y += random_range(-LOS_DEFAULT_RANGE, LOS_DEFAULT_RANGE);
206
207 dprf("Trying to scatter gold to %d, %d...", cp.x, cp.y);
208
209 if (!in_bounds(cp))
210 continue;
211
212 if (!ld.see_cell(cp))
213 continue;
214
215 dprf("Cell is visible...");
216
217 if (cell_is_solid(cp) || actor_at(cp))
218 continue;
219
220 ++chunks_made;
221
222 dprf("Success");
223
224 if (corpse.base_type == OBJ_GOLD)
225 corpse.quantity = div_rand_round(total_gold, nchunks);
226 if (corpse.quantity)
227 copy_item_to_grid(corpse, cp);
228 }
229
230 return true;
231 }
232
_calc_monster_experience(monster * victim,killer_type killer,int killer_index)233 static int _calc_monster_experience(monster* victim, killer_type killer,
234 int killer_index)
235 {
236 const int experience = exper_value(*victim);
237
238 if (!experience || !MON_KILL(killer) || invalid_monster_index(killer_index))
239 return 0;
240
241 monster* mon = &env.mons[killer_index];
242 if (!mon->alive() || !mons_gives_xp(*victim, *mon))
243 return 0;
244
245 return experience;
246 }
247
_give_monster_experience(int experience,int killer_index)248 static void _give_monster_experience(int experience, int killer_index)
249 {
250 if (experience <= 0 || invalid_monster_index(killer_index))
251 return;
252
253 monster* mon = &env.mons[killer_index];
254 if (!mon->alive())
255 return;
256
257 if (mon->gain_exp(experience))
258 {
259 if (!have_passive(passive_t::bless_followers) || !one_chance_in(3))
260 return;
261
262 // Randomly bless the follower who gained experience.
263 if (random2(you.piety) >= piety_breakpoint(2))
264 bless_follower(mon);
265 }
266 }
267
_beogh_spread_experience(int exp)268 static void _beogh_spread_experience(int exp)
269 {
270 int total_hd = 0;
271
272 for (monster_near_iterator mi(&you); mi; ++mi)
273 {
274 if (is_orcish_follower(**mi))
275 total_hd += mi->get_experience_level();
276 }
277
278 if (total_hd <= 0)
279 return;
280
281 for (monster_near_iterator mi(&you); mi; ++mi)
282 if (is_orcish_follower(**mi))
283 {
284 _give_monster_experience(exp * mi->get_experience_level() / total_hd,
285 mi->mindex());
286 }
287 }
288
_calc_player_experience(const monster * mons)289 static int _calc_player_experience(const monster* mons)
290 {
291 int experience = exper_value(*mons);
292 if (!experience)
293 return 0;
294
295 const bool already_got_half_xp = testbits(mons->flags, MF_PACIFIED);
296 const int half_xp = (experience + 1) / 2;
297
298 if (!mons->damage_total)
299 {
300 mprf(MSGCH_WARN, "Error, exp for monster with no damage: %s",
301 mons->name(DESC_PLAIN, true).c_str());
302 return 0;
303 }
304
305 experience = (experience * mons->damage_friendly / mons->damage_total
306 + 1) / 2;
307 ASSERT(mons->damage_friendly <= 2 * mons->damage_total);
308
309 // Note: This doesn't happen currently since monsters with
310 // MF_PACIFIED have always gone through pacification,
311 // hence also have MF_WAS_NEUTRAL. [rob]
312 if (already_got_half_xp)
313 {
314 experience -= half_xp;
315 if (experience < 0)
316 experience = 0;
317 }
318
319 return experience;
320 }
321
_give_player_experience(int experience,killer_type killer,bool pet_kill,bool was_visible,xp_tracking_type xp_tracking)322 static void _give_player_experience(int experience, killer_type killer,
323 bool pet_kill, bool was_visible,
324 xp_tracking_type xp_tracking)
325 {
326 if (experience <= 0 || crawl_state.game_is_arena())
327 return;
328
329 const unsigned int exp_gain = gain_exp(experience);
330
331 kill_category kc =
332 (killer == KILL_YOU || killer == KILL_YOU_MISSILE) ? KC_YOU :
333 (pet_kill) ? KC_FRIENDLY :
334 KC_OTHER;
335 PlaceInfo& curr_PlaceInfo = you.get_place_info();
336 PlaceInfo delta;
337
338 delta.mon_kill_num[kc]++;
339 delta.mon_kill_exp += exp_gain;
340
341 you.global_info += delta;
342 you.global_info.assert_validity();
343
344 curr_PlaceInfo += delta;
345 curr_PlaceInfo.assert_validity();
346
347 LevelXPInfo& curr_xp_info = you.get_level_xp_info();
348 LevelXPInfo xp_delta;
349
350 if (xp_tracking == XP_NON_VAULT)
351 {
352 xp_delta.non_vault_xp += exp_gain;
353 xp_delta.non_vault_count++;
354 }
355 else if (xp_tracking == XP_VAULT)
356 {
357 xp_delta.vault_xp += exp_gain;
358 xp_delta.vault_count++;
359 }
360
361 you.global_xp_info += xp_delta;
362 you.global_xp_info.assert_validity();
363
364 curr_xp_info += xp_delta;
365 curr_xp_info.assert_validity();
366
367 // Give a message for monsters dying out of sight.
368 if (exp_gain > 0 && !was_visible)
369 mpr("You feel a bit more experienced.");
370
371 if (kc == KC_YOU && have_passive(passive_t::share_exp))
372 _beogh_spread_experience(experience / 2);
373 }
374
_give_experience(int player_exp,int monster_exp,killer_type killer,int killer_index,bool pet_kill,bool was_visible,xp_tracking_type xp_tracking)375 static void _give_experience(int player_exp, int monster_exp,
376 killer_type killer, int killer_index,
377 bool pet_kill, bool was_visible,
378 xp_tracking_type xp_tracking)
379 {
380 _give_player_experience(player_exp, killer, pet_kill, was_visible,
381 xp_tracking);
382 _give_monster_experience(monster_exp, killer_index);
383 }
384
385 /**
386 * Makes an item into gold. Praise Gozag!
387 *
388 * Gold is random, but correlates weakly with monster mass.
389 *
390 * Also sets the gold distraction timer on the player.
391 *
392 * @param corpse[out] The item to be Midasified.
393 */
_gold_pile(item_def & corpse,monster_type corpse_class)394 static void _gold_pile(item_def &corpse, monster_type corpse_class)
395 {
396 corpse.clear();
397
398 int base_gold = 7;
399 // monsters with more chunks than SIZE_MEDIUM give more than base gold
400 const int extra_chunks = (max_corpse_chunks(corpse_class)
401 - max_corpse_chunks(MONS_HUMAN)) * 2;
402 if (extra_chunks > 0)
403 base_gold += extra_chunks;
404
405 corpse.base_type = OBJ_GOLD;
406 corpse.mon_type = corpse_class; // for _explode_corpse
407 corpse.quantity = base_gold / 2 + random2avg(base_gold, 2);
408 item_colour(corpse);
409
410 // Apply the gold aura effect to the player.
411 const int dur = corpse.quantity * 2;
412 if (dur > you.duration[DUR_GOZAG_GOLD_AURA])
413 you.set_duration(DUR_GOZAG_GOLD_AURA, dur);
414
415 // In sprint, increase the amount of gold from corpses (but not
416 // the gold aura duration!)
417 if (crawl_state.game_is_sprint())
418 corpse.quantity *= SPRINT_MULTIPLIER;
419
420 const int chance = you.props[GOZAG_GOLD_AURA_KEY].get_int();
421 if (!x_chance_in_y(chance, chance + 9))
422 ++you.props[GOZAG_GOLD_AURA_KEY].get_int();
423 you.redraw_title = true;
424 }
425
_create_monster_hide(const item_def & corpse,bool silent)426 static void _create_monster_hide(const item_def &corpse, bool silent)
427 {
428 const monster_type mtyp = corpse.mon_type;
429 const armour_type type = hide_for_monster(mons_species(mtyp));
430 ASSERT(type != NUM_ARMOURS);
431
432 int o = items(false, OBJ_ARMOUR, type, 0);
433 squash_plusses(o);
434
435 if (o == NON_ITEM)
436 return;
437 item_def& item = env.item[o];
438
439 const monster_type montype =
440 static_cast<monster_type>(corpse.orig_monnum);
441 if (!invalid_monster_type(montype) && mons_is_unique(montype))
442 item.inscription = mons_type_name(montype, DESC_PLAIN);
443
444 /// Slightly randomized bonus enchantment for certain uniques' hides
445 static const map<monster_type, int> hide_avg_plusses = {
446 { MONS_SNORG, 2 },
447 { MONS_XTAHUA, 3 },
448 { MONS_BAI_SUZHEN, 3 },
449 { MONS_BAI_SUZHEN_DRAGON, 3 },
450 };
451
452 if (mtyp == MONS_DEEP_TROLL)
453 {
454 item.props["item_tile_name"] = "deep_troll_leather";
455 item.props["worn_tile_name"] = "deep_troll_leather";
456 bind_item_tile(item);
457 }
458 else if (mtyp == MONS_IRON_TROLL)
459 {
460 item.props["item_tile_name"] = "iron_troll_leather";
461 item.props["worn_tile_name"] = "iron_troll_leather";
462 bind_item_tile(item);
463 }
464
465 const int* bonus_plus = map_find(hide_avg_plusses, montype);
466 if (bonus_plus)
467 item.plus += random_range(*bonus_plus * 2/3, *bonus_plus * 3/2);
468
469 const coord_def pos = item_pos(corpse);
470 if (pos.origin())
471 {
472 set_ident_flags(item, ISFLAG_IDENT_MASK);
473 return;
474 }
475
476 move_item_to_grid(&o, pos);
477
478 // Don't display this message if the scales were dropped over
479 // lava/deep water, because then they are hardly intact.
480 if (you.see_cell(pos) && !silent && !feat_eliminates_items(env.grid(pos)))
481 {
482 // XXX: tweak for uniques/named monsters, somehow?
483 mprf("%s %s intact enough to wear.",
484 item.name(DESC_THE).c_str(),
485 mons_genus(mtyp) == MONS_DRAGON ? "are" // scales are
486 : "is"); // troll armour is
487 // XXX: refactor
488 }
489
490 // after messaging, for better results
491 set_ident_flags(item, ISFLAG_IDENT_MASK);
492 }
493
_maybe_drop_monster_hide(const item_def & corpse,bool silent)494 static void _maybe_drop_monster_hide(const item_def &corpse, bool silent)
495 {
496 if (mons_class_leaves_hide(corpse.mon_type) && !one_chance_in(3))
497 _create_monster_hide(corpse, silent);
498 }
499
500 /**
501 * Create this monster's corpse in env.item at its position.
502 *
503 * @param mons the monster to corpsify
504 * @param silent whether to suppress all messages
505 * @param force whether to always make a corpse (no 50% chance not to make a
506 corpse, no goldification, no hides -- being summoned etc. still
507 * matters, though)
508 * @returns a pointer to an item; it may be null, if the monster can't leave a
509 * corpse or if the 50% chance is rolled; it may be gold, if the player
510 * worships Gozag, or it may be the corpse.
511 */
place_monster_corpse(const monster & mons,bool force)512 item_def* place_monster_corpse(const monster& mons, bool force)
513 {
514 if (mons.is_summoned()
515 || mons.flags & (MF_BANISHED | MF_HARD_RESET)
516 || mons.props.exists("pikel_band"))
517 {
518 return nullptr;
519 }
520
521 // Under Gozag, monsters turn into gold on death.
522 // Temporary Tukima's Dance weapons stay as weapons (no free gold),
523 // permanent dancing weapons turn to gold like other monsters.
524 bool goldify = have_passive(passive_t::goldify_corpses)
525 && mons_gives_xp(mons, you)
526 && !force;
527
528 const bool no_coinflip = mons.props.exists("always_corpse")
529 || force
530 || goldify;
531
532 // 50/50 chance of getting a corpse, usually.
533 if (!no_coinflip && coinflip())
534 return nullptr;
535
536 // The game can attempt to place a corpse for an out-of-bounds monster
537 // if a shifter turns into a ballistomycete spore and explodes. In this
538 // case we place no corpse since the explosion means anything left
539 // over would be scattered, tiny chunks of shifter.
540 if (!in_bounds(mons.pos()) && !force)
541 return nullptr;
542
543 // Don't attempt to place corpses within walls, either.
544 if (feat_is_solid(env.grid(mons.pos())) && !force)
545 return nullptr;
546
547 // If we were told not to leave a corpse, don't.
548 if (mons.props.exists(NEVER_CORPSE_KEY))
549 return nullptr;
550
551 int o = get_mitm_slot();
552
553 if (o == NON_ITEM)
554 return nullptr;
555
556 item_def& corpse(env.item[o]);
557 if (goldify)
558 {
559 _gold_pile(corpse, mons_species(mons.type));
560 // If gold would be destroyed, give it directly to the player instead.
561 if (feat_eliminates_items(env.grid(mons.pos())))
562 {
563 get_gold(corpse, corpse.quantity, false);
564 destroy_item(corpse, true);
565 return nullptr;
566 }
567 }
568 else if (!_fill_out_corpse(mons, corpse))
569 return nullptr;
570
571 origin_set_monster(corpse, &mons);
572
573 if ((mons.flags & MF_EXPLODE_KILL) && _explode_corpse(corpse, mons.pos()))
574 {
575 // The corpse itself shouldn't remain.
576 item_was_destroyed(corpse);
577 destroy_item(o);
578 return nullptr;
579 }
580
581 if (in_bounds(mons.pos()))
582 move_item_to_grid(&o, mons.pos(), !mons.swimming());
583
584 if (o == NON_ITEM)
585 return nullptr;
586
587 return &env.item[o];
588 }
589
_hints_inspect_kill()590 static void _hints_inspect_kill()
591 {
592 if (Hints.hints_events[HINT_KILLED_MONSTER])
593 learned_something_new(HINT_KILLED_MONSTER);
594 }
595
_milestone_kill_verb(killer_type killer)596 static string _milestone_kill_verb(killer_type killer)
597 {
598 return killer == KILL_BANISHED ? "banished" :
599 killer == KILL_PACIFIED ? "pacified" :
600 killer == KILL_CHARMD ? "enslaved" :
601 killer == KILL_SLIMIFIED ? "slimified" : "killed";
602 }
603
record_monster_defeat(const monster * mons,killer_type killer)604 void record_monster_defeat(const monster* mons, killer_type killer)
605 {
606 if (crawl_state.game_is_arena())
607 return;
608 if (killer == KILL_RESET || killer == KILL_DISMISSED)
609 return;
610 if (mons->has_ench(ENCH_FAKE_ABJURATION) || mons->is_summoned())
611 return;
612 if (mons->is_named() && mons->friendly()
613 && !mons_is_hepliaklqana_ancestor(mons->type))
614 {
615 take_note(Note(NOTE_ALLY_DEATH, 0, 0, mons->mname));
616 }
617 else if (mons_is_notable(*mons))
618 {
619 take_note(Note(NOTE_DEFEAT_MONSTER, mons->type, mons->friendly(),
620 mons->full_name(DESC_A).c_str(),
621 _milestone_kill_verb(killer).c_str()));
622 }
623 if (mons->type == MONS_PLAYER_GHOST)
624 {
625 monster_info mi(mons);
626 string milestone = _milestone_kill_verb(killer) + " the ghost of ";
627 milestone += get_ghost_description(mi, true);
628 milestone += ".";
629 mark_milestone("ghost", milestone);
630 }
631 if (mons_is_or_was_unique(*mons) && !testbits(mons->flags, MF_SPECTRALISED))
632 {
633 mark_milestone("uniq",
634 _milestone_kill_verb(killer)
635 + " "
636 + mons->name(DESC_THE, true)
637 + ".");
638 }
639 }
640
_is_pet_kill(killer_type killer,int i)641 static bool _is_pet_kill(killer_type killer, int i)
642 {
643 if (!MON_KILL(killer))
644 return false;
645
646 if (i == ANON_FRIENDLY_MONSTER)
647 return true;
648
649 if (invalid_monster_index(i))
650 return false;
651
652 const monster* m = &env.mons[i];
653 if (m->friendly()) // This includes enslaved monsters.
654 return true;
655
656 // Check if the monster was confused by you or a friendly, which
657 // makes casualties to this monster collateral kills.
658 const mon_enchant me = m->get_ench(ENCH_CONFUSION);
659 const mon_enchant me2 = m->get_ench(ENCH_INSANE);
660 return me.ench == ENCH_CONFUSION
661 && (me.who == KC_YOU || me.who == KC_FRIENDLY)
662 || me2.ench == ENCH_INSANE
663 && (me2.who == KC_YOU || me2.who == KC_FRIENDLY);
664 }
665
exp_rate(int killer)666 int exp_rate(int killer)
667 {
668 // Damage by the spectral weapon is considered to be the player's damage ---
669 // so the player does not lose any exp from dealing damage with a spectral weapon summon
670 // ditto hep ancestors (sigh)
671 if (!invalid_monster_index(killer)
672 && (env.mons[killer].type == MONS_SPECTRAL_WEAPON
673 || mons_is_hepliaklqana_ancestor(env.mons[killer].type))
674 && env.mons[killer].summoner == MID_PLAYER)
675 {
676 return 2;
677 }
678
679 if (killer == MHITYOU || killer == YOU_FAULTLESS)
680 return 2;
681
682 if (_is_pet_kill(KILL_MON, killer))
683 return 1;
684
685 return 0;
686 }
687
688 // Elyvilon will occasionally (5% chance) protect the life of one of
689 // your holy or natural allies.
_ely_protect_ally(monster * mons,killer_type killer)690 static bool _ely_protect_ally(monster* mons, killer_type killer)
691 {
692 ASSERT(mons); // XXX: change to monster &mons
693 if (!have_passive(passive_t::protect_ally))
694 return false;
695
696 if (!MON_KILL(killer) && !YOU_KILL(killer))
697 return false;
698
699 if ( mons->holiness() & ~(MH_HOLY | MH_NATURAL)
700 || !mons->friendly()
701 || !you.can_see(*mons) // for simplicity
702 || !one_chance_in(20))
703 {
704 return false;
705 }
706
707 mons->hit_points = 1;
708
709 const string msg = " protects " + mons->name(DESC_THE) + " from harm!";
710 simple_god_message(msg.c_str());
711
712 return true;
713 }
714
715 // Elyvilon retribution effect: Heal hostile monsters that were about to
716 // be killed by you or one of your friends.
_ely_heal_monster(monster * mons,killer_type killer,int i)717 static bool _ely_heal_monster(monster* mons, killer_type killer, int i)
718 {
719 god_type god = GOD_ELYVILON;
720
721 if (!player_under_penance(god) || !god_hates_your_god(god))
722 return false;
723
724 if (mons->wont_attack()
725 || mons_is_firewood(*mons)
726 || mons_is_object(mons->type)
727 || mons_is_tentacle_or_tentacle_segment(mons->type)
728 || mons->props.exists("ely_wrath_healed")
729 || mons->get_experience_level() < random2(you.experience_level)
730 || !one_chance_in(3))
731 {
732 return false;
733 }
734
735 if (MON_KILL(killer) && !invalid_monster_index(i))
736 {
737 monster* mon = &env.mons[i];
738 if (!mon->friendly())
739 return false;
740
741 if (!you.see_cell(mons->pos()))
742 return false;
743 }
744 else if (!YOU_KILL(killer))
745 return false;
746
747 dprf("monster hp: %d, max hp: %d", mons->hit_points, mons->max_hit_points);
748
749 mons->hit_points = 1 + random2(mons->max_hit_points);
750 mons->props["ely_wrath_healed"] = true;
751
752 dprf("new hp: %d", mons->hit_points);
753
754 const string msg = make_stringf("%s heals %s%s",
755 god_name(god, false).c_str(),
756 mons->name(DESC_THE).c_str(),
757 mons->hit_points * 2 <= mons->max_hit_points ? "." : "!");
758
759 god_speaks(god, msg.c_str());
760
761 return true;
762 }
763
_yred_enslave_soul(monster * mons,killer_type killer)764 static bool _yred_enslave_soul(monster* mons, killer_type killer)
765 {
766 if (you_worship(GOD_YREDELEMNUL) && mons_enslaved_body_and_soul(*mons)
767 && you.see_cell(mons->pos()) && killer != KILL_RESET
768 && killer != KILL_DISMISSED
769 && killer != KILL_BANISHED)
770 {
771 record_monster_defeat(mons, killer);
772 record_monster_defeat(mons, KILL_CHARMD);
773 yred_make_enslaved_soul(mons, player_under_penance());
774 return true;
775 }
776
777 return false;
778 }
779
780
781 /**
782 * Attempt to get a deathbed conversion for the given orc.
783 *
784 * @param mons A dying orc.
785 * @param killer The way in which the monster was killed (or 'killed').
786 * @return Whether the monster's life was saved (praise Beogh)
787 */
_beogh_forcibly_convert_orc(monster & mons,killer_type killer)788 static bool _beogh_forcibly_convert_orc(monster &mons, killer_type killer)
789 {
790 // Orcs may convert to Beogh under threat of death, either from
791 // you or, less often, your followers. In both cases, the
792 // checks are made against your stats. You're the potential
793 // messiah, after all.
794 #ifdef DEBUG_DIAGNOSTICS
795 mprf(MSGCH_DIAGNOSTICS, "Death convert attempt on %s, HD: %d, "
796 "your xl: %d",
797 mons.name(DESC_PLAIN).c_str(),
798 mons.get_hit_dice(),
799 you.experience_level);
800 #endif
801 if (random2(you.piety) >= piety_breakpoint(0)
802 && random2(you.experience_level) >= random2(mons.get_hit_dice())
803 // Bias beaten-up-conversion towards the stronger orcs.
804 && random2(mons.get_experience_level()) > 2)
805 {
806 beogh_convert_orc(&mons, MON_KILL(killer) ? conv_t::deathbed_follower :
807 conv_t::deathbed);
808 return true;
809 }
810
811 return false;
812 }
813
814 /**
815 * Attempt to get a deathbed conversion for the given monster.
816 *
817 * @param mons A dying monster (not necessarily an orc)
818 * @param killer The way in which the monster was killed (or 'killed').
819 * @param killer_index The mindex of the killer, if known.
820 * @return Whether the monster's life was saved (praise Beogh)
821 */
_beogh_maybe_convert_orc(monster & mons,killer_type killer,int killer_index)822 static bool _beogh_maybe_convert_orc(monster &mons, killer_type killer,
823 int killer_index)
824 {
825 if (!have_passive(passive_t::convert_orcs)
826 || mons_genus(mons.type) != MONS_ORC
827 || mons.is_summoned() || mons.is_shapeshifter()
828 || !you.see_cell(mons.pos()) || mons_is_god_gift(mons))
829 {
830 return false;
831 }
832
833 if (YOU_KILL(killer))
834 return _beogh_forcibly_convert_orc(mons, killer);
835
836 if (MON_KILL(killer) && !invalid_monster_index(killer_index))
837 {
838 const monster* responsible_monster = &env.mons[killer_index];
839 if (is_follower(*responsible_monster) && !one_chance_in(3))
840 return _beogh_forcibly_convert_orc(mons, killer);
841 }
842
843 return false;
844 }
845
846 /**
847 * Attempt to save the given monster's life at the last moment.
848 *
849 * Checks lost souls & various divine effects (Yred, Beogh, Ely).
850 *
851 * @param mons A dying monster.
852 * @param killer The way in which the monster was killed (or 'killed').
853 * @param killer_index The mindex of the killer, if known.
854 */
_monster_avoided_death(monster * mons,killer_type killer,int killer_index)855 static bool _monster_avoided_death(monster* mons, killer_type killer,
856 int killer_index)
857 {
858 if (mons->max_hit_points <= 0 || mons->get_hit_dice() < 1)
859 return false;
860
861 // Before the hp check since this should not care about the power of the
862 // finishing blow
863 if (lost_soul_revive(*mons, killer))
864 return true;
865
866 // Yredelemnul special.
867 if (_yred_enslave_soul(mons, killer))
868 return true;
869
870 // Beogh special.
871 if (_beogh_maybe_convert_orc(*mons, killer, killer_index))
872 return true;
873
874 if (mons->hit_points < -25 || mons->hit_points < -mons->max_hit_points)
875 return false;
876
877 // Elyvilon specials.
878 if (_ely_protect_ally(mons, killer))
879 return true;
880 if (_ely_heal_monster(mons, killer, killer_index))
881 return true;
882
883 return false;
884 }
885
_jiyva_died()886 static void _jiyva_died()
887 {
888 if (you_worship(GOD_JIYVA))
889 return;
890
891 add_daction(DACT_REMOVE_JIYVA_ALTARS);
892
893 if (!player_in_branch(BRANCH_SLIME))
894 return;
895
896 if (silenced(you.pos()))
897 {
898 god_speaks(GOD_JIYVA, "With an infernal shudder, the power ruling "
899 "this place vanishes!");
900 }
901 else
902 {
903 god_speaks(GOD_JIYVA, "With an infernal noise, the power ruling this "
904 "place vanishes!");
905 }
906 }
907
fire_monster_death_event(monster * mons,killer_type killer,bool polymorph)908 void fire_monster_death_event(monster* mons,
909 killer_type killer,
910 bool polymorph)
911 {
912 int type = mons->type;
913
914 // Treat whatever the Royal Jelly polymorphed into as if it were still
915 // the Royal Jelly (but if a player chooses the character name
916 // "shaped Royal Jelly" don't unlock the vaults when the player's
917 // ghost is killed).
918 if (mons->mname == "shaped Royal Jelly"
919 && !mons_is_pghost(mons->type))
920 {
921 type = MONS_ROYAL_JELLY;
922 }
923
924 if (!polymorph)
925 {
926 dungeon_events.fire_event(
927 dgn_event(DET_MONSTER_DIED, mons->pos(), 0,
928 mons->mid, killer));
929 }
930
931 bool terrain_changed = false;
932
933 for (map_marker *mark : env.markers.get_all(MAT_TERRAIN_CHANGE))
934 {
935 map_terrain_change_marker *marker =
936 dynamic_cast<map_terrain_change_marker*>(mark);
937
938 if (marker->mon_num != 0 && monster_by_mid(marker->mon_num) == mons)
939 {
940 terrain_changed = true;
941 marker->duration = 0;
942 }
943 }
944
945 if (terrain_changed)
946 timeout_terrain_changes(0, true);
947
948 if (killer == KILL_BANISHED)
949 return;
950
951 los_monster_died(mons);
952
953 if (type == MONS_ROYAL_JELLY && !mons->is_summoned() && !polymorph)
954 {
955 you.royal_jelly_dead = true;
956
957 if (jiyva_is_dead())
958 _jiyva_died();
959 }
960 }
961
mummy_curse_power(monster_type type)962 int mummy_curse_power(monster_type type)
963 {
964 // Plain mummies (and Menkaure) are too weak to curse you!
965 switch (type)
966 {
967 case MONS_GUARDIAN_MUMMY:
968 case MONS_MUMMY_PRIEST:
969 case MONS_ROYAL_MUMMY:
970 case MONS_KHUFU:
971 return mons_class_hit_dice(type);
972 default:
973 return 0;
974 }
975 }
976
977 template<typename valid_T, typename connect_T>
_search_dungeon(const coord_def & start,valid_T & valid_target,connect_T & connecting_square,set<position_node> & visited,vector<set<position_node>::iterator> & candidates,bool exhaustive=true,int connect_mode=8)978 static void _search_dungeon(const coord_def & start,
979 valid_T & valid_target,
980 connect_T & connecting_square,
981 set<position_node> & visited,
982 vector<set<position_node>::iterator> & candidates,
983 bool exhaustive = true,
984 int connect_mode = 8)
985 {
986 if (connect_mode < 1 || connect_mode > 8)
987 connect_mode = 8;
988
989 // Ordering the default compass index this way gives us the non
990 // diagonal directions as the first four elements - so by just
991 // using the first 4 elements instead of the whole array we
992 // can have 4-connectivity.
993 int compass_idx[] = {0, 2, 4, 6, 1, 3, 5, 7};
994
995 position_node temp_node;
996 temp_node.pos = start;
997 temp_node.last = nullptr;
998
999 queue<set<position_node>::iterator > fringe;
1000
1001 auto current = visited.insert(temp_node).first;
1002 fringe.push(current);
1003
1004 while (!fringe.empty())
1005 {
1006 current = fringe.front();
1007 fringe.pop();
1008
1009 shuffle_array(compass_idx, connect_mode);
1010
1011 for (int i=0; i < connect_mode; ++i)
1012 {
1013 coord_def adjacent = current->pos + Compass[compass_idx[i]];
1014 if (in_bounds(adjacent))
1015 {
1016 temp_node.pos = adjacent;
1017 temp_node.last = &(*current);
1018 auto res = visited.insert(temp_node);
1019
1020 if (!res.second)
1021 continue;
1022
1023 if (valid_target(adjacent))
1024 {
1025 candidates.push_back(res.first);
1026 if (!exhaustive)
1027 return;
1028 }
1029
1030 if (connecting_square(adjacent))
1031 fringe.push(res.first);
1032 }
1033 }
1034 }
1035 }
1036
_infestation_create_scarab(monster * mons)1037 static void _infestation_create_scarab(monster* mons)
1038 {
1039 mons->flags |= MF_EXPLODE_KILL;
1040 infestation_death_fineff::schedule(mons->pos(), mons->name(DESC_THE));
1041 }
1042
_monster_die_cloud(const monster * mons,bool corpse,bool silent,bool summoned)1043 static void _monster_die_cloud(const monster* mons, bool corpse, bool silent,
1044 bool summoned)
1045 {
1046 // Chaos spawn always leave behind a cloud of chaos.
1047 if (mons->type == MONS_CHAOS_SPAWN)
1048 {
1049 summoned = true;
1050 corpse = false;
1051 }
1052
1053 if (!summoned)
1054 return;
1055
1056 if (cell_is_solid(mons->pos()))
1057 return;
1058
1059 string prefix = " ";
1060 if (corpse)
1061 {
1062 if (!mons_class_can_leave_corpse(mons_species(mons->type)))
1063 return;
1064
1065 prefix = "'s corpse ";
1066 }
1067
1068 string msg = summoned_poof_msg(mons) + "!";
1069
1070 cloud_type cloud = CLOUD_NONE;
1071 if (msg.find("smoke") != string::npos)
1072 cloud = random_smoke_type();
1073 else if (msg.find("chaos") != string::npos)
1074 cloud = CLOUD_CHAOS;
1075
1076 if (!silent)
1077 simple_monster_message(*mons, (prefix + msg).c_str());
1078
1079 if (cloud != CLOUD_NONE)
1080 place_cloud(cloud, mons->pos(), 1 + random2(3), mons);
1081 }
1082
_killer_type_name(killer_type killer)1083 static string _killer_type_name(killer_type killer)
1084 {
1085 switch (killer)
1086 {
1087 case KILL_NONE:
1088 return "none";
1089 case KILL_YOU:
1090 return "you";
1091 case KILL_MON:
1092 return "mon";
1093 case KILL_YOU_MISSILE:
1094 return "you_missile";
1095 case KILL_MON_MISSILE:
1096 return "mon_missile";
1097 case KILL_YOU_CONF:
1098 return "you_conf";
1099 case KILL_MISCAST:
1100 return "miscast";
1101 case KILL_MISC:
1102 return "misc";
1103 case KILL_RESET:
1104 return "reset";
1105 case KILL_DISMISSED:
1106 return "dismissed";
1107 case KILL_BANISHED:
1108 return "banished";
1109 case KILL_TIMEOUT:
1110 return "timeout";
1111 #if TAG_MAJOR_VERSION == 34
1112 case KILL_UNSUMMONED:
1113 return "unsummoned";
1114 #endif
1115 case KILL_PACIFIED:
1116 return "pacified";
1117 case KILL_CHARMD:
1118 return "enslaved";
1119 case KILL_SLIMIFIED:
1120 return "slimified";
1121 }
1122 die("invalid killer type");
1123 }
1124
1125 /**
1126 * Make a spectral thing or simulacrum out of a dying/dead monster.
1127 *
1128 * @param mons the monster that died
1129 * @param quiet whether to print flavour messages
1130 * @param bound_soul whether the undead is from Bind Souls (true) or DChan
1131 */
_make_derived_undead(monster * mons,bool quiet,bool bound_soul)1132 static void _make_derived_undead(monster* mons, bool quiet, bool bound_soul)
1133 {
1134 if (mons->holiness() & MH_NATURAL && mons_can_be_zombified(*mons))
1135 {
1136 // Use the original monster type as the zombified type here, to
1137 // get the proper stats from it.
1138 mgen_data mg(bound_soul ? MONS_SIMULACRUM : MONS_SPECTRAL_THING,
1139 bound_soul ? SAME_ATTITUDE(mons) : BEH_FRIENDLY,
1140 mons->pos(),
1141 // XXX: is MHITYOU really correct here?
1142 crawl_state.game_is_arena() ? MHITNOT : MHITYOU);
1143 // Simulacra aren't summons, and we want them to stick
1144 // around even after killing the necromancer.
1145 mg.set_summoned(bound_soul ? nullptr : &you,
1146 0,
1147 bound_soul ? SPELL_BIND_SOULS : SPELL_DEATH_CHANNEL,
1148 bound_soul ?
1149 GOD_NO_GOD : static_cast<god_type>(you.attribute[ATTR_DIVINE_DEATH_CHANNEL]));
1150 mg.set_base(mons->type);
1151
1152 if (!mons->mname.empty() && !(mons->flags & MF_NAME_NOCORPSE))
1153 mg.mname = mons->mname;
1154 else if (mons_is_unique(mons->type))
1155 mg.mname = mons_type_name(mons->type, DESC_PLAIN);
1156 mg.extra_flags = mons->flags & (MF_NAME_SUFFIX
1157 | MF_NAME_ADJECTIVE
1158 | MF_NAME_DESCRIPTOR);
1159
1160 if (mons->mons_species() == MONS_HYDRA)
1161 {
1162 // No undead 0-headed hydras, sorry.
1163 if (mons->heads() == 0)
1164 {
1165 if (!quiet)
1166 {
1167 mprf("A %s mist gathers momentarily, then fades.",
1168 bound_soul ? "freezing" : "glowing");
1169 }
1170 return;
1171 }
1172 else
1173 mg.props[MGEN_NUM_HEADS] = mons->heads();
1174 }
1175
1176 string agent_name = "";
1177 if (bound_soul)
1178 {
1179 const auto agent = mons->get_ench(ENCH_BOUND_SOUL).agent();
1180 if (agent)
1181 agent_name = agent->as_monster()->full_name(DESC_A);
1182 }
1183
1184 string monster_name = "";
1185
1186 string message = quiet ? "" :
1187 make_stringf("A %s mist starts to gather...",
1188 bound_soul ? "freezing" : "glowing");
1189
1190 make_derived_undead_fineff::schedule(mons->pos(), mg,
1191 mons->get_experience_level(), agent_name, message);
1192
1193 }
1194 }
1195
_druid_final_boon(const monster * mons)1196 static void _druid_final_boon(const monster* mons)
1197 {
1198 vector<monster*> beasts;
1199 for (monster_near_iterator mi(mons); mi; ++mi)
1200 {
1201 if (mons_is_beast(mons_base_type(**mi)) && mons_aligned(mons, *mi))
1202 beasts.push_back(*mi);
1203 }
1204
1205 if (!beasts.size())
1206 return;
1207
1208 if (you.can_see(*mons))
1209 {
1210 mprf(MSGCH_MONSTER_SPELL, "With its final breath, %s offers up its power "
1211 "to the beasts of the wild!",
1212 mons->name(DESC_THE).c_str());
1213 }
1214
1215 shuffle_array(beasts);
1216 int num = min((int)beasts.size(),
1217 random_range(mons->get_hit_dice() / 3,
1218 mons->get_hit_dice() / 2 + 1));
1219
1220 // Healing and empowering done in two separate loops for tidier messages
1221 for (int i = 0; i < num; ++i)
1222 {
1223 if (beasts[i]->heal(roll_dice(3, mons->get_hit_dice()))
1224 && you.can_see(*beasts[i]))
1225 {
1226 mprf("%s %s healed.", beasts[i]->name(DESC_THE).c_str(),
1227 beasts[i]->conj_verb("are").c_str());
1228 }
1229 }
1230
1231 for (int i = 0; i < num; ++i)
1232 {
1233 simple_monster_message(*beasts[i], " seems to grow more fierce.");
1234 beasts[i]->add_ench(mon_enchant(ENCH_MIGHT, 1, mons,
1235 random_range(100, 160)));
1236 }
1237 }
1238
_mons_reaped(actor & killer,monster & victim)1239 static bool _mons_reaped(actor &killer, monster& victim)
1240 {
1241 beh_type beh;
1242 unsigned short hitting;
1243
1244 if (killer.is_player())
1245 {
1246 hitting = MHITYOU;
1247 beh = BEH_FRIENDLY;
1248 }
1249 else
1250 {
1251 monster* mon = killer.as_monster();
1252
1253 beh = SAME_ATTITUDE(mon);
1254
1255 // Get a new foe for the zombie to target.
1256 behaviour_event(mon, ME_EVAL);
1257 hitting = mon->foe;
1258 }
1259
1260 monster *zombie = 0;
1261 if (animate_remains(victim.pos(), CORPSE_BODY, beh, 0, hitting, &killer, "",
1262 GOD_NO_GOD, true, true, false, &zombie) <= 0)
1263 {
1264 return false;
1265 }
1266
1267 if (you.can_see(victim))
1268 {
1269 mprf("%s turns into a %s!", victim.name(DESC_THE).c_str(),
1270 zombie->type == MONS_ZOMBIE ? "zombie"
1271 : "skeleton");
1272 }
1273 else if (you.can_see(*zombie))
1274 mprf("%s appears out of thin air!", zombie->name(DESC_THE).c_str());
1275
1276 check_lovelessness(*zombie);
1277
1278 return true;
1279 }
1280
_reaping(monster & mons)1281 static bool _reaping(monster &mons)
1282 {
1283 if (!mons.props.exists("reaping_damage"))
1284 return false;
1285
1286 int rd = mons.props["reaping_damage"].get_int();
1287 dprf("Reaping chance: %d/%d", rd, mons.damage_total);
1288 if (!x_chance_in_y(rd, mons.damage_total))
1289 return false;
1290
1291 actor *killer = actor_by_mid(mons.props["reaper"].get_int());
1292 if (killer)
1293 return _mons_reaped(*killer, mons);
1294 return false;
1295 }
1296
_god_will_bless_follower(monster * victim)1297 static bool _god_will_bless_follower(monster* victim)
1298 {
1299 return have_passive(passive_t::bless_followers)
1300 && random2(you.piety) >= piety_breakpoint(2)
1301 || have_passive(passive_t::bless_followers_vs_evil)
1302 && victim->evil()
1303 && random2(you.piety) >= piety_breakpoint(0);
1304 }
1305
1306 /**
1307 * Trigger the appropriate god conducts for a monster's death.
1308 *
1309 * @param mons The dying monster.
1310 * @param killer The responsibility for the death.
1311 * (KILL_YOU, KILL_MON...)
1312 * @param killer_index The mindex of the killer, if known.
1313 * @param maybe_good_kill Whether the kill can be rewarding in piety.
1314 * (Not summoned, etc)
1315 */
_fire_kill_conducts(monster & mons,killer_type killer,int killer_index,bool maybe_good_kill)1316 static void _fire_kill_conducts(monster &mons, killer_type killer,
1317 int killer_index, bool maybe_good_kill)
1318 {
1319 const bool your_kill = killer == KILL_YOU ||
1320 killer == KILL_YOU_CONF ||
1321 killer == KILL_YOU_MISSILE ||
1322 killer_index == YOU_FAULTLESS;
1323 const bool pet_kill = _is_pet_kill(killer, killer_index);
1324
1325 // Pretend the monster is already dead, so that make_god_gifts_disappear
1326 // (and similar) don't kill it twice.
1327 unwind_var<int> fake_hp(mons.hit_points, 0);
1328
1329 // if you or your pets didn't do it, no one cares
1330 if (!your_kill && !pet_kill)
1331 return;
1332
1333 // player gets credit for reflection kills, but not blame
1334 const bool blameworthy = god_hates_killing(you.religion, mons)
1335 && killer_index != YOU_FAULTLESS;
1336
1337 // if you can't get piety for it & your god won't give penance/-piety for
1338 // it, no one cares
1339 // XXX: this will break holy death curses if they're added back...
1340 // but tbh that shouldn't really be in conducts anyway
1341 if (!maybe_good_kill && !blameworthy)
1342 return;
1343
1344 mon_holy_type holiness = mons.holiness();
1345
1346 if (holiness & MH_DEMONIC)
1347 did_kill_conduct(DID_KILL_DEMON, mons);
1348 else if (holiness & (MH_NATURAL | MH_PLANT))
1349 {
1350 did_kill_conduct(DID_KILL_LIVING, mons);
1351
1352 // TSO hates natural evil and unholy beings.
1353 if (mons.evil())
1354 did_kill_conduct(DID_KILL_NATURAL_EVIL, mons);
1355 }
1356 else if (holiness & MH_UNDEAD)
1357 did_kill_conduct(DID_KILL_UNDEAD, mons);
1358 else if (holiness & MH_NONLIVING)
1359 did_kill_conduct(DID_KILL_NONLIVING, mons);
1360
1361 // Zin hates unclean and chaotic beings.
1362 if (mons.how_unclean())
1363 did_kill_conduct(DID_KILL_UNCLEAN, mons);
1364 else if (mons.how_chaotic())
1365 did_kill_conduct(DID_KILL_CHAOTIC, mons);
1366
1367 // jmf: Trog hates wizards.
1368 if (mons.is_actual_spellcaster())
1369 did_kill_conduct(DID_KILL_WIZARD, mons);
1370
1371 // Beogh hates priests of other gods.
1372 if (mons.is_priest())
1373 did_kill_conduct(DID_KILL_PRIEST, mons);
1374
1375 // Jiyva hates you killing slimes, but eyeballs
1376 // mutation can confuse without you meaning it.
1377 if (mons_is_slime(mons) && killer != KILL_YOU_CONF)
1378 did_kill_conduct(DID_KILL_SLIME, mons);
1379
1380 if (mons.is_holy())
1381 did_kill_conduct(DID_KILL_HOLY, mons);
1382
1383 // Fedhas shrooms cause confusion which leads to subsequent
1384 // confusion kills, sometimes of the player's own plants
1385 if (fedhas_protects(&mons) && killer != KILL_YOU_CONF)
1386 did_kill_conduct(DID_KILL_PLANT, mons);
1387
1388 // Cheibriados hates fast monsters.
1389 if (cheibriados_thinks_mons_is_fast(mons) && !mons.cannot_move())
1390 did_kill_conduct(DID_KILL_FAST, mons);
1391 }
1392
monster_die(monster & mons,const actor * killer,bool silent,bool wizard,bool fake)1393 item_def* monster_die(monster& mons, const actor *killer, bool silent,
1394 bool wizard, bool fake)
1395 {
1396 killer_type ktype = KILL_YOU;
1397 int kindex = NON_MONSTER;
1398
1399 if (!killer)
1400 ktype = KILL_NONE;
1401 else if (killer->is_monster())
1402 {
1403 const monster *kmons = killer->as_monster();
1404 ktype = kmons->confused_by_you() ? KILL_YOU_CONF : KILL_MON;
1405 kindex = kmons->mindex();
1406 }
1407
1408 return monster_die(mons, ktype, kindex, silent, wizard, fake);
1409 }
1410
1411 /**
1412 * Print messages for dead monsters returning to their 'true form' on death.
1413 *
1414 * @param mons The monster currently dying.
1415 */
_special_corpse_messaging(monster & mons)1416 static void _special_corpse_messaging(monster &mons)
1417 {
1418 if (!mons.props.exists(ORIGINAL_TYPE_KEY) && mons.type != MONS_BAI_SUZHEN)
1419 return;
1420
1421 const monster_type orig
1422 = mons.type == MONS_BAI_SUZHEN ? mons.type :
1423 (monster_type) mons.props[ORIGINAL_TYPE_KEY].get_int();
1424
1425 if (orig == MONS_SHAPESHIFTER || orig == MONS_GLOWING_SHAPESHIFTER)
1426 {
1427 // No message for known shifters, unless they were originally
1428 // something else.
1429 if (!(mons.flags & MF_KNOWN_SHIFTER))
1430 {
1431 const string message = "'s shape twists and changes as "
1432 + mons.pronoun(PRONOUN_SUBJECTIVE) + " "
1433 + conjugate_verb("die", mons.pronoun_plurality()) + ".";
1434 simple_monster_message(mons, message.c_str());
1435 }
1436
1437 return;
1438 }
1439
1440 // Avoid "Sigmund returns to its original shape as it dies.".
1441 unwind_var<monster_type> mt(mons.type, orig);
1442 const int num = mons.mons_species() == MONS_HYDRA
1443 ? mons.props["old_heads"].get_int()
1444 : mons.number;
1445 unwind_var<unsigned int> number(mons.number, num);
1446 const string message = " returns to " +
1447 mons.pronoun(PRONOUN_POSSESSIVE) +
1448 " original shape as " +
1449 mons.pronoun(PRONOUN_SUBJECTIVE) + " " +
1450 conjugate_verb("die", mons.pronoun_plurality()) +
1451 ".";
1452 simple_monster_message(mons, message.c_str());
1453 }
1454
1455 /**
1456 * Kill off a monster.
1457 *
1458 * @param mons The monster to be killed
1459 * @param killer The method in which it was killed (XXX: these aren't properly
1460 * documented/coded)
1461 * @param killer_index The mindex of the killer (TODO: always use an actor*)
1462 * @param silent whether to print any messages about the death
1463 * @param wizard various switches
1464 * @param fake The death of the mount of a mounted monster (spriggan rider).
1465 * @returns a pointer to the created corpse, possibly null
1466 */
monster_die(monster & mons,killer_type killer,int killer_index,bool silent,bool wizard,bool fake)1467 item_def* monster_die(monster& mons, killer_type killer,
1468 int killer_index, bool silent, bool wizard, bool fake)
1469 {
1470 ASSERT(!invalid_monster(&mons));
1471
1472 const bool was_visible = you.can_see(mons);
1473
1474 // If a monster was banished to the Abyss and then killed there,
1475 // then its death wasn't a banishment.
1476 if (player_in_branch(BRANCH_ABYSS))
1477 mons.flags &= ~MF_BANISHED;
1478
1479 const bool spectralised = testbits(mons.flags, MF_SPECTRALISED);
1480
1481 if (!silent && !fake
1482 && _monster_avoided_death(&mons, killer, killer_index))
1483 {
1484 mons.flags &= ~MF_EXPLODE_KILL;
1485
1486 // revived by a lost soul?
1487 if (!spectralised && testbits(mons.flags, MF_SPECTRALISED))
1488 return place_monster_corpse(mons);
1489 return nullptr;
1490 }
1491
1492 // If the monster was calling the tide, let go now.
1493 mons.del_ench(ENCH_TIDE);
1494
1495 // Same for silencers.
1496 mons.del_ench(ENCH_SILENCE);
1497
1498 // ... and liquefiers.
1499 mons.del_ench(ENCH_LIQUEFYING);
1500
1501 // ... and wind-stillers.
1502 mons.del_ench(ENCH_STILL_WINDS, true);
1503
1504 // and webbed monsters
1505 monster_web_cleanup(mons, true);
1506
1507 // Clean up any blood from the flayed effect
1508 if (mons.has_ench(ENCH_FLAYED))
1509 heal_flayed_effect(&mons, true, true);
1510
1511 crawl_state.inc_mon_acting(&mons);
1512
1513 ASSERT(!(YOU_KILL(killer) && crawl_state.game_is_arena()));
1514
1515 if (mons.props.exists(MONSTER_DIES_LUA_KEY))
1516 {
1517 lua_stack_cleaner clean(dlua);
1518
1519 dlua_chunk &chunk = mons.props[MONSTER_DIES_LUA_KEY];
1520
1521 if (!chunk.load(dlua))
1522 {
1523 push_monster(dlua, &mons);
1524 clua_pushcxxstring(dlua, _killer_type_name(killer));
1525 dlua.callfn(nullptr, 2, 0);
1526 }
1527 else
1528 {
1529 mprf(MSGCH_ERROR,
1530 "Lua death function for monster '%s' didn't load: %s",
1531 mons.full_name(DESC_PLAIN).c_str(),
1532 dlua.error.c_str());
1533 }
1534 }
1535
1536 mons_clear_trapping_net(&mons);
1537 mons.stop_constricting_all();
1538 mons.stop_being_constricted();
1539
1540 you.remove_beholder(mons);
1541 you.remove_fearmonger(&mons);
1542 // Uniques leave notes and milestones, so this information is already leaked.
1543 remove_unique_annotation(&mons);
1544
1545 int duration = 0;
1546 const bool summoned = mons.is_summoned(&duration);
1547 const int monster_killed = mons.mindex();
1548 const bool hard_reset = testbits(mons.flags, MF_HARD_RESET);
1549 const bool timeout = killer == KILL_TIMEOUT;
1550 const bool fake_abjure = mons.has_ench(ENCH_FAKE_ABJURATION);
1551 const bool gives_player_xp = mons_gives_xp(mons, you);
1552 bool drop_items = !hard_reset;
1553 const bool submerged = mons.submerged();
1554 bool in_transit = false;
1555 const bool was_banished = (killer == KILL_BANISHED);
1556 const bool mons_reset = (killer == KILL_RESET
1557 || killer == KILL_DISMISSED);
1558 const bool leaves_corpse = !summoned && !fake_abjure && !timeout
1559 && !mons_reset
1560 && !mons_is_tentacle_segment(mons.type);
1561 // Award experience for suicide if the suicide was caused by the
1562 // player.
1563 if (MON_KILL(killer) && monster_killed == killer_index)
1564 {
1565 if (mons.confused_by_you())
1566 {
1567 ASSERT(!crawl_state.game_is_arena());
1568 killer = KILL_YOU_CONF;
1569 }
1570 }
1571 else if (MON_KILL(killer) && mons.has_ench(ENCH_CHARM))
1572 {
1573 bool arena = crawl_state.game_is_arena();
1574 mon_enchant ench = mons.get_ench(ENCH_CHARM);
1575 if (ench.who == KC_YOU || (!arena && ench.who == KC_FRIENDLY))
1576 {
1577 ASSERT(!arena);
1578 killer = KILL_YOU_CONF; // Well, it was confused in a sense... (jpeg)
1579 }
1580 }
1581
1582 // Kills by the spectral weapon are considered as kills by the player
1583 // instead. Ditto Dithmenos shadow melee and shadow throw.
1584 if (MON_KILL(killer)
1585 && !invalid_monster_index(killer_index)
1586 && ((env.mons[killer_index].type == MONS_SPECTRAL_WEAPON
1587 && env.mons[killer_index].summoner == MID_PLAYER)
1588 || mons_is_player_shadow(env.mons[killer_index])))
1589 {
1590 killer_index = you.mindex();
1591 }
1592
1593 // Set an appropriate killer; besides the cases in the preceding if,
1594 // this handles Dithmenos shadow spells, which look like they come from
1595 // you because the shadow's mid is MID_PLAYER.
1596 if (MON_KILL(killer) && killer_index == you.mindex())
1597 killer = (killer == KILL_MON_MISSILE) ? KILL_YOU_MISSILE : KILL_YOU;
1598
1599 // Take notes and mark milestones.
1600 record_monster_defeat(&mons, killer);
1601
1602 // Various sources of berserk extension on kills.
1603 if (killer == KILL_YOU && you.berserk())
1604 {
1605 if (have_passive(passive_t::extend_berserk)
1606 && you.piety > random2(1000))
1607 {
1608 const int bonus = (3 + random2avg(10, 2)) / 2;
1609
1610 you.increase_duration(DUR_BERSERK, bonus);
1611
1612 mprf(MSGCH_GOD, you.religion,
1613 "You feel the power of %s in you as your rage grows.",
1614 uppercase_first(god_name(you.religion)).c_str());
1615 }
1616 else if (player_equip_unrand(UNRAND_BLOODLUST) && coinflip())
1617 {
1618 const int bonus = (2 + random2(4)) / 2;
1619 you.increase_duration(DUR_BERSERK, bonus);
1620 mpr("The necklace of Bloodlust glows a violent red.");
1621 }
1622 else if (player_equip_unrand(UNRAND_TROG) && coinflip())
1623 {
1624 const int bonus = (2 + random2(4)) / 2;
1625 you.increase_duration(DUR_BERSERK, bonus);
1626 mpr("You feel the ancient rage of your axe.");
1627 }
1628 }
1629
1630 if (you.prev_targ == monster_killed)
1631 {
1632 you.prev_targ = MHITNOT;
1633 crawl_state.cancel_cmd_repeat();
1634 }
1635
1636 if (killer == KILL_YOU)
1637 crawl_state.cancel_cmd_repeat();
1638
1639 const bool pet_kill = _is_pet_kill(killer, killer_index);
1640
1641 bool did_death_message = false;
1642
1643
1644 if (monster_explodes(mons))
1645 {
1646 did_death_message =
1647 explode_monster(&mons, killer, pet_kill, wizard);
1648 }
1649 else if (mons.type == MONS_FULMINANT_PRISM && mons.prism_charge == 0)
1650 {
1651 if (!silent && !hard_reset && !was_banished)
1652 {
1653 simple_monster_message(mons, " detonates feebly.",
1654 MSGCH_MONSTER_DAMAGE, MDAM_DEAD);
1655 silent = true;
1656 }
1657 }
1658 else if (mons.type == MONS_FIRE_VORTEX
1659 || mons.type == MONS_SPATIAL_VORTEX
1660 || mons.type == MONS_TWISTER
1661 || (mons.type == MONS_FOXFIRE && mons.steps_remaining == 0))
1662 {
1663 if (!silent && !mons_reset && !was_banished)
1664 {
1665 simple_monster_message(mons, " dissipates!",
1666 MSGCH_MONSTER_DAMAGE, MDAM_DEAD);
1667 silent = true;
1668 }
1669
1670 if (mons.type == MONS_FIRE_VORTEX && !wizard && !mons_reset
1671 && !submerged && !was_banished && !cell_is_solid(mons.pos()))
1672 {
1673 place_cloud(CLOUD_FIRE, mons.pos(), 2 + random2(4), &mons);
1674 }
1675
1676 if (killer == KILL_RESET)
1677 killer = KILL_DISMISSED;
1678 }
1679 else if (mons.type == MONS_FOXFIRE)
1680 {
1681 // Foxfires are unkillable, they either dissapate by timing out
1682 // or hit something.
1683 silent = true;
1684 }
1685 else if (mons.type == MONS_SIMULACRUM)
1686 {
1687 if (!silent && !mons_reset && !was_banished)
1688 {
1689 simple_monster_message(mons, " vaporises!",
1690 MSGCH_MONSTER_DAMAGE, MDAM_DEAD);
1691 silent = true;
1692 }
1693
1694 if (!wizard && !mons_reset && !submerged && !was_banished
1695 && !cell_is_solid(mons.pos()))
1696 {
1697 place_cloud(CLOUD_COLD, mons.pos(), 2 + random2(4), &mons);
1698 }
1699
1700 if (killer == KILL_RESET)
1701 killer = KILL_DISMISSED;
1702 }
1703 else if (mons.type == MONS_DANCING_WEAPON)
1704 {
1705 // TODO: does any of the following ever need to happen for other
1706 // animated objects?
1707 if (!hard_reset)
1708 {
1709 if (killer == KILL_RESET)
1710 killer = KILL_DISMISSED;
1711 }
1712
1713 int w_idx = mons.inv[MSLOT_WEAPON];
1714 ASSERT(w_idx != NON_ITEM);
1715
1716 // XXX: This can probably become mons.is_summoned(): there's no
1717 // feasible way for a dancing weapon to "drop" it's weapon and somehow
1718 // gain a summoned one, or vice versa.
1719 bool summoned_it = env.item[w_idx].flags & ISFLAG_SUMMONED;
1720
1721 if (!silent && !hard_reset && !was_banished)
1722 {
1723 // Under Gozag, permanent dancing weapons get turned to gold.
1724 if (!summoned_it
1725 && (!have_passive(passive_t::goldify_corpses)
1726 || mons.has_ench(ENCH_ABJ)))
1727 {
1728 simple_monster_message(mons, " falls from the air.",
1729 MSGCH_MONSTER_DAMAGE, MDAM_DEAD);
1730 silent = true;
1731 }
1732 else
1733 {
1734 simple_monster_message(mons, " turns to gold and falls from the air.",
1735 MSGCH_MONSTER_DAMAGE, MDAM_DEAD);
1736 killer = KILL_RESET;
1737 }
1738 }
1739
1740 if (was_banished && !summoned_it && !hard_reset
1741 && mons.has_ench(ENCH_ABJ))
1742 {
1743 if (is_unrandom_artefact(env.item[w_idx]))
1744 set_unique_item_status(env.item[w_idx], UNIQ_LOST_IN_ABYSS);
1745
1746 destroy_item(w_idx);
1747 }
1748 }
1749 else if (mons.type == MONS_ELDRITCH_TENTACLE)
1750 {
1751 if (!silent && !mons_reset && !mons.has_ench(ENCH_SEVERED)
1752 && !was_banished)
1753 {
1754 if (you.can_see(mons))
1755 {
1756 mprf(MSGCH_MONSTER_DAMAGE, MDAM_DEAD, silenced(mons.pos()) ?
1757 "The tentacle is hauled back through the portal!" :
1758 "With a roar, the tentacle is hauled back through the portal!");
1759 }
1760 silent = true;
1761 }
1762
1763 if (killer == KILL_RESET)
1764 killer = KILL_DISMISSED;
1765 }
1766 else if (mons.type == MONS_BATTLESPHERE)
1767 {
1768 if (!wizard && !mons_reset && !was_banished
1769 && !cell_is_solid(mons.pos()))
1770 {
1771 place_cloud(CLOUD_MAGIC_TRAIL, mons.pos(), 3 + random2(3), &mons);
1772 }
1773 end_battlesphere(&mons, true);
1774 }
1775 else if (mons.type == MONS_BRIAR_PATCH)
1776 {
1777 if (timeout && !silent)
1778 simple_monster_message(mons, " crumbles away.");
1779 }
1780 else if (mons.type == MONS_SPECTRAL_WEAPON)
1781 {
1782 end_spectral_weapon(&mons, true, killer == KILL_RESET);
1783 silent = true;
1784 }
1785 else if (mons.type == MONS_DROWNED_SOUL)
1786 {
1787 // Suppress death message if 'killed' by touching something
1788 if (mons.hit_points == -1000)
1789 silent = true;
1790 }
1791 else if (mons.type == MONS_SPRIGGAN_DRUID && !silent && !was_banished
1792 && !wizard && !mons_reset)
1793 {
1794 _druid_final_boon(&mons);
1795 }
1796
1797 const bool death_message = !silent && !did_death_message
1798 && you.can_see(mons);
1799 const bool exploded {mons.flags & MF_EXPLODE_KILL};
1800 bool anon = (killer_index == ANON_FRIENDLY_MONSTER);
1801 const mon_holy_type targ_holy = mons.holiness();
1802
1803 // Adjust song of slaying bonus & add heals if applicable. Kills by
1804 // relevant avatars are adjusted by now to KILL_YOU and are counted.
1805 if (you.duration[DUR_WEREBLOOD]
1806 && (killer == KILL_YOU || killer == KILL_YOU_MISSILE)
1807 && gives_player_xp)
1808 {
1809 const int wereblood_bonus = you.props[WEREBLOOD_KEY].get_int();
1810 if (wereblood_bonus <= 8) // cap at +9 slay
1811 you.props[WEREBLOOD_KEY] = wereblood_bonus + 1;
1812 if (you.hp < you.hp_max
1813 && !you.duration[DUR_DEATHS_DOOR]
1814 && !mons_is_object(mons.type)
1815 && adjacent(mons.pos(), you.pos()))
1816 {
1817 const int hp = you.hp;
1818 you.heal(random_range(1, 3));
1819 if (you.hp > hp)
1820 mpr("You feel a bit better.");
1821 }
1822 }
1823
1824 switch (killer)
1825 {
1826 case KILL_YOU: // You kill in combat.
1827 case KILL_YOU_MISSILE: // You kill by missile or beam.
1828 case KILL_YOU_CONF: // You kill by confusion.
1829 {
1830 if (death_message)
1831 {
1832 if (killer == KILL_YOU_CONF
1833 && (anon || !invalid_monster_index(killer_index)))
1834 {
1835 mprf(MSGCH_MONSTER_DAMAGE, MDAM_DEAD, "%s is %s!",
1836 mons.name(DESC_THE).c_str(),
1837 exploded ? "blown up" :
1838 wounded_damaged(targ_holy) ? "destroyed"
1839 : "killed");
1840 }
1841 else
1842 {
1843 mprf(MSGCH_MONSTER_DAMAGE, MDAM_DEAD, "You %s %s!",
1844 exploded ? "blow up" :
1845 wounded_damaged(targ_holy) ? "destroy"
1846 : "kill",
1847 mons.name(DESC_THE).c_str());
1848 }
1849 // If this monster would otherwise give xp but didn't because
1850 // it grants no reward or was neutral, give a message.
1851 if (!gives_player_xp
1852 && mons_class_gives_xp(mons.type)
1853 && !summoned
1854 && !fake_abjure
1855 && !mons.friendly())
1856 {
1857 mpr("That felt strangely unrewarding.");
1858 }
1859 }
1860
1861 // Killing triggers hints mode lesson.
1862 if (gives_player_xp)
1863 _hints_inspect_kill();
1864
1865 _fire_kill_conducts(mons, killer, killer_index, gives_player_xp);
1866
1867 // Divine and innate health and mana restoration doesn't happen when
1868 // killing born-friendly monsters.
1869 if (gives_player_xp
1870 && !mons_is_object(mons.type)
1871 && (you.species == SP_GHOUL
1872 || (have_passive(passive_t::restore_hp)
1873 || have_passive(passive_t::mp_on_kill)
1874 || have_passive(passive_t::restore_hp_mp_vs_evil)
1875 && mons.evil())
1876 && !player_under_penance()
1877 && (random2(you.piety) >= piety_breakpoint(0))
1878 #if TAG_MAJOR_VERSION == 34
1879 || you_worship(GOD_PAKELLAS)
1880 #endif
1881 )
1882 )
1883 {
1884 int hp_heal = 0, mp_heal = 0;
1885
1886 if (have_passive(passive_t::restore_hp))
1887 {
1888 hp_heal = (1 + mons.get_experience_level()) / 2
1889 + random2(mons.get_experience_level() / 2);
1890 }
1891 if (you.species == SP_GHOUL
1892 && mons.holiness() & MH_NATURAL
1893 && coinflip())
1894 {
1895 hp_heal += 1 + random2avg(1 + you.experience_level, 3);
1896 }
1897 if (have_passive(passive_t::restore_hp_mp_vs_evil))
1898 {
1899 hp_heal = random2(1 + 2 * mons.get_experience_level());
1900 mp_heal = random2(2 + mons.get_experience_level() / 3);
1901 }
1902
1903 if (have_passive(passive_t::mp_on_kill))
1904 {
1905 mp_heal = 1 + random2(mons.get_experience_level() / 2);
1906 #if TAG_MAJOR_VERSION == 34
1907 if (you.religion == GOD_PAKELLAS)
1908 mp_heal = random2(2 + mons.get_experience_level() / 6);
1909 #endif
1910 }
1911
1912 if (hp_heal && you.hp < you.hp_max
1913 && !you.duration[DUR_DEATHS_DOOR])
1914 {
1915 canned_msg(MSG_GAIN_HEALTH);
1916 inc_hp(hp_heal);
1917 }
1918
1919 if (mp_heal && you.magic_points < you.max_magic_points)
1920 {
1921 int tmp = min(you.max_magic_points - you.magic_points,
1922 mp_heal);
1923 canned_msg(MSG_GAIN_MAGIC);
1924 inc_mp(mp_heal);
1925 mp_heal -= tmp;
1926 }
1927
1928 #if TAG_MAJOR_VERSION == 34
1929 // perhaps this should go to its own function
1930 if (mp_heal
1931 && have_passive(passive_t::bottle_mp)
1932 && you.can_drink(false))
1933 {
1934 simple_god_message(" collects the excess magic power.");
1935 you.attribute[ATTR_PAKELLAS_EXTRA_MP] -= mp_heal;
1936
1937 if (you.attribute[ATTR_PAKELLAS_EXTRA_MP] <= 0
1938 && (feat_has_solid_floor(env.grid(you.pos()))
1939 || feat_is_watery(env.grid(you.pos()))
1940 && species::likes_water(you.species)))
1941 {
1942 int thing_created = items(true, OBJ_POTIONS,
1943 POT_MAGIC, 1, 0,
1944 GOD_PAKELLAS);
1945 if (thing_created != NON_ITEM)
1946 {
1947 move_item_to_grid(&thing_created, you.pos(), true);
1948 env.item[thing_created].quantity = 1;
1949 env.item[thing_created].flags |= ISFLAG_KNOW_TYPE;
1950 // not a conventional gift, but use the same
1951 // messaging
1952 simple_god_message(" grants you a gift!");
1953 you.attribute[ATTR_PAKELLAS_EXTRA_MP]
1954 += POT_MAGIC_MP;
1955 }
1956 }
1957 }
1958 #endif
1959 }
1960
1961 if (gives_player_xp && you_worship(GOD_RU) && you.piety < 200
1962 && one_chance_in(2))
1963 {
1964 ASSERT(you.props.exists(RU_SACRIFICE_PROGRESS_KEY));
1965 int current_progress =
1966 you.props[RU_SACRIFICE_PROGRESS_KEY].get_int();
1967 you.props[RU_SACRIFICE_PROGRESS_KEY] = current_progress + 1;
1968 }
1969
1970 // Randomly bless a follower.
1971 if (gives_player_xp
1972 && !mons_is_object(mons.type)
1973 && _god_will_bless_follower(&mons))
1974 {
1975 bless_follower();
1976 }
1977 break;
1978 }
1979
1980 case KILL_MON: // Monster kills in combat.
1981 case KILL_MON_MISSILE: // Monster kills by missile or beam.
1982 {
1983 if (death_message)
1984 {
1985 const char* msg =
1986 exploded ? " is blown up!" :
1987 wounded_damaged(targ_holy) ? " is destroyed!"
1988 : " dies!";
1989 simple_monster_message(mons, msg, MSGCH_MONSTER_DAMAGE,
1990 MDAM_DEAD);
1991 }
1992
1993 if (crawl_state.game_is_arena())
1994 break;
1995
1996 _fire_kill_conducts(mons, killer, killer_index, gives_player_xp);
1997
1998 // Trying to prevent summoning abuse here, so we're trying to
1999 // prevent summoned creatures from being done_good kills. Only
2000 // affects creatures which were friendly when summoned.
2001 if (!gives_player_xp
2002 || !pet_kill
2003 || !anon && invalid_monster_index(killer_index))
2004 {
2005 break;
2006 }
2007
2008 monster* killer_mon = nullptr;
2009 if (!anon)
2010 killer_mon = &env.mons[killer_index];
2011
2012 if (!invalid_monster_index(killer_index)
2013 && _god_will_bless_follower(&mons))
2014 {
2015 // Randomly bless the follower who killed.
2016 bless_follower(killer_mon);
2017 }
2018 break;
2019 }
2020
2021 // Monster killed by trap/inanimate thing/itself/poison not from you.
2022 case KILL_MISC:
2023 case KILL_MISCAST:
2024 if (death_message)
2025 {
2026 if (fake_abjure)
2027 {
2028 // Sticks to Snakes
2029 if (mons_genus(mons.type) == MONS_SNAKE)
2030 simple_monster_message(mons, " withers and dies!");
2031 // ratskin cloak
2032 else if (mons_genus(mons.type) == MONS_RAT)
2033 {
2034 simple_monster_message(mons, " returns to the shadows"
2035 " of the Dungeon!");
2036 }
2037 // Death Channel
2038 else if (mons.type == MONS_SPECTRAL_THING)
2039 simple_monster_message(mons, " fades into mist!");
2040 // Animate Skeleton/Animate Dead/Infestation
2041 else if (mons.type == MONS_ZOMBIE
2042 || mons.type == MONS_SKELETON
2043 || mons.type == MONS_DEATH_SCARAB)
2044 {
2045 simple_monster_message(mons, " crumbles into dust!");
2046 }
2047 else
2048 {
2049 string msg = " " + summoned_poof_msg(&mons) + "!";
2050 simple_monster_message(mons, msg.c_str());
2051 }
2052 }
2053 else
2054 {
2055 const char* msg =
2056 exploded ? " is blown up!" :
2057 wounded_damaged(targ_holy) ? " is destroyed!"
2058 : " dies!";
2059 simple_monster_message(mons, msg, MSGCH_MONSTER_DAMAGE,
2060 MDAM_DEAD);
2061 }
2062 }
2063 break;
2064
2065 case KILL_BANISHED:
2066 // Monster doesn't die, just goes back to wherever it came from.
2067 // This must only be called by monsters running out of time (or
2068 // abjuration), because it uses the beam variables! Or does it???
2069 // Pacified monsters leave the level when this happens.
2070
2071 // Monster goes to the Abyss.
2072 mons.flags |= MF_BANISHED;
2073 // KILL_RESET monsters no longer lose their whole inventory, only
2074 // items they were generated with.
2075 if (mons.pacified() || !mons.needs_abyss_transit())
2076 {
2077 // A banished monster that doesn't go on the transit list
2078 // loses all items.
2079 if (!mons.is_summoned())
2080 drop_items = false;
2081 break;
2082 }
2083
2084 {
2085 unwind_var<int> dt(mons.damage_total, 0);
2086 unwind_var<int> df(mons.damage_friendly, 0);
2087 mons.set_transit(level_id(BRANCH_ABYSS));
2088 }
2089 set_unique_annotation(&mons, BRANCH_ABYSS);
2090 in_transit = true;
2091 drop_items = false;
2092 mons.firing_pos.reset();
2093 // Make monster stop patrolling and/or travelling.
2094 mons.patrol_point.reset();
2095 mons.travel_path.clear();
2096 mons.travel_target = MTRAV_NONE;
2097 break;
2098
2099 case KILL_RESET:
2100 drop_items = false;
2101 break;
2102
2103 case KILL_TIMEOUT:
2104 case KILL_DISMISSED:
2105 break;
2106
2107 default:
2108 drop_items = false;
2109 break;
2110 }
2111
2112 // Make sure Boris has a foe to address.
2113 if (mons.foe == MHITNOT)
2114 {
2115 if (!mons.wont_attack() && !crawl_state.game_is_arena())
2116 mons.foe = MHITYOU;
2117 else if (!invalid_monster_index(killer_index))
2118 mons.foe = killer_index;
2119 }
2120
2121 // Make sure that the monster looks dead.
2122 if (mons.alive() && (!summoned || duration > 0))
2123 {
2124 dprf("Non-damage %s of %s.", mons_reset ? "reset" : "kill",
2125 mons.name(DESC_A, true).c_str());
2126 if (YOU_KILL(killer))
2127 mons.damage_friendly += mons.hit_points * 2;
2128 else if (pet_kill)
2129 mons.damage_friendly += mons.hit_points;
2130 mons.damage_total += mons.hit_points;
2131
2132 if (!in_transit) // banishment only
2133 mons.hit_points = -1;
2134 }
2135
2136 if (!silent && !wizard && you.see_cell(mons.pos()))
2137 {
2138 // Make sure that the monster looks dead.
2139 if (mons.alive() && !in_transit && (!summoned || duration > 0))
2140 mons.hit_points = -1;
2141 // Hack: with cleanup_dead=false, a tentacle [segment] of a dead
2142 // [malign] kraken has no valid head reference.
2143 if (!mons_is_tentacle_or_tentacle_segment(mons.type))
2144 {
2145 // Make sure Natasha gets her say even if she got polymorphed.
2146 const monster_type orig =
2147 mons_is_mons_class(&mons, MONS_NATASHA) ? MONS_NATASHA
2148 : mons.type;
2149 unwind_var<monster_type> mt(mons.type, orig);
2150 mons_speaks(&mons);
2151 }
2152 }
2153
2154 // None of these effects should trigger on illusory copies.
2155 if (!mons.is_illusion())
2156 {
2157 if (mons.type == MONS_BORIS && !in_transit && !mons.pacified())
2158 {
2159 // XXX: Actual blood curse effect for Boris? - bwr
2160
2161 // Now that Boris is dead, he can be replaced when new levels
2162 // are generated.
2163 you.unique_creatures.set(mons.type, false);
2164 you.props["killed_boris_once"] = true;
2165 }
2166 if (mons.type == MONS_JORY && !in_transit)
2167 blood_spray(mons.pos(), MONS_JORY, 50);
2168 else if (mons_is_mons_class(&mons, MONS_KIRKE)
2169 && !in_transit
2170 && !testbits(mons.flags, MF_WAS_NEUTRAL))
2171 {
2172 hogs_to_humans();
2173 }
2174 else if ((mons_is_mons_class(&mons, MONS_NATASHA))
2175 && !in_transit && !mons.pacified()
2176 && mons_felid_can_revive(&mons))
2177 {
2178 drop_items = false;
2179
2180 // Like Boris, but regenerates immediately
2181 if (mons_is_mons_class(&mons, MONS_NATASHA))
2182 you.unique_creatures.set(MONS_NATASHA, false);
2183 if (!mons_reset && !wizard)
2184 mons_felid_revive(&mons);
2185 }
2186 else if (mons_is_mons_class(&mons, MONS_PIKEL))
2187 {
2188 // His slaves don't care if he's dead or not, just whether or not
2189 // he goes away.
2190 pikel_band_neutralise();
2191 }
2192 else if (mons_is_elven_twin(&mons))
2193 elven_twin_died(&mons, in_transit, killer, killer_index);
2194 else if (mons.type == MONS_BENNU && !in_transit && !was_banished
2195 && !mons_reset && !mons.pacified()
2196 && (!summoned || duration > 0) && !wizard
2197 && mons_bennu_can_revive(&mons))
2198 {
2199 // All this information may be lost by the time the monster revives.
2200 const int revives = (mons.props.exists("bennu_revives"))
2201 ? mons.props["bennu_revives"].get_byte() : 0;
2202 const beh_type att = mons.has_ench(ENCH_CHARM)
2203 ? BEH_HOSTILE : SAME_ATTITUDE(&mons);
2204
2205 bennu_revive_fineff::schedule(mons.pos(), revives, att, mons.foe);
2206 }
2207 }
2208
2209 if (mons_is_tentacle_head(mons_base_type(mons)))
2210 {
2211 if (destroy_tentacles(&mons)
2212 && !in_transit
2213 && you.see_cell(mons.pos())
2214 && !was_banished)
2215 {
2216 if (mons_base_type(mons) == MONS_KRAKEN)
2217 mpr("The dead kraken's tentacles slide back into the water.");
2218 else if (mons.type == MONS_TENTACLED_STARSPAWN)
2219 mpr("The starspawn's tentacles wither and die.");
2220 }
2221 }
2222 else if (mons_is_tentacle_or_tentacle_segment(mons.type)
2223 && killer != KILL_MISC
2224 || mons.type == MONS_ELDRITCH_TENTACLE
2225 || mons.type == MONS_SNAPLASHER_VINE)
2226 {
2227 if (mons.type == MONS_SNAPLASHER_VINE)
2228 {
2229 if (mons.props.exists("vine_awakener"))
2230 {
2231 monster* awakener =
2232 monster_by_mid(mons.props["vine_awakener"].get_int());
2233 if (awakener)
2234 awakener->props["vines_awakened"].get_int()--;
2235 }
2236 }
2237 destroy_tentacle(&mons);
2238 }
2239 else if (mons.type == MONS_ELDRITCH_TENTACLE_SEGMENT
2240 && killer != KILL_MISC)
2241 {
2242 monster_die(*monster_by_mid(mons.tentacle_connect), killer,
2243 killer_index, silent, wizard, fake);
2244 }
2245 else if (mons.type == MONS_FLAYED_GHOST)
2246 end_flayed_effect(&mons);
2247 // Give the treant a last chance to release its hornets if it is killed in a
2248 // single blow from above half health
2249 else if (mons.type == MONS_SHAMBLING_MANGROVE && !was_banished
2250 && !mons.pacified() && (!summoned || duration > 0) && !wizard
2251 && !mons_reset)
2252 {
2253 treant_release_fauna(mons);
2254 }
2255 else if (!mons.is_summoned() && mummy_curse_power(mons.type) > 0)
2256 {
2257 // TODO: set attacker better? (Player attacker is handled by checking
2258 // killer when running the fineff.)
2259 mummy_death_curse_fineff::schedule(
2260 invalid_monster_index(killer_index)
2261 ? nullptr : &env.mons[killer_index],
2262 mons.name(DESC_A),
2263 killer,
2264 mummy_curse_power(mons.type));
2265 }
2266
2267 // Necromancy
2268 if (!was_banished && !mons_reset)
2269 {
2270 if (mons.has_ench(ENCH_INFESTATION))
2271 _infestation_create_scarab(&mons);
2272 if (you.duration[DUR_DEATH_CHANNEL] && was_visible && gives_player_xp)
2273 _make_derived_undead(&mons, !death_message, false);
2274 }
2275
2276 if (!wizard && !submerged && !was_banished)
2277 {
2278 _monster_die_cloud(&mons, !fake_abjure && !timeout && !mons_reset,
2279 silent, summoned);
2280 }
2281
2282 item_def* corpse = nullptr;
2283 if (leaves_corpse && !was_banished && !spectralised)
2284 {
2285 // Have to add case for disintegration effect here? {dlb}
2286 item_def* daddy_corpse = nullptr;
2287
2288 if (mons.type == MONS_SPRIGGAN_RIDER)
2289 {
2290 daddy_corpse = mounted_kill(&mons, MONS_HORNET, killer, killer_index);
2291 mons.type = MONS_SPRIGGAN;
2292 }
2293 corpse = place_monster_corpse(mons);
2294 if (!corpse)
2295 corpse = daddy_corpse;
2296 }
2297 if (mons.has_ench(ENCH_BOUND_SOUL))
2298 _make_derived_undead(&mons, !death_message, true);
2299
2300 const unsigned int player_xp = gives_player_xp
2301 ? _calc_player_experience(&mons) : 0;
2302 const unsigned int monster_xp = _calc_monster_experience(&mons, killer,
2303 killer_index);
2304
2305 // Player Powered by Death
2306 if (gives_player_xp && you.get_mutation_level(MUT_POWERED_BY_DEATH)
2307 && (killer == KILL_YOU
2308 || killer == KILL_YOU_MISSILE
2309 || killer == KILL_YOU_CONF
2310 || pet_kill))
2311 {
2312 // Enable the status
2313 reset_powered_by_death_duration();
2314
2315 // Maybe increase strength. The chance decreases with number
2316 // of existing stacks.
2317 const int pbd_level = you.get_mutation_level(MUT_POWERED_BY_DEATH);
2318 const int pbd_str = you.props[POWERED_BY_DEATH_KEY].get_int();
2319 if (x_chance_in_y(10 - pbd_str, 10))
2320 {
2321 const int pbd_inc = random2(1 + pbd_level);
2322 you.props[POWERED_BY_DEATH_KEY] = pbd_str + pbd_inc;
2323 dprf("Powered by Death strength +%d=%d", pbd_inc,
2324 pbd_str + pbd_inc);
2325 }
2326 }
2327
2328 if (!crawl_state.game_is_arena() && leaves_corpse && !in_transit)
2329 you.kills.record_kill(&mons, killer, pet_kill);
2330
2331 if (fake)
2332 {
2333 if (corpse && _reaping(mons))
2334 corpse = nullptr;
2335 _give_experience(player_xp, monster_xp, killer, killer_index,
2336 pet_kill, was_visible, mons.xp_tracking);
2337 crawl_state.dec_mon_acting(&mons);
2338
2339 return corpse;
2340 }
2341
2342 mons_remove_from_grid(mons);
2343 fire_monster_death_event(&mons, killer, false);
2344
2345 if (crawl_state.game_is_arena())
2346 arena_monster_died(&mons, killer, killer_index, silent, corpse);
2347
2348 const coord_def mwhere = mons.pos();
2349 if (drop_items)
2350 {
2351 // monster_drop_things may lead to a level excursion (via
2352 // god_id_item -> ... -> ShoppingList::item_type_identified),
2353 // which fails to save/restore the dead monster. Keep it alive
2354 // since we still need it.
2355 unwind_var<int> fakehp(mons.hit_points, 1);
2356 monster_drop_things(&mons, YOU_KILL(killer) || pet_kill);
2357 }
2358 else
2359 {
2360 // Destroy the items belonging to MF_HARD_RESET monsters so they
2361 // don't clutter up env.item[].
2362 mons.destroy_inventory();
2363 }
2364
2365 if (leaves_corpse && corpse)
2366 {
2367 if (!silent && !wizard)
2368 _special_corpse_messaging(mons);
2369 // message ordering... :(
2370 if (corpse->base_type == OBJ_CORPSES) // not gold
2371 _maybe_drop_monster_hide(*corpse, silent);
2372 }
2373
2374 if (mons.is_divine_companion()
2375 && killer != KILL_RESET
2376 && !(mons.flags & MF_BANISHED))
2377 {
2378 remove_companion(&mons);
2379 if (mons_is_hepliaklqana_ancestor(mons.type))
2380 {
2381 if (!you.can_see(mons))
2382 {
2383 mprf("%s has departed this plane of existence.",
2384 hepliaklqana_ally_name().c_str());
2385 }
2386
2387 // respawn in ~30-60 turns, if there wasn't another ancestor through
2388 // some strange circumstance (wizmode? bug?)
2389 if (hepliaklqana_ancestor() == MID_NOBODY)
2390 you.duration[DUR_ANCESTOR_DELAY] = random_range(300, 600);
2391 }
2392 }
2393
2394 // If we kill an invisible monster reactivate autopickup.
2395 // We need to check for actual invisibility rather than whether we
2396 // can see the monster. There are several edge cases where a monster
2397 // is visible to the player but we still need to turn autopickup
2398 // back on, such as TSO's halo or sticky flame. (jpeg)
2399 if (you.see_cell(mons.pos()) && mons.has_ench(ENCH_INVIS)
2400 && !mons.friendly())
2401 {
2402 autotoggle_autopickup(false);
2403 }
2404
2405 if (corpse && _reaping(mons))
2406 corpse = nullptr;
2407
2408 crawl_state.dec_mon_acting(&mons);
2409 monster_cleanup(&mons);
2410
2411 // Force redraw for monsters that die.
2412 if (in_bounds(mwhere) && you.see_cell(mwhere))
2413 {
2414 view_update_at(mwhere);
2415 update_screen();
2416 }
2417
2418 if (!mons_reset)
2419 {
2420 _give_experience(player_xp, monster_xp, killer, killer_index,
2421 pet_kill, was_visible, mons.xp_tracking);
2422 }
2423 return corpse;
2424 }
2425
unawaken_vines(const monster * mons,bool quiet)2426 void unawaken_vines(const monster* mons, bool quiet)
2427 {
2428 int vines_seen = 0;
2429 for (monster_iterator mi; mi; ++mi)
2430 {
2431 if (mi->type == MONS_SNAPLASHER_VINE
2432 && mi->props.exists("vine_awakener")
2433 && monster_by_mid(mi->props["vine_awakener"].get_int()) == mons)
2434 {
2435 if (you.can_see(**mi))
2436 ++vines_seen;
2437 monster_die(**mi, KILL_RESET, NON_MONSTER);
2438 }
2439 }
2440
2441 if (!quiet && vines_seen)
2442 {
2443 mprf("The vine%s fall%s limply to the ground.",
2444 (vines_seen > 1 ? "s" : ""), (vines_seen == 1 ? "s" : ""));
2445 }
2446 }
2447
heal_flayed_effect(actor * act,bool quiet,bool blood_only)2448 void heal_flayed_effect(actor* act, bool quiet, bool blood_only)
2449 {
2450 ASSERT(act); // XXX: change to actor &act
2451 if (!blood_only)
2452 {
2453 if (act->is_player())
2454 you.duration[DUR_FLAYED] = 0;
2455 else
2456 act->as_monster()->del_ench(ENCH_FLAYED, true, false);
2457
2458 if (you.can_see(*act) && !quiet)
2459 {
2460 mprf("The terrible wounds on %s body vanish.",
2461 act->name(DESC_ITS).c_str());
2462 }
2463
2464 act->heal(act->props["flay_damage"].get_int());
2465 act->props.erase("flay_damage");
2466 }
2467
2468 for (const CrawlStoreValue& store : act->props["flay_blood"].get_vector())
2469 env.pgrid(store.get_coord()) &= ~FPROP_BLOODY;
2470 act->props.erase("flay_blood");
2471 }
2472
end_flayed_effect(monster * ghost)2473 void end_flayed_effect(monster* ghost)
2474 {
2475 if (you.duration[DUR_FLAYED] && !ghost->wont_attack())
2476 heal_flayed_effect(&you);
2477
2478 for (monster_iterator mi; mi; ++mi)
2479 {
2480 if (mi->has_ench(ENCH_FLAYED) && !mons_aligned(ghost, *mi))
2481 heal_flayed_effect(*mi);
2482 }
2483 }
2484
2485 // Clean up after a dead monster.
monster_cleanup(monster * mons)2486 void monster_cleanup(monster* mons)
2487 {
2488 crawl_state.mon_gone(mons);
2489
2490 if (mons->has_ench(ENCH_AWAKEN_FOREST))
2491 {
2492 forest_message(mons->pos(), "The forest abruptly stops moving.");
2493 env.forest_awoken_until = 0;
2494 }
2495
2496 if (mons->has_ench(ENCH_AWAKEN_VINES))
2497 unawaken_vines(mons, false);
2498
2499 // Monsters haloes should be removed when they die.
2500 if (mons->halo_radius()
2501 || mons->umbra_radius()
2502 || mons->silence_radius())
2503 {
2504 invalidate_agrid();
2505 }
2506
2507 // May have been constricting something. No message because that depends
2508 // on the order in which things are cleaned up: If the constrictee is
2509 // cleaned up first, we wouldn't get a message anyway.
2510 mons->stop_constricting_all(false, true);
2511
2512 mons->clear_far_engulf();
2513
2514 if (mons_is_tentacle_head(mons_base_type(*mons)))
2515 destroy_tentacles(mons);
2516
2517 const mid_t mid = mons->mid;
2518 env.mid_cache.erase(mid);
2519
2520 mons->remove_summons();
2521
2522 unsigned int monster_killed = mons->mindex();
2523 for (monster_iterator mi; mi; ++mi)
2524 {
2525 if (mi->foe == monster_killed)
2526 mi->foe = MHITNOT;
2527 }
2528
2529 if (you.pet_target == monster_killed)
2530 you.pet_target = MHITNOT;
2531
2532 mons->reset();
2533 }
2534
mounted_kill(monster * daddy,monster_type mc,killer_type killer,int killer_index)2535 item_def* mounted_kill(monster* daddy, monster_type mc, killer_type killer,
2536 int killer_index)
2537 {
2538 monster mon;
2539 mon.type = mc;
2540 mon.moveto(daddy->pos());
2541 define_monster(mon); // assumes mc is not a zombie
2542 mon.flags = daddy->flags;
2543
2544 // Need to copy ENCH_ABJ etc. or we could get real XP/meat from a summon.
2545 mon.enchantments = daddy->enchantments;
2546 mon.ench_cache = daddy->ench_cache;
2547
2548 mon.attitude = daddy->attitude;
2549 mon.damage_friendly = daddy->damage_friendly;
2550 mon.damage_total = daddy->damage_total;
2551 // Keep the rider's name, if it had one (Mercenary card).
2552 if (!daddy->mname.empty() && mon.type == MONS_SPRIGGAN)
2553 mon.mname = daddy->mname;
2554 if (daddy->props.exists("reaping_damage"))
2555 {
2556 dprf("Mounted kill: marking the other monster as reaped as well.");
2557 mon.props["reaping_damage"].get_int() = daddy->props["reaping_damage"].get_int();
2558 mon.props["reaper"].get_int() = daddy->props["reaper"].get_int();
2559 }
2560
2561 return monster_die(mon, killer, killer_index, false, false, true);
2562 }
2563
2564 /**
2565 * Applies harmful environmental effects from the current tile to monsters.
2566 *
2567 * @param mons The monster to maybe drown/incinerate.
2568 * @param oldpos Their previous tile, before landing up here.
2569 * @param killer Who's responsible for killing them, if they die here.
2570 * @param killnum The mindex of the killer, if any.
2571 */
mons_check_pool(monster * mons,const coord_def & oldpos,killer_type killer,int killnum)2572 void mons_check_pool(monster* mons, const coord_def &oldpos,
2573 killer_type killer, int killnum)
2574 {
2575 // Flying monsters don't make contact with the terrain.
2576 if (!mons->ground_level())
2577 return;
2578
2579 dungeon_feature_type grid = env.grid(mons->pos());
2580 if (grid != DNGN_LAVA && grid != DNGN_DEEP_WATER
2581 || monster_habitable_grid(mons, grid))
2582 {
2583 return;
2584 }
2585
2586
2587 // Don't worry about invisibility. You should be able to see if
2588 // something has fallen into the lava.
2589 if (you.see_cell(mons->pos()) && (oldpos == mons->pos() || env.grid(oldpos) != grid))
2590 {
2591 mprf("%s falls into the %s!",
2592 mons->name(DESC_THE).c_str(),
2593 grid == DNGN_LAVA ? "lava" : "water");
2594 }
2595
2596 // Even fire resistant monsters perish in lava.
2597 if (grid == DNGN_LAVA && mons->res_fire() < 2)
2598 {
2599 simple_monster_message(*mons, " is incinerated.",
2600 MSGCH_MONSTER_DAMAGE, MDAM_DEAD);
2601 }
2602 else if (mons->can_drown())
2603 {
2604 simple_monster_message(*mons, " drowns.",
2605 MSGCH_MONSTER_DAMAGE, MDAM_DEAD);
2606 }
2607 else
2608 {
2609 simple_monster_message(*mons, " falls apart.",
2610 MSGCH_MONSTER_DAMAGE, MDAM_DEAD);
2611 }
2612
2613 if (killer == KILL_NONE)
2614 {
2615 // Self-kill.
2616 killer = KILL_MON;
2617 killnum = mons->mindex();
2618 }
2619
2620 // Yredelemnul special, redux: It's the only one that can
2621 // work on drowned monsters.
2622 if (!_yred_enslave_soul(mons, killer))
2623 monster_die(*mons, killer, killnum, true);
2624 }
2625
2626 // Make all of the monster's original equipment disappear, unless it's a fixed
2627 // artefact or unrand artefact.
_vanish_orig_eq(monster * mons)2628 static void _vanish_orig_eq(monster* mons)
2629 {
2630 for (mon_inv_iterator ii(*mons); ii; ++ii)
2631 {
2632 if (origin_known(*ii) || ii->orig_monnum != 0
2633 || !ii->inscription.empty()
2634 || is_unrandom_artefact(*ii)
2635 || (ii->flags & (ISFLAG_DROPPED | ISFLAG_THROWN
2636 | ISFLAG_NOTED_GET)))
2637 {
2638 continue;
2639 }
2640 ii->flags |= ISFLAG_SUMMONED;
2641 }
2642 }
2643
dismiss_monsters(string pattern)2644 int dismiss_monsters(string pattern)
2645 {
2646 // Make all of the monsters' original equipment disappear unless "keepitem"
2647 // is found in the regex (except for fixed arts and unrand arts).
2648 const bool keep_item = strip_tag(pattern, "keepitem");
2649 const bool harmful = pattern == "harmful";
2650 const bool mobile = pattern == "mobile";
2651 const bool los = pattern == "los";
2652
2653 // Dismiss by regex.
2654 text_pattern tpat(pattern);
2655 int ndismissed = 0;
2656 for (monster_iterator mi; mi; ++mi)
2657 {
2658 if (mi->alive()
2659 && (mobile ? !mons_class_is_stationary(mi->type) :
2660 harmful ? mons_is_threatening(**mi) && !mi->wont_attack() :
2661 los ? you.see_cell(mi->pos())
2662 : tpat.empty() || tpat.matches(mi->name(DESC_PLAIN, true))))
2663 {
2664 if (!keep_item)
2665 _vanish_orig_eq(*mi);
2666 monster_die(**mi, KILL_DISMISSED, NON_MONSTER, false, true);
2667 ++ndismissed;
2668 }
2669 }
2670
2671 return ndismissed;
2672 }
2673
summoned_poof_msg(const monster * mons,bool plural)2674 string summoned_poof_msg(const monster* mons, bool plural)
2675 {
2676 int summon_type = 0;
2677 bool valid_mon = false;
2678 if (mons != nullptr && !invalid_monster(mons))
2679 {
2680 (void) mons->is_summoned(nullptr, &summon_type);
2681 valid_mon = true;
2682 }
2683
2684 string msg = "disappear%s in a puff of smoke";
2685 bool no_chaos = false;
2686
2687 switch (summon_type)
2688 {
2689 case SPELL_SHADOW_CREATURES:
2690 case MON_SUMM_SCROLL:
2691 msg = "dissolve%s into shadows";
2692 no_chaos = true;
2693 break;
2694
2695 case MON_SUMM_CHAOS:
2696 msg = "degenerate%s into a cloud of primal chaos";
2697 break;
2698
2699 case MON_SUMM_WRATH:
2700 case MON_SUMM_AID:
2701 if (valid_mon && is_good_god(mons->god))
2702 {
2703 msg = "dissolve%s into sparkling lights";
2704 no_chaos = true;
2705 }
2706 break;
2707
2708 case SPELL_SPECTRAL_CLOUD:
2709 case SPELL_CALL_LOST_SOUL:
2710 msg = "fade%s away";
2711 break;
2712 }
2713
2714 if (valid_mon)
2715 {
2716 if (mons->god == GOD_XOM && !no_chaos && one_chance_in(10)
2717 || mons->type == MONS_CHAOS_SPAWN)
2718 {
2719 msg = "degenerate%s into a cloud of primal chaos";
2720 }
2721
2722 if (mons->is_holy()
2723 && summon_type != SPELL_SHADOW_CREATURES
2724 && summon_type != MON_SUMM_CHAOS)
2725 {
2726 msg = "dissolve%s into sparkling lights";
2727 }
2728
2729 if (mons_is_slime(*mons)
2730 && mons->god == GOD_JIYVA)
2731 {
2732 msg = "dissolve%s into a puddle of slime";
2733 }
2734
2735 if (mons->type == MONS_DROWNED_SOUL)
2736 msg = "return%s to the deep";
2737
2738 if (mons->has_ench(ENCH_PHANTOM_MIRROR))
2739 msg = "shimmer%s and vanish" + string(plural ? "" : "es"); // Ugh
2740 }
2741
2742 // Conjugate.
2743 msg = make_stringf(msg.c_str(), plural ? "" : "s");
2744
2745 return msg;
2746 }
2747
summoned_poof_msg(const monster * mons,const item_def & item)2748 string summoned_poof_msg(const monster* mons, const item_def &item)
2749 {
2750 ASSERT(item.flags & ISFLAG_SUMMONED);
2751
2752 return summoned_poof_msg(mons, item.quantity > 1);
2753 }
2754
2755 /**
2756 * Determine if a specified monster is or was a specified monster type.
2757 *
2758 * Checks both the monster type and the ORIGINAL_TYPE_KEY prop, thus allowing
2759 * the type to be transferred through polymorph.
2760 *
2761 * @param mons The monster to be checked.
2762 * @param type The type it might be.
2763 * @return True if the monster was or is the type, otherwise false.
2764 **/
mons_is_mons_class(const monster * mons,monster_type type)2765 bool mons_is_mons_class(const monster* mons, monster_type type)
2766 {
2767 return mons->type == type
2768 || mons->props.exists(ORIGINAL_TYPE_KEY)
2769 && mons->props[ORIGINAL_TYPE_KEY].get_int() == type;
2770 }
2771
2772 /**
2773 * Perform neutralisation for members of Pikel's band upon Pikel's 'death'.
2774 *
2775 * This neutralisation occurs in multiple instances: when Pikel is neutralised,
2776 * enslaved, when Pikel dies, when Pikel is banished.
2777 * It is handled by a daction (as a fineff) to preserve across levels.
2778 **/
pikel_band_neutralise()2779 void pikel_band_neutralise()
2780 {
2781 int visible_minions = 0;
2782 for (monster_iterator mi; mi; ++mi)
2783 {
2784 if (mi->type == MONS_LEMURE
2785 && testbits(mi->flags, MF_BAND_MEMBER)
2786 && mi->props.exists("pikel_band")
2787 && mi->observable())
2788 {
2789 visible_minions++;
2790 }
2791 }
2792 string final_msg;
2793 if (visible_minions > 0 && you.num_turns > 0)
2794 {
2795 if (you.get_mutation_level(MUT_NO_LOVE))
2796 {
2797 const char *substr = visible_minions > 1 ? "minions" : "minion";
2798 final_msg = make_stringf("Pikel's spell is broken, but his former "
2799 "%s can only feel hate for you!", substr);
2800 }
2801 else
2802 {
2803 const char *substr = visible_minions > 1
2804 ? "minions thank you for their"
2805 : "minion thanks you for its";
2806 final_msg = make_stringf("With Pikel's spell broken, his former %s "
2807 "freedom.", substr);
2808 }
2809 }
2810 delayed_action_fineff::schedule(DACT_PIKEL_MINIONS, final_msg);
2811 }
2812
2813 /**
2814 * Revert porkalated hogs.
2815 *
2816 * Called upon Kirke's death. Hogs either from Kirke's band or
2817 * subsequently porkalated should be reverted to their original form.
2818 * This takes place as a daction to preserve behaviour across levels;
2819 * this function simply checks if any are visible and raises a fineff
2820 * containing an appropriate message. The fineff raises the actual
2821 * daction.
2822 */
hogs_to_humans()2823 void hogs_to_humans()
2824 {
2825 int any = 0, human = 0;
2826
2827 for (monster_iterator mi; mi; ++mi)
2828 {
2829 if (mons_genus(mi->type) != MONS_HOG)
2830 continue;
2831
2832 if (!mi->props.exists("kirke_band")
2833 && !mi->props.exists(ORIG_MONSTER_KEY))
2834 {
2835 continue;
2836 }
2837
2838 // Shapeshifters will stop being a hog when they feel like it.
2839 if (mi->is_shapeshifter())
2840 continue;
2841
2842 const bool could_see = you.can_see(**mi);
2843
2844 if (could_see) any++;
2845
2846 if (!mi->props.exists(ORIG_MONSTER_KEY) && could_see)
2847 human++;
2848 }
2849
2850 string final_msg;
2851 if (any > 0 && you.num_turns > 0)
2852 {
2853 final_msg = make_stringf("No longer under Kirke's spell, the %s %s %s!",
2854 any > 1 ? "hogs return to their"
2855 : "hog returns to its",
2856 any == human ? "human" : "original",
2857 any > 1 ? "forms" : "form");
2858 }
2859 kirke_death_fineff::schedule(final_msg);
2860 }
2861
2862 /**
2863 * Determine if a monster is either Dowan or Duvessa.
2864 *
2865 * Tracks through type and ORIGINAL_TYPE_KEY, thus tracking through polymorph.
2866 * Used to determine if a death function should be called for the monster
2867 * in question.
2868 *
2869 * @param mons The monster to check.
2870 * @return True if either Dowan or Duvessa, otherwise false.
2871 **/
mons_is_elven_twin(const monster * mons)2872 bool mons_is_elven_twin(const monster* mons)
2873 {
2874 return mons_is_mons_class(mons, MONS_DOWAN)
2875 || mons_is_mons_class(mons, MONS_DUVESSA);
2876 }
2877
2878 /**
2879 * Find Duvessa or Dowan, given the other.
2880 *
2881 * @param mons The monster whose twin we seek.
2882 * @return A pointer to the other elven twin, or nullptr if the twin
2883 * no longer exists, or if given a mons that is not an elven twin.
2884 **/
mons_find_elven_twin_of(const monster * mons)2885 monster* mons_find_elven_twin_of(const monster* mons)
2886 {
2887 if (!mons_is_elven_twin(mons))
2888 return nullptr;
2889
2890 for (monster_iterator mi; mi; ++mi)
2891 {
2892 if (*mi == mons)
2893 continue;
2894
2895 if (mons_is_elven_twin(*mi))
2896 return *mi;
2897 }
2898
2899 return nullptr;
2900 }
2901
2902 /**
2903 * Perform functional changes Dowan or Duvessa upon the other's death.
2904 *
2905 * This functional is called when either Dowan or Duvessa are killed or
2906 * banished. It performs a variety of changes in both attitude, spells, flavour,
2907 * speech, etc.
2908 *
2909 * @param twin The monster who died.
2910 * @param in_transit True if banished, otherwise false.
2911 * @param killer The kill-type related to twin.
2912 * @param killer_index The index of the actor who killed twin.
2913 **/
elven_twin_died(monster * twin,bool in_transit,killer_type killer,int killer_index)2914 void elven_twin_died(monster* twin, bool in_transit, killer_type killer, int killer_index)
2915 {
2916 if (killer == KILL_DISMISSED || killer == KILL_RESET)
2917 return;
2918
2919 // Sometimes, if you pacify one twin near a staircase, they leave
2920 // in the same turn. Convert, in those instances. The strict_neutral check
2921 // is intended to cover the slimify case, we don't want to pacify the other
2922 // if a slimified twin dies.
2923 if (twin->neutral() && !twin->has_ench(ENCH_INSANE)
2924 && !twin->strict_neutral())
2925 {
2926 elven_twins_pacify(twin);
2927 return;
2928 }
2929
2930 monster* mons = mons_find_elven_twin_of(twin);
2931
2932 if (!mons)
2933 return;
2934
2935 // Don't consider already neutralised monsters.
2936 if (mons->good_neutral() || mons->strict_neutral())
2937 return;
2938
2939 // Okay, let them climb stairs now.
2940 mons->props["can_climb"] = true;
2941 if (!in_transit)
2942 mons->props["speech_prefix"] = "twin_died";
2943 else
2944 mons->props["speech_prefix"] = "twin_banished";
2945
2946 // If you've stabbed one of them, the other one is likely asleep still.
2947 if (mons->asleep())
2948 behaviour_event(mons, ME_DISTURB, 0, mons->pos());
2949
2950 // Will generate strings such as 'Duvessa_Duvessa_dies' or, alternately
2951 // 'Dowan_Dowan_dies', but as neither will match, these can safely be
2952 // ignored.
2953 string key = mons->name(DESC_THE, true) + "_"
2954 + twin->name(DESC_THE, true) + "_dies_";
2955
2956 if (you.see_cell(mons->pos()))
2957 {
2958 if (!mons->visible_to(&you))
2959 key += "invisible_";
2960 }
2961 else
2962 key += "distance_";
2963
2964 bool i_killed = ((killer == KILL_MON || killer == KILL_MON_MISSILE)
2965 && mons->mindex() == killer_index);
2966
2967 if (i_killed)
2968 {
2969 key += "bytwin_";
2970 mons->props["speech_prefix"] = "twin_ikilled";
2971 }
2972
2973 // Drop the final '_'.
2974 key.erase(key.length() - 1);
2975
2976 string death_message = getSpeakString(key);
2977
2978 // Check if they can speak or not: they may have been polymorphed.
2979 if (you.see_cell(mons->pos()) && !death_message.empty() && mons->can_speak())
2980 mons_speaks_msg(mons, death_message, MSGCH_TALK, silenced(you.pos()));
2981 else if (mons->can_speak())
2982 mpr(death_message);
2983
2984 // Upgrade the spellbook here, as elven_twin_energize
2985 // may not be called due to lack of visibility.
2986 if (mons_is_mons_class(mons, MONS_DOWAN)
2987 && !(mons->flags & MF_POLYMORPHED))
2988 {
2989 // Don't mess with Dowan's spells if he's been polymorphed: most
2990 // possible forms have no spells, and the few that do (e.g. boggart)
2991 // have way more fun spells than this. If this ever changes, the
2992 // following code would need to be rewritten, as it'll crash.
2993 // TODO: this is a fairly brittle way of upgrading Dowan...
2994 ASSERT(mons->spells.size() >= 5);
2995 mons->spells[0].spell = SPELL_STONE_ARROW;
2996 mons->spells[1].spell = SPELL_THROW_ICICLE;
2997 mons->spells[3].spell = SPELL_BLINK;
2998 mons->spells[4].spell = SPELL_HASTE;
2999 // Nothing with 6.
3000
3001 // Indicate that he has an updated spellbook.
3002 mons->props[CUSTOM_SPELLS_KEY] = true;
3003 }
3004
3005 // Finally give them new energy
3006 if (mons->can_see(you) && !mons->has_ench(ENCH_INSANE))
3007 elven_twin_energize(mons);
3008 else
3009 mons->props[ELVEN_ENERGIZE_KEY] = true;
3010 }
3011
elven_twin_energize(monster * mons)3012 void elven_twin_energize(monster* mons)
3013 {
3014 if (mons_is_mons_class(mons, MONS_DUVESSA))
3015 mons->go_berserk(true);
3016 else
3017 {
3018 ASSERT(mons_is_mons_class(mons, MONS_DOWAN));
3019 if (mons->observable())
3020 simple_monster_message(*mons, " seems to find hidden reserves of power!");
3021
3022 mons->add_ench(ENCH_HASTE);
3023 }
3024
3025 mons->props[ELVEN_IS_ENERGIZED_KEY] = true;
3026 }
3027
3028 /**
3029 * Pacification effects for Dowan and Duvessa.
3030 *
3031 * As twins, pacifying one pacifies the other.
3032 *
3033 * @param twin The original monster pacified.
3034 **/
elven_twins_pacify(monster * twin)3035 void elven_twins_pacify(monster* twin)
3036 {
3037 monster* mons = mons_find_elven_twin_of(twin);
3038
3039 if (!mons)
3040 return;
3041
3042 // Don't consider already neutralised monsters.
3043 if (mons->neutral())
3044 return;
3045
3046 simple_monster_message(*mons, " likewise turns neutral.");
3047
3048 record_monster_defeat(mons, KILL_PACIFIED);
3049 mons_pacify(*mons, ATT_NEUTRAL);
3050 }
3051
3052 /**
3053 * Unpacification effects for Dowan and Duvessa.
3054 *
3055 * If they are both pacified and you attack one, the other will not remain
3056 * neutral. This is both for flavour (they do things together), and
3057 * functionality (so Dowan does not begin beating on Duvessa, etc).
3058 *
3059 * @param twin The monster attacked.
3060 **/
elven_twins_unpacify(monster * twin)3061 void elven_twins_unpacify(monster* twin)
3062 {
3063 monster* mons = mons_find_elven_twin_of(twin);
3064
3065 if (!mons)
3066 return;
3067
3068 // Don't consider already un-neutralised monsters.
3069 if (!mons->neutral() || mons->has_ench(ENCH_INSANE))
3070 return;
3071 simple_monster_message(*mons, " gets angry again!");
3072
3073 behaviour_event(mons, ME_WHACK, &you, you.pos(), false);
3074 }
3075
mons_felid_can_revive(const monster * mons)3076 bool mons_felid_can_revive(const monster* mons)
3077 {
3078 return !mons->props.exists("felid_revives")
3079 || mons->props["felid_revives"].get_byte() < 2;
3080 }
3081
mons_felid_revive(monster * mons)3082 void mons_felid_revive(monster* mons)
3083 {
3084 // FIXME: this should be a fineff like bennu_revive_fineff. But that
3085 // is tricky because the original actor will be dead (and not carrying
3086 // its items) by the time the fineff fires.
3087
3088 // Mostly adapted from bring_to_safety()
3089 coord_def revive_place;
3090 int tries = 10000;
3091 while (tries > 0) // Don't try too hard.
3092 {
3093 revive_place.x = random2(GXM);
3094 revive_place.y = random2(GYM);
3095 if (!in_bounds(revive_place)
3096 || env.grid(revive_place) != DNGN_FLOOR
3097 || cloud_at(revive_place)
3098 || monster_at(revive_place)
3099 || env.pgrid(revive_place) & FPROP_NO_TELE_INTO
3100 || grid_distance(revive_place, mons->pos()) < 9)
3101 {
3102 tries--;
3103 continue;
3104 }
3105 else
3106 break;
3107 }
3108 if (tries == 0)
3109 return;
3110
3111 monster_type type = mons_is_mons_class(mons, MONS_NATASHA) ? MONS_NATASHA
3112 : mons->type;
3113 const int revives = (mons->props.exists("felid_revives"))
3114 ? mons->props["felid_revives"].get_byte() + 1
3115 : 1;
3116
3117 monster *newmons =
3118 create_monster(
3119 mgen_data(type, (mons->has_ench(ENCH_CHARM) ? BEH_HOSTILE
3120 : SAME_ATTITUDE(mons)), revive_place, mons->foe));
3121
3122 if (newmons)
3123 {
3124 for (mon_inv_iterator ii(*mons); ii; ++ii)
3125 {
3126 give_specific_item(newmons, *ii);
3127 destroy_item(ii->index());
3128 }
3129
3130 newmons->props["felid_revives"].get_byte() = revives;
3131 }
3132 }
3133
mons_bennu_can_revive(const monster * mons)3134 bool mons_bennu_can_revive(const monster* mons)
3135 {
3136 return !mons->props.exists("bennu_revives")
3137 || mons->props["bennu_revives"].get_byte() < 1;
3138 }
3139