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