1 #include "mondeath.h"
2 
3 #include <algorithm>
4 #include <cmath>
5 #include <cstdlib>
6 #include <functional>
7 #include <iosfwd>
8 #include <list>
9 #include <map>
10 #include <memory>
11 #include <set>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "calendar.h"
17 #include "character.h"
18 #include "colony.h"
19 #include "creature.h"
20 #include "debug.h"
21 #include "enums.h"
22 #include "explosion.h"
23 #include "field_type.h"
24 #include "fungal_effects.h"
25 #include "game.h"
26 #include "harvest.h"
27 #include "item.h"
28 #include "item_stack.h"
29 #include "itype.h"
30 #include "kill_tracker.h"
31 #include "line.h"
32 #include "make_static.h"
33 #include "map.h"
34 #include "map_iterator.h"
35 #include "mattack_actors.h"
36 #include "mattack_common.h"
37 #include "messages.h"
38 #include "monster.h"
39 #include "morale_types.h"
40 #include "mtype.h"
41 #include "point.h"
42 #include "rng.h"
43 #include "sounds.h"
44 #include "string_formatter.h"
45 #include "timed_event.h"
46 #include "translations.h"
47 #include "type_id.h"
48 #include "units.h"
49 #include "value_ptr.h"
50 #include "viewer.h"
51 
52 static const efftype_id effect_amigara( "amigara" );
53 static const efftype_id effect_boomered( "boomered" );
54 static const efftype_id effect_controlled( "controlled" );
55 static const efftype_id effect_darkness( "darkness" );
56 static const efftype_id effect_glowing( "glowing" );
57 static const efftype_id effect_no_ammo( "no_ammo" );
58 static const efftype_id effect_rat( "rat" );
59 
60 static const json_character_flag json_flag_PRED1( "PRED1" );
61 static const json_character_flag json_flag_PRED2( "PRED2" );
62 static const json_character_flag json_flag_PRED3( "PRED3" );
63 static const json_character_flag json_flag_PRED4( "PRED4" );
64 
65 static const itype_id itype_processor( "processor" );
66 
67 static const species_id species_SLIME( "SLIME" );
68 static const species_id species_ZOMBIE( "ZOMBIE" );
69 
70 static const mtype_id mon_blob( "mon_blob" );
71 static const mtype_id mon_blob_brain( "mon_blob_brain" );
72 static const mtype_id mon_blob_small( "mon_blob_small" );
73 static const mtype_id mon_breather( "mon_breather" );
74 static const mtype_id mon_breather_hub( "mon_breather_hub" );
75 static const mtype_id mon_creeper_hub( "mon_creeper_hub" );
76 static const mtype_id mon_creeper_vine( "mon_creeper_vine" );
77 static const mtype_id mon_giant_cockroach_nymph( "mon_giant_cockroach_nymph" );
78 static const mtype_id mon_halfworm( "mon_halfworm" );
79 static const mtype_id mon_sewer_rat( "mon_sewer_rat" );
80 static const mtype_id mon_thing( "mon_thing" );
81 static const mtype_id mon_zombie_dancer( "mon_zombie_dancer" );
82 static const mtype_id mon_zombie_hulk( "mon_zombie_hulk" );
83 
84 static const trait_id trait_KILLER( "KILLER" );
85 static const trait_id trait_PACIFIST( "PACIFIST" );
86 static const trait_id trait_PSYCHOPATH( "PSYCHOPATH" );
87 
normal(monster & z)88 void mdeath::normal( monster &z )
89 {
90     if( z.no_corpse_quiet ) {
91         return;
92     }
93 
94     if( !z.quiet_death ) {
95         if( z.type->in_species( species_ZOMBIE ) ) {
96             sfx::play_variant_sound( "mon_death", "zombie_death", sfx::get_heard_volume( z.pos() ) );
97         }
98 
99         //Currently it is possible to get multiple messages that a monster died.
100         add_msg_if_player_sees( z, m_good, _( "The %s dies!" ), z.name() );
101     }
102 
103     if( z.death_drops ) {
104         const int max_hp = std::max( z.get_hp_max(), 1 );
105         const float overflow_damage = std::max( -z.get_hp(), 0 );
106         const float corpse_damage = 2.5 * overflow_damage / max_hp;
107         const bool pulverized = corpse_damage > 5 && overflow_damage > z.get_hp_max();
108 
109         z.bleed(); // leave some blood if we have to
110 
111         if( !pulverized ) {
112             make_mon_corpse( z, static_cast<int>( std::floor( corpse_damage * itype::damage_scale ) ) );
113         }
114         // if mdeath::splatter was set along normal makes sure it is not called twice
115         bool splatt = false;
116         for( const auto &deathfunction : z.type->dies ) {
117             if( deathfunction == mdeath::splatter ) {
118                 splatt = true;
119             }
120         }
121         if( !splatt ) {
122             splatter( z );
123         }
124     }
125 }
126 
scatter_chunks(const itype_id & chunk_name,int chunk_amt,monster & z,int distance,int pile_size=1)127 static void scatter_chunks( const itype_id &chunk_name, int chunk_amt, monster &z, int distance,
128                             int pile_size = 1 )
129 {
130     // can't have less than one item in a pile or it would cause an infinite loop
131     pile_size = std::max( pile_size, 1 );
132     // can't have more items in a pile than total items
133     pile_size = std::min( chunk_amt, pile_size );
134     distance = std::abs( distance );
135     const item chunk( chunk_name, calendar::turn, pile_size );
136     map &here = get_map();
137     for( int i = 0; i < chunk_amt; i += pile_size ) {
138         bool drop_chunks = true;
139         tripoint tarp( z.pos() + point( rng( -distance, distance ), rng( -distance, distance ) ) );
140         const auto traj = line_to( z.pos(), tarp );
141 
142         for( size_t j = 0; j < traj.size(); j++ ) {
143             tarp = traj[j];
144             if( one_in( 2 ) && z.bloodType().id() ) {
145                 here.add_splatter( z.bloodType(), tarp );
146             } else {
147                 here.add_splatter( z.gibType(), tarp, rng( 1, j + 1 ) );
148             }
149             if( here.impassable( tarp ) ) {
150                 here.bash( tarp, distance );
151                 if( here.impassable( tarp ) ) {
152                     // Target is obstacle, not destroyed by bashing,
153                     // stop trajectory in front of it, if this is the first
154                     // point (e.g. wall adjacent to monster), don't drop anything on it
155                     if( j > 0 ) {
156                         tarp = traj[j - 1];
157                     } else {
158                         drop_chunks = false;
159                     }
160                     break;
161                 }
162             }
163         }
164         if( drop_chunks ) {
165             here.add_item_or_charges( tarp, chunk );
166         }
167     }
168 }
169 
splatter(monster & z)170 void mdeath::splatter( monster &z )
171 {
172     const bool gibbable = !z.type->has_flag( MF_NOGIB );
173 
174     const int max_hp = std::max( z.get_hp_max(), 1 );
175     const float overflow_damage = std::max( -z.get_hp(), 0 );
176     const float corpse_damage = 2.5 * overflow_damage / max_hp;
177     bool pulverized = corpse_damage > 5 && overflow_damage > z.get_hp_max();
178     // make sure that full splatter happens when this is a set death function, not part of normal
179     for( const auto &deathfunction : z.type->dies ) {
180         if( deathfunction == mdeath::splatter ) {
181             pulverized = true;
182         }
183     }
184 
185     const field_type_id type_blood = z.bloodType();
186     const field_type_id type_gib = z.gibType();
187 
188     map &here = get_map();
189     if( gibbable ) {
190         const auto area = here.points_in_radius( z.pos(), 1 );
191         int number_of_gibs = std::min( std::floor( corpse_damage ) - 1, 1 + max_hp / 5.0f );
192 
193         if( pulverized && z.type->size >= creature_size::medium ) {
194             number_of_gibs += rng( 1, 6 );
195             sfx::play_variant_sound( "mon_death", "zombie_gibbed", sfx::get_heard_volume( z.pos() ) );
196         }
197 
198         for( int i = 0; i < number_of_gibs; ++i ) {
199             here.add_splatter( type_gib, random_entry( area ), rng( 1, i + 1 ) );
200             here.add_splatter( type_blood, random_entry( area ) );
201         }
202     }
203     // 1% of the weight of the monster is the base, with overflow damage as a multiplier
204     int gibbed_weight = rng( 0, std::round( to_gram( z.get_weight() ) / 100.0 *
205                                             ( overflow_damage / max_hp + 1 ) ) );
206     const int z_weight = to_gram( z.get_weight() );
207     // limit gibbing to 15%
208     gibbed_weight = std::min( gibbed_weight, z_weight * 15 / 100 );
209 
210     if( pulverized && gibbable ) {
211         float overflow_ratio = overflow_damage / max_hp + 1.0f;
212         int gib_distance = std::round( rng( 2, 4 ) );
213         for( const auto &entry : *z.type->harvest ) {
214             // only flesh and bones survive.
215             if( entry.type == "flesh" || entry.type == "bone" ) {
216                 // the larger the overflow damage, the less you get
217                 const int chunk_amt =
218                     entry.mass_ratio / overflow_ratio / 10 *
219                     z.get_weight() / ( item::find_type( itype_id( entry.drop ) ) )->weight;
220                 scatter_chunks( itype_id( entry.drop ), chunk_amt, z, gib_distance,
221                                 chunk_amt / ( gib_distance - 1 ) );
222                 gibbed_weight -= entry.mass_ratio / overflow_ratio / 20 * to_gram( z.get_weight() );
223             }
224         }
225         if( gibbed_weight > 0 ) {
226             const itype_id &leftover_id = z.type->id->harvest->leftovers;
227             const int chunk_amount =
228                 gibbed_weight / to_gram( ( item::find_type( leftover_id ) )->weight );
229             scatter_chunks( leftover_id, chunk_amount, z, gib_distance,
230                             chunk_amount / ( gib_distance + 1 ) );
231         }
232         // add corpse with gib flag
233         item corpse = item::make_corpse( z.type->id, calendar::turn, z.unique_name, z.get_upgrade_time() );
234         // Set corpse to damage that aligns with being pulped
235         corpse.set_damage( 4000 );
236         corpse.set_flag( STATIC( flag_id( "GIBBED" ) ) );
237         if( z.has_effect( effect_no_ammo ) ) {
238             corpse.set_var( "no_ammo", "no_ammo" );
239         }
240         here.add_item_or_charges( z.pos(), corpse );
241     }
242 }
243 
acid(monster & z)244 void mdeath::acid( monster &z )
245 {
246     if( get_player_view().sees( z ) ) {
247         if( z.type->dies.size() ==
248             1 ) { //If this death function is the only function. The corpse gets dissolved.
249             add_msg( m_mixed, _( "The %s's body dissolves into acid." ), z.name() );
250         } else {
251             add_msg( m_warning, _( "The %s's body leaks acid." ), z.name() );
252         }
253     }
254     get_map().add_field( z.pos(), fd_acid, 3 );
255 }
256 
boomer(monster & z)257 void mdeath::boomer( monster &z )
258 {
259     map &here = get_map();
260     std::string explode = string_format( _( "a %s explode!" ), z.name() );
261     sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );
262     for( const tripoint &dest : here.points_in_radius( z.pos(), 1 ) ) { // *NOPAD*
263         here.bash( dest, 10 );
264         if( monster *const target = g->critter_at<monster>( dest ) ) {
265             target->stumble();
266             target->moves -= 250;
267         }
268     }
269 
270     Character &player_character = get_player_character();
271     if( rl_dist( z.pos(), player_character.pos() ) == 1 ) {
272         player_character.add_env_effect( effect_boomered, bodypart_id( "eyes" ), 2, 24_turns );
273     }
274 
275     here.propagate_field( z.pos(), fd_bile, 15, 1 );
276 }
277 
boomer_glow(monster & z)278 void mdeath::boomer_glow( monster &z )
279 {
280     std::string explode = string_format( _( "a %s explode!" ), z.name() );
281     sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );
282     map &here = get_map();
283 
284     for( const tripoint &dest : here.points_in_radius( z.pos(), 1 ) ) { // *NOPAD*
285         here.bash( dest, 10 );
286         if( monster *const target = g->critter_at<monster>( dest ) ) {
287             target->stumble();
288             target->moves -= 250;
289         }
290         if( Creature *const critter = g->critter_at( dest ) ) {
291             critter->add_env_effect( effect_boomered, bodypart_id( "eyes" ), 5, 25_turns );
292             for( int i = 0; i < rng( 2, 4 ); i++ ) {
293                 const bodypart_id &bp = critter->random_body_part();
294                 critter->add_env_effect( effect_glowing, bp, 4, 4_minutes );
295                 if( critter->has_effect( effect_glowing ) ) {
296                     break;
297                 }
298             }
299         }
300     }
301 
302     here.propagate_field( z.pos(), fd_bile, 30, 2 );
303 }
304 
kill_vines(monster & z)305 void mdeath::kill_vines( monster &z )
306 {
307     const std::vector<Creature *> vines = g->get_creatures_if( [&]( const Creature & critter ) {
308         const monster *const mon = dynamic_cast<const monster *>( &critter );
309         return mon && mon->type->id == mon_creeper_vine;
310     } );
311     const std::vector<Creature *> hubs = g->get_creatures_if( [&]( const Creature & critter ) {
312         const monster *const mon = dynamic_cast<const monster *>( &critter );
313         return mon && mon != &z && mon->type->id == mon_creeper_hub;
314     } );
315 
316     for( Creature *const vine : vines ) {
317         int dist = rl_dist( vine->pos(), z.pos() );
318         bool closer = false;
319         for( const Creature *j : hubs ) {
320             if( rl_dist( vine->pos(), j->pos() ) < dist ) {
321                 break;
322             }
323         }
324         if( !closer ) { // TODO: closer variable is not being updated and is always false!
325             vine->die( &z );
326         }
327     }
328 }
329 
vine_cut(monster & z)330 void mdeath::vine_cut( monster &z )
331 {
332     map &here = get_map();
333     std::vector<monster *> vines;
334     for( const tripoint &tmp : here.points_in_radius( z.pos(), 1 ) ) {
335         if( tmp == z.pos() ) {
336             continue; // Skip ourselves
337         }
338         if( monster *const neighbor = g->critter_at<monster>( tmp ) ) {
339             if( neighbor->type->id == mon_creeper_vine ) {
340                 vines.push_back( neighbor );
341             }
342         }
343     }
344 
345     for( auto &vine : vines ) {
346         bool found_neighbor = false;
347         for( const tripoint &dest : here.points_in_radius( vine->pos(), 1 ) ) {
348             if( dest != z.pos() ) {
349                 // Not the dying vine
350                 if( monster *const v = g->critter_at<monster>( dest ) ) {
351                     if( v->type->id == mon_creeper_hub || v->type->id == mon_creeper_vine ) {
352                         found_neighbor = true;
353                         break;
354                     }
355                 }
356             }
357         }
358         if( !found_neighbor ) {
359             vine->die( &z );
360         }
361     }
362 }
363 
triffid_heart(monster & z)364 void mdeath::triffid_heart( monster &z )
365 {
366     add_msg_if_player_sees( z, m_warning, _( "The surrounding roots begin to crack and crumble." ) );
367     get_timed_events().add( timed_event_type::ROOTS_DIE, calendar::turn + 10_minutes );
368 }
369 
fungus(monster & z)370 void mdeath::fungus( monster &z )
371 {
372     //~ the sound of a fungus dying
373     sounds::sound( z.pos(), 10, sounds::sound_t::combat, _( "Pouf!" ), false, "misc", "puff" );
374 
375     map &here = get_map();
376     fungal_effects fe( *g, here );
377     for( const tripoint &sporep : here.points_in_radius( z.pos(), 1 ) ) { // *NOPAD*
378         if( here.impassable( sporep ) ) {
379             continue;
380         }
381         // z is dead, don't credit it with the kill
382         // Maybe credit z's killer?
383         fe.fungalize( sporep, nullptr, 0.25 );
384     }
385 }
386 
disintegrate(monster & z)387 void mdeath::disintegrate( monster &z )
388 {
389     add_msg_if_player_sees( z, m_good, _( "The %s disintegrates!" ), z.name() );
390 }
391 
worm(monster & z)392 void mdeath::worm( monster &z )
393 {
394     if( z.type->dies.size() == 1 ) {
395         add_msg_if_player_sees( z, m_good, _( "The %s splits in two!" ), z.name() );
396     } else {
397         add_msg_if_player_sees( z, m_warning, _( "Two worms crawl out of the %s's corpse." ), z.name() );
398     }
399 
400     int worms = 2;
401     while( worms > 0 && g->place_critter_around( mon_halfworm, z.pos(), 1 ) ) {
402         worms--;
403     }
404 }
405 
disappear(monster & z)406 void mdeath::disappear( monster &z )
407 {
408     add_msg_if_player_sees( z, m_good, _( "The %s disappears." ), z.name() );
409 }
410 
guilt(monster & z)411 void mdeath::guilt( monster &z )
412 {
413     const int MAX_GUILT_DISTANCE = 5;
414     int kill_count = g->get_kill_tracker().kill_count( z.type->id );
415     int maxKills = 100; // this is when the player stop caring altogether.
416 
417     // different message as we kill more of the same monster
418     std::string msg = _( "You feel guilty for killing %s." ); // default guilt message
419     game_message_type msgtype = m_bad; // default guilt message type
420     std::map<int, std::string> guilt_tresholds;
421     guilt_tresholds[75] = _( "You feel ashamed for killing %s." );
422     guilt_tresholds[50] = _( "You regret killing %s." );
423     guilt_tresholds[25] = _( "You feel remorse for killing %s." );
424 
425     Character &player_character = get_player_character();
426     if( player_character.has_trait( trait_PSYCHOPATH ) ||
427         player_character.has_trait_flag( json_flag_PRED3 ) ||
428         player_character.has_trait_flag( json_flag_PRED4 ) || player_character.has_trait( trait_KILLER ) ) {
429         return;
430     }
431     if( rl_dist( z.pos(), player_character.pos() ) > MAX_GUILT_DISTANCE ) {
432         // Too far away, we can deal with it.
433         return;
434     }
435     if( z.get_hp() >= 0 ) {
436         // We probably didn't kill it
437         return;
438     }
439     if( kill_count >= maxKills ) {
440         // player no longer cares
441         if( kill_count == maxKills ) {
442             //~ Message after killing a lot of monsters which would normally affect the morale negatively. %s is the monster name, it will be pluralized with a number of 100.
443             add_msg( m_good, _( "After killing so many bloody %s you no longer care "
444                                 "about their deaths anymore." ), z.name( maxKills ) );
445         }
446         return;
447     } else if( ( player_character.has_trait_flag( json_flag_PRED1 ) ) ||
448                ( player_character.has_trait_flag( json_flag_PRED2 ) ) ) {
449         msg = ( _( "Culling the weak is distasteful, but necessary." ) );
450         msgtype = m_neutral;
451     } else {
452         for( auto &guilt_treshold : guilt_tresholds ) {
453             if( kill_count >= guilt_treshold.first ) {
454                 msg = guilt_treshold.second;
455                 break;
456             }
457         }
458     }
459 
460     add_msg( msgtype, msg, z.name() );
461 
462     int moraleMalus = -50 * ( 1.0 - ( static_cast<float>( kill_count ) / maxKills ) );
463     int maxMalus = -250 * ( 1.0 - ( static_cast<float>( kill_count ) / maxKills ) );
464     time_duration duration = 30_minutes * ( 1.0 - ( static_cast<float>( kill_count ) / maxKills ) );
465     time_duration decayDelay = 3_minutes * ( 1.0 - ( static_cast<float>( kill_count ) / maxKills ) );
466     if( z.type->in_species( species_ZOMBIE ) ) {
467         moraleMalus /= 10;
468         if( player_character.has_trait( trait_PACIFIST ) ) {
469             moraleMalus *= 5;
470         } else if( player_character.has_trait_flag( json_flag_PRED1 ) ) {
471             moraleMalus /= 4;
472         } else if( player_character.has_trait_flag( json_flag_PRED2 ) ) {
473             moraleMalus /= 5;
474         }
475     }
476     player_character.add_morale( MORALE_KILLED_MONSTER, moraleMalus, maxMalus, duration, decayDelay );
477 
478 }
479 
blobsplit(monster & z)480 void mdeath::blobsplit( monster &z )
481 {
482     int speed = z.get_speed() - rng( 30, 50 );
483     get_map().spawn_item( z.pos(), "slime_scrap", 1, 0, calendar::turn );
484     bool sees_blob = get_player_view().sees( z );
485     if( z.get_speed() <= 0 && sees_blob ) {
486         // TODO: Add vermin-tagged tiny versions of the splattered blob  :)
487         add_msg( m_good, _( "The %s splatters apart." ), z.name() );
488         return;
489     }
490     if( sees_blob ) {
491         if( z.type->dies.size() == 1 ) {
492             add_msg( m_good, _( "The %s splits in two!" ), z.name() );
493         } else {
494             add_msg( m_bad, _( "Two small slimes slither out of the corpse." ) );
495         }
496     }
497 
498     const mtype_id &child = speed < 50 ? mon_blob_small : mon_blob;
499     for( int s = 0; s < 2; s++ ) {
500         if( monster *const blob = g->place_critter_around( child, z.pos(), 1 ) ) {
501             blob->make_ally( z );
502             blob->set_speed_base( speed );
503             blob->set_hp( speed );
504         }
505     }
506 }
507 
brainblob(monster & z)508 void mdeath::brainblob( monster &z )
509 {
510     for( monster &critter : g->all_monsters() ) {
511         if( critter.type->in_species( species_SLIME ) && critter.type->id != mon_blob_brain ) {
512             critter.remove_effect( effect_controlled );
513         }
514     }
515     blobsplit( z );
516 }
517 
jackson(monster & z)518 void mdeath::jackson( monster &z )
519 {
520     bool music_stopped = false;
521     for( monster &critter : g->all_monsters() ) {
522         if( critter.type->id == mon_zombie_dancer ) {
523             critter.poly( mon_zombie_hulk );
524             critter.remove_effect( effect_controlled );
525         }
526         music_stopped = true;
527     }
528     if( music_stopped ) {
529         add_msg_if_player_sees( z, m_warning, _( "The music stops!" ) );
530     }
531 }
532 
melt(monster & z)533 void mdeath::melt( monster &z )
534 {
535     add_msg_if_player_sees( z, m_good, _( "The %s melts away." ), z.name() );
536 }
537 
amigara(monster & z)538 void mdeath::amigara( monster &z )
539 {
540     const bool has_others = g->get_creature_if( [&]( const Creature & critter ) {
541         if( const monster *const candidate = dynamic_cast<const monster *>( &critter ) ) {
542             return candidate->type == z.type;
543         }
544         return false;
545     } );
546     if( has_others ) {
547         return;
548     }
549 
550     // We were the last!
551     Character &player_character = get_player_character();
552     if( player_character.has_effect( effect_amigara ) ) {
553         player_character.remove_effect( effect_amigara );
554         add_msg( _( "Your obsession with the fault fades away…" ) );
555     }
556 
557     get_map().spawn_artifact( z.pos(), relic_procgen_id( "netherum_tunnels" ) );
558 }
559 
thing(monster & z)560 void mdeath::thing( monster &z )
561 {
562     g->place_critter_at( mon_thing, z.pos() );
563 }
564 
explode(monster & z)565 void mdeath::explode( monster &z )
566 {
567     int size = 0;
568     switch( z.type->size ) {
569         case creature_size::tiny:
570             size = 4;
571             break;
572         case creature_size::small:
573             size = 8;
574             break;
575         case creature_size::medium:
576             size = 14;
577             break;
578         case creature_size::large:
579             size = 20;
580             break;
581         case creature_size::huge:
582             size = 26;
583             break;
584         case creature_size::num_sizes:
585             debugmsg( "ERROR: Invalid Creature size class." );
586             break;
587     }
588     explosion_handler::explosion( z.pos(), size );
589 }
590 
focused_beam(monster & z)591 void mdeath::focused_beam( monster &z )
592 {
593     map &here = get_map();
594     map_stack items = here.i_at( z.pos() );
595     for( map_stack::iterator it = items.begin(); it != items.end(); ) {
596         if( it->typeId() == itype_processor ) {
597             it = items.erase( it );
598         } else {
599             ++it;
600         }
601     }
602 
603     if( !z.inv.empty() ) {
604         add_msg_if_player_sees( z, m_warning,
605                                 _( "As the final light is destroyed, it erupts in a blinding flare!" ) );
606         item &settings = z.inv[0];
607 
608         point p2( z.posx() + settings.get_var( "SL_SPOT_X", 0 ), z.posy() + settings.get_var( "SL_SPOT_Y",
609                   0 ) );
610         tripoint p( p2, z.posz() );
611 
612         std::vector <tripoint> traj = line_to( z.pos(), p, 0, 0 );
613         for( auto &elem : traj ) {
614             if( !here.is_transparent( elem ) ) {
615                 break;
616             }
617             here.add_field( elem, fd_dazzling, 2 );
618         }
619     }
620 
621     z.inv.clear();
622 
623     explosion_handler::explosion( z.pos(), 8 );
624 }
625 
broken(monster & z)626 void mdeath::broken( monster &z )
627 {
628     // Bail out if flagged (simulates eyebot flying away)
629     if( z.no_corpse_quiet ) {
630         return;
631     }
632     std::string item_id = z.type->id.str();
633     if( item_id.compare( 0, 4, "mon_" ) == 0 ) {
634         item_id.erase( 0, 4 );
635     }
636     // make "broken_manhack", or "broken_eyebot", ...
637     item_id.insert( 0, "broken_" );
638     item broken_mon( item_id, calendar::turn );
639     const int max_hp = std::max( z.get_hp_max(), 1 );
640     const float overflow_damage = std::max( -z.get_hp(), 0 );
641     const float corpse_damage = 2.5 * overflow_damage / max_hp;
642     broken_mon.set_damage( static_cast<int>( std::floor( corpse_damage * itype::damage_scale ) ) );
643 
644     map &here = get_map();
645     here.add_item_or_charges( z.pos(), broken_mon );
646 
647     if( z.type->has_flag( MF_DROPS_AMMO ) ) {
648         for( const std::pair<const itype_id, int> &ammo_entry : z.ammo ) {
649             if( ammo_entry.second > 0 ) {
650                 bool spawned = false;
651                 for( const std::pair<const std::string, mtype_special_attack> &attack : z.type->special_attacks ) {
652                     if( attack.second->id == "gun" ) {
653                         item gun = item( dynamic_cast<const gun_actor *>( attack.second.get() )->gun_type );
654                         bool same_ammo = false;
655                         if( gun.typeId()->magazine_default.count( item( ammo_entry.first ).ammo_type() ) ) {
656                             same_ammo = true;
657                         }
658                         const bool uses_mags = !gun.magazine_compatible().empty();
659                         if( same_ammo && uses_mags ) {
660                             std::vector<item> mags;
661                             int ammo_count = ammo_entry.second;
662                             while( ammo_count > 0 ) {
663                                 item mag = item( gun.type->magazine_default.find( item( ammo_entry.first ).ammo_type() )->second );
664                                 mag.ammo_set( ammo_entry.first,
665                                               std::min( ammo_count, mag.type->magazine->capacity ) );
666                                 mags.insert( mags.end(), mag );
667                                 ammo_count -= mag.type->magazine->capacity;
668                             }
669                             here.spawn_items( z.pos(), mags );
670                             spawned = true;
671                             break;
672                         }
673                     }
674                 }
675                 if( !spawned ) {
676                     here.spawn_item( z.pos(), ammo_entry.first, ammo_entry.second, 1,
677                                      calendar::turn );
678                 }
679             }
680         }
681     }
682 
683     // TODO: make mdeath::splatter work for robots
684     if( ( broken_mon.damage() >= broken_mon.max_damage() ) ) {
685         add_msg_if_player_sees( z.pos(), m_good, _( "The %s is destroyed!" ), z.name() );
686     } else {
687         add_msg_if_player_sees( z.pos(), m_good, _( "The %s collapses!" ), z.name() );
688     }
689 }
690 
ratking(monster & z)691 void mdeath::ratking( monster &z )
692 {
693     Character &player_character = get_player_character();
694     player_character.remove_effect( effect_rat );
695     add_msg_if_player_sees( z, m_warning, _( "Rats suddenly swarm into view." ) );
696 
697     for( int rats = 0; rats < 7; rats++ ) {
698         g->place_critter_around( mon_sewer_rat, z.pos(), 1 );
699     }
700 }
701 
darkman(monster & z)702 void mdeath::darkman( monster &z )
703 {
704     Character &player_character = get_player_character();
705     player_character.remove_effect( effect_darkness );
706     add_msg_if_player_sees( z, m_good, _( "The %s melts away." ), z.name() );
707 }
708 
gas(monster & z)709 void mdeath::gas( monster &z )
710 {
711     std::string explode = string_format( _( "a %s explode!" ), z.name() );
712     sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );
713     get_map().emit_field( z.pos(), emit_id( "emit_toxic_blast" ) );
714 }
715 
smokeburst(monster & z)716 void mdeath::smokeburst( monster &z )
717 {
718     std::string explode = string_format( _( "a %s explode!" ), z.name() );
719     sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );
720     get_map().emit_field( z.pos(), emit_id( "emit_smoke_blast" ) );
721 }
722 
fungalburst(monster & z)723 void mdeath::fungalburst( monster &z )
724 {
725     map &here = get_map();
726     // If the fungus died from anti-fungal poison, don't pouf
727     if( here.get_field_intensity( z.pos(), fd_fungicidal_gas ) ) {
728         add_msg_if_player_sees( z, m_good, _( "The %s inflates and melts away." ), z.name() );
729         return;
730     }
731 
732     std::string explode = string_format( _( "a %s explodes!" ), z.name() );
733     sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );
734     here.emit_field( z.pos(), emit_id( "emit_fungal_blast" ) );
735 }
736 
jabberwock(monster & z)737 void mdeath::jabberwock( monster &z )
738 {
739     Character *ch = dynamic_cast<Character *>( z.get_killer() );
740 
741     bool vorpal = ch && ch->is_player() &&
742                   ch->weapon.has_flag( STATIC( flag_id( "DIAMOND" ) ) ) &&
743                   ch->weapon.volume() > 750_ml;
744 
745     if( vorpal && !ch->weapon.has_technique( matec_id( "VORPAL" ) ) ) {
746         if( ch->sees( z ) ) {
747             ch->add_msg_if_player( m_info,
748                                    //~ %s is the possessive form of the monster's name
749                                    _( "As the flames in %s eyes die out, your weapon seems to shine slightly brighter." ),
750                                    z.disp_name( true ) );
751         }
752         ch->weapon.add_technique( matec_id( "VORPAL" ) );
753     }
754 
755     mdeath::normal( z );
756 }
757 
gameover(monster & z)758 void mdeath::gameover( monster &z )
759 {
760     add_msg( m_bad, _( "The %s was destroyed!  GAME OVER!" ), z.name() );
761     get_player_character().set_part_hp_cur( bodypart_id( "torso" ), 0 );
762 }
763 
kill_breathers(monster &)764 void mdeath::kill_breathers( monster &/*z*/ )
765 {
766     for( monster &critter : g->all_monsters() ) {
767         const mtype_id &monID = critter.type->id;
768         if( monID == mon_breather_hub || monID == mon_breather ) {
769             critter.die( nullptr );
770         }
771     }
772 }
773 
broken_ammo(monster & z)774 void mdeath::broken_ammo( monster &z )
775 {
776     add_msg_if_player_sees( z.pos(), m_info,
777                             //~ %s is the possessive form of the monster's name
778                             _( "The %s's interior compartment sizzles with destructive energy." ),
779                             z.name() );
780     mdeath::broken( z );
781 }
782 
make_mon_corpse(monster & z,int damageLvl)783 void make_mon_corpse( monster &z, int damageLvl )
784 {
785     item corpse = item::make_corpse( z.type->id, calendar::turn, z.unique_name, z.get_upgrade_time() );
786     // All corpses are at 37 C at time of death
787     // This may not be true but anything better would be way too complicated
788     if( z.is_warm() ) {
789         corpse.set_item_temperature( 310.15 );
790     }
791     corpse.set_damage( damageLvl );
792     if( z.has_effect( effect_no_ammo ) ) {
793         corpse.set_var( "no_ammo", "no_ammo" );
794     }
795     get_map().add_item_or_charges( z.pos(), corpse );
796 }
797 
preg_roach(monster & z)798 void mdeath::preg_roach( monster &z )
799 {
800     int num_roach = rng( 1, 3 );
801     while( num_roach > 0 && g->place_critter_around( mon_giant_cockroach_nymph, z.pos(), 1 ) ) {
802         num_roach--;
803         add_msg_if_player_sees( z, m_warning,
804                                 _( "A cockroach nymph crawls out of the pregnant giant cockroach corpse." ) );
805     }
806 }
807 
fireball(monster & z)808 void mdeath::fireball( monster &z )
809 {
810     if( one_in( 10 ) ) {
811         get_map().propagate_field( z.pos(), fd_fire, 15, 3 );
812         std::string explode = string_format( _( "an explosion of tank of the %s's flamethrower!" ),
813                                              z.name() );
814         sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "default" );
815         add_msg( m_good, _( "I love the smell of burning zed in the morning." ) );
816     } else {
817         normal( z );
818     }
819 }
820 
conflagration(monster & z)821 void mdeath::conflagration( monster &z )
822 {
823     map &here = get_map();
824     for( const auto &dest : here.points_in_radius( z.pos(), 1 ) ) {
825         here.propagate_field( dest, fd_fire, 18, 3 );
826     }
827     const std::string explode = string_format( _( "a %s explode!" ), z.name() );
828     sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );
829 
830 }
831 
necro_boomer(monster & z)832 void mdeath::necro_boomer( monster &z )
833 {
834     map &here = get_map();
835     std::string explode = string_format( _( "a %s explode!" ), z.name() );
836     sounds::sound( z.pos(), 24, sounds::sound_t::combat, explode, false, "explosion", "small" );
837     for( const tripoint &aoe : here.points_in_radius( z.pos(), 10 ) ) {
838         for( item &corpse : here.i_at( aoe ) ) {
839             const mtype *mt = corpse.get_mtype();
840             if( !( corpse.is_corpse() && corpse.can_revive() && corpse.active &&
841                    mt->has_flag( MF_REVIVES ) && mt->in_species( species_ZOMBIE ) &&
842                    !mt->has_flag( MF_NO_NECRO ) ) ) {
843                 continue;
844             }
845             if( g->revive_corpse( aoe, corpse ) ) {
846                 here.i_rem( aoe, &corpse );
847                 break;
848             }
849         }
850     }
851     for( const tripoint &aoe : here.points_in_radius( z.pos(), 10 ) ) {
852         monster *mon = g->critter_at<monster>( aoe );
853         if( mon != nullptr && one_in( 10 ) ) {
854             mon->allow_upgrade();
855             mon->try_upgrade( false );
856         }
857     }
858 }
859