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 ¤t_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