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