1 #include "monattack.h"
2 
3 #include <algorithm>
4 #include <array>
5 #include <climits>
6 #include <cmath>
7 #include <cstdlib>
8 #include <functional>
9 #include <list>
10 #include <map>
11 #include <memory>
12 #include <new>
13 #include <ostream>
14 #include <set>
15 #include <string>
16 #include <utility>
17 #include <vector>
18 
19 #include "activity_type.h"
20 #include "ballistics.h"
21 #include "bodypart.h"
22 #include "calendar.h"
23 #include "cata_assert.h"
24 #include "character.h"
25 #include "character_id.h"
26 #include "character_martial_arts.h"
27 #include "colony.h"
28 #include "creature.h"
29 #include "damage.h"
30 #include "debug.h"
31 #include "dispersion.h"
32 #include "effect.h"
33 #include "effect_source.h"
34 #include "enums.h"
35 #include "event.h"
36 #include "event_bus.h"
37 #include "explosion.h"
38 #include "field_type.h"
39 #include "flag.h"
40 #include "fungal_effects.h"
41 #include "game.h"
42 #include "game_constants.h"
43 #include "gun_mode.h"
44 #include "item.h"
45 #include "item_stack.h"
46 #include "itype.h"
47 #include "iuse.h"
48 #include "iuse_actor.h"
49 #include "line.h"
50 #include "location.h"
51 #include "make_static.h"
52 #include "map.h"
53 #include "map_iterator.h"
54 #include "mapdata.h"
55 #include "martialarts.h"
56 #include "material.h"
57 #include "memorial_logger.h"
58 #include "messages.h"
59 #include "mission.h"
60 #include "mondefense.h"
61 #include "monfaction.h"
62 #include "monster.h"
63 #include "morale_types.h"
64 #include "mtype.h"
65 #include "name.h"
66 #include "npc.h"
67 #include "optional.h"
68 #include "output.h"
69 #include "pathfinding.h"
70 #include "pimpl.h"
71 #include "player.h"
72 #include "point.h"
73 #include "projectile.h"
74 #include "rng.h"
75 #include "sounds.h"
76 #include "speech.h"
77 #include "string_formatter.h"
78 #include "text_snippets.h"
79 #include "tileray.h"
80 #include "timed_event.h"
81 #include "translations.h"
82 #include "type_id.h"
83 #include "ui.h"
84 #include "ui_manager.h"
85 #include "units.h"
86 #include "value_ptr.h"
87 #include "viewer.h"
88 #include "weighted_list.h"
89 
90 static const activity_id ACT_RELOAD( "ACT_RELOAD" );
91 
92 static const efftype_id effect_assisted( "assisted" );
93 static const efftype_id effect_bite( "bite" );
94 static const efftype_id effect_bleed( "bleed" );
95 static const efftype_id effect_blind( "blind" );
96 static const efftype_id effect_boomered( "boomered" );
97 static const efftype_id effect_controlled( "controlled" );
98 static const efftype_id effect_corroding( "corroding" );
99 static const efftype_id effect_countdown( "countdown" );
100 static const efftype_id effect_darkness( "darkness" );
101 static const efftype_id effect_dazed( "dazed" );
102 static const efftype_id effect_deaf( "deaf" );
103 static const efftype_id effect_dermatik( "dermatik" );
104 static const efftype_id effect_downed( "downed" );
105 static const efftype_id effect_dragging( "dragging" );
106 static const efftype_id effect_fearparalyze( "fearparalyze" );
107 static const efftype_id effect_fungus( "fungus" );
108 static const efftype_id effect_glowing( "glowing" );
109 static const efftype_id effect_got_checked( "got_checked" );
110 static const efftype_id effect_grabbed( "grabbed" );
111 static const efftype_id effect_grabbing( "grabbing" );
112 static const efftype_id effect_grown_of_fuse( "grown_of_fuse" );
113 static const efftype_id effect_hallu( "hallu" );
114 static const efftype_id effect_has_bag( "has_bag" );
115 static const efftype_id effect_infected( "infected" );
116 static const efftype_id effect_laserlocked( "laserlocked" );
117 static const efftype_id effect_onfire( "onfire" );
118 static const efftype_id effect_operating( "operating" );
119 static const efftype_id effect_paid( "paid" );
120 static const efftype_id effect_paralyzepoison( "paralyzepoison" );
121 static const efftype_id effect_pet( "pet" );
122 static const efftype_id effect_raising( "raising" );
123 static const efftype_id effect_rat( "rat" );
124 static const efftype_id effect_shrieking( "shrieking" );
125 static const efftype_id effect_slimed( "slimed" );
126 static const efftype_id effect_stunned( "stunned" );
127 static const efftype_id effect_taint( "taint" );
128 static const efftype_id effect_targeted( "targeted" );
129 static const efftype_id effect_tindrift( "tindrift" );
130 static const efftype_id effect_under_operation( "under_operation" );
131 
132 static const itype_id itype_ant_egg( "ant_egg" );
133 static const itype_id itype_badge_cybercop( "badge_cybercop" );
134 static const itype_id itype_badge_deputy( "badge_deputy" );
135 static const itype_id itype_badge_detective( "badge_detective" );
136 static const itype_id itype_badge_doctor( "badge_doctor" );
137 static const itype_id itype_badge_marshal( "badge_marshal" );
138 static const itype_id itype_badge_swat( "badge_swat" );
139 static const itype_id itype_bot_c4_hack( "bot_c4_hack" );
140 static const itype_id itype_bot_flashbang_hack( "bot_flashbang_hack" );
141 static const itype_id itype_bot_gasbomb_hack( "bot_gasbomb_hack" );
142 static const itype_id itype_bot_grenade_hack( "bot_grenade_hack" );
143 static const itype_id itype_bot_manhack( "bot_manhack" );
144 static const itype_id itype_bot_mininuke_hack( "bot_mininuke_hack" );
145 static const itype_id itype_bot_pacification_hack( "bot_pacification_hack" );
146 static const itype_id itype_c4( "c4" );
147 static const itype_id itype_c4armed( "c4armed" );
148 static const itype_id itype_e_handcuffs( "e_handcuffs" );
149 static const itype_id itype_mininuke( "mininuke" );
150 static const itype_id itype_mininuke_act( "mininuke_act" );
151 
152 static const skill_id skill_gun( "gun" );
153 static const skill_id skill_launcher( "launcher" );
154 static const skill_id skill_melee( "melee" );
155 static const skill_id skill_rifle( "rifle" );
156 static const skill_id skill_unarmed( "unarmed" );
157 
158 static const species_id species_SLIME( "SLIME" );
159 static const species_id species_LEECH_PLANT( "LEECH_PLANT" );
160 static const species_id species_ZOMBIE( "ZOMBIE" );
161 
162 static const std::string flag_AUTODOC_COUCH( "AUTODOC_COUCH" );
163 
164 static const trait_id trait_ACIDBLOOD( "ACIDBLOOD" );
165 static const trait_id trait_MARLOSS( "MARLOSS" );
166 static const trait_id trait_MARLOSS_BLUE( "MARLOSS_BLUE" );
167 static const trait_id trait_PARAIMMUNE( "PARAIMMUNE" );
168 static const trait_id trait_PROF_CHURL( "PROF_CHURL" );
169 static const trait_id trait_PROF_CYBERCO( "PROF_CYBERCO" );
170 static const trait_id trait_PROF_FED( "PROF_FED" );
171 static const trait_id trait_PROF_PD_DET( "PROF_PD_DET" );
172 static const trait_id trait_PROF_POLICE( "PROF_POLICE" );
173 static const trait_id trait_PROF_SWAT( "PROF_SWAT" );
174 static const trait_id trait_TAIL_CATTLE( "TAIL_CATTLE" );
175 static const trait_id trait_THRESH_MARLOSS( "THRESH_MARLOSS" );
176 static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" );
177 static const trait_id trait_FAST_REFLEXES( "FAST_REFLEXES" );
178 
179 static const mtype_id mon_ant_acid_larva( "mon_ant_acid_larva" );
180 static const mtype_id mon_ant_acid_queen( "mon_ant_acid_queen" );
181 static const mtype_id mon_ant_larva( "mon_ant_larva" );
182 static const mtype_id mon_biollante( "mon_biollante" );
183 static const mtype_id mon_blob( "mon_blob" );
184 static const mtype_id mon_blob_brain( "mon_blob_brain" );
185 static const mtype_id mon_blob_large( "mon_blob_large" );
186 static const mtype_id mon_blob_small( "mon_blob_small" );
187 static const mtype_id mon_breather( "mon_breather" );
188 static const mtype_id mon_breather_hub( "mon_breather_hub" );
189 static const mtype_id mon_creeper_hub( "mon_creeper_hub" );
190 static const mtype_id mon_creeper_vine( "mon_creeper_vine" );
191 static const mtype_id mon_nursebot_defective( "mon_nursebot_defective" );
192 static const mtype_id mon_dermatik( "mon_dermatik" );
193 static const mtype_id mon_fungal_hedgerow( "mon_fungal_hedgerow" );
194 static const mtype_id mon_fungal_tendril( "mon_fungal_tendril" );
195 static const mtype_id mon_fungal_wall( "mon_fungal_wall" );
196 static const mtype_id mon_fungaloid( "mon_fungaloid" );
197 static const mtype_id mon_fungaloid_young( "mon_fungaloid_young" );
198 static const mtype_id mon_headless_dog_thing( "mon_headless_dog_thing" );
199 static const mtype_id mon_hound_tindalos_afterimage( "mon_hound_tindalos_afterimage" );
200 static const mtype_id mon_leech_blossom( "mon_leech_blossom" );
201 static const mtype_id mon_leech_root_drone( "mon_leech_root_drone" );
202 static const mtype_id mon_leech_root_runner( "mon_leech_root_runner" );
203 static const mtype_id mon_leech_stalk( "mon_leech_stalk" );
204 static const mtype_id mon_manhack( "mon_manhack" );
205 static const mtype_id mon_shadow( "mon_shadow" );
206 static const mtype_id mon_triffid( "mon_triffid" );
207 static const mtype_id mon_turret_searchlight( "mon_turret_searchlight" );
208 static const mtype_id mon_zombie_dancer( "mon_zombie_dancer" );
209 static const mtype_id mon_zombie_gasbag_crawler( "mon_zombie_gasbag_crawler" );
210 static const mtype_id mon_zombie_gasbag_impaler( "mon_zombie_gasbag_impaler" );
211 static const mtype_id mon_zombie_jackson( "mon_zombie_jackson" );
212 static const mtype_id mon_zombie_skeltal_minion( "mon_zombie_skeltal_minion" );
213 
214 static const bionic_id bio_uncanny_dodge( "bio_uncanny_dodge" );
215 
216 // shared utility functions
within_visual_range(monster * z,int max_range)217 static bool within_visual_range( monster *z, int max_range )
218 {
219     Character &player_character = get_player_character();
220     return !( rl_dist( z->pos(), player_character.pos() ) > max_range ||
221               !z->sees( player_character ) );
222 }
223 
within_target_range(const monster * const z,const Creature * const target,int range)224 static bool within_target_range( const monster *const z, const Creature *const target, int range )
225 {
226     return target != nullptr &&
227            rl_dist( z->pos(), target->pos() ) <= range &&
228            z->sees( *target );
229 }
230 
sting_get_target(monster * z,float range=5.0f)231 static Creature *sting_get_target( monster *z, float range = 5.0f )
232 {
233     Creature *target = z->attack_target();
234 
235     if( target == nullptr ) {
236         return nullptr;
237     }
238 
239     // Can't see/reach target, no attack
240     if( !z->sees( *target ) ||
241         !get_map().clear_path( z->pos(), target->pos(), range, 1, 100 ) ) {
242         return nullptr;
243     }
244 
245     return rl_dist( z->pos(), target->pos() ) <= range ? target : nullptr;
246 }
247 
sting_shoot(monster * z,Creature * target,damage_instance & dam,float range)248 static bool sting_shoot( monster *z, Creature *target, damage_instance &dam, float range )
249 {
250     if( target->uncanny_dodge() ) {
251         target->add_msg_if_player( m_bad, _( "The %s shoots a dart but you dodge it." ),
252                                    z->name() );
253         return false;
254     }
255 
256     projectile proj;
257     proj.speed = 10;
258     proj.range = range;
259     proj.impact.add( dam );
260     proj.proj_effects.insert( "NO_OVERSHOOT" );
261 
262     dealt_projectile_attack atk = projectile_attack( proj, z->pos(), target->pos(),
263                                   dispersion_sources{ 500 }, z );
264     if( atk.dealt_dam.total_damage() > 0 ) {
265         target->add_msg_if_player( m_bad, _( "The %s shoots a dart into you!" ), z->name() );
266         return true;
267     } else {
268         if( atk.missed_by == 1 ) {
269             target->add_msg_if_player( m_good,
270                                        _( "The %s shoots a dart at you, but misses!" ),
271                                        z->name() );
272         } else {
273             target->add_msg_if_player( m_good,
274                                        _( "The %s shoots a dart but it bounces off your armor." ),
275                                        z->name() );
276         }
277         return false;
278     }
279 }
280 
make_fake_npc(monster * z,int str,int dex,int inte,int per)281 static npc make_fake_npc( monster *z, int str, int dex, int inte, int per )
282 {
283     npc tmp;
284     tmp.name = _( "The " ) + z->name();
285     tmp.set_fake( true );
286     tmp.recoil = 0;
287     tmp.setpos( z->pos() );
288     tmp.str_cur = str;
289     tmp.dex_cur = dex;
290     tmp.int_cur = inte;
291     tmp.per_cur = per;
292     if( z->friendly != 0 ) {
293         tmp.set_attitude( NPCATT_FOLLOW );
294     } else {
295         tmp.set_attitude( NPCATT_KILL );
296     }
297     return tmp;
298 }
299 
none(monster *)300 bool mattack::none( monster * )
301 {
302     return true;
303 }
304 
eat_crop(monster * z)305 bool mattack::eat_crop( monster *z )
306 {
307     cata::optional<tripoint> target;
308     int num_targets = 1;
309     map &here = get_map();
310     for( const auto &p : here.points_in_radius( z->pos(), 1 ) ) {
311         if( here.has_flag( "PLANT", p ) && one_in( num_targets ) ) {
312             num_targets++;
313             target = p;
314         }
315     }
316     if( target ) {
317         here.furn_set( *target, furn_str_id( here.furn( *target )->plant->base ) );
318         here.i_clear( *target );
319         return true;
320     }
321     return true;
322 }
323 
eat_food(monster * z)324 bool mattack::eat_food( monster *z )
325 {
326     map &here = get_map();
327     for( const auto &p : here.points_in_radius( z->pos(), 1 ) ) {
328         //Protect crop seeds from carnivores, give omnivores eat_crop special also
329         if( here.has_flag( "PLANT", p ) ) {
330             continue;
331         }
332         // Don't snap up food RIGHT under the player's nose.
333         if( z->friendly && rl_dist( get_player_location().pos(), p ) <= 2 ) {
334             continue;
335         }
336         map_stack items = here.i_at( p );
337         for( auto &item : items ) {
338             //Fun limit prevents scavengers from eating feces
339             if( !item.is_food() || item.get_comestible_fun() < -20 ) {
340                 continue;
341             }
342             //Don't eat own eggs
343             if( z->type->baby_egg != item.type->get_id() ) {
344                 int consumed = 1;
345                 if( item.count_by_charges() ) {
346                     here.use_charges( p, 0, item.type->get_id(), consumed );
347                 } else {
348                     here.use_amount( p, 0, item.type->get_id(), consumed );
349                 }
350                 return true;
351             }
352         }
353     }
354     return true;
355 }
356 
antqueen(monster * z)357 bool mattack::antqueen( monster *z )
358 {
359     std::vector<tripoint> egg_points;
360     std::vector<monster *> ants;
361     map &here = get_map();
362     // Count up all adjacent tiles the contain at least one egg.
363     for( const auto &dest : here.points_in_radius( z->pos(), 2 ) ) {
364         if( here.impassable( dest ) ) {
365             continue;
366         }
367 
368         if( monster *const mon = g->critter_at<monster>( dest ) ) {
369             if( mon->type->default_faction == STATIC( mfaction_str_id( "ant" ) ) && mon->type->upgrades ) {
370                 ants.push_back( mon );
371             }
372 
373             continue;
374         }
375 
376         if( g->is_empty( dest ) && here.has_items( dest ) ) {
377             for( item &i : here.i_at( dest ) ) {
378                 if( i.typeId() == itype_ant_egg ) {
379                     egg_points.push_back( dest );
380                     // Done looking at this tile
381                     break;
382                 }
383             }
384         }
385     }
386 
387     Character &player_character = get_player_character();
388     if( !ants.empty() ) {
389         // It takes a while
390         z->moves -= 100;
391         monster *ant = random_entry( ants );
392         if( player_character.sees( *z ) && player_character.sees( *ant ) ) {
393             add_msg( m_warning, _( "The %1$s feeds an %2$s and it grows!" ), z->name(),
394                      ant->name() );
395         }
396         ant->poly( ant->type->upgrade_into );
397     } else if( egg_points.empty() ) {
398         // There's no eggs nearby--lay one.
399         add_msg_if_player_sees( *z, _( "The %s lays an egg!" ), z->name() );
400         here.spawn_item( z->pos(), "ant_egg", 1, 0, calendar::turn );
401     } else {
402         // There are eggs nearby.  Let's hatch some.
403         // It takes a while
404         z->moves -= 20 * egg_points.size();
405         add_msg_if_player_sees( *z, m_warning, _( "The %s tends nearby eggs, and they hatch!" ),
406                                 z->name() );
407         for( const tripoint &egg_pos : egg_points ) {
408             map_stack items = here.i_at( egg_pos );
409             for( map_stack::iterator it = items.begin(); it != items.end(); ) {
410                 if( it->typeId() != itype_ant_egg ) {
411                     ++it;
412                     continue;
413                 }
414                 const mtype_id &mt = z->type->id == mon_ant_acid_queen ? mon_ant_acid_larva : mon_ant_larva;
415                 // Max one hatch per tile
416                 if( monster *const mon = g->place_critter_at( mt, egg_pos ) ) {
417                     mon->make_ally( *z );
418                     it = items.erase( it );
419                     break;
420                 }
421             }
422         }
423     }
424 
425     return true;
426 }
427 
shriek(monster * z)428 bool mattack::shriek( monster *z )
429 {
430     Creature *target = z->attack_target();
431     if( target == nullptr ||
432         rl_dist( z->pos(), target->pos() ) > 4 ||
433         !z->sees( *target ) ) {
434         return false;
435     }
436 
437     // It takes a while
438     z->moves -= 240;
439     sounds::sound( z->pos(), 50, sounds::sound_t::alert, _( "a terrible shriek!" ), false, "shout",
440                    "shriek" );
441     return true;
442 }
443 
shriek_alert(monster * z)444 bool mattack::shriek_alert( monster *z )
445 {
446     if( !z->can_act() || z->has_effect( effect_shrieking ) ) {
447         return false;
448     }
449 
450     Creature *target = z->attack_target();
451 
452     if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 15 ||
453         !z->sees( *target ) ) {
454         return false;
455     }
456     add_msg_if_player_sees( *z, _( "The %s begins shrieking!" ), z->name() );
457     z->moves -= 150;
458     sounds::sound( z->pos(), 120, sounds::sound_t::alert, _( "a piercing wail!" ), false, "shout",
459                    "wail" );
460     z->add_effect( effect_shrieking, 1_minutes );
461 
462     return true;
463 }
464 
shriek_stun(monster * z)465 bool mattack::shriek_stun( monster *z )
466 {
467     if( !z->can_act() || !z->has_effect( effect_shrieking ) ) {
468         return false;
469     }
470 
471     Creature *target = z->attack_target();
472     if( target == nullptr ) {
473         return false;
474     }
475 
476     int dist = rl_dist( z->pos(), target->pos() );
477     // Currently the cone is 2D, so don't use it for 3D attacks
478     if( dist > 7 ||
479         z->posz() != target->posz() ||
480         !z->sees( *target ) ) {
481         return false;
482     }
483 
484     units::angle target_angle = coord_to_angle( z->pos(), target->pos() );
485     units::angle cone_angle = 20_degrees;
486     map &here = get_map();
487     for( const tripoint &cone : here.points_in_radius( z->pos(), 4 ) ) {
488         units::angle tile_angle = coord_to_angle( z->pos(), cone );
489         units::angle diff = units::fabs( target_angle - tile_angle );
490         // Skip the target, because it's outside cone or it's the source
491         if( diff + cone_angle > 360_degrees || diff > cone_angle || cone == z->pos() ) {
492             continue;
493         }
494         // Affect the target
495         // Small bash to every square, silent to not flood message box
496         here.bash( cone, 4, true );
497 
498         // If a monster is there, chance for stun
499         Creature *target = g->critter_at( cone );
500         if( target == nullptr ) {
501             continue;
502         }
503         if( one_in( dist / 2 ) && !( target->is_immune_effect( effect_deaf ) ) ) {
504             target->add_effect( effect_dazed, rng( 1_minutes, 2_minutes ), false, rng( 1, ( 15 - dist ) / 3 ) );
505         }
506 
507     }
508 
509     return true;
510 }
511 
howl(monster * z)512 bool mattack::howl( monster *z )
513 {
514     Creature *target = z->attack_target();
515     if( target == nullptr ||
516         rl_dist( z->pos(), target->pos() ) > 4 ||
517         !z->sees( *target ) ) {
518         return false;
519     }
520 
521     // It takes a while
522     z->moves -= 200;
523     sounds::sound( z->pos(), 35, sounds::sound_t::alert, _( "an ear-piercing howl!" ), false, "shout",
524                    "howl" );
525 
526     // TODO: Make this use mon's faction when those are in
527     if( z->friendly != 0 ) {
528         for( monster &other : g->all_monsters() ) {
529             if( other.type != z->type ) {
530                 continue;
531             }
532             // Quote KA101: Chance of friendlying other howlers in the area, I'd imagine:
533             // wolves use howls for communication and can convey that the ape is on Team Wolf.
534             if( one_in( 4 ) ) {
535                 other.friendly = z->friendly;
536                 break;
537             }
538         }
539     }
540 
541     return true;
542 }
543 
rattle(monster * z)544 bool mattack::rattle( monster *z )
545 {
546     // TODO: Let it rattle at non-player friendlies
547     const int min_dist = z->friendly != 0 ? 1 : 4;
548     Creature *target = &get_player_character();
549     // Can't use attack_target - the snake has no target
550     if( rl_dist( z->pos(), target->pos() ) > min_dist ||
551         !z->sees( *target ) ) {
552         return false;
553     }
554 
555     // It takes a very short while
556     z->moves -= 20;
557     sounds::sound( z->pos(), 10, sounds::sound_t::alarm, _( "a sibilant rattling sound!" ), false,
558                    "misc", "rattling" );
559 
560     return true;
561 }
562 
acid(monster * z)563 bool mattack::acid( monster *z )
564 {
565     if( !z->can_act() ) {
566         return false;
567     }
568 
569     Creature *target = z->attack_target();
570     if( target == nullptr ) {
571         return false;
572     }
573 
574     map &here = get_map();
575     // Can't see/reach target, no attack
576     if( !z->sees( *target ) ||
577         !here.clear_path( z->pos(), target->pos(), 10, 1, 100 ) ) {
578         return false;
579     }
580     // It takes a while
581     z->moves -= 300;
582     sounds::sound( z->pos(), 4, sounds::sound_t::combat, _( "a spitting noise." ), false, "misc",
583                    "spitting" );
584 
585     projectile proj;
586     proj.speed = 10;
587     // Mostly just for momentum
588     proj.impact.add_damage( damage_type::ACID, 5 );
589     proj.range = 10;
590     proj.proj_effects.insert( "NO_OVERSHOOT" );
591     dealt_projectile_attack dealt = projectile_attack( proj, z->pos(), target->pos(), dispersion_sources{ 5400 },
592                                     z );
593     const tripoint &hitp = dealt.end_point;
594     const Creature *hit_critter = dealt.hit_critter;
595     if( hit_critter == nullptr && here.hit_with_acid( hitp ) ) {
596         add_msg_if_player_sees( hitp,  _( "A glob of acid hits the %s!" ), here.tername( hitp ) );
597         if( here.impassable( hitp ) ) {
598             // TODO: Allow it to spill on the side it hit from
599             return true;
600         }
601     }
602 
603     for( int i = -3; i <= 3; i++ ) {
604         for( int j = -3; j <= 3; j++ ) {
605             tripoint dest = hitp + tripoint( i, j, 0 );
606             if( here.passable( dest ) &&
607                 here.clear_path( dest, hitp, 6, 1, 100 ) &&
608                 ( ( one_in( std::abs( j ) ) && one_in( std::abs( i ) ) ) || ( i == 0 && j == 0 ) ) ) {
609                 here.add_field( dest, fd_acid, 2 );
610             }
611         }
612     }
613 
614     return true;
615 }
616 
acid_barf(monster * z)617 bool mattack::acid_barf( monster *z )
618 {
619     if( !z->can_act() ) {
620         return false;
621     }
622 
623     // Let it be used on non-player creatures
624     Creature *target = z->attack_target();
625     if( target == nullptr || !z->is_adjacent( target, false ) ) {
626         return false;
627     }
628 
629     z->moves -= 80;
630     // Make sure it happens before uncanny dodge
631     get_map().add_field( target->pos(), fd_acid, 1 );
632     bool uncanny = target->uncanny_dodge();
633     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
634     if( uncanny || dodge_check( z, target ) ) {
635         game_message_type msg_type = target->is_avatar() ? m_warning : m_info;
636         target->add_msg_player_or_npc( msg_type,
637                                        _( "The %s barfs acid at you, but you dodge!" ),
638                                        _( "The %s barfs acid at <npcname>, but they dodge!" ),
639                                        z->name() );
640         if( !uncanny ) {
641             target->on_dodge( z, z->type->melee_skill );
642         }
643 
644         return true;
645     }
646 
647     const bodypart_id &hit = target->get_random_body_part();
648     int dam = rng( 5, 12 );
649     dam = target->deal_damage( z,  hit, damage_instance( damage_type::ACID,
650                                dam ) ).total_damage();
651     target->add_env_effect( effect_corroding, hit, 5, time_duration::from_turns( dam / 2 + 5 ), hit );
652 
653     if( dam > 0 ) {
654         game_message_type msg_type = target->is_avatar() ? m_bad : m_info;
655         target->add_msg_player_or_npc( msg_type,
656                                        //~ 1$s is monster name, 2$s bodypart in accusative
657                                        _( "The %1$s barfs acid on your %2$s for %3$d damage!" ),
658                                        //~ 1$s is monster name, 2$s bodypart in accusative
659                                        _( "The %1$s barfs acid on <npcname>'s %2$s for %3$d damage!" ),
660                                        z->name(),
661                                        body_part_name_accusative( hit ),
662                                        dam );
663 
664         if( hit == bodypart_id( "eyes" ) ) {
665             target->add_env_effect( effect_blind, bodypart_id( "eyes" ), 3, 1_minutes );
666         }
667     } else {
668         target->add_msg_player_or_npc(
669             _( "The %1$s barfs acid on your %2$s, but it washes off the armor!" ),
670             _( "The %1$s barfs acid on <npcname>'s %2$s, but it washes off the armor!" ),
671             z->name(),
672             body_part_name_accusative( hit ) );
673     }
674 
675     target->on_hit( z, hit,  z->type->melee_skill );
676 
677     return true;
678 }
679 
acid_accurate(monster * z)680 bool mattack::acid_accurate( monster *z )
681 {
682     if( !z->can_act() ) {
683         return false;
684     }
685 
686     Creature *target = z->attack_target();
687     if( target == nullptr ) {
688         return false;
689     }
690 
691     const int range = rl_dist( z->pos(), target->pos() );
692     if( range > 10 || range < 2 || !z->sees( *target ) ) {
693         return false;
694     }
695 
696     z->moves -= 50;
697 
698     projectile proj;
699     proj.speed = 10;
700     proj.range = 10;
701     proj.proj_effects.insert( "BLINDS_EYES" );
702     proj.proj_effects.insert( "NO_DAMAGE_SCALING" );
703     proj.impact.add_damage( damage_type::ACID, rng( 3, 5 ) );
704     // Make it arbitrarily less accurate at close ranges
705     projectile_attack( proj, z->pos(), target->pos(), dispersion_sources{ 8000.0 * range }, z );
706 
707     return true;
708 }
709 
shockstorm(monster * z)710 bool mattack::shockstorm( monster *z )
711 {
712     if( !z->can_act() ) {
713         return false;
714     }
715 
716     Creature *target = z->attack_target();
717     if( target == nullptr ) {
718         return false;
719     }
720 
721     Character &player_character = get_player_character();
722     bool seen = player_character.sees( *z );
723     map &here = get_map();
724     // Can't see/reach target, no attack
725     if( !z->sees( *target ) ||
726         !here.clear_path( z->pos(), target->pos(), 12, 1, 100 ) ) {
727         return false;
728     }
729 
730     // It takes a while
731     z->moves -= 50;
732 
733     if( seen ) {
734         game_message_type msg_type = target->is_avatar() ? m_bad : m_neutral;
735         add_msg( msg_type, _( "A bolt of electricity arcs towards %s!" ), target->disp_name() );
736     }
737     if( !player_character.is_deaf() ) {
738         sfx::play_variant_sound( "fire_gun", "bio_lightning", sfx::get_heard_volume( z->pos() ) );
739     }
740     tripoint tarp( target->posx() + rng( -1, 1 ) + rng( -1, 1 ),
741                    target->posy() + rng( -1, 1 ) + rng( -1, 1 ),
742                    target->posz() );
743     std::vector<tripoint> bolt = line_to( z->pos(), tarp, 0, 0 );
744     // Fill the LOS with electricity
745     for( auto &i : bolt ) {
746         if( !one_in( 4 ) ) {
747             here.add_field( i, fd_electricity, rng( 1, 3 ) );
748         }
749     }
750     // 5x5 cloud of electricity at the square hit
751     for( const auto &dest : here.points_in_radius( tarp, 2 ) ) {
752         if( !one_in( 4 ) ) {
753             here.add_field( dest, fd_electricity, rng( 1, 3 ) );
754         }
755     }
756 
757     return true;
758 }
759 
shocking_reveal(monster * z)760 bool mattack::shocking_reveal( monster *z )
761 {
762     shockstorm( z );
763     const translation WHAT_A_SCOOP = SNIPPET.random_from_category( "clickbait" ).value_or(
764                                          translation() );
765     sounds::sound( z->pos(), 10, sounds::sound_t::alert,
766                    string_format( _( "the %s obnoxiously yelling \"%s!!!\"" ),
767                                   z->name(), WHAT_A_SCOOP ) );
768     return true;
769 }
770 
pull_metal_weapon(monster * z)771 bool mattack::pull_metal_weapon( monster *z )
772 {
773     ////////////////////////////////////////////////////////////////////////////////////////////////
774     // Constants and Configuration
775 
776     // max distance that "pull_metal_weapon" can be applied to the target.
777     constexpr int max_distance = 12;
778 
779     // attack movement costs
780     constexpr int att_cost_pull = 150;
781 
782     // minimum str to resist "pull_metal_weapon"
783     constexpr int min_str = 4;
784 
785     Creature *target = z->attack_target();
786     if( target == nullptr ) {
787         return false;
788     }
789 
790     // Can't see/reach target, no attack
791     if( !z->sees( *target ) || !get_map().clear_path( z->pos(), target->pos(),
792             max_distance, 1, 100 ) ) {
793         return false;
794     }
795     Character *foe = dynamic_cast< Character * >( target );
796     if( foe != nullptr ) {
797         // Wielded steel or iron items except for built-in things like bionic claws or monomolecular blade
798         if( !foe->weapon.has_flag( flag_NO_UNWIELD ) &&
799             ( foe->weapon.made_of( material_id( "iron" ) ) ||
800               foe->weapon.made_of( material_id( "hardsteel" ) ) ||
801               foe->weapon.made_of( material_id( "steel" ) ) ||
802               foe->weapon.made_of( material_id( "budget_steel" ) ) ) ) {
803             const int wp_skill = foe->get_skill_level( skill_melee );
804             // It takes a while
805             z->moves -= att_cost_pull;
806             int success = 100;
807             ///\EFFECT_STR increases resistance to pull_metal_weapon special attack
808             if( foe->str_cur > min_str ) {
809                 ///\EFFECT_MELEE increases resistance to pull_metal_weapon special attack
810                 success = std::max( 100 - ( 6 * ( foe->str_cur - 6 ) ) - ( 6 * wp_skill ), 0 );
811             }
812             game_message_type m_type = foe->is_avatar() ? m_bad : m_neutral;
813             if( rng( 1, 100 ) <= success ) {
814                 item pulled_weapon = foe->remove_weapon();
815                 projectile proj;
816                 proj.speed  = 50;
817                 proj.impact = damage_instance::physical( pulled_weapon.weight() / 250_gram, 0, 0, 0 );
818                 // make the projectile stop one tile short to prevent hitting the monster
819                 proj.range = rl_dist( foe->pos(), z->pos() ) - 1;
820                 proj.proj_effects = { { "NO_ITEM_DAMAGE", "DRAW_AS_LINE", "NO_DAMAGE_SCALING", "JET" } };
821 
822                 dealt_projectile_attack dealt = projectile_attack( proj, foe->pos(), z->pos(), dispersion_sources { 0 },
823                                                 z );
824                 get_map().add_item( dealt.end_point, pulled_weapon );
825                 target->add_msg_player_or_npc( m_type, _( "The %s is pulled away from your hands!" ),
826                                                _( "The %s is pulled away from <npcname>'s hands!" ), pulled_weapon.tname() );
827                 if( foe->has_activity( ACT_RELOAD ) ) {
828                     foe->cancel_activity();
829                 }
830             } else {
831                 target->add_msg_player_or_npc( m_type,
832                                                _( "The %s unsuccessfully attempts to pull your weapon away." ),
833                                                _( "The %s unsuccessfully attempts to pull <npcname>'s weapon away." ), z->name() );
834             }
835         }
836     }
837 
838     return true;
839 }
840 
boomer(monster * z)841 bool mattack::boomer( monster *z )
842 {
843     if( !z->can_act() ) {
844         return false;
845     }
846 
847     Creature *target = z->attack_target();
848     if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 3 || !z->sees( *target ) ) {
849         return false;
850     }
851 
852     map &here = get_map();
853     std::vector<tripoint> line = here.find_clear_path( z->pos(), target->pos() );
854     // It takes a while
855     z->moves -= 250;
856     Character &player_character = get_player_character();
857     bool u_see = player_character.sees( *z );
858     if( u_see ) {
859         add_msg( m_warning, _( "The %s spews bile!" ), z->name() );
860     }
861     for( auto &i : line ) {
862         here.add_field( i, fd_bile, 1 );
863         // If bile hit a solid tile, return.
864         if( here.impassable( i ) ) {
865             here.add_field( i, fd_bile, 3 );
866             add_msg_if_player_sees( i,  _( "Bile splatters on the %s!" ), here.tername( i ) );
867             return true;
868         }
869     }
870     if( !target->uncanny_dodge() ) {
871         ///\EFFECT_DODGE increases chance to avoid boomer effect
872         if( rng( 0, 10 ) > target->get_dodge() || one_in( target->get_dodge() ) ) {
873             target->add_env_effect( effect_boomered, bodypart_id( "eyes" ), 3, 12_turns );
874         } else if( u_see ) {
875             target->add_msg_player_or_npc( _( "You dodge it!" ),
876                                            _( "<npcname> dodges it!" ) );
877         }
878         target->on_dodge( z, 5 );
879     }
880 
881     return true;
882 }
883 
boomer_glow(monster * z)884 bool mattack::boomer_glow( monster *z )
885 {
886     if( !z->can_act() ) {
887         return false;
888     }
889 
890     Creature *target = z->attack_target();
891     if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 3 || !z->sees( *target ) ) {
892         return false;
893     }
894 
895     map &here = get_map();
896     std::vector<tripoint> line = here.find_clear_path( z->pos(), target->pos() );
897     // It takes a while
898     z->moves -= 250;
899     Character &player_character = get_player_character();
900     bool u_see = player_character.sees( *z );
901     if( u_see ) {
902         add_msg( m_warning, _( "The %s spews bile!" ), z->name() );
903     }
904     for( auto &i : line ) {
905         here.add_field( i, fd_bile, 1 );
906         if( here.impassable( i ) ) {
907             here.add_field( i, fd_bile, 3 );
908             add_msg_if_player_sees( i, _( "Bile splatters on the %s!" ), here.tername( i ) );
909             return true;
910         }
911     }
912     if( !target->uncanny_dodge() ) {
913         ///\EFFECT_DODGE increases chance to avoid glowing boomer effect
914         if( rng( 0, 10 ) > target->get_dodge() || one_in( target->get_dodge() ) ) {
915             target->add_env_effect( effect_boomered, bodypart_id( "eyes" ), 5, 25_turns );
916             target->on_dodge( z, 5 );
917             for( int i = 0; i < rng( 2, 4 ); i++ ) {
918                 const bodypart_id &bp = target->random_body_part();
919                 target->add_env_effect( effect_glowing, bp, 4, 4_minutes );
920                 if( target->has_effect( effect_glowing ) ) {
921                     break;
922                 }
923             }
924         } else {
925             target->add_msg_player_or_npc( _( "You dodge it!" ),
926                                            _( "<npcname> dodges it!" ) );
927         }
928     }
929 
930     return true;
931 }
932 
resurrect(monster * z)933 bool mattack::resurrect( monster *z )
934 {
935     // Chance to recover some of our missing speed (yes this will regain
936     // loses from being revived ourselves as well).
937     // Multiplying by (current base speed / max speed) means that the
938     // rate of speed regaining is unaffected by what our current speed is, i.e.
939     // we will regain the same amount per minute at speed 50 as speed 200.
940     if( one_in( static_cast<int>( 15 * static_cast<double>( z->get_speed_base() ) / static_cast<double>
941                                   ( z->type->speed ) ) ) ) {
942         // Restore 10% of our current speed, capping at our type maximum
943         z->set_speed_base( std::min( z->type->speed,
944                                      static_cast<int>( z->get_speed_base() + .1 * z->type->speed ) ) );
945     }
946 
947     int raising_level = 0;
948     if( z->has_effect( effect_raising ) ) {
949         raising_level = z->get_effect_int( effect_raising ) * 40;
950     }
951 
952     Character &player_character = get_player_character();
953     bool sees_necromancer = player_character.sees( *z );
954     std::vector<std::pair<tripoint, item *>> corpses;
955     // Find all corpses that we can see within 10 tiles.
956     int range = 10;
957     bool found_eligible_corpse = false;
958     int lowest_raise_score = INT_MAX;
959     map &here = get_map();
960     for( const tripoint &p : here.points_in_radius( z->pos(), range ) ) {
961         if( !g->is_empty( p ) || here.get_field_intensity( p, fd_fire ) > 1 ||
962             !here.sees( z->pos(), p, -1 ) ) {
963             continue;
964         }
965 
966         for( item &i : here.i_at( p ) ) {
967             const mtype *mt = i.get_mtype();
968             if( !( i.is_corpse() && i.can_revive() && i.active && mt->has_flag( MF_REVIVES ) &&
969                    mt->in_species( species_ZOMBIE ) && !mt->has_flag( MF_NO_NECRO ) ) ) {
970                 continue;
971             }
972 
973             found_eligible_corpse = true;
974             if( raising_level == 0 ) {
975                 // Since we have a target, start charging to raise it.
976                 if( sees_necromancer ) {
977                     add_msg( m_info, _( "The %s throws its arms wide." ), z->name() );
978                 }
979                 while( z->moves >= 0 ) {
980                     z->add_effect( effect_raising, 1_minutes );
981                     z->moves -= 100;
982                 }
983                 return false;
984             }
985             int raise_score = ( i.damage_level() + 1 ) * mt->hp + i.burnt;
986             lowest_raise_score = std::min( lowest_raise_score, raise_score );
987             if( raise_score <= raising_level ) {
988                 corpses.push_back( std::make_pair( p, &i ) );
989             }
990         }
991     }
992 
993     if( corpses.empty() ) { // No nearby corpses
994         if( found_eligible_corpse ) {
995             // There was a corpse, but we haven't charged enough.
996             if( sees_necromancer && x_in_y( 1, std::sqrt( lowest_raise_score / 30.0 ) ) ) {
997                 add_msg( m_info, _( "The %s gesticulates wildly." ), z->name() );
998             }
999             while( z->moves >= 0 ) {
1000                 z->add_effect( effect_raising, 1_minutes );
1001                 z->moves -= 100;
1002                 return false;
1003             }
1004         } else if( raising_level != 0 ) {
1005             z->remove_effect( effect_raising );
1006         }
1007         // Check to see if there are any nearby living zombies to see if we should get angry
1008         const bool allies = g->get_creature_if( [&]( const Creature & critter ) {
1009             const monster *const zed = dynamic_cast<const monster *>( &critter );
1010             return zed && zed != z && zed->type->has_flag( MF_REVIVES ) &&
1011                    zed->type->in_species( species_ZOMBIE ) &&
1012                    z->attitude_to( *zed ) == Creature::Attitude::FRIENDLY  &&
1013                    within_target_range( z, zed, 10 );
1014         } );
1015         if( !allies ) {
1016             // Nobody around who we could revive, get angry
1017             z->anger = 100;
1018         } else {
1019             // Someone is around who might die and we could revive,
1020             // calm down.
1021             z->anger = 5;
1022         }
1023         return false;
1024     } else {
1025         // We're reviving someone/could revive someone, calm down.
1026         z->anger = 5;
1027     }
1028 
1029     if( z->get_speed_base() <= z->type->speed / 2 ) {
1030         // We can only resurrect so many times in a time period
1031         // and we're currently out
1032         return false;
1033     }
1034 
1035     std::pair<tripoint, item *> raised = random_entry( corpses );
1036     // To appease static analysis
1037     cata_assert( raised.second );
1038     // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
1039     float corpse_damage = raised.second->damage_level();
1040     // Did we successfully raise something?
1041     if( g->revive_corpse( raised.first, *raised.second ) ) {
1042         here.i_rem( raised.first, raised.second );
1043         if( sees_necromancer ) {
1044             add_msg( m_info, _( "You feel a strange pulse of energy from the %s." ), z->name() );
1045         }
1046         z->remove_effect( effect_raising );
1047         // Takes one turn
1048         z->moves -= z->type->speed;
1049         // Penalize speed by between 10% and 50% based on how damaged the corpse is.
1050         float speed_penalty = 0.1 + ( corpse_damage * 0.1 );
1051         z->set_speed_base( z->get_speed_base() - speed_penalty * z->type->speed );
1052         monster *const zed = g->critter_at<monster>( raised.first );
1053         if( !zed ) {
1054             debugmsg( "Misplaced or failed to revive a zombie corpse" );
1055             return true;
1056         }
1057 
1058         zed->make_ally( *z );
1059         if( player_character.sees( *zed ) ) {
1060             add_msg( m_warning, _( "A nearby %s rises from the dead!" ), zed->name() );
1061         } else if( sees_necromancer ) {
1062             // We saw the necromancer but not the revival
1063             add_msg( m_info, _( "But nothing seems to happen." ) );
1064         }
1065     }
1066 
1067     return true;
1068 }
1069 
smash_specific(monster * z,Creature * target)1070 void mattack::smash_specific( monster *z, Creature *target )
1071 {
1072     if( target == nullptr || !z->is_adjacent( target, false ) ) {
1073         return;
1074     }
1075     if( z->has_flag( MF_RIDEABLE_MECH ) ) {
1076         z->use_mech_power( -5 );
1077     }
1078     z->set_goal( target->pos() );
1079     smash( z );
1080 }
1081 
smash(monster * z)1082 bool mattack::smash( monster *z )
1083 {
1084     if( !z->can_act() ) {
1085         return false;
1086     }
1087 
1088     Creature *target = z->attack_target();
1089     if( target == nullptr || !z->is_adjacent( target, false ) ) {
1090         return false;
1091     }
1092 
1093     //Don't try to smash immobile targets
1094     if( target->has_flag( MF_IMMOBILE ) ) {
1095         return false;
1096     }
1097 
1098     // Costs lots of moves to give you a little bit of a chance to get away.
1099     z->moves -= 400;
1100 
1101     if( target->uncanny_dodge() ) {
1102         return true;
1103     }
1104 
1105     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
1106     if( dodge_check( z, target ) ) {
1107         target->add_msg_player_or_npc( _( "The %s takes a powerful swing at you, but you dodge it!" ),
1108                                        _( "The %s takes a powerful swing at <npcname>, who dodges it!" ),
1109                                        z->name() );
1110         target->on_dodge( z, z->type->melee_skill );
1111         return true;
1112     }
1113 
1114     target->add_msg_player_or_npc( _( "A blow from the %1$s sends %2$s flying!" ),
1115                                    _( "A blow from the %s sends <npcname> flying!" ),
1116                                    z->name(), target->disp_name() );
1117     // TODO: Make this parabolic
1118     g->fling_creature( target, coord_to_angle( z->pos(), target->pos() ),
1119                        z->type->melee_sides * z->type->melee_dice * 3 );
1120 
1121     return true;
1122 }
1123 
1124 //--------------------------------------------------------------------------------------------------
1125 // TODO: move elsewhere
1126 //--------------------------------------------------------------------------------------------------
1127 
1128 //--------------------------------------------------------------------------------------------------
1129 /**
1130  * Find empty spaces around origin within a radius of N.
1131  *
1132  * @returns a pair with first  = array<tripoint, area>; area = (2*N + 1)^2.
1133  *                      second = the number of empty spaces found.
1134  */
1135 template <size_t N = 1>
1136 std::pair < std::array < tripoint, ( 2 * N + 1 ) * ( 2 * N + 1 ) >, size_t >
find_empty_neighbors(const tripoint & origin)1137 find_empty_neighbors( const tripoint &origin )
1138 {
1139     constexpr int r = static_cast<int>( N );
1140 
1141     std::pair < std::array < tripoint, ( 2 * N + 1 )*( 2 * N + 1 ) >, size_t > result;
1142 
1143     for( const tripoint &tmp : get_map().points_in_radius( origin, r ) ) {
1144         if( g->is_empty( tmp ) ) {
1145             result.first[result.second++] = tmp;
1146         }
1147     }
1148 
1149     return result;
1150 }
1151 
1152 //--------------------------------------------------------------------------------------------------
1153 /**
1154  * Find empty spaces around a creature within a radius of N.
1155  *
1156  * @see find_empty_neighbors
1157  */
1158 template <size_t N = 1>
1159 std::pair < std::array < tripoint, ( 2 * N + 1 ) * ( 2 * N + 1 ) >, size_t >
find_empty_neighbors(const Creature & c)1160 find_empty_neighbors( const Creature &c )
1161 {
1162     return find_empty_neighbors<N>( c.pos() );
1163 }
1164 
1165 //--------------------------------------------------------------------------------------------------
1166 /**
1167  * Get a size_t value in the closed interval [0, size]; a convenience to avoid messy casting.
1168   */
get_random_index(const size_t size)1169 static size_t get_random_index( const size_t size )
1170 {
1171     return static_cast<size_t>( rng( 0, static_cast<int>( size - 1 ) ) );
1172 }
1173 
1174 //--------------------------------------------------------------------------------------------------
1175 /**
1176  * Get a size_t value in the closed interval [0, c.size() - 1]; a convenience to avoid messy casting.
1177  */
1178 template <typename Container>
get_random_index(const Container & c)1179 size_t get_random_index( const Container &c )
1180 {
1181     return get_random_index( c.size() );
1182 }
1183 
science(monster * const z)1184 bool mattack::science( monster *const z ) // I said SCIENCE again!
1185 {
1186     ////////////////////////////////////////////////////////////////////////////////////////////////
1187     // Constants and Configuration
1188 
1189     // attack types
1190     enum : int {
1191         att_shock,
1192         att_radiation,
1193         att_manhack,
1194         att_acid_pool,
1195         att_flavor,
1196         att_enum_size
1197     };
1198 
1199     // max distance that "science" can be applied to the target.
1200     constexpr int max_distance = 5;
1201 
1202     // attack movement costs
1203     constexpr int att_cost_shock   = 0;
1204     constexpr int att_cost_rad     = 400;
1205     constexpr int att_cost_manhack = 200;
1206     constexpr int att_cost_acid    = 100;
1207     constexpr int att_cost_flavor  = 80;
1208 
1209     // radiation attack behavior
1210     // how hard it is to dodge
1211     constexpr int att_rad_dodge_diff    = 16;
1212     // (1/x) inverse chance to cause mutation.
1213     constexpr int att_rad_mutate_chance = 6;
1214     // min radiation
1215     constexpr int att_rad_dose_min      = 20;
1216     // max radiation
1217     constexpr int att_rad_dose_max      = 50;
1218 
1219     // acid attack behavior
1220     constexpr int att_acid_intensity = 3;
1221 
1222     if( !z->can_act() ) {
1223         return false;
1224     }
1225 
1226     ////////////////////////////////////////////////////////////////////////////////////////////////
1227     // Look for a valid target...
1228     Creature *const target = z->attack_target();
1229     if( !target ) {
1230         return false;
1231     }
1232 
1233     // too far
1234     const int dist = rl_dist( z->pos(), target->pos() );
1235     if( dist > max_distance ) {
1236         return false;
1237     }
1238 
1239     // can't attack what you can't see
1240     if( !z->sees( *target ) ) {
1241         return false;
1242     }
1243 
1244     ////////////////////////////////////////////////////////////////////////////////////////////////
1245     // okay, we have a valid target; populate valid attack options...
1246     std::array<int, att_enum_size> valid_attacks;
1247     size_t valid_attack_count = 0;
1248 
1249     // can only shock if adjacent
1250     if( dist == 1 ) {
1251         valid_attacks[valid_attack_count++] = att_shock;
1252     }
1253 
1254     Character *const foe = dynamic_cast<Character *>( target );
1255     if( foe && foe->is_avatar() && dist <= 2 ) {
1256         valid_attacks[valid_attack_count++] = att_radiation;
1257     }
1258 
1259     // need an open space for these attacks
1260     const auto empty_neighbors = find_empty_neighbors( *z );
1261     const size_t empty_neighbor_count = empty_neighbors.second;
1262 
1263     if( empty_neighbor_count ) {
1264         if( z->ammo[itype_bot_manhack] > 0 ) {
1265             valid_attacks[valid_attack_count++] = att_manhack;
1266         }
1267         valid_attacks[valid_attack_count++] = att_acid_pool;
1268     }
1269 
1270     // flavor is always okay
1271     valid_attacks[valid_attack_count++] = att_flavor;
1272 
1273     ////////////////////////////////////////////////////////////////////////////////////////////////
1274     // choose and do a valid attack
1275     const int attack_index = get_random_index( valid_attack_count );
1276     switch( valid_attacks[attack_index] ) {
1277         default :
1278             DebugLog( D_WARNING, D_GAME ) << "Bad enum value in science.";
1279             break;
1280         case att_shock :
1281             z->moves -= att_cost_shock;
1282 
1283             // Just reuse the taze - it's a bit different (shocks torso vs all),
1284             // but let's go for consistency here
1285             taze( z, target );
1286             break;
1287         case att_radiation : {
1288             z->moves -= att_cost_rad;
1289             add_msg_if_player_sees( *z, m_bad, _( "The %1$s fires a shimmering beam towards %2$s!" ),
1290                                     z->name(), target->disp_name() );
1291 
1292             // (1) Give the target a chance at an uncanny_dodge.
1293             // (2) If that fails, always fail to dodge 1 in dodge_skill times.
1294             // (3) If okay, dodge if dodge_skill > att_rad_dodge_diff.
1295             // (4) Otherwise, fail 1 in (att_rad_dodge_diff - dodge_skill) times.
1296             if( foe->uncanny_dodge() ) {
1297                 break;
1298             }
1299 
1300             const int  dodge_skill  = foe->get_dodge();
1301             const bool critial_fail = one_in( dodge_skill );
1302             const bool is_trivial   = dodge_skill > att_rad_dodge_diff;
1303 
1304             ///\EFFECT_DODGE increases chance to avoid science effect
1305             if( !critial_fail && ( is_trivial || dodge_skill > rng( 0, att_rad_dodge_diff ) ) ) {
1306                 target->add_msg_player_or_npc( _( "You dodge the beam!" ),
1307                                                _( "<npcname> dodges the beam!" ) );
1308             } else {
1309                 bool rad_proof = !foe->irradiate( rng( att_rad_dose_min, att_rad_dose_max ) );
1310                 if( rad_proof ) {
1311                     target->add_msg_if_player( m_good, _( "Your armor protects you from the radiation!" ) );
1312                 } else if( one_in( att_rad_mutate_chance ) ) {
1313                     foe->mutate();
1314                 } else {
1315                     target->add_msg_if_player( m_bad, _( "You get pins and needles all over." ) );
1316                 }
1317             }
1318         }
1319         break;
1320         case att_manhack : {
1321             z->moves -= att_cost_manhack;
1322             z->ammo[itype_bot_manhack]--;
1323             add_msg_if_player_sees( *z, m_warning, _( "A manhack flies out of one of the holes on the %s!" ),
1324                                     z->name() );
1325 
1326             const tripoint where = empty_neighbors.first[get_random_index( empty_neighbor_count )];
1327             if( monster *const manhack = g->place_critter_at( mon_manhack, where ) ) {
1328                 manhack->make_ally( *z );
1329             }
1330         }
1331         break;
1332         case att_acid_pool : {
1333             z->moves -= att_cost_acid;
1334             add_msg_if_player_sees( *z, m_warning,
1335                                     _( "The %s shudders, and some sort of caustic fluid leaks from a its damaged shell!" ),
1336                                     z->name() );
1337 
1338             map &here = get_map();
1339             // fill empty tiles with acid
1340             for( size_t i = 0; i < empty_neighbor_count; ++i ) {
1341                 const tripoint &p = empty_neighbors.first[i];
1342                 here.add_field( p, fd_acid, att_acid_intensity );
1343             }
1344         }
1345         break;
1346         case att_flavor : {
1347             // flavor messages
1348             static const std::array<std::string, 4> m_flavor = {{
1349                     translate_marker( "The %s shudders, letting out an eery metallic whining noise!" ),
1350                     translate_marker( "The %s scratches its long legs along the floor, shooting sparks." ),
1351                     translate_marker( "The %s bleeps inquiringly and focuses a red camera-eye on you." ),
1352                     translate_marker( "The %s's combat arms crackle with electricity." ),
1353                     //special case; leave the electricity last
1354                 }
1355             };
1356 
1357             const size_t i = get_random_index( m_flavor );
1358 
1359             // the special case; see above
1360             if( i == m_flavor.size() - 1 ) {
1361                 z->moves -= att_cost_flavor;
1362             }
1363             add_msg_if_player_sees( *z, m_warning, _( m_flavor[i] ), z->name() );
1364         }
1365         break;
1366     }
1367 
1368     return true;
1369 }
1370 
body_part_hit_by_plant()1371 static bodypart_id body_part_hit_by_plant()
1372 {
1373     bodypart_id hit;
1374     if( one_in( 2 ) ) {
1375         hit = bodypart_id( "leg_l" );
1376     } else {
1377         hit = bodypart_id( "leg_r" );
1378     }
1379     if( one_in( 4 ) ) {
1380         hit = bodypart_id( "torso" );
1381     } else if( one_in( 2 ) ) {
1382         if( one_in( 2 ) ) {
1383             hit = bodypart_id( "foot_l" );
1384         } else {
1385             hit = bodypart_id( "foot_r" );
1386         }
1387     }
1388     return hit;
1389 }
1390 
growplants(monster * z)1391 bool mattack::growplants( monster *z )
1392 {
1393     map &here = get_map();
1394     for( const auto &p : here.points_in_radius( z->pos(), 3 ) ) {
1395 
1396         // Only affect natural, dirtlike terrain or trees.
1397         if( !( here.has_flag_ter( "DIGGABLE", p ) ||
1398                here.has_flag_ter( "TREE", p ) ||
1399                here.ter( p ) == t_tree_young ) ) {
1400             continue;
1401         }
1402 
1403         if( here.is_bashable( p ) && one_in( 3 ) ) {
1404             // Destroy everything
1405             here.destroy( p );
1406             // And then make the ground fertile
1407             here.ter_set( p, t_dirtmound );
1408             continue;
1409         }
1410 
1411         // 1 in 4 chance to grow a tree
1412         if( !one_in( 4 ) ) {
1413             if( one_in( 3 ) ) {
1414                 // If no tree, perhaps underbrush
1415                 here.ter_set( p, t_underbrush );
1416             }
1417 
1418             continue;
1419         }
1420 
1421         // Grow a tree and pierce stuff with it
1422         Creature *critter = g->critter_at( p );
1423         // Don't grow under friends (and self)
1424         if( critter != nullptr &&
1425             z->attitude_to( *critter ) == Creature::Attitude::FRIENDLY ) {
1426             continue;
1427         }
1428 
1429         here.ter_set( p, t_tree_young );
1430         if( critter == nullptr || critter->uncanny_dodge() ) {
1431             continue;
1432         }
1433 
1434         const bodypart_id &hit = body_part_hit_by_plant();
1435         critter->add_msg_player_or_npc( m_bad,
1436                                         //~ %s is bodypart name in accusative.
1437                                         _( "A tree bursts forth from the earth and pierces your %s!" ),
1438                                         //~ %s is bodypart name in accusative.
1439                                         _( "A tree bursts forth from the earth and pierces <npcname>'s %s!" ),
1440                                         body_part_name_accusative( hit ) );
1441         critter->deal_damage( z, hit, damage_instance( damage_type::STAB, rng( 10, 30 ) ) );
1442     }
1443 
1444     // 1 in 5 chance of making existing vegetation grow larger
1445     if( !one_in( 5 ) ) {
1446         return true;
1447     }
1448     for( const tripoint &p : here.points_in_radius( z->pos(), 5 ) ) {
1449         const auto ter = here.ter( p );
1450         if( ter != t_tree_young && ter != t_underbrush ) {
1451             // Skip as soon as possible to avoid all the checks
1452             continue;
1453         }
1454 
1455         Creature *critter = g->critter_at( p );
1456         if( critter != nullptr && z->attitude_to( *critter ) == Creature::Attitude::FRIENDLY ) {
1457             // Don't buff terrain below friends (and self)
1458             continue;
1459         }
1460 
1461         if( ter == t_tree_young ) {
1462             // Young tree => tree
1463             // TODO: Make this deal damage too - young tree can be walked on, tree can't
1464             here.ter_set( p, t_tree );
1465         } else if( ter == t_underbrush ) {
1466             // Underbrush => young tree
1467             here.ter_set( p, t_tree_young );
1468             if( critter != nullptr && !critter->uncanny_dodge() ) {
1469                 const bodypart_id &hit = body_part_hit_by_plant();
1470                 critter->add_msg_player_or_npc( m_bad,
1471                                                 //~ %s is bodypart name in accusative.
1472                                                 _( "The underbrush beneath your feet grows and pierces your %s!" ),
1473                                                 //~ %s is bodypart name in accusative.
1474                                                 _( "Underbrush grows into a tree, and it pierces <npcname>'s %s!" ),
1475                                                 body_part_name_accusative( hit ) );
1476                 critter->deal_damage( z, hit, damage_instance( damage_type::STAB, rng( 10, 30 ) ) );
1477             }
1478         }
1479     }
1480 
1481     // added during refactor, previously had no cooldown reset
1482     return true;
1483 }
1484 
grow_vine(monster * z)1485 bool mattack::grow_vine( monster *z )
1486 {
1487     if( z->friendly ) {
1488         if( rl_dist( get_player_location().pos(), z->pos() ) <= 3 ) {
1489             // Friendly vines keep the area around you free, so you can move.
1490             return false;
1491         }
1492     }
1493     z->moves -= 100;
1494     // Attempt to fill up to 8 surrounding tiles.
1495     for( int i = 0; i < rng( 1, 8 ); ++i ) {
1496         if( monster *const vine = g->place_critter_around( mon_creeper_vine, z->pos(), 1 ) ) {
1497             vine->make_ally( *z );
1498             // Store position of parent hub in vine goal point.
1499             vine->set_goal( z->pos() );
1500         }
1501     }
1502 
1503     return true;
1504 }
1505 
vine(monster * z)1506 bool mattack::vine( monster *z )
1507 {
1508     int vine_neighbors = 0;
1509     map &here = get_map();
1510     bool parent_out_of_range = !here.inbounds( z->move_target() );
1511     monster *parent = g->critter_at<monster>( z->move_target() );
1512     if( !parent_out_of_range && ( parent == nullptr || parent->type->id != mon_creeper_hub ) ) {
1513         // TODO: Should probably die instead.
1514         return true;
1515     }
1516     z->moves -= 100;
1517     for( const tripoint &dest : here.points_in_radius( z->pos(), 1 ) ) {
1518         Creature *critter = g->critter_at( dest );
1519         if( critter != nullptr && z->attitude_to( *critter ) == Creature::Attitude::HOSTILE ) {
1520             if( critter->uncanny_dodge() ) {
1521                 return true;
1522             }
1523 
1524             const bodypart_id &bphit = critter->get_random_body_part();
1525             critter->add_msg_player_or_npc( m_bad,
1526                                             //~ 1$s monster name(vine), 2$s bodypart in accusative
1527                                             _( "The %1$s lashes your %2$s!" ),
1528                                             _( "The %1$s lashes <npcname>'s %2$s!" ),
1529                                             z->name(),
1530                                             body_part_name_accusative( bphit ) );
1531             damage_instance d;
1532             d.add_damage( damage_type::CUT, 8 );
1533             d.add_damage( damage_type::BASH, 8 );
1534             critter->deal_damage( z, bphit, d );
1535             critter->check_dead_state();
1536             z->moves -= 100;
1537             return true;
1538         }
1539 
1540         if( monster *const neighbor = g->critter_at<monster>( dest ) ) {
1541             if( neighbor->type->id == mon_creeper_vine ) {
1542                 vine_neighbors++;
1543             }
1544         }
1545     }
1546     // Calculate distance from nearest hub
1547     int dist_from_hub = rl_dist( z->pos(), z->move_target() );
1548     if( dist_from_hub > 20 || vine_neighbors > 5 || one_in( 7 - vine_neighbors ) ||
1549         !one_in( dist_from_hub ) ) {
1550         return true;
1551     }
1552     if( monster *const vine = g->place_critter_around( mon_creeper_vine, z->pos(), 1 ) ) {
1553         vine->make_ally( *z );
1554         vine->reset_special( "VINE" );
1555         // Store position of parent hub in vine goal point.
1556         vine->set_goal( z->move_target() );
1557     }
1558 
1559     return true;
1560 }
1561 
spit_sap(monster * z)1562 bool mattack::spit_sap( monster *z )
1563 {
1564     if( !z->can_act() ) {
1565         return false;
1566     }
1567 
1568     Creature *target = z->attack_target();
1569     if( target == nullptr ||
1570         rl_dist( z->pos(), target->pos() ) > 12 ||
1571         !z->sees( *target ) ) {
1572         return false;
1573     }
1574 
1575     z->moves -= 150;
1576 
1577     projectile proj;
1578     proj.speed = 10;
1579     proj.range = 12;
1580     proj.proj_effects.insert( "APPLY_SAP" );
1581     proj.impact.add_damage( damage_type::ACID, rng( 5, 10 ) );
1582     projectile_attack( proj, z->pos(), target->pos(), dispersion_sources{ 150 }, z );
1583 
1584     return true;
1585 }
1586 
triffid_heartbeat(monster * z)1587 bool mattack::triffid_heartbeat( monster *z )
1588 {
1589     sounds::sound( z->pos(), 14, sounds::sound_t::movement, _( "thu-THUMP." ), true, "misc",
1590                    "heartbeat" );
1591     z->moves -= 300;
1592     if( z->friendly != 0 ) {
1593         return true;
1594         // TODO: when friendly: open a way to the stairs, don't spawn monsters
1595     }
1596     Character &player_character = get_player_character();
1597     if( player_character.posz() != z->posz() ) {
1598         // Maybe remove this and allow spawning monsters above?
1599         return true;
1600     }
1601 
1602     map &here = get_map();
1603     static pathfinding_settings root_pathfind( 10, 20, 50, 0, false, false, false, false, false );
1604     if( rl_dist( z->pos(), player_character.pos() ) > 5 &&
1605         !here.route( player_character.pos(), z->pos(), root_pathfind ).empty() ) {
1606         add_msg( m_warning, _( "The root walls creak around you." ) );
1607         for( const tripoint &dest : here.points_in_radius( z->pos(), 3 ) ) {
1608             if( g->is_empty( dest ) && one_in( 4 ) ) {
1609                 here.ter_set( dest, t_root_wall );
1610             } else if( here.ter( dest ) == t_root_wall && one_in( 10 ) ) {
1611                 here.ter_set( dest, t_dirt );
1612             }
1613         }
1614         // Open blank tiles as long as there's no possible route
1615         int tries = 0;
1616         while( here.route( player_character.pos(), z->pos(), root_pathfind ).empty() &&
1617                tries < 20 ) {
1618             point p( rng( player_character.posx(), z->posx() - 3 ),
1619                      rng( player_character.posy(), z->posy() - 3 ) );
1620             tripoint dest( p, z->posz() );
1621             tries++;
1622             here.ter_set( dest, t_dirt );
1623             if( rl_dist( dest, player_character.pos() ) > 3 && g->num_creatures() < 30 &&
1624                 !g->critter_at( dest ) && one_in( 20 ) ) { // Spawn an extra monster
1625                 mtype_id montype = mon_triffid;
1626                 if( one_in( 4 ) ) {
1627                     montype = mon_creeper_hub;
1628                 } else if( one_in( 3 ) ) {
1629                     montype = mon_biollante;
1630                 }
1631                 if( monster *const plant = g->place_critter_at( montype, dest ) ) {
1632                     plant->make_ally( *z );
1633                 }
1634             }
1635         }
1636 
1637     } else { // The player is close enough for a fight!
1638 
1639         // Spawn a monster in (about) every second surrounding tile.
1640         for( int i = 0; i < 4; ++i ) {
1641             if( monster *const triffid = g->place_critter_around( mon_triffid, z->pos(), 1 ) ) {
1642                 triffid->make_ally( *z );
1643             }
1644         }
1645     }
1646 
1647     return true;
1648 }
1649 
fungus(monster * z)1650 bool mattack::fungus( monster *z )
1651 {
1652     // TODO: Infect NPCs?
1653     // It takes a while
1654     z->moves -= 200;
1655 
1656     //~ the sound of a fungus releasing spores
1657     sounds::sound( z->pos(), 10, sounds::sound_t::combat, _( "Pouf!" ), false, "misc", "puff" );
1658     add_msg_if_player_sees( *z, m_warning, _( "Spores are released from the %s!" ), z->name() );
1659 
1660     // Use less laggy methods of reproduction when there is a lot of mons around
1661     double spore_chance = 0.25;
1662     int radius = 1;
1663     if( g->num_creatures() > 25 ) {
1664         // Number of creatures in the bubble and the resulting average number of spores per "Pouf!":
1665         // 0-25: 2
1666         // 50  : 0.5
1667         // 75  : 0.22
1668         // 100 : 0.125
1669         // Assuming all creatures in the bubble were fungaloids (unlikely), the average number of spores per generation:
1670         // 25  : 50
1671         // 50  : 25
1672         // 75  : 17
1673         // 100 : 13
1674         spore_chance *= ( 25.0 / g->num_creatures() ) * ( 25.0 / g->num_creatures() );
1675         if( x_in_y( g->num_creatures(), 100 ) ) {
1676             // Don't make the increased radius spawn more spores
1677             const double old_area = ( ( 2 * radius + 1 ) * ( 2 * radius + 1 ) ) - 1;
1678             radius++;
1679             const double new_area = ( ( 2 * radius + 1 ) * ( 2 * radius + 1 ) ) - 1;
1680             spore_chance *= old_area / new_area;
1681         }
1682     }
1683 
1684     map &here = get_map();
1685     fungal_effects fe( *g, here );
1686     for( const tripoint &sporep : here.points_in_radius( z->pos(), radius ) ) {
1687         if( sporep == z->pos() ) {
1688             continue;
1689         }
1690         const int dist = rl_dist( z->pos(), sporep );
1691         if( !one_in( dist ) ||
1692             here.impassable( sporep ) ||
1693             ( dist > 1 && !here.clear_path( z->pos(), sporep, 2, 1, 10 ) ) ) {
1694             continue;
1695         }
1696 
1697         fe.fungalize( sporep, z, spore_chance );
1698     }
1699 
1700     return true;
1701 }
1702 
fungus_corporate(monster * z)1703 bool mattack::fungus_corporate( monster *z )
1704 {
1705     if( x_in_y( 1, 20 ) ) {
1706         sounds::sound( z->pos(), 10, sounds::sound_t::speech, _( "\"Buy SpOreos(tm) now!\"" ) );
1707         if( get_player_view().sees( *z ) ) {
1708             add_msg( m_warning, _( "Delicious snacks are released from the %s!" ), z->name() );
1709             get_map().add_item( z->pos(), item( "sporeos" ) );
1710         } // only spawns SpOreos if the player is near; can't have the COMMONERS stealing our product from good customers
1711         return true;
1712     } else {
1713         return fungus( z );
1714     }
1715 }
1716 
fungus_haze(monster * z)1717 bool mattack::fungus_haze( monster *z )
1718 {
1719     //~ That spore sound again
1720     sounds::sound( z->pos(), 10, sounds::sound_t::combat, _( "Pouf!" ), true, "misc", "puff" );
1721     add_msg_if_player_sees( *z, m_info, _( "The %s pulses, and fresh fungal material bursts forth." ),
1722                             z->name() );
1723     z->moves -= 150;
1724     map &here = get_map();
1725     for( const tripoint &dest : here.points_in_radius( z->pos(), 3 ) ) {
1726         here.add_field( dest, fd_fungal_haze, rng( 1, 2 ) );
1727     }
1728 
1729     return true;
1730 }
1731 
fungus_big_blossom(monster * z)1732 bool mattack::fungus_big_blossom( monster *z )
1733 {
1734     bool firealarm = false;
1735     const bool u_see = get_player_view().sees( *z );
1736     map &here = get_map();
1737     // Fungal fire-suppressor! >:D
1738     for( const tripoint &dest : here.points_in_radius( z->pos(), 6 ) ) {
1739         if( here.get_field_intensity( dest, fd_fire ) != 0 ) {
1740             firealarm = true;
1741         }
1742         if( firealarm ) {
1743             here.remove_field( dest, fd_fire );
1744             here.remove_field( dest, fd_smoke );
1745             here.add_field( dest, fd_fungal_haze, 3 );
1746         }
1747     }
1748     // Special effects handled outside the loop
1749     if( firealarm ) {
1750         if( u_see ) {
1751             // Sucks up all the smoke
1752             add_msg( m_warning, _( "The %s suddenly inhales!" ), z->name() );
1753         }
1754         //~Sound of a giant fungal blossom inhaling
1755         sounds::sound( z->pos(), 20, sounds::sound_t::combat, _( "WOOOSH!" ), true, "misc", "inhale" );
1756         if( u_see ) {
1757             add_msg( m_bad, _( "The %s discharges an immense flow of spores, smothering the flames!" ),
1758                      z->name() );
1759         }
1760         //~Sound of a giant fungal blossom blowing out the dangerous fire!
1761         sounds::sound( z->pos(), 20, sounds::sound_t::combat, _( "POUFF!" ), true, "misc", "exhale" );
1762         return true;
1763     } else {
1764         // No fire detected, routine haze-emission
1765         //~ That spore sound, much louder
1766         sounds::sound( z->pos(), 15, sounds::sound_t::combat, _( "POUF." ), true, "misc", "puff" );
1767         if( u_see ) {
1768             add_msg( m_info, _( "The %s pulses, and fresh fungal material bursts forth!" ), z->name() );
1769         }
1770         z->moves -= 150;
1771         for( const tripoint &dest : here.points_in_radius( z->pos(), 12 ) ) {
1772             here.add_field( dest, fd_fungal_haze, rng( 1, 2 ) );
1773         }
1774     }
1775 
1776     return true;
1777 }
1778 
fungus_inject(monster * z)1779 bool mattack::fungus_inject( monster *z )
1780 {
1781     // For faster copy+paste
1782     Creature *target = &get_player_character();
1783     Character &player_character = get_player_character();
1784     if( rl_dist( z->pos(), player_character.pos() ) > 1 ) {
1785         return false;
1786     }
1787 
1788     if( player_character.has_trait( trait_THRESH_MARLOSS ) ||
1789         player_character.has_trait( trait_THRESH_MYCUS ) ) {
1790         z->friendly = 1;
1791         return true;
1792     }
1793     if( ( player_character.has_trait( trait_MARLOSS ) ) &&
1794         ( player_character.has_trait( trait_MARLOSS_BLUE ) ) &&
1795         !player_character.crossed_threshold() ) {
1796         add_msg( m_info, _( "The %s seems to wave you toward the tower…" ), z->name() );
1797         z->anger = 0;
1798         return true;
1799     }
1800     if( z->friendly ) {
1801         // TODO: attack other creatures, not just player_character, for now just skip the code below as it
1802         // only attacks player_character but the monster is friendly.
1803         return true;
1804     }
1805     add_msg( m_warning, _( "The %s jabs at you with a needlelike point!" ), z->name() );
1806     z->moves -= 150;
1807 
1808     if( player_character.uncanny_dodge() ) {
1809         return true;
1810     }
1811 
1812     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
1813     if( dodge_check( z, target ) ) {
1814         target->add_msg_player_or_npc( _( "You dodge it!" ),
1815                                        _( "<npcname> dodges it!" ) );
1816         target->on_dodge( z, z->type->melee_skill );
1817         return true;
1818     }
1819 
1820     const bodypart_id hit = target->get_random_body_part();
1821     int dam = rng( 5, 11 );
1822     dam = player_character.deal_damage( z, hit, damage_instance( damage_type::CUT,
1823                                         dam ) ).total_damage();
1824 
1825     if( dam > 0 ) {
1826         //~ 1$s is monster name, 2$s bodypart in accusative
1827         add_msg( m_bad, _( "The %1$s sinks its point into your %2$s!" ), z->name(),
1828                  body_part_name_accusative( hit ) );
1829 
1830         if( one_in( 10 - dam ) ) {
1831             player_character.add_effect( effect_fungus, 10_minutes, true );
1832             add_msg( m_warning, _( "You feel thousands of live spores pumping into you…" ) );
1833         }
1834     } else {
1835         //~ 1$s is monster name, 2$s bodypart in accusative
1836         add_msg( _( "The %1$s strikes your %2$s, but your armor protects you." ), z->name(),
1837                  body_part_name_accusative( hit ) );
1838     }
1839 
1840     target->on_hit( z, hit,  z->type->melee_skill );
1841     player_character.check_dead_state();
1842 
1843     return true;
1844 }
1845 
fungus_bristle(monster * z)1846 bool mattack::fungus_bristle( monster *z )
1847 {
1848     Character &player_character = get_player_character();
1849     if( player_character.has_trait( trait_THRESH_MARLOSS ) ||
1850         player_character.has_trait( trait_THRESH_MYCUS ) ) {
1851         z->friendly = 1;
1852     }
1853     Creature *target = z->attack_target();
1854     if( target == nullptr ||
1855         !z->is_adjacent( target, true ) ||
1856         !z->sees( *target ) ) {
1857         return false;
1858     }
1859 
1860     game_message_type msg_type = target->is_avatar() ? m_warning : m_neutral;
1861 
1862     add_msg( msg_type, _( "The %1$s swipes at %2$s with a barbed tendril!" ), z->name(),
1863              target->disp_name() );
1864     z->moves -= 150;
1865 
1866     if( target->uncanny_dodge() ) {
1867         return true;
1868     }
1869 
1870     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
1871     if( dodge_check( z, target ) ) {
1872         target->add_msg_player_or_npc( _( "You dodge it!" ),
1873                                        _( "<npcname> dodges it!" ) );
1874         target->on_dodge( z, z->type->melee_skill );
1875         return true;
1876     }
1877 
1878     const bodypart_id hit = target->get_random_body_part();
1879     int dam = rng( 7, 16 );
1880     dam = target->deal_damage( z, hit, damage_instance( damage_type::CUT, dam ) ).total_damage();
1881 
1882     if( dam > 0 ) {
1883         //~ 1$s is monster name, 2$s bodypart in accusative
1884         target->add_msg_if_player( m_bad, _( "The %1$s sinks several needlelike barbs into your %2$s!" ),
1885                                    z->name(), body_part_name_accusative( hit ) );
1886 
1887         if( one_in( 15 - dam ) ) {
1888             target->add_effect( effect_fungus, 20_minutes, true );
1889             target->add_msg_if_player( m_warning,
1890                                        _( "You feel thousands of live spores pumping into you…" ) );
1891         }
1892     } else {
1893         //~ 1$s is monster name, 2$s bodypart in accusative
1894         target->add_msg_if_player( _( "The %1$s slashes your %2$s, but your armor protects you." ),
1895                                    z->name(), body_part_name_accusative( hit ) );
1896     }
1897 
1898     target->on_hit( z, hit,  z->type->melee_skill );
1899 
1900     return true;
1901 }
1902 
fungus_growth(monster * z)1903 bool mattack::fungus_growth( monster *z )
1904 {
1905     add_msg_if_player_sees( *z, m_warning, _( "The %s grows into an adult!" ), z->name() );
1906     z->poly( mon_fungaloid );
1907 
1908     return false;
1909 }
1910 
fungus_sprout(monster * z)1911 bool mattack::fungus_sprout( monster *z )
1912 {
1913     // To avoid map shift weirdness
1914     bool push_player = false;
1915     Character &player_character = get_player_character();
1916     for( const tripoint &dest : get_map().points_in_radius( z->pos(), 1 ) ) {
1917         if( player_character.pos() == dest ) {
1918             push_player = true;
1919         }
1920         if( monster *const wall = g->place_critter_at( mon_fungal_wall, dest ) ) {
1921             wall->make_ally( *z );
1922         }
1923     }
1924 
1925     if( push_player ) {
1926         const units::angle angle = coord_to_angle( z->pos(), player_character.pos() );
1927         add_msg( m_bad, _( "You're shoved away as a fungal wall grows!" ) );
1928         g->fling_creature( &player_character, angle, rng( 10, 50 ) );
1929     }
1930 
1931     return true;
1932 }
1933 
fungus_fortify(monster * z)1934 bool mattack::fungus_fortify( monster *z )
1935 {
1936     if( z->friendly ) {
1937         // TODO: handle friendly monsters
1938         return false;
1939     }
1940     Creature *target = &get_player_character();
1941     Character &player_character = get_player_character();
1942     bool mycus = false;
1943     bool peaceful = true;
1944     //No nifty support effects.  Yet.  This lets it rebuild hedges.
1945     if( player_character.has_trait( trait_THRESH_MARLOSS ) ||
1946         player_character.has_trait( trait_THRESH_MYCUS ) ) {
1947         mycus = true;
1948     }
1949     map &here = get_map();
1950     if( ( player_character.has_trait( trait_MARLOSS ) ) &&
1951         ( player_character.has_trait( trait_MARLOSS_BLUE ) ) &&
1952         !player_character.crossed_threshold() && !mycus ) {
1953         // You have the other two.  Is it really necessary for us to fight?
1954         add_msg( m_info, _( "The %s spreads its tendrils.  It seems as though it's expecting you…" ),
1955                  z->name() );
1956         if( rl_dist( z->pos(), player_character.pos() ) < 3 ) {
1957             if( query_yn( _( "The tower extends and aims several tendrils from its depths.  Hold still?" ) ) ) {
1958                 add_msg( m_warning,
1959                          _( "The %s works several tendrils into your arms, legs, torso, and even neck…" ),
1960                          z->name() );
1961                 player_character.hurtall( 1, z );
1962                 add_msg( m_warning,
1963                          _( "You see a clear golden liquid pump through the tendrils--and then lose consciousness." ) );
1964                 player_character.unset_mutation( trait_MARLOSS );
1965                 player_character.unset_mutation( trait_MARLOSS_BLUE );
1966                 player_character.set_mutation( trait_THRESH_MARLOSS );
1967                 here.ter_set( player_character.pos(),
1968                               t_marloss ); // We only show you the door.  You walk through it on your own.
1969                 get_memorial().add(
1970                     pgettext( "memorial_male", "Was shown to the Marloss Gateway." ),
1971                     pgettext( "memorial_female", "Was shown to the Marloss Gateway." ) );
1972                 add_msg( m_good,
1973                          _( "You wake up in a marloss bush.  Almost *cradled* in it, actually, as though it grew there for you." ) );
1974                 add_msg( m_good,
1975                          //~ Beginning to hear the Mycus while conscious: this is it speaking
1976                          _( "assistance, on an arduous quest.  unity.  together we have reached the door.  now to pass through…" ) );
1977                 return true;
1978             } else {
1979                 peaceful = false; // You declined the offer.  Fight!
1980             }
1981         }
1982     } else {
1983         peaceful = false; // You weren't eligible.  Fight!
1984     }
1985 
1986     bool fortified = false;
1987     bool push_player = false; // To avoid map shift weirdness
1988     for( const tripoint &dest : here.points_in_radius( z->pos(), 1 ) ) {
1989         if( player_character.pos() == dest ) {
1990             push_player = true;
1991         }
1992         if( monster *const wall = g->place_critter_at( mon_fungal_hedgerow, dest ) ) {
1993             wall->make_ally( *z );
1994             fortified = true;
1995         }
1996     }
1997     if( push_player ) {
1998         add_msg( m_bad, _( "You're shoved away as a fungal hedgerow grows!" ) );
1999         g->fling_creature( &player_character, coord_to_angle( z->pos(), player_character.pos() ), rng( 10,
2000                            50 ) );
2001     }
2002     if( fortified || mycus || peaceful ) {
2003         return true;
2004     }
2005 
2006     // TODO: De-playerize the whole block
2007     const int dist = rl_dist( z->pos(), player_character.pos() );
2008     if( dist >= 12 ) {
2009         return false;
2010     }
2011 
2012     if( dist > 3 ) {
2013         // Oops, can't reach. ):
2014         // How's about we spawn more tendrils? :)
2015         // Aimed at the player, too?  Sure!
2016         const tripoint hit_pos = target->pos() + point( rng( -1, 1 ), rng( -1, 1 ) );
2017         if( hit_pos == target->pos() && !target->uncanny_dodge() ) {
2018             const bodypart_id &hit = body_part_hit_by_plant();
2019             //~ %s is bodypart name in accusative.
2020             add_msg( m_bad, _( "A fungal tendril bursts forth from the earth and pierces your %s!" ),
2021                      body_part_name_accusative( hit ) );
2022             player_character.deal_damage( z,  hit, damage_instance( damage_type::CUT, rng( 5, 11 ) ) );
2023             player_character.check_dead_state();
2024             // Probably doesn't have spores available *just* yet.  Let's be nice.
2025         } else if( monster *const tendril = g->place_critter_at( mon_fungal_tendril, hit_pos ) ) {
2026             add_msg( m_bad, _( "A fungal tendril bursts forth from the earth!" ) );
2027             tendril->make_ally( *z );
2028         }
2029         return true;
2030     }
2031 
2032     add_msg( m_warning, _( "The %s takes aim, and spears at you with a massive tendril!" ),
2033              z->name() );
2034     z->moves -= 150;
2035 
2036     if( player_character.uncanny_dodge() ) {
2037         return true;
2038     }
2039     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
2040     if( dodge_check( z, target ) ) {
2041         target->add_msg_player_or_npc( _( "You dodge it!" ),
2042                                        _( "<npcname> dodges it!" ) );
2043         target->on_dodge( z, z->type->melee_skill );
2044         return true;
2045     }
2046 
2047     // TODO: 21 damage with no chance to critical isn't scary
2048     const bodypart_id hit = target->get_random_body_part();
2049     int dam = rng( 15, 21 );
2050     dam = player_character.deal_damage( z, hit, damage_instance( damage_type::STAB,
2051                                         dam ) ).total_damage();
2052 
2053     if( dam > 0 ) {
2054         //~ 1$s is monster name, 2$s bodypart in accusative
2055         add_msg( m_bad, _( "The %1$s sinks its point into your %2$s!" ), z->name(),
2056                  body_part_name_accusative( hit ) );
2057         player_character.add_effect( effect_fungus, 40_minutes, true );
2058         add_msg( m_warning, _( "You feel millions of live spores pumping into you…" ) );
2059     } else {
2060         //~ 1$s is monster name, 2$s bodypart in accusative
2061         add_msg( _( "The %1$s strikes your %2$s, but your armor protects you." ), z->name(),
2062                  body_part_name_accusative( hit ) );
2063     }
2064 
2065     target->on_hit( z, hit,  z->type->melee_skill );
2066     player_character.check_dead_state();
2067     return true;
2068 }
2069 
impale(monster * z)2070 bool mattack::impale( monster *z )
2071 {
2072     if( !z->can_act() ) {
2073         return false;
2074     }
2075     Creature *target = z->attack_target();
2076     if( target == nullptr || !z->is_adjacent( target, false ) ) {
2077         return false;
2078     }
2079 
2080     z->moves -= 80;
2081     bool uncanny = target->uncanny_dodge();
2082     if( uncanny || dodge_check( z, target ) ) {
2083         game_message_type msg_type = target->is_avatar() ? m_warning : m_info;
2084         target->add_msg_player_or_npc( msg_type, _( "The %s lunges at you, but you dodge!" ),
2085                                        _( "The %s lunges at <npcname>, but they dodge!" ),
2086                                        z->name() );
2087         if( !uncanny ) {
2088             target->on_dodge( z, z->type->melee_skill );
2089         }
2090 
2091         return true;
2092     }
2093 
2094     int dam = target->deal_damage( z, bodypart_id( "torso" ), damage_instance( damage_type::STAB,
2095                                    rng( 10, 20 ),
2096                                    rng( 5, 15 ),
2097                                    .5 ) ).total_damage();
2098     if( dam > 0 ) {
2099         game_message_type msg_type = target->is_avatar() ? m_bad : m_info;
2100         target->add_msg_player_or_npc( msg_type,
2101                                        //~ 1$s is monster name, 2$s bodypart in accusative
2102                                        _( "The %1$s impales your torso!" ),
2103                                        //~ 1$s is monster name, 2$s bodypart in accusative
2104                                        _( "The %1$s impales <npcname>'s torso!" ),
2105                                        z->name() );
2106 
2107         target->on_hit( z, bodypart_id( "torso" ),  z->type->melee_skill );
2108         if( rng( 0, 200 + dam ) > 100 ) {
2109             target->add_effect( effect_downed, 3_turns );
2110         }
2111         z->moves -= 80; //Takes extra time for the creature to pull out the protrusion
2112     } else {
2113         target->add_msg_player_or_npc(
2114             _( "The %1$s tries to impale your torso, but fails to penetrate your armor!" ),
2115             _( "The %1$s tries to impale <npcname>'s torso, but fails to penetrate their armor!" ),
2116             z->name() );
2117     }
2118 
2119     target->check_dead_state();
2120 
2121     return true;
2122 }
2123 
dermatik(monster * z)2124 bool mattack::dermatik( monster *z )
2125 {
2126     if( !z->can_act() ) {
2127         return false;
2128     }
2129 
2130     Creature *target = z->attack_target();
2131     if( target == nullptr ||
2132         !z->is_adjacent( target, true ) ||
2133         !z->sees( *target ) ) {
2134         return false;
2135     }
2136 
2137     if( target->uncanny_dodge() ) {
2138         return true;
2139     }
2140     player *foe = dynamic_cast< player * >( target );
2141     if( foe == nullptr ) {
2142         return true; // No implanting monsters for now
2143     }
2144     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
2145     if( dodge_check( z, target ) ) {
2146         if( target->is_avatar() ) {
2147             add_msg( _( "The %s tries to land on you, but you dodge." ), z->name() );
2148         }
2149         z->stumble();
2150         target->on_dodge( z, z->type->melee_skill );
2151         return true;
2152     }
2153 
2154     // Can we swat the bug away?
2155     int dodge_roll = z->dodge_roll();
2156     ///\EFFECT_MELEE increases chance to deflect dermatik attack
2157 
2158     ///\EFFECT_UNARMED increases chance to deflect dermatik attack
2159     int swat_skill = ( foe->get_skill_level( skill_melee ) + foe->get_skill_level(
2160                            skill_unarmed ) * 2 ) / 3;
2161     int player_swat = dice( swat_skill, 10 );
2162     if( foe->has_trait( trait_TAIL_CATTLE ) ) {
2163         target->add_msg_if_player( _( "You swat at the %s with your tail!" ), z->name() );
2164         ///\EFFECT_DEX increases chance of deflecting dermatik attack with TAIL_CATTLE
2165 
2166         ///\EFFECT_UNARMED increases chance of deflecting dermatik attack with TAIL_CATTLE
2167         player_swat += ( ( foe->dex_cur + foe->get_skill_level( skill_unarmed ) ) / 2 );
2168     }
2169     Character &player_character = get_player_character();
2170     if( player_swat > dodge_roll ) {
2171         target->add_msg_if_player( _( "The %s lands on you, but you swat it off." ), z->name() );
2172         if( z->get_hp() >= z->get_hp_max() / 2 ) {
2173             z->apply_damage( &player_character, bodypart_id( "torso" ), 1 );
2174             z->check_dead_state();
2175         }
2176         if( player_swat > dodge_roll * 1.5 ) {
2177             z->stumble();
2178         }
2179         return true;
2180     }
2181 
2182     // Can the bug penetrate our armor?
2183     const bodypart_id targeted = target->get_random_body_part();
2184     if( 4 < player_character.get_armor_cut( targeted ) / 3 ) {
2185         //~ 1$s monster name(dermatik), 2$s bodypart name in accusative.
2186         target->add_msg_if_player( _( "The %1$s lands on your %2$s, but can't penetrate your armor." ),
2187                                    z->name(), body_part_name_accusative( targeted ) );
2188         z->moves -= 150; // Attempted laying takes a while
2189         return true;
2190     }
2191 
2192     // Success!
2193     z->moves -= 500; // Successful laying takes a long time
2194     //~ 1$s monster name(dermatik), 2$s bodypart name in accusative.
2195     target->add_msg_if_player( m_bad, _( "The %1$s sinks its ovipositor into your %2$s!" ),
2196                                z->name(),
2197                                body_part_name_accusative( targeted ) );
2198     if( !foe->has_trait( trait_PARAIMMUNE ) && !foe->has_trait( trait_ACIDBLOOD ) ) {
2199         foe->add_effect( effect_dermatik, 1_turns, targeted, true );
2200         get_event_bus().send<event_type::dermatik_eggs_injected>( foe->getID() );
2201     }
2202 
2203     return true;
2204 }
2205 
dermatik_growth(monster * z)2206 bool mattack::dermatik_growth( monster *z )
2207 {
2208     add_msg_if_player_sees( *z, m_warning, _( "The %s dermatik larva grows into an adult!" ),
2209                             z->name() );
2210     z->poly( mon_dermatik );
2211     return false;
2212 }
2213 
fungal_trail(monster * z)2214 bool mattack::fungal_trail( monster *z )
2215 {
2216     fungal_effects fe( *g, get_map() );
2217     fe.spread_fungus( z->pos() );
2218     return false;
2219 }
2220 
plant(monster * z)2221 bool mattack::plant( monster *z )
2222 {
2223     map &here = get_map();
2224     fungal_effects fe( *g, here );
2225     const tripoint monster_position = z->pos();
2226     const bool is_fungi = here.has_flag_ter( "FUNGUS", monster_position );
2227     // Spores taking seed and growing into a fungaloid
2228     fe.spread_fungus( monster_position );
2229     if( is_fungi && one_in( 10 + g->num_creatures() / 5 ) ) {
2230         add_msg_if_player_sees( *z, m_warning, _( "The %s takes seed and becomes a young fungaloid!" ),
2231                                 z->name() );
2232 
2233         z->poly( mon_fungaloid_young );
2234         z->moves -= 1000; // It takes a while
2235         return false;
2236     } else {
2237         add_msg_if_player_sees( *z, _( "The %s falls to the ground and bursts!" ), z->name() );
2238         z->set_hp( 0 );
2239         // Try fungifying once again
2240         fe.spread_fungus( monster_position );
2241         return true;
2242     }
2243 }
2244 
disappear(monster * z)2245 bool mattack::disappear( monster *z )
2246 {
2247     z->set_hp( 0 );
2248     return true;
2249 }
2250 
poly_keep_speed(monster & mon,const mtype_id & id)2251 static void poly_keep_speed( monster &mon, const mtype_id &id )
2252 {
2253     // Retain old speed after polymorph
2254     // This prevents blobs regenerating speed through polymorphs
2255     // and thus replicating indefinitely, covering entire map
2256     const int old_speed = mon.get_speed_base();
2257     mon.poly( id );
2258     mon.set_speed_base( old_speed );
2259 }
2260 
blobify(monster & blob,monster & target)2261 static bool blobify( monster &blob, monster &target )
2262 {
2263     add_msg_if_player_sees( target, m_warning, _( "%s is engulfed by %s!" ),
2264                             target.disp_name(), blob.disp_name() );
2265     switch( target.get_size() ) {
2266         case creature_size::tiny:
2267             // Just consume it
2268             target.set_hp( 0 );
2269             blob.set_speed_base( blob.get_speed_base() + 5 );
2270             return false;
2271         case creature_size::small:
2272             target.poly( mon_blob_small );
2273             break;
2274         case creature_size::medium:
2275             target.poly( mon_blob );
2276             break;
2277         case creature_size::large:
2278             target.poly( mon_blob_large );
2279             break;
2280         case creature_size::huge:
2281             // No polymorphing huge stuff
2282             target.add_effect( effect_slimed, rng( 2_turns, 10_turns ) );
2283             break;
2284         default:
2285             debugmsg( "Tried to blobify %s with invalid size: %d",
2286                       target.disp_name(), static_cast<int>( target.get_size() ) );
2287             return false;
2288     }
2289 
2290     target.make_ally( blob );
2291     return true;
2292 }
2293 
formblob(monster * z)2294 bool mattack::formblob( monster *z )
2295 {
2296     if( z->friendly ) {
2297         // TODO: handle friendly monsters
2298         return false;
2299     }
2300 
2301     bool didit = false;
2302     std::vector<tripoint> pts = closest_points_first( z->pos(), 1 );
2303     // Don't check own tile
2304     pts.erase( pts.begin() );
2305     for( const tripoint &dest : pts ) {
2306         Creature *critter = g->critter_at( dest );
2307         if( critter == nullptr ) {
2308             if( z->get_speed_base() > 85 && rng( 0, 250 ) < z->get_speed_base() ) {
2309                 // If we're big enough, spawn a baby blob.
2310                 didit = true;
2311                 z->set_speed_base( z->get_speed_base() - 15 );
2312                 if( monster *const blob = g->place_critter_at( mon_blob_small, dest ) ) {
2313                     blob->make_ally( *z );
2314                 }
2315 
2316                 break;
2317             }
2318 
2319             continue;
2320         }
2321 
2322         if( critter->is_player() || critter->is_npc() ) {
2323             // If we hit the player or some NPC, cover them with slime
2324             didit = true;
2325             // TODO: Add some sort of a resistance/dodge roll
2326             critter->add_effect( effect_slimed, rng( 0_turns, 1_turns * z->get_hp() ) );
2327             break;
2328         }
2329 
2330         monster &othermon = *( dynamic_cast<monster *>( critter ) );
2331         // Hit a monster.  If it's a blob, give it our speed.  Otherwise, blobify it?
2332         if( z->get_speed_base() > 40 && othermon.type->in_species( species_SLIME ) ) {
2333             if( othermon.type->id == mon_blob_brain ) {
2334                 // Brain blobs don't get sped up, they heal at the cost of the other blob.
2335                 // But only if they are hurt badly.
2336                 if( othermon.get_hp() < othermon.get_hp_max() / 2 ) {
2337                     othermon.heal( z->get_speed_base(), true );
2338                     z->set_hp( 0 );
2339                     return true;
2340                 }
2341                 continue;
2342             }
2343             didit = true;
2344             othermon.set_speed_base( othermon.get_speed_base() + 5 );
2345             z->set_speed_base( z->get_speed_base() - 5 );
2346             if( othermon.type->id == mon_blob_small && othermon.get_speed_base() >= 60 ) {
2347                 poly_keep_speed( othermon, mon_blob );
2348             } else if( othermon.type->id == mon_blob && othermon.get_speed_base() >= 80 ) {
2349                 poly_keep_speed( othermon, mon_blob_large );
2350             }
2351         } else if( ( othermon.made_of( material_id( "flesh" ) ) ||
2352                      othermon.made_of( material_id( "veggy" ) ) ||
2353                      othermon.made_of( material_id( "iflesh" ) ) ) &&
2354                    rng( 0, z->get_hp() ) > rng( othermon.get_hp() / 2, othermon.get_hp() ) ) {
2355             didit = blobify( *z, othermon );
2356         }
2357     }
2358 
2359     if( didit ) { // We did SOMEthing.
2360         if( z->type->id == mon_blob && z->get_speed_base() <= 50 ) {
2361             // We shrank!
2362             poly_keep_speed( *z, mon_blob_small );
2363         } else if( z->type->id == mon_blob_large && z->get_speed_base() <= 70 ) {
2364             // We shrank!
2365             poly_keep_speed( *z, mon_blob );
2366         }
2367 
2368         z->moves = 0;
2369         return true;
2370     }
2371 
2372     return true; // consider returning false to try again immediately if nothing happened?
2373 }
2374 
callblobs(monster * z)2375 bool mattack::callblobs( monster *z )
2376 {
2377     if( z->friendly ) {
2378         // TODO: handle friendly monsters
2379         return false;
2380     }
2381     // The huge brain blob interposes other blobs between it and any threat.
2382     // For the moment just target the player, this gets a bit more complicated
2383     // if we want to deal with NPCS and friendly monsters as well.
2384     // The strategy is to send about 1/3 of the available blobs after the player,
2385     // and keep the rest near the brain blob for protection.
2386     tripoint enemy = get_player_location().pos();
2387     std::list<monster *> allies;
2388     std::vector<tripoint> nearby_points = closest_points_first( z->pos(), 3 );
2389     for( monster &candidate : g->all_monsters() ) {
2390         if( candidate.type->in_species( species_SLIME ) && candidate.type->id != mon_blob_brain ) {
2391             // Just give the allies consistent assignments.
2392             // Don't worry about trying to make the orders optimal.
2393             allies.push_back( &candidate );
2394         }
2395     }
2396     // 1/3 of the available blobs, unless they would fill the entire area near the brain.
2397     const int num_guards = std::min( allies.size() / 3, nearby_points.size() );
2398     int guards = 0;
2399     for( std::list<monster *>::iterator ally = allies.begin();
2400          ally != allies.end(); ++ally, ++guards ) {
2401         tripoint post = enemy;
2402         if( guards < num_guards ) {
2403             // Each guard is assigned a spot in the nearby_points vector based on their order.
2404             int assigned_spot = ( nearby_points.size() * guards ) / num_guards;
2405             post = nearby_points[ assigned_spot ];
2406         }
2407         ( *ally )->set_dest( post );
2408         if( !( *ally )->has_effect( effect_controlled ) ) {
2409             ( *ally )->add_effect( effect_controlled, 1_turns, true );
2410         }
2411     }
2412     // This is telepathy, doesn't take any moves.
2413 
2414     return true;
2415 }
2416 
jackson(monster * z)2417 bool mattack::jackson( monster *z )
2418 {
2419     // Jackson draws nearby zombies into the dance.
2420     std::list<monster *> allies;
2421     std::vector<tripoint> nearby_points = closest_points_first( z->pos(), 3 );
2422     for( monster &candidate : g->all_monsters() ) {
2423         if( candidate.type->in_species( species_ZOMBIE ) && candidate.type->id != mon_zombie_jackson ) {
2424             // Just give the allies consistent assignments.
2425             // Don't worry about trying to make the orders optimal.
2426             allies.push_back( &candidate );
2427         }
2428     }
2429     const int num_dancers = std::min( allies.size(), nearby_points.size() );
2430     int dancers = 0;
2431     bool converted = false;
2432     for( auto ally = allies.begin(); ally != allies.end(); ++ally, ++dancers ) {
2433         tripoint post = z->pos();
2434         if( dancers < num_dancers ) {
2435             // Each dancer is assigned a spot in the nearby_points vector based on their order.
2436             int assigned_spot = ( nearby_points.size() * dancers ) / num_dancers;
2437             post = nearby_points[ assigned_spot ];
2438         }
2439         if( ( *ally )->type->id != mon_zombie_dancer ) {
2440             ( *ally )->poly( mon_zombie_dancer );
2441             converted = true;
2442         }
2443         ( *ally )->set_dest( post );
2444         if( !( *ally )->has_effect( effect_controlled ) ) {
2445             ( *ally )->add_effect( effect_controlled, 1_turns, true );
2446         }
2447     }
2448     // Did we convert anybody?
2449     if( converted ) {
2450         add_msg_if_player_sees( *z, m_warning, _( "The %s lets out a high-pitched cry!" ), z->name() );
2451     }
2452     // This is telepathy, doesn't take any moves.
2453     return true;
2454 }
2455 
dance(monster * z)2456 bool mattack::dance( monster *z )
2457 {
2458     if( get_player_view().sees( *z ) ) {
2459         switch( rng( 1, 10 ) ) {
2460             case 1:
2461                 add_msg( m_neutral, _( "The %s swings its arms from side to side!" ), z->name() );
2462                 break;
2463             case 2:
2464                 add_msg( m_neutral, _( "The %s does some fancy footwork!" ), z->name() );
2465                 break;
2466             case 3:
2467                 add_msg( m_neutral, _( "The %s shrugs its shoulders!" ), z->name() );
2468                 break;
2469             case 4:
2470                 add_msg( m_neutral, _( "The %s spins in place!" ), z->name() );
2471                 break;
2472             case 5:
2473                 add_msg( m_neutral, _( "The %s crouches on the ground!" ), z->name() );
2474                 break;
2475             case 6:
2476                 add_msg( m_neutral, _( "The %s looks left and right!" ), z->name() );
2477                 break;
2478             case 7:
2479                 add_msg( m_neutral, _( "The %s jumps back and forth!" ), z->name() );
2480                 break;
2481             case 8:
2482                 add_msg( m_neutral, _( "The %s raises its arms in the air!" ), z->name() );
2483                 break;
2484             case 9:
2485                 add_msg( m_neutral, _( "The %s swings its hips!" ), z->name() );
2486                 break;
2487             case 10:
2488                 add_msg( m_neutral, _( "The %s claps!" ), z->name() );
2489                 break;
2490         }
2491     }
2492 
2493     return true;
2494 }
2495 
dogthing(monster * z)2496 bool mattack::dogthing( monster *z )
2497 {
2498     if( z == nullptr ) {
2499         // TODO: replace pointers with references
2500         return false;
2501     }
2502 
2503     if( !one_in( 3 ) || !get_player_view().sees( *z ) ) {
2504         return false;
2505     }
2506 
2507     add_msg( _( "The %s's head explodes in a mass of roiling tentacles!" ),
2508              z->name() );
2509 
2510     get_map().add_splash( z->bloodType(), z->pos(), 2, 3 );
2511 
2512     z->friendly = 0;
2513     z->poly( mon_headless_dog_thing );
2514 
2515     return false;
2516 }
2517 
tentacle(monster * z)2518 bool mattack::tentacle( monster *z )
2519 {
2520     if( z->friendly ) {
2521         // TODO: handle friendly monsters
2522         return false;
2523     }
2524     Creature *target = z->attack_target();
2525 
2526     // Can't see/reach target, no attack
2527     if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 3 || !z->sees( *target ) ||
2528         !get_map().clear_path( z->pos(), target->pos(), 3, 1, 100 ) ) {
2529         return false;
2530     }
2531     game_message_type msg_type = target->is_avatar() ? m_bad : m_info;
2532     target->add_msg_player_or_npc( msg_type,
2533                                    _( "The %s lashes its tentacle at you!" ),
2534                                    _( "The %s lashes its tentacle at <npcname>!" ),
2535                                    z->name() );
2536     z->moves -= 100;
2537 
2538     if( target->uncanny_dodge() ) {
2539         return true;
2540     }
2541     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
2542     if( dodge_check( z, target ) ) {
2543         target->add_msg_player_or_npc( _( "You dodge it!" ),
2544                                        _( "<npcname> dodges it!" ) );
2545         target->on_dodge( z, z->type->melee_skill );
2546         return true;
2547     }
2548 
2549     const bodypart_id hit = target->get_random_body_part();
2550     int dam = rng( 10, 20 );
2551     dam = target->deal_damage( z, hit, damage_instance( damage_type::BASH, dam ) ).total_damage();
2552 
2553     if( dam > 0 ) {
2554         target->add_msg_player_or_npc( msg_type,
2555                                        //~ 1$s is bodypart name, 2$d is damage value.
2556                                        _( "Your %1$s is hit for %2$d damage!" ),
2557                                        //~ 1$s is bodypart name, 2$d is damage value.
2558                                        _( "<npcname>'s %1$s is hit for %2$d damage!" ),
2559                                        body_part_name( hit ),
2560                                        dam );
2561     } else {
2562         target->add_msg_player_or_npc(
2563             _( "The %1$s lashes its tentacle at your %2$s, but glances off your armor!" ),
2564             _( "The %1$s lashes its tentacle at <npcname>'s %2$s, but glances off their armor!" ),
2565             z->name(),
2566             body_part_name_accusative( hit ) );
2567     }
2568 
2569     target->on_hit( z, hit,  z->type->melee_skill );
2570     target->check_dead_state();
2571 
2572     return true;
2573 }
2574 
ranged_pull(monster * z)2575 bool mattack::ranged_pull( monster *z )
2576 {
2577     Creature *target = z->attack_target();
2578     if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 3 ||
2579         rl_dist( z->pos(), target->pos() ) <= 1 || !z->sees( *target ) ||
2580         z->has_effect( effect_grabbing ) ) {
2581         return false;
2582     }
2583 
2584     player *foe = dynamic_cast< player * >( target );
2585     map &here = get_map();
2586     std::vector<tripoint> line = here.find_clear_path( z->pos(), target->pos() );
2587     bool seen = get_player_view().sees( *z );
2588 
2589     for( auto &i : line ) {
2590         // Player can't be pulled though bars, furniture, cars or creatures
2591         // TODO: Add bashing? Currently a window is enough to prevent grabbing
2592         if( !g->is_empty( i ) && i != z->pos() && i != target->pos() ) {
2593             return false;
2594         }
2595     }
2596 
2597     z->moves -= 150;
2598 
2599     const bool uncanny = target->uncanny_dodge();
2600     if( uncanny || dodge_check( z, target ) ) {
2601         z->moves -= 200;
2602         game_message_type msg_type = foe && foe->is_avatar() ? m_warning : m_info;
2603         target->add_msg_player_or_npc( msg_type, _( "The %s's arms fly out at you, but you dodge!" ),
2604                                        _( "The %s's arms fly out at <npcname>, but they dodge!" ),
2605                                        z->name() );
2606 
2607         if( !uncanny ) {
2608             target->on_dodge( z, z->type->melee_skill );
2609         }
2610 
2611         return true;
2612     }
2613 
2614     // Limit the range in case some weird math thing would cause the target to fly past us
2615     int range = std::min( ( z->type->melee_sides * z->type->melee_dice ) / 10,
2616                           rl_dist( z->pos(), target->pos() ) + 1 );
2617     tripoint pt = target->pos();
2618     while( range > 0 ) {
2619         // Recalculate the ray each step
2620         // We can't depend on either the target position being constant (obviously),
2621         // but neither on z pos staying constant, because we may want to shift the map mid-pull
2622         const units::angle dir = coord_to_angle( target->pos(), z->pos() );
2623         tileray tdir( dir );
2624         tdir.advance();
2625         pt.x = target->posx() + tdir.dx();
2626         pt.y = target->posy() + tdir.dy();
2627         if( !g->is_empty( pt ) ) { //Cancel the grab if the space is occupied by something
2628             break;
2629         }
2630 
2631         if( foe != nullptr ) {
2632             if( foe->in_vehicle ) {
2633                 here.unboard_vehicle( foe->pos() );
2634             }
2635 
2636             if( target->is_player() && ( pt.x < HALF_MAPSIZE_X || pt.y < HALF_MAPSIZE_Y ||
2637                                          pt.x >= HALF_MAPSIZE_X + SEEX || pt.y >= HALF_MAPSIZE_Y + SEEY ) ) {
2638                 g->update_map( pt.x, pt.y );
2639             }
2640         }
2641 
2642         target->setpos( pt );
2643         range--;
2644         if( target->is_player() && seen ) {
2645             g->invalidate_main_ui_adaptor();
2646             ui_manager::redraw_invalidated();
2647             refresh_display();
2648         }
2649     }
2650     // The monster might drag a target that's not on it's z level
2651     // So if they leave them on open air, make them fall
2652     here.creature_on_trap( *target );
2653     if( seen ) {
2654         if( z->type->bodytype == "human" || z->type->bodytype == "angel" ) {
2655             add_msg( _( "The %1$s's arms fly out and pull and grab %2$s!" ), z->name(),
2656                      target->disp_name() );
2657         } else {
2658             add_msg( _( "The %1$s reaches out and pulls %2$s!" ), z->name(),
2659                      target->disp_name() );
2660         }
2661     }
2662 
2663     const int prev_effect = target->get_effect_int( effect_grabbed );
2664     //Duration needs to be at least 2, or grab will immediately be removed
2665     target->add_effect( effect_grabbed, 2_turns, bodypart_id( "torso" ), false, prev_effect + 4 );
2666     z->add_effect( effect_grabbing, 2_turns );
2667     return true;
2668 }
2669 
grab(monster * z)2670 bool mattack::grab( monster *z )
2671 {
2672     if( !z->can_act() ) {
2673         return false;
2674     }
2675     Creature *target = z->attack_target();
2676     if( target == nullptr || !z->is_adjacent( target, false ) ) {
2677         return false;
2678     }
2679 
2680     z->moves -= 80;
2681     const bool uncanny = target->uncanny_dodge();
2682     const game_message_type msg_type = target->is_avatar() ? m_warning : m_info;
2683     if( uncanny || dodge_check( z, target ) ) {
2684         target->add_msg_player_or_npc( msg_type, _( "The %s gropes at you, but you dodge!" ),
2685                                        _( "The %s gropes at <npcname>, but they dodge!" ),
2686                                        z->name() );
2687 
2688         if( !uncanny ) {
2689             target->on_dodge( z, z->type->melee_skill );
2690         }
2691 
2692         return true;
2693     }
2694 
2695     player *pl = dynamic_cast<player *>( target );
2696     if( pl == nullptr ) {
2697         return true;
2698     }
2699 
2700     item &cur_weapon = pl->weapon;
2701     ///\EFFECT_DEX increases chance to avoid being grabbed
2702     int reflex_mod = pl->has_trait( trait_FAST_REFLEXES ) ? 2 : 1;
2703     const bool dodged_grab = rng( 0, reflex_mod * pl->get_dex() ) > rng( 0,
2704                              z->type->melee_sides + z->type->melee_dice );
2705 
2706     if( pl->can_grab_break( cur_weapon ) && dodged_grab ) {
2707         if( target->has_effect( effect_grabbed ) ) {
2708             target->add_msg_if_player( m_info, _( "The %s tries to grab you as well, but you bat it away!" ),
2709                                        z->name() );
2710         } else if( pl->is_throw_immune() && ( !pl->is_armed() ||
2711                                               pl->martial_arts_data->selected_has_weapon( pl->weapon.typeId() ) ) ) {
2712             target->add_msg_if_player( m_info, _( "The %s tries to grab you…" ), z->name() );
2713             thrown_by_judo( z );
2714         } else if( pl->has_grab_break_tec() ) {
2715             ma_technique tech = pl->martial_arts_data->get_grab_break_tec( cur_weapon );
2716             target->add_msg_player_or_npc( m_info, tech.avatar_message.translated(),
2717                                            tech.npc_message.translated(), z->name() );
2718         } else {
2719             add_msg_if_player_sees( *z, m_info, _( "The %1$s tries to grab %2$s, but %2$s break its grab!" ),
2720                                     z->name(), target->disp_name() );
2721         }
2722         return true;
2723     }
2724 
2725     const int prev_effect = target->get_effect_int( effect_grabbed );
2726     z->add_effect( effect_grabbing, 2_turns );
2727     target->add_effect( effect_grabbed, 2_turns, bodypart_id( "torso" ), false,
2728                         prev_effect + z->get_grab_strength() );
2729     add_msg_if_player_sees( *z, m_bad, _( "The %1$s grabs %2$s!" ), z->name(), target->disp_name() );
2730 
2731     return true;
2732 }
2733 
grab_drag(monster * z)2734 bool mattack::grab_drag( monster *z )
2735 {
2736     if( !z || !z->can_act() ) {
2737         return false;
2738     }
2739     Creature *target = z->attack_target();
2740     if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 1 ) {
2741         return false;
2742     }
2743 
2744     if( target->has_effect( effect_under_operation ) ) {
2745         target->add_msg_player_or_npc( m_good,
2746                                        _( "The %s tries to drag you, but you're securely fastened in the autodoc." ),
2747                                        _( "The %s tries to drag <npcname>, but they're securely fastened in the autodoc." ), z->name() );
2748         return false;
2749     }
2750 
2751     // First, grab the target
2752     grab( z );
2753 
2754     if( !target->has_effect( effect_grabbed ) ) { //Can't drag if isn't grabbed, otherwise try and move
2755         return false;
2756     }
2757     const tripoint target_square = z->pos() - ( target->pos() - z->pos() );
2758     if( z->can_move_to( target_square ) &&
2759         target->stability_roll() < dice( z->type->melee_sides, z->type->melee_dice ) ) {
2760         player *foe = dynamic_cast<player *>( target );
2761         monster *zz = dynamic_cast<monster *>( target );
2762         tripoint zpt = z->pos();
2763         z->move_to( target_square );
2764         if( !g->is_empty( zpt ) ) { //Cancel the grab if the space is occupied by something
2765             return false;
2766         }
2767         if( target->is_player() && ( zpt.x < HALF_MAPSIZE_X ||
2768                                      zpt.y < HALF_MAPSIZE_Y ||
2769                                      zpt.x >= HALF_MAPSIZE_X + SEEX || zpt.y >= HALF_MAPSIZE_Y + SEEY ) ) {
2770             g->update_map( zpt.x, zpt.y );
2771         }
2772         if( foe != nullptr ) {
2773             if( foe->in_vehicle ) {
2774                 get_map().unboard_vehicle( foe->pos() );
2775             }
2776             foe->setpos( zpt );
2777         } else {
2778             zz->setpos( zpt );
2779         }
2780         target->add_msg_player_or_npc( m_bad, _( "You are dragged behind the %s!" ),
2781                                        _( "<npcname> gets dragged behind the %s!" ), z->name() );
2782     } else {
2783         target->add_msg_player_or_npc( m_good, _( "You resist the %s as it tries to drag you!" ),
2784                                        _( "<npcname> resist the %s as it tries to drag them!" ), z->name() );
2785     }
2786     int prev_effect = target->get_effect_int( effect_grabbed );
2787     z->add_effect( effect_grabbing, 2_turns );
2788     target->add_effect( effect_grabbed, 2_turns, bodypart_id( "torso" ), false, prev_effect + 3 );
2789 
2790     // cooldown was not reset prior to refactor here
2791     return true;
2792 }
2793 
gene_sting(monster * z)2794 bool mattack::gene_sting( monster *z )
2795 {
2796     const float range = 7.0f;
2797     Creature *target = sting_get_target( z, range );
2798     if( target == nullptr || !( target->is_player() || target->is_npc() ) ) {
2799         return false;
2800     }
2801 
2802     z->moves -= 150;
2803 
2804     damage_instance dam = damage_instance();
2805     dam.add_damage( damage_type::STAB, 6, 10, 0.6, 1 );
2806     bool hit = sting_shoot( z, target, dam, range );
2807     if( hit ) {
2808         //Add checks if previous NPC/player conditions are removed
2809         dynamic_cast<player *>( target )->mutate();
2810     }
2811 
2812     return true;
2813 }
2814 
para_sting(monster * z)2815 bool mattack::para_sting( monster *z )
2816 {
2817     const float range = 4.0f;
2818     Creature *target = sting_get_target( z, range );
2819     if( target == nullptr ) {
2820         return false;
2821     }
2822 
2823     z->moves -= 150;
2824 
2825     damage_instance dam = damage_instance();
2826     dam.add_damage( damage_type::STAB, 6, 8, 0.8, 1 );
2827     bool hit = sting_shoot( z, target, dam, range );
2828     if( hit ) {
2829         target->add_msg_if_player( m_bad, _( "You feel poison enter your body!" ) );
2830         target->add_effect( effect_paralyzepoison, 5_minutes );
2831     }
2832 
2833     return true;
2834 }
2835 
triffid_growth(monster * z)2836 bool mattack::triffid_growth( monster *z )
2837 {
2838     add_msg_if_player_sees( *z, m_warning, _( "The %s young triffid grows into an adult!" ),
2839                             z->name() );
2840     z->poly( mon_triffid );
2841 
2842     return false;
2843 }
2844 
stare(monster * z)2845 bool mattack::stare( monster *z )
2846 {
2847     if( z->friendly ) {
2848         // TODO: handle friendly monsters
2849         return false;
2850     }
2851     z->moves -= 200;
2852     Character &player_character = get_player_character();
2853     if( z->sees( player_character ) ) {
2854         //dimensional effects don't take against dimensionally anchored foes.
2855         if( player_character.worn_with_flag( flag_DIMENSIONAL_ANCHOR ) ||
2856             player_character.has_effect_with_flag( flag_DIMENSIONAL_ANCHOR ) ) {
2857             add_msg( m_warning, _( "You feel a strange reverberation across your body." ) );
2858             return true;
2859         }
2860         if( player_character.sees( *z ) ) {
2861             add_msg( m_bad, _( "The %s stares at you, and you shudder." ), z->name() );
2862         } else {
2863             add_msg( m_bad, _( "You feel like you're being watched, it makes you sick." ) );
2864         }
2865         player_character.add_effect( effect_taint, rng( 2_minutes, 5_minutes ) );
2866         //Check severity before adding more debuffs
2867         if( player_character.get_effect_int( effect_taint ) > 2 ) {
2868             player_character.add_effect( effect_hallu, 30_minutes );
2869             //Check if target is a player before spawning hallucinations
2870             if( player_character.is_player() && one_in( 2 ) ) {
2871                 g->spawn_hallucination( player_character.pos() + tripoint( rng( -10, 10 ), rng( -10, 10 ), 0 ) );
2872             }
2873             if( one_in( 12 ) ) {
2874                 player_character.add_effect( effect_blind, 5_minutes );
2875                 add_msg( m_bad, _( "Your sight darkens as the visions overtake you!" ) );
2876             }
2877         }
2878         if( player_character.get_effect_int( effect_taint ) >= 3 && one_in( 12 ) ) {
2879             player_character.add_effect( effect_tindrift, 1_turns );
2880         }
2881     }
2882     return true;
2883 }
2884 
fear_paralyze(monster * z)2885 bool mattack::fear_paralyze( monster *z )
2886 {
2887     if( z->friendly ) {
2888         // TODO: handle friendly monsters
2889         return false;
2890     }
2891     Character &player_character = get_player_character();
2892     if( player_character.sees( *z ) && !player_character.has_effect( effect_fearparalyze ) ) {
2893         if( player_character.worn_with_flag( flag_PSYSHIELD_PARTIAL ) && one_in( 4 ) ) {
2894             add_msg( _( "The %s probes your mind, but is rebuffed!" ), z->name() );
2895             ///\EFFECT_INT decreases chance of being paralyzed by fear attack
2896         } else if( rng( 0, 20 ) > player_character.get_int() ) {
2897             add_msg( m_bad, _( "The terrifying visage of the %s paralyzes you." ), z->name() );
2898             player_character.add_effect( effect_fearparalyze, 5_turns );
2899             player_character.moves -= 4 * player_character.get_speed();
2900         } else {
2901             add_msg( _( "You manage to avoid staring at the horrendous %s." ), z->name() );
2902         }
2903     }
2904 
2905     return true;
2906 }
nurse_check_up(monster * z)2907 bool mattack::nurse_check_up( monster *z )
2908 {
2909     bool found_target = false;
2910     player *target = nullptr;
2911     tripoint tmp_pos( z->pos() + point( 12, 12 ) );
2912     map &here = get_map();
2913     for( Creature *critter : here.get_creatures_in_radius( z->pos(), 6 ) ) {
2914         player *tmp_player = dynamic_cast<player *>( critter );
2915         if( tmp_player != nullptr && z->sees( *tmp_player ) &&
2916             here.clear_path( z->pos(), tmp_player->pos(), 10, 0,
2917                              100 ) ) { // no need to scan players we can't reach
2918             if( rl_dist( z->pos(), tmp_player->pos() ) < rl_dist( z->pos(), tmp_pos ) ) {
2919                 tmp_pos = tmp_player->pos();
2920                 target = tmp_player;
2921                 found_target = true;
2922             }
2923         }
2924     }
2925     if( found_target ) {
2926 
2927         // First we offer the check up then we wait to the player to come close
2928         if( !z->has_effect( effect_countdown ) ) {
2929             sounds::sound( z->pos(), 8, sounds::sound_t::electronic_speech,
2930                            string_format(
2931                                _( "a soft robotic voice say, \"Come here and stand still for a few minutes, I'll give you a check-up.\"" ) ) );
2932             z->add_effect( effect_countdown, 30_minutes );
2933         } else if( rl_dist( target->pos(), z->pos() ) > 1 ) {
2934             // Giving them some encouragement
2935             sounds::sound( z->pos(), 8, sounds::sound_t::electronic_speech,
2936                            string_format(
2937                                _( "a soft robotic voice say, \"Come on.  I don't bite, I promise it won't hurt one bit.\"" ) ) );
2938         } else {
2939             sounds::sound( z->pos(), 8, sounds::sound_t::electronic_speech,
2940                            string_format(
2941                                _( "a soft robotic voice say, \"Here we go.  Just hold still.\"" ) ) );
2942             if( target->is_avatar() ) {
2943                 add_msg( m_good, _( "You get a medical check-up." ) );
2944             }
2945             target->add_effect( effect_got_checked, 10_turns );
2946             z->remove_effect( effect_countdown );
2947         }
2948         return true;
2949     }
2950     return false;
2951 }
nurse_assist(monster * z)2952 bool mattack::nurse_assist( monster *z )
2953 {
2954 
2955     const bool u_see = get_player_view().sees( *z );
2956 
2957     if( u_see && one_in( 100 ) ) {
2958         add_msg( m_info, _( "The %s is scanning its surroundings." ), z->name() );
2959     }
2960 
2961     bool found_target = false;
2962     player *target = nullptr;
2963     map &here = get_map();
2964     tripoint tmp_pos( z->pos() + point( 12, 12 ) );
2965     for( Creature *critter : here.get_creatures_in_radius( z->pos(), 6 ) ) {
2966         player *tmp_player = dynamic_cast<player *>( critter );
2967         // No need to scan players we can't reach
2968         if( tmp_player != nullptr && z->sees( *tmp_player ) &&
2969             here.clear_path( z->pos(), tmp_player->pos(), 10, 0, 100 ) ) {
2970             if( rl_dist( z->pos(), tmp_player->pos() ) < rl_dist( z->pos(), tmp_pos ) ) {
2971                 tmp_pos = tmp_player->pos();
2972                 target = tmp_player;
2973                 found_target = true;
2974             }
2975         }
2976     }
2977 
2978     if( found_target ) {
2979         if( target->is_wearing( itype_badge_doctor ) ||
2980             z->attitude_to( *target ) == Creature::Attitude::FRIENDLY ) {
2981             sounds::sound( z->pos(), 8, sounds::sound_t::electronic_speech,
2982                            string_format(
2983                                _( "a soft robotic voice say, \"Welcome doctor %s.  I'll be your assistant today.\"" ),
2984                                Name::generate( target->male ) ) );
2985             target->add_effect( effect_assisted, 20_turns, false, 12 );
2986             return true;
2987         }
2988     }
2989     return false;
2990 }
nurse_operate(monster * z)2991 bool mattack::nurse_operate( monster *z )
2992 {
2993     const itype_id ammo_type( "anesthetic" );
2994 
2995     if( z->has_effect( effect_dragging ) || z->has_effect( effect_operating ) ) {
2996         return false;
2997     }
2998     Character &player_character = get_player_character();
2999     const bool u_see = player_character.sees( *z );
3000 
3001     if( u_see && one_in( 100 ) ) {
3002         add_msg( m_info, _( "The %s is scanning its surroundings." ), z->name() );
3003     }
3004 
3005     if( ( ( player_character.is_wearing( itype_badge_doctor ) ||
3006             z->attitude_to( player_character ) == Creature::Attitude::FRIENDLY ) && u_see ) && one_in( 100 ) ) {
3007 
3008         add_msg( m_info, _( "The %s doesn't seem to register you as a doctor." ), z->name() );
3009     }
3010 
3011     if( z->ammo[ammo_type] == 0 && u_see ) {
3012         if( one_in( 100 ) ) {
3013             add_msg( m_info, _( "The %s looks at its empty anesthesia kit with a dejected look." ), z->name() );
3014         }
3015         return false;
3016     }
3017 
3018     bool found_target = false;
3019     player *target = nullptr;
3020     map &here = get_map();
3021     tripoint tmp_pos( z->pos() + point( 12, 12 ) );
3022     for( Creature *critter : here.get_creatures_in_radius( z->pos(), 6 ) ) {
3023         player *tmp_player = dynamic_cast< player *>( critter );
3024         // No need to scan players we can't reach
3025         if( tmp_player != nullptr && z->sees( *tmp_player ) &&
3026             here.clear_path( z->pos(), tmp_player->pos(), 10, 0, 100 ) ) {
3027             if( tmp_player->has_any_bionic() ) {
3028                 if( rl_dist( z->pos(), tmp_player->pos() ) < rl_dist( z->pos(), tmp_pos ) ) {
3029                     tmp_pos = tmp_player->pos();
3030                     target = tmp_player;
3031                     found_target = true;
3032                 }
3033             }
3034         }
3035     }
3036     if( found_target && z->attitude_to( player_character ) == Creature::Attitude::FRIENDLY ) {
3037         // 50% chance to not turn hostile again
3038         if( one_in( 2 ) ) {
3039             return false;
3040         }
3041     }
3042     if( found_target && u_see ) {
3043         add_msg( m_info, _( "The %1$s scans %2$s and seems to detect something." ), z->name(),
3044                  target->disp_name() );
3045     }
3046 
3047     if( found_target ) {
3048 
3049         z->friendly = 0;
3050         z->anger = 100;
3051         std::list<tripoint> couch_pos = here.find_furnitures_with_flag_in_radius( z->pos(), 10,
3052                                         flag_AUTODOC_COUCH );
3053 
3054         if( couch_pos.empty() ) {
3055             add_msg( m_info, _( "The %s looks for something but doesn't seem to find it." ), z->name() );
3056             z->anger = 0;
3057             return false;
3058         }
3059         // Should designate target as the attack_target
3060         z->set_dest( target->pos() );
3061 
3062         // Check if target is already grabbed by something else
3063         if( target->has_effect( effect_grabbed ) ) {
3064             for( Creature *critter : here.get_creatures_in_radius( target->pos(), 1 ) ) {
3065                 monster *mon = dynamic_cast<monster *>( critter );
3066                 if( mon != nullptr && mon != z ) {
3067                     if( mon->type->id != mon_nursebot_defective ) {
3068                         sounds::sound( z->pos(), 8, sounds::sound_t::electronic_speech,
3069                                        string_format(
3070                                            _( "a soft robotic voice say, \"Unhand this patient immediately!  If you keep interfering with the procedure I'll be forced to call law enforcement.\"" ) ) );
3071                         // Try to push the perpetrator away
3072                         z->push_to( mon->pos(), 6, 0 );
3073                     } else {
3074                         sounds::sound( z->pos(), 8, sounds::sound_t::electronic_speech,
3075                                        string_format(
3076                                            _( "a soft robotic voice say, \"Greetings kinbot.  Please take good care of this patient.\"" ) ) );
3077                         z->anger = 0;
3078                         // Situation is under control no need to intervene;
3079                         return false;
3080                     }
3081                 }
3082             }
3083         } else {
3084             grab( z );
3085             // Check if we successfully grabbed the target
3086             if( target->has_effect( effect_grabbed ) ) {
3087                 z->dragged_foe_id = target->getID();
3088                 z->add_effect( effect_dragging, 1_turns, true );
3089                 return true;
3090             }
3091         }
3092         return false;
3093     }
3094     z->anger = 0;
3095     return false;
3096 }
check_money_left(monster * z)3097 bool mattack::check_money_left( monster *z )
3098 {
3099     if( !z->has_effect( effect_pet ) ) {
3100         if( z->friendly == -1 &&
3101             z->has_effect( effect_paid ) ) { // if the pet effect runs out we're no longer friends
3102             z->friendly = 0;
3103 
3104             if( !z->inv.empty() ) {
3105                 for( const item &it : z->inv ) {
3106                     get_map().add_item_or_charges( z->pos(), it );
3107                 }
3108                 z->inv.clear();
3109                 z->remove_effect( effect_has_bag );
3110                 add_msg( m_info,
3111                          _( "The %s dumps the contents of its bag on the ground and drops the bag on top of it." ),
3112                          z->get_name() );
3113             }
3114 
3115             const SpeechBubble &speech_no_time = get_speech( "mon_grocerybot_friendship_done" );
3116             sounds::sound( z->pos(), speech_no_time.volume,
3117                            sounds::sound_t::electronic_speech, speech_no_time.text );
3118             z->remove_effect( effect_paid );
3119             return true;
3120         }
3121     } else {
3122         const time_duration time_left = z->get_effect_dur( effect_pet );
3123         if( time_left < 1_minutes ) {
3124             if( calendar::once_every( 20_seconds ) ) {
3125                 const SpeechBubble &speech_time_low = get_speech( "mon_grocerybot_running_out_of_friendship" );
3126                 sounds::sound( z->pos(), speech_time_low.volume,
3127                                sounds::sound_t::electronic_speech, speech_time_low.text );
3128             }
3129         }
3130     }
3131     if( z->friendly == -1 && !z->has_effect( effect_paid ) ) {
3132         if( calendar::once_every( 3_hours ) ) {
3133             const SpeechBubble &speech_override_start = get_speech( "mon_grocerybot_hacked" );
3134             sounds::sound( z->pos(), speech_override_start.volume,
3135                            sounds::sound_t::electronic_speech, speech_override_start.text );
3136         }
3137     }
3138     return false;
3139 }
photograph(monster * z)3140 bool mattack::photograph( monster *z )
3141 {
3142     if( !within_visual_range( z, 6 ) ) {
3143         return false;
3144     }
3145 
3146     Character &player_character = get_player_character();
3147     // Badges should NOT be swappable between roles.
3148     // Hence separate checking.
3149     // If you are in fact listed as a police officer
3150     if( player_character.has_trait( trait_PROF_POLICE ) ) {
3151         // And you're wearing your badge
3152         if( player_character.is_wearing( itype_badge_deputy ) ) {
3153             if( one_in( 3 ) ) {
3154                 add_msg( m_info, _( "The %s flashes a LED and departs.  Human officer on scene." ),
3155                          z->name() );
3156                 z->no_corpse_quiet = true;
3157                 z->no_extra_death_drops = true;
3158                 z->die( nullptr );
3159                 return false;
3160             } else {
3161                 add_msg( m_info,
3162                          _( "The %s acknowledges you as an officer responding, but hangs around to watch." ),
3163                          z->name() );
3164                 add_msg( m_info, _( "Probably some now-obsolete Internal Affairs subroutine…" ) );
3165                 return true;
3166             }
3167         }
3168     }
3169 
3170     if( player_character.has_trait( trait_PROF_PD_DET ) ) {
3171         // And you have your shield on
3172         if( player_character.is_wearing( itype_badge_detective ) ) {
3173             if( one_in( 4 ) ) {
3174                 add_msg( m_info, _( "The %s flashes a LED and departs.  Human officer on scene." ),
3175                          z->name() );
3176                 z->no_corpse_quiet = true;
3177                 z->no_extra_death_drops = true;
3178                 z->die( nullptr );
3179                 return false;
3180             } else {
3181                 add_msg( m_info,
3182                          _( "The %s acknowledges you as an officer responding, but hangs around to watch." ),
3183                          z->name() );
3184                 add_msg( m_info, _( "Ops used to do that in case you needed backup…" ) );
3185                 return true;
3186             }
3187         }
3188     } else if( player_character.has_trait( trait_PROF_SWAT ) ) {
3189         // And you're wearing your badge
3190         if( player_character.is_wearing( itype_badge_swat ) ) {
3191             if( one_in( 3 ) ) {
3192                 add_msg( m_info, _( "The %s flashes a LED and departs.  SWAT's working the area." ),
3193                          z->name() );
3194                 z->no_corpse_quiet = true;
3195                 z->no_extra_death_drops = true;
3196                 z->die( nullptr );
3197                 return false;
3198             } else {
3199                 add_msg( m_info, _( "The %s acknowledges you as SWAT onsite, but hangs around to watch." ),
3200                          z->name() );
3201                 add_msg( m_info, _( "Probably some now-obsolete Internal Affairs subroutine…" ) );
3202                 return true;
3203             }
3204         }
3205     } else if( player_character.has_trait( trait_PROF_CYBERCO ) ) {
3206         // And you're wearing your badge
3207         if( player_character.is_wearing( itype_badge_cybercop ) ) {
3208             if( one_in( 3 ) ) {
3209                 add_msg( m_info, _( "The %s winks a LED and departs.  One machine to another?" ),
3210                          z->name() );
3211                 z->no_corpse_quiet = true;
3212                 z->no_extra_death_drops = true;
3213                 z->die( nullptr );
3214                 return false;
3215             } else {
3216                 add_msg( m_info,
3217                          _( "The %s acknowledges you as an officer responding, but hangs around to watch." ),
3218                          z->name() );
3219                 add_msg( m_info, _( "Apparently yours aren't the only systems kept alive post-apocalypse." ) );
3220                 return true;
3221             }
3222         }
3223     }
3224 
3225     if( player_character.has_trait( trait_PROF_FED ) ) {
3226         // And you're wearing your badge
3227         if( player_character.is_wearing( itype_badge_marshal ) ) {
3228             add_msg( m_info, _( "The %s flashes a LED and departs.  The Feds got this." ), z->name() );
3229             z->no_corpse_quiet = true;
3230             z->no_extra_death_drops = true;
3231             z->die( nullptr );
3232             return false;
3233         }
3234     }
3235 
3236     if( z->friendly || player_character.weapon.typeId() == itype_e_handcuffs ) {
3237         // Friendly (hacked?) bot ignore the player. Arrested suspect ignored too.
3238         // TODO: might need to be revisited when it can target npcs.
3239         return false;
3240     }
3241     z->moves -= 150;
3242     add_msg( m_warning, _( "The %s takes your picture!" ), z->name() );
3243     // TODO: Make the player known to the faction
3244     std::string cname = _( "…database connection lost!" );
3245     if( one_in( 6 ) ) {
3246         cname = Name::generate( player_character.male );
3247     } else if( one_in( 3 ) ) {
3248         cname = player_character.name;
3249     }
3250     sounds::sound( z->pos(), 15, sounds::sound_t::alert,
3251                    string_format( _( "a robotic voice boom, \"Citizen %s!\"" ), cname ), false, "speech",
3252                    z->type->id.str() );
3253 
3254     if( player_character.weapon.is_gun() ) {
3255         sounds::sound( z->pos(), 15, sounds::sound_t::alert, _( "\"Drop your gun!  Now!\"" ) );
3256     } else if( player_character.is_armed() ) {
3257         sounds::sound( z->pos(), 15, sounds::sound_t::alert, _( "\"Drop your weapon!  Now!\"" ) );
3258     }
3259     const SpeechBubble &speech = get_speech( z->type->id.str() );
3260     sounds::sound( z->pos(), speech.volume, sounds::sound_t::alert, speech.text.translated() );
3261     get_timed_events().add( timed_event_type::ROBOT_ATTACK, calendar::turn + rng( 15_turns, 30_turns ),
3262                             0,
3263                             player_character.global_sm_location() );
3264 
3265     return true;
3266 }
3267 
tazer(monster * z)3268 bool mattack::tazer( monster *z )
3269 {
3270     Creature *target = z->attack_target();
3271     if( target == nullptr || !z->is_adjacent( target, false ) ) {
3272         return false;
3273     }
3274 
3275     taze( z, target );
3276     return true;
3277 }
3278 
taze(monster * z,Creature * target)3279 void mattack::taze( monster *z, Creature *target )
3280 {
3281     // It takes a while
3282     z->moves -= 200;
3283     if( target == nullptr || target->uncanny_dodge() ) {
3284         return;
3285     }
3286 
3287     int dam = target->deal_damage( z, bodypart_id( "torso" ), damage_instance( damage_type::ELECTRIC,
3288                                    rng( 1,
3289                                         5 ) ) ).total_damage();
3290     if( dam == 0 ) {
3291         target->add_msg_player_or_npc( _( "The %s unsuccessfully attempts to shock you." ),
3292                                        _( "The %s unsuccessfully attempts to shock <npcname>." ),
3293                                        z->name() );
3294         return;
3295     }
3296 
3297     game_message_type m_type = target->attitude_to( get_player_character() ) ==
3298                                Creature::Attitude::FRIENDLY ?
3299                                m_bad : m_neutral;
3300     target->add_msg_player_or_npc( m_type,
3301                                    _( "The %s shocks you!" ),
3302                                    _( "The %s shocks <npcname>!" ),
3303                                    z->name() );
3304     target->check_dead_state();
3305 }
3306 
rifle(monster * z,Creature * target)3307 void mattack::rifle( monster *z, Creature *target )
3308 {
3309     const itype_id ammo_type( "556" );
3310     // Make sure our ammo isn't weird.
3311     if( z->ammo[ammo_type] > 3000 ) {
3312         debugmsg( "Generated too much ammo (%d) for %s in mattack::rifle", z->ammo[ammo_type],
3313                   z->name() );
3314         z->ammo[ammo_type] = 3000;
3315     }
3316 
3317     npc tmp = make_fake_npc( z, 16, 10, 8, 12 );
3318     tmp.set_skill_level( skill_rifle, 8 );
3319     tmp.set_skill_level( skill_gun, 6 );
3320     // No need to aim
3321     tmp.recoil = 0;
3322 
3323     if( target && target->is_avatar() ) {
3324         if( !z->has_effect( effect_targeted ) ) {
3325             sounds::sound( z->pos(), 8, sounds::sound_t::alarm, _( "beep-beep." ), false, "misc", "beep" );
3326             z->add_effect( effect_targeted, 8_turns );
3327             z->moves -= 100;
3328             return;
3329         }
3330     }
3331     // It takes a while
3332     z->moves -= 150;
3333 
3334     if( z->ammo[ammo_type] <= 0 ) {
3335         if( one_in( 3 ) ) {
3336             sounds::sound( z->pos(), 2, sounds::sound_t::combat, _( "a chk!" ), false, "fire_gun", "empty" );
3337         } else if( one_in( 4 ) ) {
3338             sounds::sound( z->pos(), 6, sounds::sound_t::combat,  _( "boop!" ), false, "fire_gun", "empty" );
3339         }
3340         return;
3341     }
3342     add_msg_if_player_sees( *z, m_warning, _( "The %s opens up with its rifle!" ), z->name() );
3343 
3344     tmp.weapon = item( "m4a1" ).ammo_set( ammo_type, z->ammo[ ammo_type ] );
3345     int burst = std::max( tmp.weapon.gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 );
3346 
3347     z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required();
3348 
3349     if( target && target->is_avatar() ) {
3350         z->add_effect( effect_targeted, 3_turns );
3351     }
3352 }
3353 
frag(monster * z,Creature * target)3354 void mattack::frag( monster *z, Creature *target ) // This is for the bots, not a standalone turret
3355 {
3356     const itype_id ammo_type( "40x46mm_m433" );
3357     // Make sure our ammo isn't weird.
3358     if( z->ammo[ammo_type] > 200 ) {
3359         debugmsg( "Generated too much ammo (%d) for %s in mattack::frag", z->ammo[ammo_type],
3360                   z->name() );
3361         z->ammo[ammo_type] = 200;
3362     }
3363 
3364     Character &player_character = get_player_character();
3365     if( target && target->is_avatar() ) {
3366         if( !z->has_effect( effect_targeted ) ) {
3367             if( player_character.has_trait( trait_PROF_CHURL ) ) {
3368                 //~ Potential grenading detected.
3369                 add_msg( m_warning, _( "Thee eye o dat divil be upon me!" ) );
3370             } else {
3371                 //~ Potential grenading detected.
3372                 add_msg( m_warning, _( "Those laser dots don't seem very friendly…" ) );
3373             }
3374             // Effect removed in game.cpp, duration doesn't much matter
3375             player_character.add_effect( effect_laserlocked, 3_turns );
3376             sounds::sound( z->pos(), 10, sounds::sound_t::electronic_speech, _( "Targeting." ),
3377                            false, "speech", z->type->id.str() );
3378             z->add_effect( effect_targeted, 5_turns );
3379             z->moves -= 150;
3380             // Should give some ability to get behind cover,
3381             // even though it's patently unrealistic.
3382             return;
3383         }
3384     }
3385     npc tmp = make_fake_npc( z, 16, 10, 8, 12 );
3386     tmp.set_skill_level( skill_launcher, 8 );
3387     tmp.set_skill_level( skill_gun, 6 );
3388     // No need to aim
3389     tmp.recoil = 0;
3390     // It takes a while
3391     z->moves -= 150;
3392 
3393     if( z->ammo[ammo_type] <= 0 ) {
3394         if( one_in( 3 ) ) {
3395             sounds::sound( z->pos(), 2, sounds::sound_t::combat, _( "a chk!" ), false, "fire_gun", "empty" );
3396         } else if( one_in( 4 ) ) {
3397             sounds::sound( z->pos(), 6, sounds::sound_t::combat, _( "boop!" ), false, "fire_gun", "empty" );
3398         }
3399         return;
3400     }
3401     add_msg_if_player_sees( *z, m_warning, _( "The %s's grenade launcher fires!" ), z->name() );
3402 
3403     tmp.weapon = item( "mgl" ).ammo_set( ammo_type, z->ammo[ ammo_type ] );
3404     int burst = std::max( tmp.weapon.gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 );
3405 
3406     z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required();
3407 
3408     if( target && target->is_avatar() ) {
3409         z->add_effect( effect_targeted, 3_turns );
3410     }
3411 }
3412 
tankgun(monster * z,Creature * target)3413 void mattack::tankgun( monster *z, Creature *target )
3414 {
3415     const itype_id ammo_type( "120mm_HEAT" );
3416     // Make sure our ammo isn't weird.
3417     if( z->ammo[ammo_type] > 40 ) {
3418         debugmsg( "Generated too much ammo (%d) for %s in mattack::tankgun", z->ammo[ammo_type],
3419                   z->name() );
3420         z->ammo[ammo_type] = 40;
3421     }
3422 
3423     int dist = rl_dist( z->pos(), target->pos() );
3424     if( dist > 50 ) {
3425         return;
3426     }
3427 
3428     if( !z->has_effect( effect_targeted ) ) {
3429         //~ There will be a 120mm HEAT shell sent at high speed to your location next turn.
3430         target->add_msg_if_player( m_warning, _( "You're not sure why you've got a laser dot on you…" ) );
3431         //~ Sound of a tank turret swiveling into place
3432         sounds::sound( z->pos(), 10, sounds::sound_t::combat, _( "whirrrrrclick." ), false, "misc",
3433                        "servomotor" );
3434         z->add_effect( effect_targeted, 1_minutes );
3435         target->add_effect( effect_laserlocked, 1_minutes );
3436         z->moves -= 200;
3437         // Should give some ability to get behind cover,
3438         // even though it's patently unrealistic.
3439         return;
3440     }
3441     // kevingranade KA101: yes, but make it really inaccurate
3442     // Sure thing.
3443     npc tmp = make_fake_npc( z, 12, 8, 8, 8 );
3444     tmp.set_skill_level( skill_launcher, 1 );
3445     tmp.set_skill_level( skill_gun, 1 );
3446     // No need to aim
3447     tmp.recoil = 0;
3448     // It takes a while
3449     z->moves -= 150;
3450 
3451     if( z->ammo[ammo_type] <= 0 ) {
3452         if( one_in( 3 ) ) {
3453             sounds::sound( z->pos(), 2, sounds::sound_t::combat, _( "a chk!" ), false, "fire_gun", "empty" );
3454         } else if( one_in( 4 ) ) {
3455             sounds::sound( z->pos(), 6, sounds::sound_t::combat, _( "clank!" ), false, "fire_gun", "empty" );
3456         }
3457         return;
3458     }
3459     add_msg_if_player_sees( *z, m_warning, _( "The %s's 120mm cannon fires!" ), z->name() );
3460     tmp.weapon = item( "TANK" ).ammo_set( ammo_type, z->ammo[ ammo_type ] );
3461     int burst = std::max( tmp.weapon.gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 );
3462 
3463     z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required();
3464 }
3465 
searchlight(monster * z)3466 bool mattack::searchlight( monster *z )
3467 {
3468 
3469     int max_lamp_count = 3;
3470     if( z->get_hp() < z->get_hp_max() ) {
3471         max_lamp_count--;
3472     }
3473     if( z->get_hp() < z->get_hp_max() / 3 ) {
3474         max_lamp_count--;
3475     }
3476 
3477     const int zposx = z->posx();
3478     const int zposy = z->posy();
3479 
3480     map &here = get_map();
3481     //this searchlight is not initialized
3482     if( z->inv.empty() ) {
3483 
3484         for( int i = 0; i < max_lamp_count; i++ ) {
3485 
3486             item settings( "processor", calendar::turn_zero );
3487 
3488             settings.set_var( "SL_PREFER_UP", "TRUE" );
3489             settings.set_var( "SL_PREFER_DOWN", "TRUE" );
3490             settings.set_var( "SL_PREFER_RIGHT", "TRUE" );
3491             settings.set_var( "SL_PREFER_LEFT", "TRUE" );
3492 
3493             for( const tripoint &dest : here.points_in_radius( z->pos(), 24 ) ) {
3494                 const monster *const mon = g->critter_at<monster>( dest );
3495                 if( mon && mon->type->id == mon_turret_searchlight ) {
3496                     if( dest.x < zposx ) {
3497                         settings.set_var( "SL_PREFER_LEFT", "FALSE" );
3498                     }
3499                     if( dest.x > zposx ) {
3500                         settings.set_var( "SL_PREFER_RIGHT", "FALSE" );
3501                     }
3502                     if( dest.y < zposy ) {
3503                         settings.set_var( "SL_PREFER_UP", "FALSE" );
3504                     }
3505                     if( dest.y > zposy ) {
3506                         settings.set_var( "SL_PREFER_DOWN", "FALSE" );
3507                     }
3508                 }
3509             }
3510 
3511             settings.set_var( "SL_SPOT_X", 0 );
3512             settings.set_var( "SL_SPOT_Y", 0 );
3513 
3514             z->add_item( settings );
3515         }
3516     }
3517 
3518     //battery charge from the generator is enough for some time of work
3519     if( calendar::once_every( 10_minutes ) ) {
3520 
3521         bool generator_ok = false;
3522 
3523         for( int x = zposx - 24; x < zposx + 24; x++ ) {
3524             for( int y = zposy - 24; y < zposy + 24; y++ ) {
3525                 tripoint dest( x, y, z->posz() );
3526                 if( here.ter( dest ) == ter_str_id( "t_plut_generator" ) ) {
3527                     generator_ok = true;
3528                 }
3529             }
3530         }
3531 
3532         if( !generator_ok ) {
3533             for( auto &settings : z->inv ) {
3534                 settings.set_var( "SL_POWER", "OFF" );
3535             }
3536 
3537             return true;
3538         }
3539     }
3540 
3541     Character &player_character = get_player_character();
3542     for( int i = 0; i < max_lamp_count; i++ ) {
3543 
3544         item &settings = z->inv[i];
3545 
3546         if( settings.get_var( "SL_POWER" )  == "OFF" ) {
3547             return true;
3548         }
3549 
3550         const int rng_dir = rng( 0, 7 );
3551 
3552         if( one_in( 5 ) ) {
3553 
3554             if( !one_in( 5 ) ) {
3555                 settings.set_var( "SL_DIR", rng_dir );
3556             } else {
3557                 const int rng_pref = rng( 0, 3 ) * 2;
3558                 if( rng_pref == 0 && settings.get_var( "SL_PREFER_UP" ) == "TRUE" ) {
3559                     settings.set_var( "SL_DIR", rng_pref );
3560                 } else            if( rng_pref == 2 && settings.get_var( "SL_PREFER_RIGHT" ) == "TRUE" ) {
3561                     settings.set_var( "SL_DIR", rng_pref );
3562                 } else            if( rng_pref == 4 && settings.get_var( "SL_PREFER_DOWN" ) == "TRUE" ) {
3563                     settings.set_var( "SL_DIR", rng_pref );
3564                 } else            if( rng_pref == 6 && settings.get_var( "SL_PREFER_LEFT" ) == "TRUE" ) {
3565                     settings.set_var( "SL_DIR", rng_pref );
3566                 }
3567             }
3568         }
3569 
3570         int x = zposx + settings.get_var( "SL_SPOT_X", 0 );
3571         int y = zposy + settings.get_var( "SL_SPOT_Y", 0 );
3572         int shift = 0;
3573 
3574         for( int i = 0; i < rng( 1, 2 ); i++ ) {
3575 
3576             if( !z->sees( player_character ) ) {
3577                 shift = settings.get_var( "SL_DIR", shift );
3578 
3579                 switch( shift ) {
3580                     case 0:
3581                         y--;
3582                         break;
3583                     case 1:
3584                         y--;
3585                         x++;
3586                         break;
3587                     case 2:
3588                         x++;
3589                         break;
3590                     case 3:
3591                         x++;
3592                         y++;
3593                         break;
3594                     case 4:
3595                         y++;
3596                         break;
3597                     case 5:
3598                         y++;
3599                         x--;
3600                         break;
3601                     case 6:
3602                         x--;
3603                         break;
3604                     case 7:
3605                         x--;
3606                         y--;
3607                         break;
3608 
3609                     default:
3610                         break;
3611                 }
3612 
3613             } else {
3614                 if( x < player_character.posx() ) {
3615                     x++;
3616                 }
3617                 if( x > player_character.posx() ) {
3618                     x--;
3619                 }
3620                 if( y < player_character.posy() ) {
3621                     y++;
3622                 }
3623                 if( y > player_character.posy() ) {
3624                     y--;
3625                 }
3626             }
3627 
3628             if( rl_dist( point( x, y ), point( zposx, zposy ) ) > 50 ) {
3629                 if( x > zposx ) {
3630                     x--;
3631                 }
3632                 if( x < zposx ) {
3633                     x++;
3634                 }
3635                 if( y > zposy ) {
3636                     y--;
3637                 }
3638                 if( y < zposy ) {
3639                     y++;
3640                 }
3641             }
3642         }
3643 
3644         settings.set_var( "SL_SPOT_X", x - zposx );
3645         settings.set_var( "SL_SPOT_Y", y - zposy );
3646 
3647         here.add_field( tripoint( x, y, z->posz() ), field_type_id( "fd_spotlight" ), 1 );
3648     }
3649 
3650     return true;
3651 }
3652 
flamethrower(monster * z)3653 bool mattack::flamethrower( monster *z )
3654 {
3655     if( z->friendly ) {
3656         // TODO: handle friendly monsters
3657         return false;
3658     }
3659     Character &player_character = get_player_character();
3660     // TODO: that is always false!
3661     if( z->friendly != 0 ) {
3662         // Attacking monsters, not the player!
3663         int boo_hoo;
3664         Creature *target = z->auto_find_hostile_target( 5, boo_hoo );
3665         // Couldn't find any targets!
3666         if( target == nullptr ) {
3667             // Because that stupid oaf was in the way!
3668             if( boo_hoo > 0 ) {
3669                 add_msg_if_player_sees( *z, m_warning,
3670                                         ngettext( "Pointed in your direction, the %s emits an IFF warning beep.",
3671                                                   "Pointed in your direction, the %s emits %d annoyed sounding beeps.",
3672                                                   boo_hoo ),
3673                                         z->name(), boo_hoo );
3674             }
3675             // Did reset before refactor, changed to match other turret behaviors
3676             return false;
3677         }
3678         flame( z, target );
3679         return true;
3680     }
3681 
3682     if( !within_visual_range( z, 5 ) ) {
3683         return false;
3684     }
3685 
3686     flame( z, &player_character );
3687 
3688     return true;
3689 }
3690 
flame(monster * z,Creature * target)3691 void mattack::flame( monster *z, Creature *target )
3692 {
3693     int dist = rl_dist( z->pos(), target->pos() );
3694     Character &player_character = get_player_character();
3695     map &here = get_map();
3696     if( target != &player_character ) {
3697         // friendly
3698         // It takes a while
3699         z->moves -= 500;
3700         if( !here.sees( z->pos(), target->pos(), dist ) ) {
3701             // shouldn't happen
3702             debugmsg( "mattack::flame invoked on invisible target" );
3703         }
3704         std::vector<tripoint> traj = here.find_clear_path( z->pos(), target->pos() );
3705 
3706         for( auto &i : traj ) {
3707             // break out of attack if flame hits a wall
3708             // TODO: Z
3709             if( here.hit_with_fire( tripoint( i.xy(), z->posz() ) ) ) {
3710                 add_msg_if_player_sees( i, _( "The tongue of flame hits the %s!" ),
3711                                         here.tername( i.xy() ) );
3712                 return;
3713             }
3714             here.add_field( i, fd_fire, 1 );
3715         }
3716         target->add_effect( effect_onfire, 8_turns, bodypart_id( "torso" ) );
3717 
3718         return;
3719     }
3720 
3721     // It takes a while
3722     z->moves -= 500;
3723     if( !here.sees( z->pos(), target->pos(), dist + 1 ) ) {
3724         // shouldn't happen
3725         debugmsg( "mattack::flame invoked on invisible target" );
3726     }
3727     std::vector<tripoint> traj = here.find_clear_path( z->pos(), target->pos() );
3728 
3729     for( auto &i : traj ) {
3730         // break out of attack if flame hits a wall
3731         if( here.hit_with_fire( tripoint( i.xy(), z->posz() ) ) ) {
3732             add_msg_if_player_sees( i,  _( "The tongue of flame hits the %s!" ),
3733                                     here.tername( i.xy() ) );
3734             return;
3735         }
3736         here.add_field( i, fd_fire, 1 );
3737     }
3738     if( !target->uncanny_dodge() ) {
3739         target->add_effect( effect_onfire, 8_turns, bodypart_id( "torso" ) );
3740     }
3741 }
3742 
copbot(monster * z)3743 bool mattack::copbot( monster *z )
3744 {
3745     Creature *target = z->attack_target();
3746     if( target == nullptr ) {
3747         return false;
3748     }
3749 
3750     // TODO: Make it recognize zeds as human, but ignore animals
3751     player *foe = dynamic_cast<player *>( target );
3752     bool sees_u = foe != nullptr && z->sees( *foe );
3753     bool cuffed = foe != nullptr && foe->weapon.typeId() == itype_e_handcuffs;
3754     // Taze first, then ask questions (simplifies later checks for non-humans)
3755     if( !cuffed && z->is_adjacent( target, true ) ) {
3756         taze( z, target );
3757         return true;
3758     }
3759 
3760     if( rl_dist( z->pos(), target->pos() ) > 2 || foe == nullptr || !z->sees( *target ) ) {
3761         if( one_in( 3 ) ) {
3762             if( sees_u ) {
3763                 if( foe->unarmed_attack() ) {
3764                     sounds::sound( z->pos(), 18, sounds::sound_t::alert,
3765                                    _( "a robotic voice boom, \"Citizen, Halt!\"" ), false, "speech", z->type->id.str() );
3766                 } else if( !cuffed ) {
3767                     sounds::sound( z->pos(), 18, sounds::sound_t::alert,
3768                                    _( "a robotic voice boom, \"Please put down your weapon.\"" ), false, "speech", z->type->id.str() );
3769                 }
3770             } else {
3771                 sounds::sound( z->pos(), 18, sounds::sound_t::alert,
3772                                _( "a robotic voice boom, \"Come out with your hands up!\"" ), false, "speech", z->type->id.str() );
3773             }
3774         } else {
3775             sounds::sound( z->pos(), 18, sounds::sound_t::alarm,
3776                            _( "a police siren, whoop WHOOP" ), false, "environment", "police_siren" );
3777         }
3778         return true;
3779     }
3780 
3781     // If cuffed don't attack the player, unless the bot is damaged
3782     // presumably because of the player's actions
3783     if( z->get_hp() == z->get_hp_max() ) {
3784         z->anger = 1;
3785     } else {
3786         z->anger = z->type->agro;
3787     }
3788 
3789     return true;
3790 }
3791 
chickenbot(monster * z)3792 bool mattack::chickenbot( monster *z )
3793 {
3794     int mode = 0;
3795     int boo_hoo = 0;
3796     Creature *target;
3797     Character &player_character = get_player_character();
3798     if( z->friendly == 0 ) {
3799         target = z->attack_target();
3800         if( target == nullptr ) {
3801             return false;
3802         }
3803     } else {
3804         target = z->auto_find_hostile_target( 38, boo_hoo );
3805         if( target == nullptr ) {
3806             if( boo_hoo > 0 ) { // because that stupid oaf was in the way!
3807                 add_msg_if_player_sees( *z, m_warning,
3808                                         ngettext( "Pointed in your direction, the %s emits an IFF warning beep.",
3809                                                   "Pointed in your direction, the %s emits %d annoyed sounding beeps.",
3810                                                   boo_hoo ),
3811                                         z->name(), boo_hoo );
3812             }
3813             return false;
3814         }
3815     }
3816 
3817     int cap = target->power_rating() - 1;
3818     monster *mon = dynamic_cast< monster * >( target );
3819     // Their attitude to us and not ours to them, so that bobcats won't get gunned down
3820     // Only monster-types for now - assuming humans are smart enough not to make it obvious
3821     // Unless damaged - then everything is hostile
3822     if( z->get_hp() <= z->get_hp_max() ||
3823         ( mon != nullptr && mon->attitude_to( *z ) == Creature::Attitude::HOSTILE ) ) {
3824         cap += 2;
3825     }
3826 
3827     int dist = rl_dist( z->pos(), target->pos() );
3828     int player_dist = rl_dist( target->pos(), player_character.pos() );
3829     if( dist == 1 && one_in( 2 ) ) {
3830         // Use tazer at point-blank range, and even then, not continuously.
3831         mode = 1;
3832     } else if( ( z->friendly == 0 || player_dist >= 6 ) &&
3833                // Avoid shooting near player if we're friendly.
3834                ( dist >= 12 || ( player_character.in_vehicle && dist >= 6 ) ) ) {
3835         // Only use at long range, unless player is in a vehicle, then tolerate closer targeting.
3836         mode = 3;
3837     } else if( dist >= 4 ) {
3838         // Don't use machine gun at very close range, under the assumption that targets at that range can dodge?
3839         mode = 2;
3840     }
3841 
3842     // No attacks were valid!
3843     if( mode == 0 ) {
3844         return false;
3845     }
3846 
3847     if( mode > cap ) {
3848         mode = cap;
3849     }
3850     switch( mode ) {
3851         case 0:
3852         case 1:
3853             // If we downgraded to taze, but are out of range, don't act.
3854             if( dist <= 1 ) {
3855                 taze( z, target );
3856             }
3857             break;
3858         case 2:
3859             if( dist <= 20 ) {
3860                 rifle( z, target );
3861             }
3862             break;
3863         case 3:
3864             if( dist <= 38 ) {
3865                 frag( z, target );
3866             }
3867             break;
3868         default:
3869             // Weak stuff, shouldn't bother with
3870             return false;
3871     }
3872 
3873     return true;
3874 }
3875 
multi_robot(monster * z)3876 bool mattack::multi_robot( monster *z )
3877 {
3878     int mode = 0;
3879     int boo_hoo = 0;
3880     Creature *target;
3881     Character &player_character = get_player_character();
3882     if( z->friendly == 0 ) {
3883         target = z->attack_target();
3884         if( target == nullptr ) {
3885             return false;
3886         }
3887     } else {
3888         target = z->auto_find_hostile_target( 48, boo_hoo );
3889         if( target == nullptr ) {
3890             if( boo_hoo > 0 ) { // because that stupid oaf was in the way!
3891                 add_msg_if_player_sees( *z, m_warning,
3892                                         ngettext( "Pointed in your direction, the %s emits an IFF warning beep.",
3893                                                   "Pointed in your direction, the %s emits %d annoyed sounding beeps.",
3894                                                   boo_hoo ),
3895                                         z->name(), boo_hoo );
3896             }
3897             return false;
3898         }
3899     }
3900 
3901     int cap = target->power_rating();
3902     monster *mon = dynamic_cast< monster * >( target );
3903     // Their attitude to us and not ours to them, so that bobcats won't get gunned down
3904     // Only monster-types for now - assuming humans are smart enough not to make it obvious
3905     // Unless damaged - then everything is hostile
3906     if( z->get_hp() <= z->get_hp_max() ||
3907         ( mon != nullptr && mon->attitude_to( *z ) == Creature::Attitude::HOSTILE ) ) {
3908         cap += 2;
3909     }
3910 
3911     int dist = rl_dist( z->pos(), target->pos() );
3912     if( dist <= 15 ) {
3913         mode = 1;
3914     } else if( dist <= 30 ) {
3915         mode = 2;
3916     } else if( ( target && target->is_avatar() && player_character.in_vehicle ) ||
3917                z->friendly != 0 || cap > 4 ) {
3918         // Primary only kicks in if you're in a vehicle or are big enough to be mistaken for one.
3919         // Or if you've hacked it so the turret's on your side.  ;-)
3920         if( dist < 50 ) {
3921             // Enforced max-range of 50.
3922             mode = 5;
3923             cap = 5;
3924         }
3925     }
3926 
3927     // No attacks were valid!
3928     if( mode == 0 ) {
3929         return false;
3930     }
3931 
3932     if( mode > cap ) {
3933         mode = cap;
3934     }
3935     switch( mode ) {
3936         case 1:
3937             if( dist <= 15 ) {
3938                 rifle( z, target );
3939             }
3940             break;
3941         case 2:
3942             if( dist <= 30 ) {
3943                 frag( z, target );
3944             }
3945             break;
3946         default:
3947             // Weak stuff, shouldn't bother with
3948             return false;
3949     }
3950 
3951     return true;
3952 }
3953 
ratking(monster * z)3954 bool mattack::ratking( monster *z )
3955 {
3956     if( z->friendly ) {
3957         // TODO: handle friendly monsters
3958         return false;
3959     }
3960     Character &player_character = get_player_character();
3961     // Disable z-level ratting or it can get silly
3962     if( rl_dist( z->pos(), player_character.pos() ) > 50 ||
3963         z->posz() != player_character.posz() ) {
3964         return false;
3965     }
3966 
3967     switch( rng( 1, 5 ) ) { // What do we say?
3968         case 1:
3969             add_msg( m_warning, _( "\"YOU… ARE FILTH…\"" ) );
3970             break;
3971         case 2:
3972             add_msg( m_warning, _( "\"VERMIN… YOU ARE VERMIN…\"" ) );
3973             break;
3974         case 3:
3975             add_msg( m_warning, _( "\"LEAVE NOW…\"" ) );
3976             break;
3977         case 4:
3978             add_msg( m_warning, _( "\"WE… WILL FEAST… UPON YOU…\"" ) );
3979             break;
3980         case 5:
3981             add_msg( m_warning, _( "\"FOUL INTERLOPER…\"" ) );
3982             break;
3983     }
3984     if( rl_dist( z->pos(), player_character.pos() ) <= 10 ) {
3985         player_character.add_effect( effect_rat, 3_minutes );
3986     }
3987 
3988     return true;
3989 }
3990 
generator(monster * z)3991 bool mattack::generator( monster *z )
3992 {
3993     sounds::sound( z->pos(), 100, sounds::sound_t::activity, "hmmmm" );
3994     if( calendar::once_every( 1_minutes ) && z->get_hp() < z->get_hp_max() ) {
3995         z->heal( 1 );
3996     }
3997 
3998     return true;
3999 }
4000 
upgrade(monster * z)4001 bool mattack::upgrade( monster *z )
4002 {
4003     std::vector<monster *> targets;
4004     for( monster &zed : g->all_monsters() ) {
4005         // Check this first because it is a relatively cheap check
4006         if( zed.can_upgrade() ) {
4007             // Then do the more expensive ones
4008             if( z->attitude_to( zed ) != Creature::Attitude::HOSTILE &&
4009                 within_target_range( z, &zed, 10 ) ) {
4010                 targets.push_back( &zed );
4011             }
4012         }
4013     }
4014     if( targets.empty() ) {
4015         // Nobody to upgrade, get MAD!
4016         z->anger = 100;
4017         return false;
4018     } else {
4019         // We've got zombies to upgrade now, calm down again
4020         z->anger = 5;
4021     }
4022 
4023     // Takes one turn
4024     z->moves -= z->type->speed;
4025 
4026     monster *target = random_entry( targets );
4027 
4028     std::string old_name = target->name();
4029     viewer &player_view = get_player_view();
4030     const bool could_see = player_view.sees( *target );
4031     target->hasten_upgrade();
4032     target->try_upgrade( false );
4033     const bool can_see = player_view.sees( *target );
4034     if( player_view.sees( *z ) ) {
4035         if( could_see ) {
4036             add_msg( m_warning,
4037                      //~ %1$s is the name of the zombie upgrading the other, %2$s is the zombie being upgraded.
4038                      _( "You feel a sudden, intense burst of energy in the air between the %1$s and the %2$s." ),
4039                      z->name(), old_name );
4040         } else {
4041             add_msg( m_warning, _( "You feel a sudden, intense burst of energy from the %s." ), z->name() );
4042         }
4043     }
4044     if( target->name() != old_name ) {
4045         if( could_see && can_see ) {
4046             //~ %1$s is the pre-upgrade monster, %2$s is the post-upgrade monster.
4047             add_msg( m_warning, _( "The %1$s becomes a %2$s!" ), old_name,
4048                      target->name() );
4049         } else if( could_see ) {
4050             add_msg( m_warning, _( "The %s vanishes!" ), old_name );
4051         } else if( can_see ) {
4052             add_msg( m_warning, _( "A %s appears!" ), target->name() );
4053         }
4054     }
4055 
4056     return true;
4057 }
4058 
breathe(monster * z)4059 bool mattack::breathe( monster *z )
4060 {
4061     // It takes a while
4062     z->moves -= 100;
4063 
4064     bool able = ( z->type->id == mon_breather_hub );
4065     if( !able ) {
4066         for( const tripoint &dest : get_map().points_in_radius( z->pos(), 3 ) ) {
4067             monster *const mon = g->critter_at<monster>( dest );
4068             if( mon && mon->type->id == mon_breather_hub ) {
4069                 able = true;
4070                 break;
4071             }
4072         }
4073     }
4074     if( !able ) {
4075         return true;
4076     }
4077 
4078     if( monster *const spawned = g->place_critter_around( mon_breather, z->pos(), 1 ) ) {
4079         spawned->reset_special( "BREATHE" );
4080         spawned->make_ally( *z );
4081     }
4082 
4083     return true;
4084 }
4085 
stretch_bite(monster * z)4086 bool mattack::stretch_bite( monster *z )
4087 {
4088     if( !z->can_act() ) {
4089         return false;
4090     }
4091 
4092     // Let it be used on non-player creatures
4093     // can be used at close range too!
4094     Creature *target = z->attack_target();
4095     if( target == nullptr ) {
4096         return false;
4097     }
4098     int distance = rl_dist( z->pos(), target->pos() );
4099     // Hack, only allow attacking above or below if the target is adjacent.
4100     if( z->pos().z != target->pos().z ) {
4101         distance += 2;
4102     }
4103     if( distance > 3 || !z->sees( *target ) ) {
4104         return false;
4105     }
4106 
4107     z->moves -= 150;
4108 
4109     map &here = get_map();
4110     for( auto &pnt : here.find_clear_path( z->pos(), target->pos() ) ) {
4111         if( here.impassable( pnt ) ) {
4112             z->add_effect( effect_stunned, 6_turns );
4113             target->add_msg_player_or_npc( _( "The %1$s stretches its head at you, but bounces off the %2$s" ),
4114                                            _( "The %1$s stretches its head at <npcname>, but bounces off the %2$s" ),
4115                                            z->name(), here.obstacle_name( pnt ) );
4116             return true;
4117         }
4118     }
4119     bool uncanny = target->uncanny_dodge();
4120     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
4121     if( uncanny || dodge_check( z, target ) ) {
4122         z->moves -= 150;
4123         z->add_effect( effect_stunned, 3_turns );
4124         game_message_type msg_type = target->is_avatar() ? m_warning : m_info;
4125         target->add_msg_player_or_npc( msg_type,
4126                                        _( "The %s's head extends to bite you, but you dodge and the head sails past!" ),
4127                                        _( "The %s's head extends to bite <npcname>, but they dodge and the head sails past!" ),
4128                                        z->name() );
4129         if( !uncanny ) {
4130             target->on_dodge( z, z->type->melee_skill );
4131         }
4132         return true;
4133     }
4134 
4135     const bodypart_id hit = target->get_random_body_part();
4136     // More damage due to the speed of the moving head
4137     int dam = rng( 5, 15 );
4138     dam = target->deal_damage( z, hit, damage_instance( damage_type::STAB, dam ) ).total_damage();
4139 
4140     if( dam > 0 ) {
4141         game_message_type msg_type = target->is_avatar() ? m_bad : m_info;
4142         target->add_msg_player_or_npc( msg_type,
4143                                        //~ 1$s is monster name, 2$s bodypart in accusative
4144                                        _( "The %1$s's teeth sink into your %2$s!" ),
4145                                        //~ 1$s is monster name, 2$s bodypart in accusative
4146                                        _( "The %1$s's teeth sink into <npcname>'s %2$s!" ),
4147                                        z->name(),
4148                                        body_part_name_accusative( hit ) );
4149 
4150         if( one_in( 16 - dam ) ) {
4151             if( target->has_effect( effect_bite, hit.id() ) ) {
4152                 target->add_effect( effect_bite, 40_minutes, hit, true );
4153             } else if( target->has_effect( effect_infected, hit.id() ) ) {
4154                 target->add_effect( effect_infected, 25_minutes, hit, true );
4155             } else {
4156                 target->add_effect( effect_bite, 1_turns, hit, true );
4157             }
4158         }
4159     } else {
4160         target->add_msg_player_or_npc( _( "The %1$s's head hits your %2$s, but glances off your armor!" ),
4161                                        _( "The %1$s's head hits <npcname>'s %2$s, but glances off armor!" ),
4162                                        z->name(),
4163                                        body_part_name_accusative( hit ) );
4164     }
4165 
4166     target->on_hit( z, hit,  z->type->melee_skill );
4167 
4168     return true;
4169 }
4170 
brandish(monster * z)4171 bool mattack::brandish( monster *z )
4172 {
4173     if( z->friendly ) {
4174         // TODO: handle friendly monsters
4175         return false;
4176     }
4177     // Only brandish if we can see you!
4178     if( !z->sees( get_player_character() ) ) {
4179         return false;
4180     }
4181     add_msg( m_warning, _( "He's brandishing a knife!" ) );
4182     add_msg( _( "Quiet, quiet" ) );
4183 
4184     return true;
4185 }
4186 
flesh_golem(monster * z)4187 bool mattack::flesh_golem( monster *z )
4188 {
4189     if( !z->can_act() ) {
4190         return false;
4191     }
4192 
4193     Creature *target = z->attack_target();
4194     if( target == nullptr ) {
4195         return false;
4196     }
4197 
4198     int dist = rl_dist( z->pos(), target->pos() );
4199     if( dist > 20 ||
4200         !z->sees( *target ) ) {
4201         return false;
4202     }
4203 
4204     if( dist > 1 ) {
4205         if( one_in( 12 ) ) {
4206             z->moves -= 200;
4207             // It doesn't "nearly deafen you" when it roars from the other side of bubble
4208             sounds::sound( z->pos(), 80, sounds::sound_t::alert, _( "a terrifying roar!" ), false, "shout",
4209                            "roar" );
4210             return true;
4211         }
4212         return false;
4213     }
4214     if( !z->is_adjacent( target, true ) ) {
4215         // No attacking through floor, even if we can see the target somehow
4216         return false;
4217     }
4218     add_msg_if_player_sees( *z, _( "The %1$s swings a massive claw at %2$s!" ),
4219                             z->name(), target->disp_name() );
4220     z->moves -= 100;
4221 
4222     if( target->uncanny_dodge() ) {
4223         return true;
4224     }
4225 
4226     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
4227     if( dodge_check( z, target ) ) {
4228         target->add_msg_player_or_npc( _( "You dodge it!" ),
4229                                        _( "<npcname> dodges it!" ) );
4230         target->on_dodge( z, z->type->melee_skill );
4231         return true;
4232     }
4233     const bodypart_id hit = target->get_random_body_part();
4234     // TODO: 10 bashing damage doesn't sound like a "massive claw" but a mediocre punch
4235     int dam = rng( 5, 10 );
4236     target->deal_damage( z, hit, damage_instance( damage_type::BASH, dam ) );
4237     if( one_in( 6 ) ) {
4238         target->add_effect( effect_downed, 3_minutes );
4239     }
4240 
4241     //~ 1$s is bodypart name, 2$d is damage value.
4242     target->add_msg_if_player( m_bad, _( "Your %1$s is battered for %2$d damage!" ),
4243                                body_part_name( hit ), dam );
4244     target->on_hit( z, hit,  z->type->melee_skill );
4245 
4246     return true;
4247 }
4248 
absorb_meat(monster * z)4249 bool mattack::absorb_meat( monster *z )
4250 {
4251     //Absorb no more than 1/10th monster's volume, times the volume of a meat chunk
4252     const int monster_volume = units::to_liter( z->get_volume() );
4253     const float average_meat_chunk_volume = 0.5f;
4254     // TODO: dynamically get volume of meat
4255     const int max_meat_absorbed = monster_volume / 10.0f * average_meat_chunk_volume;
4256     //For every milliliter of meat absorbed, heal this many HP
4257     const float meat_absorption_factor = 0.01f;
4258     Character &player_character = get_player_character();
4259     map &here = get_map();
4260     //Search surrounding tiles for meat
4261     for( const auto &p : here.points_in_radius( z->pos(), 1 ) ) {
4262         map_stack items = here.i_at( p );
4263         for( auto &current_item : items ) {
4264             const material_id current_item_material = current_item.get_base_material().ident();
4265             if( current_item_material == material_id( "flesh" ) ||
4266                 current_item_material == material_id( "hflesh" ) ) {
4267                 //We have something meaty! Calculate how much it will heal the monster
4268                 const int ml_of_meat = units::to_milliliter<int>( current_item.volume() );
4269                 const int total_charges = current_item.count();
4270                 const int ml_per_charge = ml_of_meat / total_charges;
4271                 //We have a max size of meat here to avoid absorbing whole corpses.
4272                 if( ml_per_charge > max_meat_absorbed * 1000 ) {
4273                     add_msg( m_info, _( "The %1$s quivers hungrily in the direction of the %2$s." ), z->name(),
4274                              current_item.tname() );
4275                     return false;
4276                 }
4277                 if( current_item.count_by_charges() ) {
4278                     //Choose a random amount of meat charges to absorb
4279                     int meat_absorbed = std::min( max_meat_absorbed, rng( 1, total_charges ) );
4280                     const int hp_to_heal = meat_absorbed * ml_per_charge * meat_absorption_factor;
4281                     z->heal( hp_to_heal, true );
4282                     here.use_charges( p, 0, current_item.type->get_id(), meat_absorbed );
4283                 } else {
4284                     //Only absorb one meaty item
4285                     int meat_absorbed = 1;
4286                     const int hp_to_heal = meat_absorbed * ml_per_charge * meat_absorption_factor;
4287                     z->heal( hp_to_heal, true );
4288                     here.use_amount( p, 0, current_item.type->get_id(), meat_absorbed );
4289                 }
4290                 if( player_character.sees( *z ) ) {
4291                     add_msg( m_warning, _( "The %1$s absorbs the %2$s, growing larger." ), z->name(),
4292                              current_item.tname() );
4293                     add_msg_debug( "The %1$s now has %2$s out of %3$s hp", z->name(), z->get_hp(),
4294                                    z->get_hp_max() );
4295                 }
4296                 return true;
4297             }
4298         }
4299     }
4300     return false;
4301 }
4302 
lunge(monster * z)4303 bool mattack::lunge( monster *z )
4304 {
4305     if( !z->can_act() ) {
4306         return false;
4307     }
4308 
4309     Creature *target = z->attack_target();
4310     if( target == nullptr ) {
4311         return false;
4312     }
4313 
4314     int dist = rl_dist( z->pos(), target->pos() );
4315     if( dist > 20 ||
4316         !z->sees( *target ) ) {
4317         return false;
4318     }
4319 
4320     bool seen = get_player_view().sees( *z );
4321     if( dist > 1 ) {
4322         if( one_in( 5 ) ) {
4323             // Out of range
4324             if( dist > 4 || !z->sees( *target ) ) {
4325                 return false;
4326             }
4327             z->moves += 200;
4328             if( seen ) {
4329                 add_msg( _( "The %1$s lunges for %2$s!" ), z->name(), target->disp_name() );
4330             }
4331             return true;
4332         }
4333         return false;
4334     }
4335 
4336     if( !z->is_adjacent( target, false ) ) {
4337         // No attacking up or down - lunging requires contact
4338         // There could be a lunge down attack, though
4339         return false;
4340     }
4341 
4342     z->moves -= 100;
4343 
4344     if( target->uncanny_dodge() ) {
4345         return true;
4346     }
4347 
4348     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
4349     if( dodge_check( z, target ) ) {
4350         target->add_msg_player_or_npc( _( "The %1$s lunges at you, but you sidestep it!" ),
4351                                        _( "The %1$s lunges at <npcname>, but they sidestep it!" ), z->name() );
4352         target->on_dodge( z, z->type->melee_skill );
4353         return true;
4354     }
4355     const bodypart_id hit = target->get_random_body_part();
4356     int dam = rng( 3, 7 );
4357     dam = target->deal_damage( z, hit, damage_instance( damage_type::BASH, dam ) ).total_damage();
4358     if( dam > 0 ) {
4359         game_message_type msg_type = target->is_avatar() ? m_bad : m_warning;
4360         target->add_msg_player_or_npc( msg_type,
4361                                        _( "The %1$s lunges at your %2$s, battering it for %3$d damage!" ),
4362                                        _( "The %1$s lunges at <npcname>'s %2$s, battering it for %3$d damage!" ),
4363                                        z->name(), body_part_name( hit ), dam );
4364     } else {
4365         target->add_msg_player_or_npc( _( "The %1$s lunges at your %2$s, but your armor prevents injury!" ),
4366                                        _( "The %1$s lunges at <npcname>'s %2$s, but their armor prevents injury!" ),
4367                                        z->name(),
4368                                        body_part_name_accusative( hit ) );
4369     }
4370     if( one_in( 6 ) ) {
4371         target->add_effect( effect_downed, 3_turns );
4372     }
4373     target->on_hit( z, hit,  z->type->melee_skill );
4374     target->check_dead_state();
4375     return true;
4376 }
4377 
longswipe(monster * z)4378 bool mattack::longswipe( monster *z )
4379 {
4380     if( z->friendly ) {
4381         // TODO: handle friendly monsters
4382         return false;
4383     }
4384     Creature *target = z->attack_target();
4385     if( target == nullptr ) {
4386         return false;
4387     }
4388     // Out of range
4389     int distance = rl_dist( z->pos(), target->pos() );
4390     // Hack, only allow attacking above or below if the target is adjacent.
4391     if( z->pos().z != target->pos().z ) {
4392         distance += 2;
4393     }
4394     if( distance > 3 || !z->sees( *target ) ) {
4395         return false;
4396     }
4397     map &here = get_map();
4398     //Is there something impassable blocking the claw?
4399     for( const auto &pnt : here.find_clear_path( z->pos(), target->pos() ) ) {
4400         if( here.impassable( pnt ) ) {
4401             //If we're here, it's an nonadjacent attack, which is only attempted 1/5 of the time.
4402             if( !one_in( 5 ) ) {
4403                 return false;
4404             }
4405             target->add_msg_player_or_npc( _( "The %1$s thrusts a claw at you, but it bounces off the %2$s!" ),
4406                                            _( "The %1$s thrusts a claw at <npcname>, but it bounces off the %2$s!" ),
4407                                            z->name(), here.obstacle_name( pnt ) );
4408             z->mod_moves( -150 );
4409             return true;
4410         }
4411     }
4412 
4413     if( !z->is_adjacent( target, true ) ) {
4414         if( one_in( 5 ) ) {
4415 
4416             z->moves -= 150;
4417 
4418             if( target->uncanny_dodge() ) {
4419                 return true;
4420             }
4421             // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
4422             if( dodge_check( z, target ) ) {
4423                 target->add_msg_player_or_npc( _( "The %s thrusts a claw at you, but you evade it!" ),
4424                                                _( "The %s thrusts a claw at <npcname>, but they evade it!" ),
4425                                                z->name() );
4426                 target->on_dodge( z, z->type->melee_skill );
4427                 return true;
4428             }
4429             const bodypart_id hit = target->get_random_body_part();
4430             int dam = rng( 3, 7 );
4431             dam = target->deal_damage( z, hit, damage_instance( damage_type::CUT, dam ) ).total_damage();
4432             if( dam > 0 ) {
4433                 game_message_type msg_type = target->is_avatar() ? m_bad : m_warning;
4434                 target->add_msg_player_or_npc( msg_type,
4435                                                //~ 1$s is bodypart name, 2$d is damage value.
4436                                                _( "The %1$s thrusts a claw at your %2$s, slashing it for %3$d damage!" ),
4437                                                //~ 1$s is bodypart name, 2$d is damage value.
4438                                                _( "The %1$s thrusts a claw at <npcname>'s %2$s, slashing it for %3$d damage!" ),
4439                                                z->name(), body_part_name( hit ), dam );
4440             } else {
4441                 target->add_msg_player_or_npc(
4442                     _( "The %1$s thrusts a claw at your %2$s, but glances off your armor!" ),
4443                     _( "The %1$s thrusts a claw at <npcname>'s %2$s, but glances off armor!" ),
4444                     z->name(),
4445                     body_part_name_accusative( hit ) );
4446             }
4447             target->on_hit( z, hit,  z->type->melee_skill );
4448             return true;
4449         }
4450         return false;
4451     }
4452     z->moves -= 100;
4453 
4454     if( target->uncanny_dodge() ) {
4455         return true;
4456     }
4457 
4458     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
4459     if( dodge_check( z, target ) ) {
4460         target->add_msg_player_or_npc( _( "The %s slashes at your neck!  You duck!" ),
4461                                        _( "The %s slashes at <npcname>'s neck!  They duck!" ), z->name() );
4462         target->on_dodge( z, z->type->melee_skill );
4463         return true;
4464     }
4465 
4466     int dam = rng( 6, 10 );
4467     dam = target->deal_damage( z, bodypart_id( "head" ), damage_instance( damage_type::CUT,
4468                                dam ) ).total_damage();
4469     if( dam > 0 ) {
4470         game_message_type msg_type = target->is_avatar() ? m_bad : m_warning;
4471         target->add_msg_player_or_npc( msg_type,
4472                                        _( "The %1$s slashes at your neck, cutting your throat for %2$d damage!" ),
4473                                        _( "The %1$s slashes at <npcname>'s neck, cutting their throat for %2$d damage!" ),
4474                                        z->name(), dam );
4475         if( target->is_player() || target->is_npc() ) {
4476             target->as_character()->make_bleed( effect_source( z ), bodypart_id( "head" ), 15_minutes );
4477         } else {
4478             target->add_effect( effect_source( z ), effect_bleed, 15_minutes, bodypart_id( "head" ) );
4479         }
4480 
4481     } else {
4482         target->add_msg_player_or_npc( _( "The %1$s slashes at your %2$s, but glances off your armor!" ),
4483                                        _( "The %1$s slashes at <npcname>'s %2$s, but glances off armor!" ),
4484                                        z->name(),
4485                                        body_part_name_accusative( bodypart_id( "head" ) ) );
4486     }
4487     target->on_hit( z, bodypart_id( "head" ),  z->type->melee_skill );
4488     target->check_dead_state();
4489 
4490     return true;
4491 }
4492 
parrot_common(monster * parrot)4493 static void parrot_common( monster *parrot )
4494 {
4495     // It takes a while
4496     parrot->moves -= 100;
4497     const SpeechBubble &speech = get_speech( parrot->type->id.str() );
4498     sounds::sound( parrot->pos(), speech.volume, sounds::sound_t::speech, speech.text.translated(),
4499                    false, "speech", parrot->type->id.str() );
4500 }
4501 
parrot(monster * z)4502 bool mattack::parrot( monster *z )
4503 {
4504     if( z->has_effect( effect_shrieking ) ) {
4505         sounds::sound( z->pos(), 120, sounds::sound_t::alert, _( "a piercing wail!" ), false, "shout",
4506                        "wail" );
4507         z->moves -= 40;
4508         return false;
4509     } else if( one_in( 20 ) ) {
4510         parrot_common( z );
4511         return true;
4512     }
4513 
4514     return false;
4515 }
4516 
parrot_at_danger(monster * parrot)4517 bool mattack::parrot_at_danger( monster *parrot )
4518 {
4519     for( monster &monster : g->all_monsters() ) {
4520         if( one_in( 20 ) && ( monster.faction->attitude( parrot->faction ) == mf_attitude::MFA_HATE ||
4521                               ( monster.anger > 0 &&
4522                                 monster.faction->attitude( parrot->faction ) == mf_attitude::MFA_BY_MOOD ) ) &&
4523             parrot->sees( monster ) ) {
4524             parrot_common( parrot );
4525             return true;
4526         }
4527     }
4528 
4529     return false;
4530 }
4531 
darkman(monster * z)4532 bool mattack::darkman( monster *z )
4533 {
4534     if( z->friendly ) {
4535         // TODO: handle friendly monsters
4536         return false;
4537     }
4538     Character &player_character = get_player_character();
4539     if( rl_dist( z->pos(), player_character.pos() ) > 40 ) {
4540         return false;
4541     }
4542     if( monster *const shadow = g->place_critter_around( mon_shadow, z->pos(), 1 ) ) {
4543         z->moves -= 10;
4544         shadow->make_ally( *z );
4545         add_msg_if_player_sees( *z, m_warning, _( "A shadow splits from the %s!" ), z->name() );
4546     }
4547     // Wont do the combat stuff unless it can see you
4548     if( !z->sees( player_character ) ) {
4549         return true;
4550     }
4551     // What do we say?
4552     switch( rng( 1, 7 ) ) {
4553         case 1:
4554             add_msg( _( "\"Stop it please\"" ) );
4555             break;
4556         case 2:
4557             add_msg( _( "\"Let us help you\"" ) );
4558             break;
4559         case 3:
4560             add_msg( _( "\"We wish you no harm\"" ) );
4561             break;
4562         case 4:
4563             add_msg( _( "\"Do not fear\"" ) );
4564             break;
4565         case 5:
4566             add_msg( _( "\"We can help you\"" ) );
4567             break;
4568         case 6:
4569             add_msg( _( "\"We are friendly\"" ) );
4570             break;
4571         case 7:
4572             add_msg( _( "\"Please dont\"" ) );
4573             break;
4574     }
4575     player_character.add_effect( effect_darkness, 1_turns, true );
4576 
4577     return true;
4578 }
4579 
slimespring(monster * z)4580 bool mattack::slimespring( monster *z )
4581 {
4582     Character &player_character = get_player_character();
4583     if( rl_dist( z->pos(), player_character.pos() ) > 30 ) {
4584         return false;
4585     }
4586 
4587     // This morale buff effect could get spammy
4588     if( player_character.get_morale_level() <= 1 ) {
4589         switch( rng( 1, 3 ) ) {
4590             case 1:
4591                 //~ Your slimes try to cheer you up!
4592                 //~ Lowercase is intended: they're small voices.
4593                 add_msg( m_good, _( "\"hey, it's gonna be all right!\"" ) );
4594                 player_character.add_morale( MORALE_SUPPORT, 10, 50 );
4595                 break;
4596             case 2:
4597                 //~ Your slimes try to cheer you up!
4598                 //~ Lowercase is intended: they're small voices.
4599                 add_msg( m_good, _( "\"we'll get through this!\"" ) );
4600                 player_character.add_morale( MORALE_SUPPORT, 10, 50 );
4601                 break;
4602             case 3:
4603                 //~ Your slimes try to cheer you up!
4604                 //~ Lowercase is intended: they're small voices.
4605                 add_msg( m_good, _( "\"i'm here for you!\"" ) );
4606                 player_character.add_morale( MORALE_SUPPORT, 10, 50 );
4607                 break;
4608         }
4609     }
4610     if( rl_dist( z->pos(), player_character.pos() ) <= 3 && z->sees( player_character ) ) {
4611         if( ( player_character.has_effect( effect_bleed ) ) ||
4612             ( player_character.has_effect( effect_bite ) ) ) {
4613             //~ Lowercase is intended: they're small voices.
4614             add_msg( _( "\"let me help!\"" ) );
4615             // Yes, your slimespring(s) handle/don't all Bad Damage at the same time.
4616             for( const bodypart_id &bp_healed :
4617                  player_character.get_all_body_parts( get_body_part_flags::only_main ) ) {
4618                 if( player_character.has_effect( effect_bite, bp_healed.id() ) ) {
4619                     if( one_in( 3 ) ) {
4620                         player_character.remove_effect( effect_bite, bp_healed );
4621                         add_msg( m_good, _( "The slime cleans you out!" ) );
4622                     } else {
4623                         add_msg( _( "The slime flows over you, but your gouges still ache." ) );
4624                     }
4625                 }
4626                 if( player_character.has_effect( effect_bleed, bp_healed.id() ) ) {
4627                     if( one_in( 2 ) ) {
4628                         effect &e = player_character.get_effect( effect_bleed, bp_healed );
4629                         e.mod_duration( -e.get_int_dur_factor() * rng( 1, 5 ) );
4630                         add_msg( m_good, _( "The slime seals up your leaks!" ) );
4631                     } else {
4632                         add_msg( _( "The slime flows over you, but your fluids are still leaking." ) );
4633                     }
4634                 }
4635             }
4636         }
4637     }
4638     return true;
4639 }
4640 
thrown_by_judo(monster * z)4641 bool mattack::thrown_by_judo( monster *z )
4642 {
4643     Creature *target = z->attack_target();
4644     if( target == nullptr ||
4645         !z->is_adjacent( target, false ) ||
4646         !z->sees( *target ) ) {
4647         return false;
4648     }
4649 
4650     player *foe = dynamic_cast< player * >( target );
4651     if( foe == nullptr ) {
4652         // No mons for now
4653         return false;
4654     }
4655     // "Wimpy" Judo is about to pay off... :D
4656     if( foe->is_throw_immune() ) {
4657         // DX + Unarmed
4658         ///\EFFECT_DEX increases chance judo-throwing a monster
4659 
4660         ///\EFFECT_UNARMED increases chance of judo-throwing monster, vs their melee skill
4661         if( ( ( foe->dex_cur + foe->get_skill_level( skill_unarmed ) ) > ( z->type->melee_skill + rng( 0,
4662                 3 ) ) ) ) {
4663             target->add_msg_if_player( m_good, _( "but you grab its arm and flip it to the ground!" ) );
4664 
4665             // most of the time, when not isolated
4666             if( !one_in( 4 ) && !target->is_elec_immune() && z->type->sp_defense == &mdefense::zapback ) {
4667                 // If it all pans out, we're zap the player's arm as he flips the monster.
4668                 target->add_msg_if_player( _( "The flip does shock you…" ) );
4669                 // Discounted electric damage for quick flip
4670                 damage_instance shock;
4671                 shock.add_damage( damage_type::ELECTRIC, rng( 1, 3 ) );
4672                 foe->deal_damage( z, bodypart_id( "arm_l" ), shock );
4673                 foe->deal_damage( z, bodypart_id( "arm_r" ), shock );
4674                 foe->check_dead_state();
4675             }
4676             // Monster is down,
4677             z->add_effect( effect_downed, 5_turns );
4678             const int min_damage = 10 + foe->get_skill_level( skill_unarmed );
4679             const int max_damage = 20 + foe->get_skill_level( skill_unarmed );
4680             // Deal moderate damage
4681             const int damage = rng( min_damage, max_damage );
4682             z->apply_damage( foe, bodypart_id( "torso" ), damage );
4683             z->check_dead_state();
4684         } else {
4685             // Still avoids the major hit!
4686             target->add_msg_if_player( _( "but you deftly spin out of its grasp!" ) );
4687         }
4688         return true;
4689     } else {
4690         return false;
4691     }
4692 }
4693 
riotbot(monster * z)4694 bool mattack::riotbot( monster *z )
4695 {
4696     Creature *target = z->attack_target();
4697     if( target == nullptr ) {
4698         return false;
4699     }
4700 
4701     player *foe = dynamic_cast<player *>( target );
4702 
4703     map &here = get_map();
4704     if( calendar::once_every( 1_minutes ) ) {
4705         for( const tripoint &dest : here.points_in_radius( z->pos(), 4 ) ) {
4706             if( here.passable( dest ) &&
4707                 here.clear_path( z->pos(), dest, 3, 1, 100 ) ) {
4708                 here.add_field( dest, fd_relax_gas, rng( 1, 3 ) );
4709             }
4710         }
4711     }
4712 
4713     //already arrested?
4714     //and yes, if the player has no hands, we are not going to arrest him.
4715     if( foe != nullptr &&
4716         ( foe->weapon.typeId() == itype_e_handcuffs || !foe->has_two_arms() ) ) {
4717         z->anger = 0;
4718 
4719         if( calendar::once_every( 25_turns ) ) {
4720             sounds::sound( z->pos(), 10, sounds::sound_t::electronic_speech,
4721                            _( "Halt and submit to arrest, citizen!  The police will be here any moment." ), false, "speech",
4722                            z->type->id.str() );
4723         }
4724 
4725         return true;
4726     }
4727 
4728     if( z->anger < z->type->agro ) {
4729         z->anger += z->type->agro / 20;
4730         return true;
4731     }
4732 
4733     const int dist = rl_dist( z->pos(), target->pos() );
4734 
4735     //we need empty hands to arrest
4736     if( foe && foe->is_avatar() && !foe->is_armed() ) {
4737 
4738         sounds::sound( z->pos(), 15, sounds::sound_t::electronic_speech,
4739                        _( "Please stay in place, citizen, do not make any movements!" ), false, "speech",
4740                        z->type->id.str() );
4741 
4742         //we need to come closer and arrest
4743         if( !z->is_adjacent( foe, false ) ) {
4744             return true;
4745         }
4746 
4747         //Strain the atmosphere, forcing the player to wait. Let him feel the power of law!
4748         if( !one_in( 10 ) ) {
4749             foe->add_msg_player_or_npc( _( "The robot carefully scans you." ),
4750                                         _( "The robot carefully scans <npcname>." ) );
4751             return true;
4752         }
4753 
4754         enum {ur_arrest, ur_resist, ur_trick};
4755 
4756         //arrest!
4757         uilist amenu;
4758         amenu.allow_cancel = false;
4759         amenu.text = _( "The riotbot orders you to present your hands and be cuffed." );
4760 
4761         amenu.addentry( ur_arrest, true, 'a', _( "Allow yourself to be arrested." ) );
4762         amenu.addentry( ur_resist, true, 'r', _( "Resist arrest!" ) );
4763         ///\EFFECT_INT >10 allows and increases chance whether you can feign death to avoid riot bot arrest
4764         if( foe->int_cur > 12 || ( foe->int_cur > 10 && !one_in( foe->int_cur - 8 ) ) ) {
4765             amenu.addentry( ur_trick, true, 't', _( "Feign death." ) );
4766         }
4767 
4768         amenu.query();
4769         const int choice = amenu.ret;
4770 
4771         if( choice == ur_arrest ) {
4772             z->anger = 0;
4773 
4774             item handcuffs( "e_handcuffs", calendar::turn_zero );
4775             handcuffs.charges = handcuffs.type->maximum_charges();
4776             handcuffs.active = true;
4777             handcuffs.set_var( "HANDCUFFS_X", foe->posx() );
4778             handcuffs.set_var( "HANDCUFFS_Y", foe->posy() );
4779 
4780             const bool is_uncanny = foe->has_active_bionic( bio_uncanny_dodge ) &&
4781                                     foe->get_power_level() > 74_kJ &&
4782                                     !one_in( 3 );
4783             ///\EFFECT_DEX >13 allows and increases chance to slip out of riot bot handcuffs
4784             const bool is_dex = foe->dex_cur > 13 && !one_in( foe->dex_cur - 11 );
4785 
4786             if( is_uncanny || is_dex ) {
4787 
4788                 if( is_uncanny ) {
4789                     foe->mod_power_level( -75_kJ );
4790                 }
4791 
4792                 add_msg( m_good,
4793                          _( "You deftly slip out of the handcuffs just as the robot closes them.  The robot didn't seem to notice!" ) );
4794                 foe->i_add( handcuffs );
4795             } else {
4796                 handcuffs.set_flag( flag_NO_UNWIELD );
4797                 foe->wield( foe->i_add( handcuffs ) );
4798                 foe->moves -= 300;
4799                 add_msg( _( "The robot puts handcuffs on you." ) );
4800             }
4801 
4802             sounds::sound( z->pos(), 5, sounds::sound_t::electronic_speech,
4803                            _( "You are under arrest, citizen.  You have the right to remain silent.  If you do not remain silent, anything you say may be used against you in a court of law." ),
4804                            false, "speech", z->type->id.str() );
4805             sounds::sound( z->pos(), 5, sounds::sound_t::electronic_speech,
4806                            _( "You have the right to an attorney.  If you cannot afford an attorney, one will be provided at no cost to you.  You may have your attorney present during any questioning." ) );
4807             sounds::sound( z->pos(), 5, sounds::sound_t::electronic_speech,
4808                            _( "If you do not understand these rights, an officer will explain them in greater detail when taking you into custody." ) );
4809             sounds::sound( z->pos(), 5, sounds::sound_t::electronic_speech,
4810                            _( "Do not attempt to flee or to remove the handcuffs, citizen.  That can be dangerous to your health." ) );
4811 
4812             z->moves -= 300;
4813 
4814             return true;
4815         }
4816 
4817         bool bad_trick = false;
4818 
4819         if( choice == ur_trick ) {
4820 
4821             ///\EFFECT_INT >10 allows and increases chance of successful feign death against riot bot
4822             if( !one_in( foe->int_cur - 10 ) ) {
4823 
4824                 add_msg( m_good,
4825                          _( "You fall to the ground and feign a sudden convulsive attack.  Though you're obviously still alive, the riotbot cannot tell the difference between your 'attack' and a potentially fatal medical condition.  It backs off, signaling for medical help." ) );
4826 
4827                 z->moves -= 300;
4828                 z->anger = -rng( 0, 50 );
4829                 return true;
4830             } else {
4831                 add_msg( m_bad, _( "Your awkward movements do not fool the riotbot." ) );
4832                 foe->moves -= 100;
4833                 bad_trick = true;
4834             }
4835         }
4836 
4837         if( ( choice == ur_resist ) || bad_trick ) {
4838 
4839             add_msg( m_bad, _( "The robot sprays tear gas!" ) );
4840             z->moves -= 200;
4841 
4842             for( const tripoint &dest : here.points_in_radius( z->pos(), 2 ) ) {
4843                 if( here.passable( dest ) &&
4844                     here.clear_path( z->pos(), dest, 3, 1, 100 ) ) {
4845                     here.add_field( dest, fd_tear_gas, rng( 1, 3 ) );
4846                 }
4847             }
4848 
4849             return true;
4850         }
4851 
4852         return true;
4853     }
4854 
4855     if( calendar::once_every( 5_turns ) ) {
4856         sounds::sound( z->pos(), 25, sounds::sound_t::electronic_speech,
4857                        _( "Empty your hands and hold your position, citizen!" ), false, "speech", z->type->id.str() );
4858     }
4859 
4860     if( dist > 5 && dist < 18 && one_in( 10 ) ) {
4861 
4862         z->moves -= 50;
4863 
4864         // Precautionary shot
4865         int delta = dist / 4 + 1;
4866         // Precision shot
4867         if( z->get_hp() < z->get_hp_max() ) {
4868             delta = 1;
4869         }
4870 
4871         tripoint dest( target->posx() + rng( 0, delta ) - rng( 0, delta ),
4872                        target->posy() + rng( 0, delta ) - rng( 0, delta ),
4873                        target->posz() );
4874 
4875         //~ Sound of a riotbot using its blinding flash
4876         sounds::sound( z->pos(), 3, sounds::sound_t::combat, _( "fzzzzzt" ), false, "misc", "flash" );
4877 
4878         std::vector<tripoint> traj = line_to( z->pos(), dest, 0, 0 );
4879         for( auto &elem : traj ) {
4880             if( !here.is_transparent( elem ) ) {
4881                 break;
4882             }
4883             here.add_field( elem, fd_dazzling, 1 );
4884         }
4885         return true;
4886 
4887     }
4888 
4889     return true;
4890 }
4891 
evolve_kill_strike(monster * z)4892 bool mattack::evolve_kill_strike( monster *z )
4893 {
4894     Creature *target = z->attack_target();
4895     if( target == nullptr ||
4896         !z->is_adjacent( target, false ) ||
4897         !z->sees( *target ) ) {
4898         return false;
4899     }
4900     if( !z->can_act() ) {
4901         return false;
4902     }
4903 
4904     z->moves -= 100;
4905     const bool uncanny = target->uncanny_dodge();
4906     if( uncanny || dodge_check( z, target ) ) {
4907         game_message_type msg_type = target->is_avatar() ? m_warning : m_info;
4908         target->add_msg_player_or_npc( msg_type, _( "The %s lunges at you, but you dodge!" ),
4909                                        _( "The %s lunges at <npcname>, but they dodge!" ),
4910                                        z->name() );
4911         if( !uncanny ) {
4912             target->on_dodge( z, z->type->melee_skill );
4913             target->add_msg_player_or_npc( msg_type, _( "The %s lunges at you, but you dodge!" ),
4914                                            _( "The %s lunges at <npcname>, but they dodge!" ),
4915                                            z->name() );
4916         }
4917         return true;
4918     }
4919     tripoint const target_pos = target->pos();
4920     const std::string target_name = target->disp_name();
4921     damage_instance damage( z->type->melee_damage );
4922     damage.mult_damage( 1.33f );
4923     damage.add( damage_instance( damage_type::STAB, dice( z->type->melee_dice, z->type->melee_sides ),
4924                                  rng( 5,
4925                                       15 ), 1.0, 0.5 ) );
4926     int damage_dealt = target->deal_damage( z, bodypart_id( "torso" ), damage ).total_damage();
4927     if( damage_dealt > 0 ) {
4928         game_message_type msg_type = target->is_avatar() ? m_bad : m_warning;
4929         target->add_msg_player_or_npc( msg_type,
4930                                        _( "The %1$s impales yor chest for %2$d damage!" ),
4931                                        _( "The %1$s impales <npcname>'s chest for %2$d damage!" ),
4932                                        z->name(), damage_dealt );
4933     } else {
4934         target->add_msg_player_or_npc(
4935             _( "The %1$s attempts to burrow itself into you, but is stopped by your armor!" ),
4936             _( "The %1$s slashes at <npcname>'s torso, but is stopped by their armor!" ),
4937             z->name() );
4938         return true;
4939     }
4940     Character &player_character = get_player_character();
4941     if( target->is_dead_state() && g->is_empty( target_pos ) &&
4942         target->made_of_any( Creature::cmat_flesh ) ) {
4943         const std::string old_name = z->name();
4944         const bool could_see_z = player_character.sees( *z );
4945         z->allow_upgrade();
4946         z->try_upgrade( false );
4947         z->setpos( target_pos );
4948         const std::string upgrade_name = z->name();
4949         const bool can_see_z_upgrade = player_character.sees( *z );
4950         if( could_see_z && can_see_z_upgrade ) {
4951             add_msg( m_warning, _( "The %1$s burrows within %2$s corpse and a %3$s emerges from the remains!" ),
4952                      old_name,
4953                      target_name, upgrade_name );
4954         } else if( could_see_z ) {
4955             add_msg( m_warning, _( "The %1$s burrows within %2$s corpse!" ), old_name, target_name );
4956         } else if( can_see_z_upgrade ) {
4957             add_msg( m_warning, _( "A %1$s emerges from %2$s corpse!" ), upgrade_name, target_name );
4958         }
4959     }
4960     return true;
4961 }
4962 
leech_spawner(monster * z)4963 bool mattack::leech_spawner( monster *z )
4964 {
4965     const bool u_see = get_player_view().sees( *z );
4966     std::list<monster *> allies;
4967     for( monster &candidate : g->all_monsters() ) {
4968         if( candidate.in_species( species_LEECH_PLANT ) && !candidate.has_flag( MF_IMMOBILE ) ) {
4969             allies.push_back( &candidate );
4970         }
4971     }
4972     if( allies.size() > 45 ) {
4973         return true;
4974     }
4975     const int monsters_spawned = rng( 1, 4 );
4976     const mtype_id monster_type = one_in( 3 ) ? mon_leech_root_runner : mon_leech_root_drone;
4977     for( int i = 0; i < monsters_spawned; i++ ) {
4978         if( monster *const new_mon = g->place_critter_around( monster_type, z->pos(), 1 ) ) {
4979             if( u_see ) {
4980                 add_msg( m_warning,
4981                          _( "An egg pod ruptures and a %s crawls out from the remains!" ), new_mon->name() );
4982             }
4983             if( one_in( 25 ) ) {
4984                 z->poly( mon_leech_stalk );
4985                 if( u_see ) {
4986                     add_msg( m_warning,
4987                              _( "Resplendent fronds emerge from the still intact pods!" ) );
4988                 }
4989             }
4990         }
4991     }
4992     return true;
4993 }
4994 
mon_leech_evolution(monster * z)4995 bool mattack::mon_leech_evolution( monster *z )
4996 {
4997     const bool is_queen = z->has_flag( MF_QUEEN );
4998     std::list<monster *> queens;
4999     for( monster &candidate : g->all_monsters() ) {
5000         if( candidate.in_species( species_LEECH_PLANT ) && candidate.has_flag( MF_QUEEN ) &&
5001             rl_dist( z->pos(), candidate.pos() ) < 35 ) {
5002             queens.push_back( &candidate );
5003         }
5004     }
5005     if( !is_queen ) {
5006         if( queens.empty() ) {
5007             z->poly( mon_leech_blossom );
5008             z->set_hp( z->get_hp_max() );
5009             add_msg_if_player_sees( *z, m_warning, _( "The %s blooms into flowers!" ), z->name() );
5010         }
5011     }
5012     return true;
5013 }
5014 
tindalos_teleport(monster * z)5015 bool mattack::tindalos_teleport( monster *z )
5016 {
5017     Creature *target = z->attack_target();
5018     if( target == nullptr ) {
5019         return false;
5020     }
5021     if( one_in( 7 ) ) {
5022         if( monster *const afterimage = g->place_critter_around( mon_hound_tindalos_afterimage, z->pos(),
5023                                         1 ) ) {
5024             z->moves -= 140;
5025             afterimage->make_ally( *z );
5026             add_msg_if_player_sees( *z, m_warning,
5027                                     _( "The hound's movements chaotically rewind as a living afterimage splits from it!" ) );
5028         }
5029     }
5030     const int distance_to_target = rl_dist( z->pos(), target->pos() );
5031     map &here = get_map();
5032     if( distance_to_target > 5 ) {
5033         const tripoint oldpos = z->pos();
5034         for( const tripoint &dest : here.points_in_radius( target->pos(), 4 ) ) {
5035             if( here.is_cornerfloor( dest ) ) {
5036                 if( g->is_empty( dest ) ) {
5037                     z->setpos( dest );
5038                     // Not teleporting if it means losing sight of our current target
5039                     if( z->sees( *target ) ) {
5040                         here.add_field( oldpos, fd_tindalos_rift, 2 );
5041                         here.add_field( dest, fd_tindalos_rift, 2 );
5042                         add_msg_if_player_sees( *z, m_bad, _( "The %s dissipates and reforms close by." ), z->name() );
5043                         return true;
5044                     }
5045                 }
5046             }
5047         }
5048         // couldn't teleport without losing sight of target
5049         z->setpos( oldpos );
5050         return true;
5051     }
5052     return true;
5053 }
5054 
flesh_tendril(monster * z)5055 bool mattack::flesh_tendril( monster *z )
5056 {
5057     Creature *target = z->attack_target();
5058 
5059     if( target == nullptr || !z->sees( *target ) ) {
5060         if( one_in( 70 ) ) {
5061             add_msg( _( "The floor trembles underneath your feet." ) );
5062             z->moves -= 200;
5063             sounds::sound( z->pos(), 60, sounds::sound_t::alert, _( "a deafening roar!" ), false, "shout",
5064                            "roar" );
5065         }
5066         return false;
5067     }
5068 
5069     const int distance_to_target = rl_dist( z->pos(), target->pos() );
5070 
5071     // the monster summons stuff to fight you
5072     if( distance_to_target > 3 && one_in( 12 ) ) {
5073         mtype_id spawned = mon_zombie_gasbag_crawler;
5074         if( one_in( 2 ) ) {
5075             spawned = mon_zombie_gasbag_impaler;
5076         }
5077         if( monster *const summoned = g->place_critter_around( spawned, z->pos(), 1 ) ) {
5078             z->moves -= 100;
5079             summoned->make_ally( *z );
5080             get_map().propagate_field( z->pos(), fd_gibs_flesh, 75, 1 );
5081             add_msg_if_player_sees( *z, m_warning, _( "A %s struggles to pull itself free from the %s!" ),
5082                                     summoned->name(), z->name() );
5083         }
5084         return true;
5085     }
5086 
5087     if( ( distance_to_target == 2 || distance_to_target == 3 ) && one_in( 4 ) ) {
5088         //it pulls you towards itself and then knocks you away
5089         bool pulled = ranged_pull( z );
5090         if( pulled && one_in( 4 ) ) {
5091             sounds::sound( z->pos(), 60, sounds::sound_t::alarm, _( "a deafening roar!" ), false, "shout",
5092                            "roar" );
5093         }
5094         return pulled;
5095     }
5096 
5097     if( distance_to_target <= 1 ) {
5098         if( one_in( 8 ) ) {
5099             g->fling_creature( target, coord_to_angle( z->pos(), target->pos() ),
5100                                z->type->melee_sides * z->type->melee_dice * 3 );
5101         } else {
5102             grab( z );
5103         }
5104     }
5105 
5106     return false;
5107 }
5108 
bio_op_random_biojutsu(monster * z)5109 bool mattack::bio_op_random_biojutsu( monster *z )
5110 {
5111     int choice = 0;
5112     bool redo = false;
5113 
5114     if( !z->can_act() ) {
5115         return false;
5116     }
5117 
5118     Creature *target = z->attack_target();
5119     if( target == nullptr ||
5120         !z->is_adjacent( target, false ) ||
5121         !z->sees( *target ) ) {
5122         return false;
5123     }
5124 
5125     player *foe = dynamic_cast< player * >( target );
5126 
5127     do {
5128         choice = rng( 1, 3 );
5129         redo = false;
5130 
5131         // ignore disarm if the target isn't a "player" or isn't armed
5132         if( choice == 3 && foe != nullptr && !foe->is_armed() ) {
5133             redo = true;
5134         }
5135 
5136     } while( redo );
5137 
5138     switch( choice ) {
5139         case 1:
5140             bio_op_takedown( z );
5141             break;
5142         case 2:
5143             bio_op_impale( z );
5144             break;
5145         case 3:
5146             bio_op_disarm( z );
5147             break;
5148     }
5149 
5150     return true;
5151 }
5152 
bio_op_takedown(monster * z)5153 bool mattack::bio_op_takedown( monster *z )
5154 {
5155     if( !z->can_act() ) {
5156         return false;
5157     }
5158 
5159     Creature *target = z->attack_target();
5160     // TODO: Allow drop-takedown form above
5161     if( target == nullptr ||
5162         !z->is_adjacent( target, false ) ||
5163         !z->sees( *target ) ) {
5164         return false;
5165     }
5166 
5167     bool seen = get_player_view().sees( *z );
5168     player *foe = dynamic_cast< player * >( target );
5169     if( seen ) {
5170         add_msg( _( "The %1$s mechanically grabs at %2$s!" ), z->name(),
5171                  target->disp_name() );
5172     }
5173     z->moves -= 100;
5174 
5175     if( target->uncanny_dodge() ) {
5176         return true;
5177     }
5178 
5179     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
5180     if( dodge_check( z, target ) ) {
5181         target->add_msg_player_or_npc( _( "You dodge it!" ),
5182                                        _( "<npcname> dodges it!" ) );
5183         target->on_dodge( z, z->type->melee_skill );
5184         return true;
5185     }
5186     int dam = rng( 3, 9 );
5187     if( foe == nullptr ) {
5188         // Handle mons earlier - less to check for
5189         dam = rng( 6, 18 );
5190         // Always aim for the torso
5191         target->deal_damage( z, bodypart_id( "torso" ), damage_instance( damage_type::BASH, dam ) );
5192         // Two hits - "leg" and torso
5193         target->deal_damage( z, bodypart_id( "torso" ), damage_instance( damage_type::BASH, dam ) );
5194         target->add_effect( effect_downed, 3_turns );
5195         if( seen ) {
5196             add_msg( _( "%1$s slams %2$s to the ground!" ), z->name(), target->disp_name() );
5197         }
5198         target->check_dead_state();
5199         return true;
5200     }
5201     // Yes, it has the CQC bionic.
5202     bodypart_id hit( "bp_null" );
5203     if( one_in( 2 ) ) {
5204         hit = bodypart_id( "leg_l" );
5205     } else {
5206         hit = bodypart_id( "leg_r" );
5207     }
5208     // Weak kick to start with, knocks you off your footing
5209 
5210     // TODO: Literally "The zombie kicks" vvvvv | Fix message or comment why Literally.
5211     //~ 1$s is bodypart name in accusative, 2$d is damage value.
5212     target->add_msg_if_player( m_bad, _( "The zombie kicks your %1$s for %2$d damage…" ),
5213                                body_part_name_accusative( hit ), dam );
5214     foe->deal_damage( z,  hit, damage_instance( damage_type::BASH, dam ) );
5215     // At this point, Judo or Tentacle Bracing can make this much less painful
5216     if( !foe->is_throw_immune() ) {
5217         if( !target->is_immune_effect( effect_downed ) ) {
5218             if( one_in( 4 ) ) {
5219                 hit = bodypart_id( "head" );
5220                 // 50% damage buff for the headshot.
5221                 dam = rng( 9, 21 );
5222                 target->add_msg_if_player( m_bad, _( "and slams you, face first, to the ground for %d damage!" ),
5223                                            dam );
5224                 foe->deal_damage( z, bodypart_id( "head" ), damage_instance( damage_type::BASH, dam ) );
5225             } else {
5226                 hit = bodypart_id( "torso" );
5227                 dam = rng( 6, 18 );
5228                 target->add_msg_if_player( m_bad, _( "and slams you to the ground for %d damage!" ), dam );
5229                 foe->deal_damage( z, bodypart_id( "torso" ), damage_instance( damage_type::BASH, dam ) );
5230             }
5231             foe->add_effect( effect_downed, 3_turns );
5232         }
5233     } else if( ( !foe->is_armed() ||
5234                  foe->martial_arts_data->selected_has_weapon( foe->weapon.typeId() ) ) &&
5235                !thrown_by_judo( z ) ) {
5236         // Saved by the tentacle-bracing! :)
5237         hit = bodypart_id( "torso" );
5238         dam = rng( 3, 9 );
5239         target->add_msg_if_player( m_bad, _( "and slams you for %d damage!" ), dam );
5240         foe->deal_damage( z, bodypart_id( "torso" ), damage_instance( damage_type::BASH, dam ) );
5241     }
5242     target->on_hit( z, hit,  z->type->melee_skill );
5243     foe->check_dead_state();
5244 
5245     return true;
5246 }
5247 
bio_op_impale(monster * z)5248 bool mattack::bio_op_impale( monster *z )
5249 {
5250     if( !z->can_act() ) {
5251         return false;
5252     }
5253 
5254     Creature *target = z->attack_target();
5255     if( target == nullptr ||
5256         !z->is_adjacent( target, false ) ||
5257         !z->sees( *target ) ) {
5258         return false;
5259     }
5260 
5261     const bool seen = get_player_view().sees( *z );
5262     player *foe = dynamic_cast< player * >( target );
5263     if( seen ) {
5264         add_msg( _( "The %1$s mechanically lunges at %2$s!" ), z->name(),
5265                  target->disp_name() );
5266     }
5267     z->moves -= 100;
5268 
5269     if( target->uncanny_dodge() ) {
5270         return true;
5271     }
5272 
5273     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
5274     if( dodge_check( z, target ) ) {
5275         target->add_msg_player_or_npc( _( "You dodge it!" ),
5276                                        _( "<npcname> dodges it!" ) );
5277         target->on_dodge( z, z->type->melee_skill );
5278         return true;
5279     }
5280 
5281     // Yes, it has the CQC bionic.
5282     int dam = rng( 8, 24 );
5283     bool do_bleed = false;
5284     int t_dam;
5285 
5286     if( one_in( 4 ) ) {
5287         dam = rng( 12, 36 ); // 50% damage buff for the crit.
5288         do_bleed = true;
5289     }
5290 
5291     if( foe == nullptr ) {
5292         // Handle mons earlier - less to check for
5293         target->deal_damage( z, bodypart_id( "torso" ), damage_instance( damage_type::STAB, dam ) );
5294         if( do_bleed ) {
5295             target->add_effect( effect_bleed, rng( 3_minutes, 10_minutes ), bodypart_id( "torso" ), true );
5296         }
5297         if( seen ) {
5298             add_msg( _( "The %1$s impales %2$s!" ), z->name(), target->disp_name() );
5299         }
5300         target->check_dead_state();
5301         return true;
5302     }
5303 
5304     const bodypart_id hit = target->get_random_body_part();
5305 
5306     t_dam = foe->deal_damage( z, hit, damage_instance( damage_type::STAB, dam ) ).total_damage();
5307 
5308     target->add_msg_player_or_npc( _( "The %1$s tries to impale your %2$s…" ),
5309                                    _( "The %1$s tries to impale <npcname>'s %2$s…" ),
5310                                    z->name(), body_part_name_accusative( hit ) );
5311 
5312     if( t_dam > 0 ) {
5313         target->add_msg_if_player( m_bad, _( "and deals %d damage!" ), t_dam );
5314 
5315         if( do_bleed ) {
5316             target->as_character()->make_bleed( effect_source( z ), hit, rng( 75_turns, 125_turns ), 1, true );
5317         }
5318     } else {
5319         target->add_msg_player_or_npc( _( "but fails to penetrate your armor!" ),
5320                                        _( "but fails to penetrate <npcname>'s armor!" ) );
5321     }
5322 
5323     target->on_hit( z, hit, z->type->melee_skill );
5324     foe->check_dead_state();
5325 
5326     return true;
5327 }
5328 
bio_op_disarm(monster * z)5329 bool mattack::bio_op_disarm( monster *z )
5330 {
5331     if( !z->can_act() ) {
5332         return false;
5333     }
5334 
5335     Creature *target = z->attack_target();
5336     if( target == nullptr ||
5337         !z->is_adjacent( target, false ) ||
5338         !z->sees( *target ) ) {
5339         return false;
5340     }
5341 
5342     const bool seen = get_player_view().sees( *z );
5343     player *foe = dynamic_cast< player * >( target );
5344 
5345     // disarm doesn't work on creatures or unarmed targets
5346     if( foe == nullptr || !foe->is_armed() ) {
5347         return false;
5348     }
5349 
5350     if( seen ) {
5351         add_msg( _( "The %1$s mechanically reaches for %2$s!" ), z->name(),
5352                  target->disp_name() );
5353     }
5354     z->moves -= 100;
5355 
5356     if( target->uncanny_dodge() ) {
5357         return true;
5358     }
5359 
5360     // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
5361     if( dodge_check( z, target ) ) {
5362         target->add_msg_player_or_npc( _( "You dodge it!" ),
5363                                        _( "<npcname> dodges it!" ) );
5364         target->on_dodge( z, z->type->melee_skill );
5365         return true;
5366     }
5367 
5368     int mon_stat = z->type->melee_dice * z->type->melee_sides;
5369     int my_roll = dice( 3, 2 * mon_stat );
5370     my_roll += dice( 3, z->type->melee_skill );
5371 
5372     /** @EFFECT_STR increases chance to avoid disarm, primary stat */
5373     /** @EFFECT_DEX increases chance to avoid disarm, secondary stat */
5374     /** @EFFECT_PER increases chance to avoid disarm, secondary stat */
5375     /** @EFFECT_MELEE increases chance to avoid disarm */
5376     int their_roll = dice( 3, 2 * foe->get_str() + foe->get_dex() );
5377     their_roll += dice( 3, foe->get_per() );
5378     their_roll += dice( 3, foe->get_skill_level( skill_melee ) );
5379 
5380     item &it = foe->weapon;
5381 
5382     target->add_msg_if_player( m_bad, _( "The zombie grabs your %s…" ), it.tname() );
5383 
5384     if( my_roll >= their_roll && !it.has_flag( flag_NO_UNWIELD ) ) {
5385         target->add_msg_if_player( m_bad, _( "and throws it to the ground!" ) );
5386         const tripoint tp = foe->pos() + tripoint( rng( -1, 1 ), rng( -1, 1 ), 0 );
5387         get_map().add_item_or_charges( tp, foe->i_rem( &it ) );
5388     } else {
5389         target->add_msg_if_player( m_good, _( "but you break its grip!" ) );
5390     }
5391 
5392     return true;
5393 }
5394 
suicide(monster * z)5395 bool mattack::suicide( monster *z )
5396 {
5397     Creature *target = z->attack_target();
5398     if( !within_target_range( z, target, 2 ) ) {
5399         return false;
5400     }
5401     z->die( z );
5402 
5403     return false;
5404 }
5405 
kamikaze(monster * z)5406 bool mattack::kamikaze( monster *z )
5407 {
5408     if( z->ammo.empty() ) {
5409         // We somehow lost our ammo! Toggle this special off so we stop processing
5410         add_msg_debug( "Missing ammo in kamikaze special for %s.", z->name() );
5411         z->disable_special( "KAMIKAZE" );
5412         return true;
5413     }
5414 
5415     // Get the bomb type and it's data
5416     const itype *bomb_type = item::find_type( z->ammo.begin()->first );
5417     const itype *act_bomb_type;
5418     int charges;
5419     // Hardcoded data for charge variant items
5420     if( z->ammo.begin()->first == itype_mininuke ) {
5421         act_bomb_type = item::find_type( itype_mininuke_act );
5422         charges = 20;
5423     } else if( z->ammo.begin()->first == itype_c4 ) {
5424         act_bomb_type = item::find_type( itype_c4armed );
5425         charges = 10;
5426     } else {
5427         const use_function *usage = bomb_type->get_use( "transform" );
5428         if( usage == nullptr ) {
5429             // Invalid item usage, Toggle this special off so we stop processing
5430             add_msg_debug( "Invalid bomb transform use in kamikaze special for %s.", z->name() );
5431             z->disable_special( "KAMIKAZE" );
5432             return true;
5433         }
5434         const iuse_transform *actor = dynamic_cast<const iuse_transform *>( usage->get_actor_ptr() );
5435         if( actor == nullptr ) {
5436             // Invalid bomb item, Toggle this special off so we stop processing
5437             add_msg_debug( "Invalid bomb type in kamikaze special for %s.", z->name() );
5438             z->disable_special( "KAMIKAZE" );
5439             return true;
5440         }
5441         act_bomb_type = item::find_type( actor->target );
5442         charges = actor->ammo_qty;
5443     }
5444 
5445     // HACK: HORRIBLE HACK ALERT! Remove the following code completely once we have working monster inventory processing
5446     if( z->has_effect( effect_countdown ) ) {
5447         if( z->get_effect( effect_countdown ).get_duration() == 1_turns ) {
5448             z->die( nullptr );
5449             // Timer is out, detonate
5450             item i_explodes( act_bomb_type, calendar::turn, 0 );
5451             i_explodes.active = true;
5452             i_explodes.process( nullptr, z->pos() );
5453             return false;
5454         }
5455         return false;
5456     }
5457     // END HORRIBLE HACK
5458 
5459     const use_function *use = act_bomb_type->get_use( "explosion" );
5460     if( use == nullptr ) {
5461         // Invalid active bomb item usage, Toggle this special off so we stop processing
5462         add_msg_debug( "Invalid active bomb explosion use in kamikaze special for %s.",
5463                        z->name() );
5464         z->disable_special( "KAMIKAZE" );
5465         return true;
5466     }
5467     const explosion_iuse *exp_actor = dynamic_cast<const explosion_iuse *>( use->get_actor_ptr() );
5468     if( exp_actor == nullptr ) {
5469         // Invalid active bomb item, Toggle this special off so we stop processing
5470         add_msg_debug( "Invalid active bomb type in kamikaze special for %s.", z->name() );
5471         z->disable_special( "KAMIKAZE" );
5472         return true;
5473     }
5474 
5475     // Get our blast radius
5476     int radius = -1;
5477     if( exp_actor->fields_radius > radius ) {
5478         radius = exp_actor->fields_radius;
5479     }
5480     if( exp_actor->emp_blast_radius > radius ) {
5481         radius = exp_actor->emp_blast_radius;
5482     }
5483     // Extra check here to avoid sqrt if not needed
5484     if( exp_actor->explosion.power > -1 ) {
5485         int tmp = static_cast<int>( std::sqrt( static_cast<double>( exp_actor->explosion.power / 4 ) ) );
5486         if( tmp > radius ) {
5487             radius = tmp;
5488         }
5489     }
5490     if( exp_actor->explosion.shrapnel.casing_mass > 0 ) {
5491         // Actual factor is 2 * radius, but figure most pieces of shrapnel will miss
5492         int tmp = static_cast<int>( std::sqrt( exp_actor->explosion.power ) );
5493         if( tmp > radius ) {
5494             radius = tmp;
5495         }
5496     }
5497     // Flashbangs have a max range of 8
5498     if( exp_actor->do_flashbang && radius < 8 ) {
5499         radius = 8;
5500     }
5501     if( radius <= -1 ) {
5502         // Not a valid explosion size, toggle this special off to stop processing
5503         z->disable_special( "KAMIKAZE" );
5504         return true;
5505     }
5506 
5507     Creature *target = z->attack_target();
5508     if( target == nullptr ) {
5509         return false;
5510     }
5511     // Range is (radius + distance they expect to gain on you during the countdown)
5512     // We double target speed because if the player is walking and then start to run their effective speed doubles
5513     // .65 factor was determined experimentally to be about the factor required for players to be able to *just barely*
5514     // outrun the explosion if they drop everything and run.
5515     float factor = static_cast<float>( z->get_speed() ) / static_cast<float>( target->get_speed() * 2 );
5516     int range = std::max( 1, static_cast<int>( .65 * ( radius + 1 + factor * charges ) ) );
5517 
5518     // Check if we are in range to begin the countdown
5519     if( !within_target_range( z, target, range ) ) {
5520         return false;
5521     }
5522 
5523     // HACK: HORRIBLE HACK ALERT! Currently uses the amount of ammo as a pseudo-timer.
5524     // Once we have proper monster inventory item processing replace the following
5525     // line with the code below.
5526     z->add_effect( effect_countdown, 1_turns * charges + 1_turns );
5527     /* Replacement code here for once we have working monster inventories
5528 
5529     item i_explodes(act_bomb_type->id, 0);
5530     i_explodes.charges = charges;
5531     z->add_item(i_explodes);
5532     z->disable_special("KAMIKAZE");
5533     */
5534     // END HORRIBLE HACK
5535 
5536     add_msg_if_player_sees( z->pos(), m_bad, _( "The %s lights up menacingly." ), z->name() );
5537 
5538     return true;
5539 }
5540 
5541 struct grenade_helper_struct {
5542     std::string message;
5543     int chance = 1;
5544 };
5545 
5546 // Returns 0 if this should be retired, 1 if it was successful, and -1 if something went horribly wrong
grenade_helper(monster * const z,Creature * const target,const int dist,const int moves,std::map<itype_id,grenade_helper_struct> data)5547 static int grenade_helper( monster *const z, Creature *const target, const int dist,
5548                            const int moves, std::map<itype_id, grenade_helper_struct> data )
5549 {
5550     // Can't do anything if we can't act
5551     if( !z->can_act() ) {
5552         return 0;
5553     }
5554     // Too far or we can't target them
5555     if( !within_target_range( z, target, dist ) ) {
5556         return 0;
5557     }
5558     // We need an open space for these attacks
5559     const auto empty_neighbors = find_empty_neighbors( *z );
5560     const size_t empty_neighbor_count = empty_neighbors.second;
5561     if( !empty_neighbor_count ) {
5562         return 0;
5563     }
5564 
5565     // Find how much ammo we currently have
5566     int curr_ammo = 0;
5567     for( const std::pair<const itype_id, int> &amm : z->ammo ) {
5568         curr_ammo += amm.second;
5569     }
5570     if( curr_ammo == 0 ) {
5571         // We've run out of ammo, get angry and toggle the special off.
5572         z->anger = 100;
5573         return -1;
5574     }
5575 
5576     // Grab all attacks that pass their chance check
5577     weighted_float_list<itype_id> possible_attacks;
5578     for( const std::pair<const itype_id, int> &amm : z->ammo ) {
5579         if( amm.second > 0 ) {
5580             possible_attacks.add( amm.first, 1.0 / data[amm.first].chance );
5581         }
5582     }
5583     itype_id att = *possible_attacks.pick();
5584 
5585     z->moves -= moves;
5586     z->ammo[att]--;
5587 
5588     // if the player can see it
5589     if( get_player_view().sees( *z ) ) {
5590         if( data[att].message.empty() ) {
5591             add_msg_debug( "Invalid ammo message in grenadier special." );
5592         } else {
5593             add_msg( m_bad, data[att].message, z->name() );
5594         }
5595     }
5596 
5597     // Get our monster type
5598     const itype *bomb_type = item::find_type( att );
5599     const use_function *usage = bomb_type->get_use( "place_monster" );
5600     if( usage == nullptr ) {
5601         // Invalid bomb item usage, Toggle this special off so we stop processing
5602         add_msg_debug( "Invalid bomb item usage in grenadier special for %s.", z->name() );
5603         return -1;
5604     }
5605     const place_monster_iuse *actor = dynamic_cast<const place_monster_iuse *>
5606                                       ( usage->get_actor_ptr() );
5607     if( actor == nullptr ) {
5608         // Invalid bomb item, Toggle this special off so we stop processing
5609         add_msg_debug( "Invalid bomb type in grenadier special for %s.", z->name() );
5610         return -1;
5611     }
5612 
5613     const tripoint where = empty_neighbors.first[get_random_index( empty_neighbor_count )];
5614 
5615     if( monster *const hack = g->place_critter_at( actor->mtypeid, where ) ) {
5616         hack->make_ally( *z );
5617     }
5618     return 1;
5619 }
5620 
grenadier(monster * const z)5621 bool mattack::grenadier( monster *const z )
5622 {
5623     // Build our grenade map
5624     std::map<itype_id, grenade_helper_struct> grenades;
5625     // Grenades
5626     grenades[itype_bot_pacification_hack].message =
5627         _( "The %s deploys a pacification hack!" );
5628     // Flashbangs
5629     grenades[itype_bot_flashbang_hack].message =
5630         _( "The %s deploys a flashbang hack!" );
5631     // Gasbombs
5632     grenades[itype_bot_gasbomb_hack].message =
5633         _( "The %s deploys a tear gas hack!" );
5634     // C-4
5635     grenades[itype_bot_c4_hack].message = _( "The %s buzzes and deploys a C-4 hack!" );
5636     grenades[itype_bot_c4_hack].chance = 8;
5637 
5638     // Only can actively target the player right now. Once we have the ability to grab targets that we aren't
5639     // actively attacking change this to use that instead.
5640     Creature *const target = static_cast<Creature *>( &get_player_character() );
5641     if( z->attitude_to( *target ) == Creature::Attitude::FRIENDLY ) {
5642         return false;
5643     }
5644     int ret = grenade_helper( z, target, 30, 60, grenades );
5645     if( ret == -1 ) {
5646         // Something broke badly, disable our special
5647         z->disable_special( "GRENADIER" );
5648     }
5649     return true;
5650 }
5651 
grenadier_elite(monster * const z)5652 bool mattack::grenadier_elite( monster *const z )
5653 {
5654     // Build our grenade map
5655     std::map<itype_id, grenade_helper_struct> grenades;
5656     // Grenades
5657     grenades[itype_bot_grenade_hack].message = _( "The %s deploys a grenade hack!" );
5658     // Flashbangs
5659     grenades[itype_bot_flashbang_hack].message =
5660         _( "The %s deploys a flashbang hack!" );
5661     // Gasbombs
5662     grenades[itype_bot_gasbomb_hack].message = _( "The %s deploys a tear gas hack!" );
5663     // C-4
5664     grenades[itype_bot_c4_hack].message = _( "The %s buzzes and deploys a C-4 hack!" );
5665     grenades[itype_bot_c4_hack].chance = 8;
5666     // Mininuke
5667     grenades[itype_bot_mininuke_hack].message =
5668         _( "A klaxon blares from %s as it deploys a mininuke hack!" );
5669     grenades[itype_bot_mininuke_hack].chance = 50;
5670 
5671     // Only can actively target the player right now. Once we have the ability to grab targets that we aren't
5672     // actively attacking change this to use that instead.
5673     Creature *const target = static_cast<Creature *>( &get_player_character() );
5674     if( z->attitude_to( *target ) == Creature::Attitude::FRIENDLY ) {
5675         return false;
5676     }
5677     int ret = grenade_helper( z, target, 30, 60, grenades );
5678     if( ret == -1 ) {
5679         // Something broke badly, disable our special
5680         z->disable_special( "GRENADIER_ELITE" );
5681     }
5682 
5683     return true;
5684 }
5685 
stretch_attack(monster * z)5686 bool mattack::stretch_attack( monster *z )
5687 {
5688     if( !z->can_act() ) {
5689         return false;
5690     }
5691 
5692     Creature *target = z->attack_target();
5693     if( target == nullptr ) {
5694         return false;
5695     }
5696 
5697     int distance = rl_dist( z->pos(), target->pos() );
5698     // Hack, only allow attacking above or below if the target is adjacent.
5699     if( z->pos().z != target->pos().z ) {
5700         distance += 2;
5701     }
5702     if( distance < 2 || distance > 3 || !z->sees( *target ) ) {
5703         return false;
5704     }
5705 
5706     int dam = rng( 5, 10 );
5707     z->moves -= 100;
5708     map &here = get_map();
5709     for( auto &pnt : here.find_clear_path( z->pos(), target->pos() ) ) {
5710         if( here.impassable( pnt ) ) {
5711             target->add_msg_player_or_npc( _( "The %1$s thrusts its arm at you, but bounces off the %2$s." ),
5712                                            _( "The %1$s thrusts its arm at <npcname>, but bounces off the %2$s." ),
5713                                            z->name(), here.obstacle_name( pnt ) );
5714             return true;
5715         }
5716     }
5717 
5718     game_message_type msg_type = target->is_avatar() ? m_warning : m_info;
5719     target->add_msg_player_or_npc( msg_type,
5720                                    _( "The %s thrusts its arm at you, stretching to reach you from afar." ),
5721                                    _( "The %s thrusts its arm at <npcname>." ),
5722                                    z->name() );
5723     if( dodge_check( z, target ) || target->uncanny_dodge() ) {
5724         target->add_msg_player_or_npc( msg_type, _( "You evade the stretched arm and it sails past you!" ),
5725                                        _( "<npcname> evades the stretched arm!" ) );
5726         target->on_dodge( z, z->type->melee_skill );
5727         //takes some time to retract the arm
5728         z->moves -= 150;
5729         return true;
5730     }
5731 
5732     const bodypart_id hit = target->get_random_body_part();
5733     dam = target->deal_damage( z, hit, damage_instance( damage_type::STAB, dam ) ).total_damage();
5734 
5735     if( dam > 0 ) {
5736         game_message_type msg_type = target->is_avatar() ? m_bad : m_info;
5737         target->add_msg_player_or_npc( msg_type,
5738                                        //~ 1$s is monster name, 2$s bodypart in accusative
5739                                        _( "The %1$s's arm pierces your %2$s!" ),
5740                                        //~ 1$s is monster name, 2$s bodypart in accusative
5741                                        _( "The %1$s arm pierces <npcname>'s %2$s!" ),
5742                                        z->name(),
5743                                        body_part_name_accusative( hit ) );
5744 
5745         target->check_dead_state();
5746     } else {
5747         target->add_msg_player_or_npc( _( "The %1$s arm hits your %2$s, but glances off your armor!" ),
5748                                        _( "The %1$s hits <npcname>'s %2$s, but glances off armor!" ),
5749                                        z->name(),
5750                                        body_part_name_accusative( hit ) );
5751     }
5752 
5753     target->on_hit( z, hit,  z->type->melee_skill );
5754 
5755     return true;
5756 }
5757 
zombie_fuse(monster * z)5758 bool mattack::zombie_fuse( monster *z )
5759 {
5760     monster *critter = nullptr;
5761     for( const tripoint &p : get_map().points_in_radius( z->pos(), 1 ) ) {
5762         critter = g->critter_at<monster>( p );
5763         if( critter != nullptr && critter->faction == z->faction
5764             && critter != z && critter->get_size() <= z->get_size() ) {
5765             break;
5766         }
5767         critter = nullptr;
5768     }
5769 
5770     if( critter == nullptr || ( z->has_effect( effect_grown_of_fuse ) &&
5771                                 ( z->get_hp() + critter->get_hp() > z->get_hp_max() + z->get_effect(
5772                                       effect_grown_of_fuse ).get_max_intensity() ) ) ) {
5773         return false;
5774     }
5775     add_msg_if_player_sees( *z, _( "The %1$s fuses with the %2$s." ),
5776                             critter->name(), z->name() );
5777     z->moves -= 200;
5778     z->add_effect( effect_grown_of_fuse, 10_days, true,
5779                    critter->get_hp_max() + z->get_effect( effect_grown_of_fuse ).get_intensity() );
5780     z->heal( critter->get_hp(), true );
5781     if( mission::on_creature_fusion( *z, *critter ) ) {
5782         z->mission_fused.emplace_back( critter->name() );
5783         z->mission_fused.insert( z->mission_fused.end(),
5784                                  critter->mission_fused.begin(), critter->mission_fused.end() );
5785         // let the player know that they still need to kill the fusing monster
5786         add_msg_if_player_sees( *z, _( "%1$s still seems to be moving inside %2$s…" ),
5787                                 critter->name(), z->name() );
5788     }
5789     critter->death_drops = false;
5790     critter->quiet_death = true;
5791     critter->die( z );
5792     return true;
5793 }
5794 
doot(monster * z)5795 bool mattack::doot( monster *z )
5796 {
5797     z->moves -= 300;
5798     add_msg_if_player_sees( *z, _( "The %s doots its trumpet!" ), z->name() );
5799     int spooks = 0;
5800     for( const tripoint &spookyscary : get_map().points_in_radius( z->pos(), 2 ) ) {
5801         if( !g->is_empty( spookyscary ) ) {
5802             continue;
5803         }
5804         const int dist = rl_dist( z->pos(), spookyscary );
5805         if( ( one_in( dist + 3 ) || spooks == 0 ) && spooks < 5 ) {
5806             add_msg_if_player_sees( spookyscary, _( "A spooky skeleton rises from the ground!" ) );
5807             g->place_critter_at( mon_zombie_skeltal_minion, spookyscary );
5808             spooks++;
5809             continue;
5810         }
5811     }
5812     sounds::sound( z->pos(), 200, sounds::sound_t::music, _( "DOOT." ), false, "music_instrument",
5813                    "trumpet" );
5814     return true;
5815 }
5816 
dodge_check(monster * z,Creature * target)5817 bool mattack::dodge_check( monster *z, Creature *target )
5818 {
5819     ///\EFFECT_DODGE increases chance of dodging, vs their melee skill
5820     float dodge = std::max( target->get_dodge() - rng( 0, z->get_hit() ), 0.0f );
5821     return dodge > 0.0 && rng( 0, 10000 ) < 10000 / ( 1 + 99 * std::exp( -.6 * dodge ) );
5822 }
5823 
speaker(monster * z)5824 bool mattack::speaker( monster *z )
5825 {
5826     sounds::sound( z->pos(), 60, sounds::sound_t::order,
5827                    SNIPPET.random_from_category( "speaker_warning" ).value_or( translation() ) );
5828     return true;
5829 }
5830