• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CleverRaven / Cataclysm-DDA / 13108

pending completion
13108

Pull #26705

travis-ci

web-flow
Add proportional damage field to ammo type.

Updated proportional damage to use cata::optional
Pull Request #26705: Add proportional damage field to ammo type

37 of 37 new or added lines in 4 files covered. (100.0%)

31245 of 118162 relevant lines covered (26.44%)

98332.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

3.66
/src/monattack.cpp
1
#include "monattack.h"
2

3
#include "ballistics.h"
4
#include "bodypart.h"
5
#include "debug.h"
6
#include "dispersion.h"
7
#include "effect.h"
8
#include "event.h"
9
#include "field.h"
10
#include "fungal_effects.h"
11
#include "game.h"
12
#include "gun_mode.h"
13
#include "itype.h"
14
#include "iuse_actor.h"
15
#include "line.h"
16
#include "map.h"
17
#include "map_iterator.h"
18
#include "mapdata.h"
19
#include "messages.h"
20
#include "mondefense.h"
21
#include "monster.h"
22
#include "morale_types.h"
23
#include "mtype.h"
24
#include "npc.h"
25
#include "output.h"
26
#include "projectile.h"
27
#include "rng.h"
28
#include "sounds.h"
29
#include "speech.h"
30
#include "text_snippets.h"
31
#include "translations.h"
32
#include "ui.h"
33
#include "vehicle.h"
34
#include "vpart_position.h"
35
#include "weighted_list.h"
36

37
#include <algorithm>
38
#include <cmath>
39
#include <map>
40

41
const mtype_id mon_ant( "mon_ant" );
1✔
42
const mtype_id mon_ant_acid( "mon_ant_acid" );
1✔
43
const mtype_id mon_ant_acid_larva( "mon_ant_acid_larva" );
1✔
44
const mtype_id mon_ant_acid_soldier( "mon_ant_acid_soldier" );
1✔
45
const mtype_id mon_ant_acid_queen( "mon_ant_acid_queen" );
1✔
46
const mtype_id mon_ant_larva( "mon_ant_larva" );
1✔
47
const mtype_id mon_ant_soldier( "mon_ant_soldier" );
1✔
48
const mtype_id mon_biollante( "mon_biollante" );
1✔
49
const mtype_id mon_blob( "mon_blob" );
1✔
50
const mtype_id mon_blob_brain( "mon_blob_brain" );
1✔
51
const mtype_id mon_blob_large( "mon_blob_large" );
1✔
52
const mtype_id mon_blob_small( "mon_blob_small" );
1✔
53
const mtype_id mon_breather( "mon_breather" );
1✔
54
const mtype_id mon_breather_hub( "mon_breather_hub" );
1✔
55
const mtype_id mon_creeper_hub( "mon_creeper_hub" );
1✔
56
const mtype_id mon_creeper_vine( "mon_creeper_vine" );
1✔
57
const mtype_id mon_dermatik( "mon_dermatik" );
1✔
58
const mtype_id mon_fungal_hedgerow( "mon_fungal_hedgerow" );
1✔
59
const mtype_id mon_fungaloid( "mon_fungaloid" );
1✔
60
const mtype_id mon_fungaloid_young( "mon_fungaloid_young" );
1✔
61
const mtype_id mon_fungal_tendril( "mon_fungal_tendril" );
1✔
62
const mtype_id mon_fungal_wall( "mon_fungal_wall" );
1✔
63
const mtype_id mon_headless_dog_thing( "mon_headless_dog_thing" );
1✔
64
const mtype_id mon_manhack( "mon_manhack" );
1✔
65
const mtype_id mon_shadow( "mon_shadow" );
1✔
66
const mtype_id mon_triffid( "mon_triffid" );
1✔
67
const mtype_id mon_turret_searchlight( "mon_turret_searchlight" );
1✔
68
const mtype_id mon_zombie_dancer( "mon_zombie_dancer" );
1✔
69
const mtype_id mon_zombie_jackson( "mon_zombie_jackson" );
1✔
70
const mtype_id mon_zombie_skeltal_minion( "mon_zombie_skeltal_minion" );
1✔
71

72
const skill_id skill_melee( "melee" );
1✔
73
const skill_id skill_gun( "gun" );
1✔
74
const skill_id skill_unarmed( "unarmed" );
1✔
75
const skill_id skill_rifle( "rifle" );
1✔
76
const skill_id skill_launcher( "launcher" );
1✔
77

78
const species_id ZOMBIE( "ZOMBIE" );
1✔
79
const species_id BLOB( "BLOB" );
1✔
80

81
const efftype_id effect_bite( "bite" );
1✔
82
const efftype_id effect_bleed( "bleed" );
1✔
83
const efftype_id effect_blind( "blind" );
1✔
84
const efftype_id effect_boomered( "boomered" );
1✔
85
const efftype_id effect_controlled( "controlled" );
1✔
86
const efftype_id effect_corroding( "corroding" );
1✔
87
const efftype_id effect_countdown( "countdown" );
1✔
88
const efftype_id effect_darkness( "darkness" );
1✔
89
const efftype_id effect_dazed( "dazed" );
1✔
90
const efftype_id effect_deaf( "deaf" );
1✔
91
const efftype_id effect_dermatik( "dermatik" );
1✔
92
const efftype_id effect_downed( "downed" );
1✔
93
const efftype_id effect_fearparalyze( "fearparalyze" );
1✔
94
const efftype_id effect_fungus( "fungus" );
1✔
95
const efftype_id effect_glowing( "glowing" );
1✔
96
const efftype_id effect_grabbed( "grabbed" );
1✔
97
const efftype_id effect_infected( "infected" );
1✔
98
const efftype_id effect_laserlocked( "laserlocked" );
1✔
99
const efftype_id effect_onfire( "onfire" );
1✔
100
const efftype_id effect_paralyzepoison( "paralyzepoison" );
1✔
101
const efftype_id effect_raising( "raising" );
1✔
102
const efftype_id effect_rat( "rat" );
1✔
103
const efftype_id effect_shrieking( "shrieking" );
1✔
104
const efftype_id effect_slimed( "slimed" );
1✔
105
const efftype_id effect_stunned( "stunned" );
1✔
106
const efftype_id effect_targeted( "targeted" );
1✔
107
const efftype_id effect_teleglow( "teleglow" );
1✔
108

109
static const trait_id trait_ACIDBLOOD( "ACIDBLOOD" );
1✔
110
static const trait_id trait_MARLOSS_BLUE( "MARLOSS_BLUE" );
1✔
111
static const trait_id trait_MARLOSS( "MARLOSS" );
1✔
112
static const trait_id trait_PARAIMMUNE( "PARAIMMUNE" );
1✔
113
static const trait_id trait_TAIL_CATTLE( "TAIL_CATTLE" );
1✔
114
static const trait_id trait_THRESH_MARLOSS( "THRESH_MARLOSS" );
1✔
115
static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" );
1✔
116

117
// shared utility functions
118
int within_visual_range( monster *z, int max_range )
×
119
{
120
    int dist = rl_dist( z->pos(), g->u.pos() );
×
121
    if( dist > max_range || !z->sees( g->u ) ) {
×
122
        return -1;    // Out of range
123
    }
124
    return dist;
×
125
}
126

127
bool within_target_range( const monster *const z, const Creature *const target, int range )
×
128
{
129
    if( target == nullptr ||
×
130
        rl_dist( z->pos(), target->pos() ) > range ||
×
131
        !z->sees( *target ) ) {
×
132
        return false;
133
    }
134
    return true;
×
135
}
136

137
// Distance == 1 and on the same z-level or with a clear shot up/down.
138
// If allow_zlev is false, don't allow attacking up/down at all.
139
// If allow_zlev is true, also allow distance == 1 and on different z-level
140
// as long as floor/ceiling doesn't exist.
141
bool is_adjacent( const monster *z, const Creature *target, const bool allow_zlev )
1,544✔
142
{
143
    if( target == nullptr ) {
1,544✔
144
        return false;
145
    }
146

147
    if( rl_dist( z->pos(), target->pos() ) != 1 ) {
1,544✔
148
        return false;
149
    }
150

151
    if( z->posz() == target->posz() ) {
×
152
        return true;
153
    }
154

155
    if( !allow_zlev ) {
×
156
        return false;
157
    }
158

159
    // The square above must have no floor (currently only open air).
160
    // The square below must have no ceiling (ie. be outside).
161
    const bool target_above = target->posz() > z->posz();
×
162
    const tripoint &up =   target_above ? target->pos() : z->pos();
×
163
    const tripoint &down = target_above ? z->pos() : target->pos();
×
164
    return g->m.ter( up ) == t_open_air && g->m.is_outside( down );
×
165
}
166

167
npc make_fake_npc( monster *z, int str, int dex, int inte, int per )
×
168
{
169
    npc tmp;
×
170
    tmp.name = _( "The " ) + z->name();
×
171
    tmp.set_fake( true );
×
172
    tmp.recoil = 0;
×
173
    tmp.setpos( z->pos() );
×
174
    tmp.str_cur = str;
×
175
    tmp.dex_cur = dex;
×
176
    tmp.int_cur = inte;
×
177
    tmp.per_cur = per;
×
178
    if( z->friendly != 0 ) {
×
179
        tmp.set_attitude( NPCATT_FOLLOW );
×
180
    } else {
181
        tmp.set_attitude( NPCATT_KILL );
×
182
    }
183
    return tmp;
×
184
}
185

186
bool mattack::none( monster * )
×
187
{
188
    return true;
×
189
}
190

191
bool mattack::eat_crop( monster *z )
×
192
{
193
    for( const auto &p : g->m.points_in_radius( z->pos(), 1 ) ) {
×
194
        if( g->m.has_flag( "PLANT", p ) && one_in( 4 ) ) {
×
195
            g->m.ter_set( p, t_dirt );
×
196
            g->m.furn_set( p, f_null );
×
197
            g->m.i_clear( p );
×
198
            return true;
×
199
        }
200
    }
201
    return true;
×
202
}
203

204
bool mattack::eat_food( monster *z )
42✔
205
{
206
    for( const auto &p : g->m.points_in_radius( z->pos(), 1 ) ) {
798✔
207
        //Protect crop seeds from carnivores, give omnivores eat_crop special also
208
        if( g->m.has_flag( "PLANT", p ) ) {
378✔
209
            continue;
×
210
        }
211
        auto items = g->m.i_at( p );
378✔
212
        for( auto i = items.begin(); i != items.end(); i++ ) {
1,134✔
213
            //Fun limit prevents scavengers from eating feces
214
            if( !i->is_food() || i->type->comestible->fun < -20 ) {
×
215
                continue;
216
            }
217
            //Don't eat own eggs
218
            if( z->type->baby_egg != i->type->get_id() ) {
×
219
                long consumed = 1;
×
220
                if( i->count_by_charges() ) {
×
221
                    g->m.use_charges( p, 0, i->type->get_id(), consumed );
×
222
                } else {
223
                    g->m.use_amount( p, 0, i->type->get_id(), consumed );
×
224
                }
225
                return true;
226
            }
227
        }
228
    }
229
    return true;
42✔
230
}
231

232
bool mattack::antqueen( monster *z )
×
233
{
234
    std::vector<tripoint> egg_points;
235
    std::vector<monster *> ants;
236
    // Count up all adjacent tiles the contain at least one egg.
237
    for( const auto &dest : g->m.points_in_radius( z->pos(), 2 ) ) {
×
238
        if( g->m.impassable( dest ) ) {
×
239
            continue;
240
        }
241

242
        if( monster *const mon = g->critter_at<monster>( dest ) ) {
×
243
            if( mon->type->default_faction == mfaction_id( "ant" ) && mon->type->upgrades ) {
×
244
                ants.push_back( mon );
×
245
            }
246

247
            continue;
×
248
        }
249

250
        if( g->is_empty( dest ) && g->m.has_items( dest ) ) {
×
251
            for( auto &i : g->m.i_at( dest ) ) {
×
252
                if( i.typeId() == "ant_egg" ) {
×
253
                    egg_points.push_back( dest );
×
254
                    // Done looking at this tile
255
                    break;
256
                }
257
            }
258
        }
259
    }
260

261
    if( !ants.empty() ) {
×
262
        z->moves -= 100; // It takes a while
×
263
        monster *ant = random_entry( ants );
×
264
        if( g->u.sees( *z ) && g->u.sees( *ant ) ) {
×
265
            add_msg( m_warning, _( "The %1$s feeds an %2$s and it grows!" ), z->name().c_str(),
×
266
                     ant->name().c_str() );
×
267
        }
268
        ant->poly( ant->type->upgrade_into );
×
269
    } else if( egg_points.empty() ) {  // There's no eggs nearby--lay one.
×
270
        if( g->u.sees( *z ) ) {
×
271
            add_msg( _( "The %s lays an egg!" ), z->name().c_str() );
×
272
        }
273
        g->m.spawn_item( z->pos(), "ant_egg", 1, 0, calendar::turn );
×
274
    } else { // There are eggs nearby.  Let's hatch some.
275
        z->moves -= 20 * egg_points.size(); // It takes a while
×
276
        if( g->u.sees( *z ) ) {
×
277
            add_msg( m_warning, _( "The %s tends nearby eggs, and they hatch!" ), z->name().c_str() );
×
278
        }
279
        for( auto &i : egg_points ) {
×
280
            auto eggs = g->m.i_at( i );
×
281
            for( size_t j = 0; j < eggs.size(); j++ ) {
×
282
                if( eggs[j].typeId() != "ant_egg" ) {
×
283
                    continue;
×
284
                }
285
                g->m.i_rem( i, j );
×
286
                monster tmp( z->type->id == mon_ant_acid_queen ? mon_ant_acid_larva : mon_ant_larva, i );
×
287
                tmp.make_ally( *z );
×
288
                g->add_zombie( tmp );
×
289
                break; // Max one hatch per tile
290
            }
291
        }
292
    }
293

294
    return true;
×
295
}
296

297
bool mattack::shriek( monster *z )
×
298
{
299
    Creature *target = z->attack_target();
×
300
    if( target == nullptr ||
×
301
        rl_dist( z->pos(), target->pos() ) > 4 ||
×
302
        !z->sees( *target ) ) {
×
303
        return false;
304
    }
305

306
    z->moves -= 240;   // It takes a while
×
307
    sounds::sound( z->pos(), 50, _( "a terrible shriek!" ) );
×
308
    return true;
×
309
}
310

311
bool mattack::shriek_alert( monster *z )
×
312
{
313
    if( !z->can_act() || z->has_effect( effect_shrieking ) ) {
×
314
        return false;
315
    }
316

317
    Creature *target = z->attack_target();
×
318

319
    int dist;
320
    if( target == nullptr || ( dist = rl_dist( z->pos(), target->pos() ) ) > 15 ||
×
321
        !z->sees( *target ) ) {
×
322
        return false;
323
    }
324

325
    if( g->u.sees( *z ) ) {
×
326
        add_msg( _( "The %s begins shrieking!" ), z->name().c_str() );
×
327
    }
328

329
    z->moves -= 150;
×
330
    sounds::sound( z->pos(), 120, _( "a piercing wail!" ) );
×
331
    z->add_effect( effect_shrieking, 1_minutes );
×
332

333
    return true;
×
334
}
335

336
bool mattack::shriek_stun( monster *z )
×
337
{
338
    if( !z->can_act() || !z->has_effect( effect_shrieking ) ) {
×
339
        return false;
340
    }
341

342
    Creature *target = z->attack_target();
×
343
    if( target == nullptr ) {
×
344
        return false;
345
    }
346

347
    int dist = rl_dist( z->pos(), target->pos() );
×
348
    // Currently the cone is 2D, so don't use it for 3D attacks
349
    if( dist > 7 ||
×
350
        z->posz() != target->posz() ||
×
351
        !z->sees( *target ) ) {
×
352
        return false;
353
    }
354

355
    int target_angle = g->m.coord_to_angle( z->posx(), z->posy(), target->posx(), target->posy() );
×
356
    int cone_angle = 20;
×
357
    for( const tripoint &cone : g->m.points_in_radius( z->pos(), 4 ) ) {
×
358
        int tile_angle = g->m.coord_to_angle( z->posx(), z->posy(), cone.x, cone.y );
×
359
        int diff = abs( target_angle - tile_angle );
×
360
        if( diff + cone_angle > 360 || diff > cone_angle || cone == z->pos() ) {
×
361
            continue; // skip the target, because it's outside cone or it's the source
362
        }
363
        // affect the target
364
        g->m.bash( cone, 4, true ); //Small bash to every square, silent to not flood message box
×
365

366
        Creature *target = g->critter_at( cone ); //If a monster is there, chance for stun
×
367
        if( target == nullptr ) {
×
368
            continue;
369
        }
370
        if( one_in( dist / 2 ) && !( target->is_immune_effect( effect_deaf ) ) ) {
×
371
            target->add_effect( effect_dazed, rng( 1_minutes, 2_minutes ), num_bp, false, rng( 1,
372
                                ( 15 - dist ) / 3 ) );
×
373
        }
374

375
    }
376

377
    return true;
×
378
}
379

380
bool mattack::howl( monster *z )
×
381
{
382
    Creature *target = z->attack_target();
×
383
    if( target == nullptr ||
×
384
        rl_dist( z->pos(), target->pos() ) > 4 ||
×
385
        !z->sees( *target ) ) {
×
386
        return false;
387
    }
388

389
    z->moves -= 200;   // It takes a while
×
390
    sounds::sound( z->pos(), 35, _( "an ear-piercing howl!" ) );
×
391

392
    if( z->friendly != 0 ) { // TODO: Make this use mon's faction when those are in
×
393
        for( monster &other : g->all_monsters() ) {
×
394
            if( other.type != z->type ) {
×
395
                continue;
396
            }
397
            // Quote KA101: Chance of friendlying other howlers in the area, I'd imagine:
398
            // wolves use howls for communication and can convey that the ape is on Team Wolf.
399
            if( one_in( 4 ) ) {
×
400
                other.friendly = z->friendly;
×
401
                break;
×
402
            }
403
        }
404
    }
405

406
    return true;
407
}
408

409
bool mattack::rattle( monster *z )
×
410
{
411
    // TODO: Let it rattle at non-player friendlies
412
    const int min_dist = z->friendly != 0 ? 1 : 4;
×
413
    Creature *target = &g->u; // Can't use attack_target - the snake has no target
×
414
    if( target == nullptr ||
×
415
        rl_dist( z->pos(), target->pos() ) > min_dist ||
×
416
        !z->sees( *target ) ) {
×
417
        return false;
418
    }
419

420
    z->moves -= 20;   // It takes a very short while
×
421
    sounds::sound( z->pos(), 10, _( "a sibilant rattling sound!" ) );
×
422

423
    return true;
×
424
}
425

426
bool mattack::acid( monster *z )
×
427
{
428
    if( !z->can_act() ) {
×
429
        return false;
430
    }
431

432
    Creature *target = z->attack_target();
×
433
    if( target == nullptr ) {
×
434
        return false;
435
    }
436

437
    if( !z->sees( *target ) ||
×
438
        !g->m.clear_path( z->pos(), target->pos(), 10, 1, 100 ) ) {
×
439
        return false; // Can't see/reach target, no attack
440
    }
441
    z->moves -= 300;   // It takes a while
×
442
    sounds::sound( z->pos(), 4, _( "a spitting noise." ) );
×
443

444
    projectile proj;
×
445
    proj.speed = 10;
×
446
    proj.impact.add_damage( DT_ACID, 5 ); // Mostly just for momentum
×
447
    proj.range = 10;
×
448
    proj.proj_effects.insert( "NO_OVERSHOOT" );
×
449
    auto dealt = projectile_attack( proj, z->pos(), target->pos(), { 5400 }, z );
×
450
    const tripoint &hitp = dealt.end_point;
×
451
    const Creature *hit_critter = dealt.hit_critter;
×
452
    if( hit_critter == nullptr && g->m.hit_with_acid( hitp ) && g->u.sees( hitp ) ) {
×
453
        add_msg( _( "A glob of acid hits the %s!" ),
454
                 g->m.tername( hitp ).c_str() );
×
455
        if( g->m.impassable( hitp ) ) {
×
456
            // TODO: Allow it to spill on the side it hit from
457
            return true;
458
        }
459
    }
460

461
    for( int i = -3; i <= 3; i++ ) {
×
462
        for( int j = -3; j <= 3; j++ ) {
×
463
            tripoint dest = hitp + tripoint( i, j, 0 );
×
464
            if( g->m.passable( dest ) &&
×
465
                g->m.clear_path( dest, hitp, 6, 1, 100 ) &&
×
466
                ( ( one_in( abs( j ) ) && one_in( abs( i ) ) ) || ( i == 0 && j == 0 ) ) ) {
×
467
                g->m.add_field( dest, fd_acid, 2 );
×
468
            }
469
        }
470
    }
471

472
    return true;
×
473
}
474

475
bool mattack::acid_barf( monster *z )
×
476
{
477
    if( !z->can_act() ) {
×
478
        return false;
479
    }
480

481
    // Let it be used on non-player creatures
482
    Creature *target = z->attack_target();
×
483
    if( target == nullptr || !is_adjacent( z, target, false ) ) {
×
484
        return false;
485
    }
486

487
    z->moves -= 80;
×
488
    // Make sure it happens before uncanny dodge
489
    g->m.add_field( target->pos(), fd_acid, 1 );
×
490
    bool uncanny = target->uncanny_dodge();
×
491
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
492
    if( uncanny || dodge_check( z, target ) ) {
×
493
        auto msg_type = target == &g->u ? m_warning : m_info;
×
494
        target->add_msg_player_or_npc( msg_type,
495
                                       _( "The %s barfs acid at you, but you dodge!" ),
496
                                       _( "The %s barfs acid at <npcname>, but they dodge!" ),
497
                                       z->name().c_str() );
×
498
        if( !uncanny ) {
×
499
            target->on_dodge( z, z->type->melee_skill * 2 );
×
500
        }
501

502
        return true;
503
    }
504

505
    body_part hit = target->get_random_body_part();
×
506
    int dam = rng( 5, 12 );
×
507
    dam = target->deal_damage( z, hit, damage_instance( DT_ACID, dam ) ).total_damage();
×
508
    target->add_env_effect( effect_corroding, hit, 5, time_duration::from_turns( dam / 2 + 5 ), hit );
×
509

510
    if( dam > 0 ) {
×
511
        auto msg_type = target == &g->u ? m_bad : m_info;
×
512
        //~ 1$s is monster name, 2$s bodypart in accusative
513
        target->add_msg_player_or_npc( msg_type,
514
                                       _( "The %1$s barfs acid on your %2$s for %3$d damage!" ),
515
                                       _( "The %1$s barfs acid on <npcname>'s %2$s for %3$d damage!" ),
516
                                       z->name().c_str(),
×
517
                                       body_part_name_accusative( hit ).c_str(),
×
518
                                       dam );
×
519

520
        if( hit == bp_eyes ) {
×
521
            target->add_env_effect( effect_blind, bp_eyes, 3, 1_minutes );
×
522
        }
523
    } else {
524
        target->add_msg_player_or_npc(
525
            _( "The %1$s barfs acid on your %2$s, but it washes off the armor!" ),
526
            _( "The %1$s barfs acid on <npcname>'s %2$s, but it washes off the armor!" ),
527
            z->name().c_str(),
×
528
            body_part_name_accusative( hit ).c_str() );
×
529
    }
530

531
    target->on_hit( z, hit,  z->type->melee_skill );
×
532

533
    return true;
534
}
535

536
bool mattack::acid_accurate( monster *z )
×
537
{
538
    if( !z->can_act() ) {
×
539
        return false;
540
    }
541

542
    Creature *target = z->attack_target();
×
543
    if( target == nullptr ) {
×
544
        return false;
545
    }
546

547
    const int range = rl_dist( z->pos(), target->pos() );
×
548
    if( range > 10 || range < 2 || !z->sees( *target ) ) {
×
549
        return false;
550
    }
551

552
    z->moves -= 50;
×
553

554
    projectile proj;
×
555
    proj.speed = 10;
×
556
    proj.range = 10;
×
557
    proj.proj_effects.insert( "BLINDS_EYES" );
×
558
    proj.proj_effects.insert( "NO_DAMAGE_SCALING" );
×
559
    proj.impact.add_damage( DT_ACID, rng( 3, 5 ) );
×
560
    // Make it arbitrarily less accurate at close ranges
561
    projectile_attack( proj, z->pos(), target->pos(), { 8000.0 * static_cast<double>( range ) }, z );
×
562

563
    return true;
×
564
}
565

566
bool mattack::shockstorm( monster *z )
×
567
{
568
    if( !z->can_act() ) {
×
569
        return false;
570
    }
571

572
    Creature *target = z->attack_target();
×
573
    if( target == nullptr ) {
×
574
        return false;
575
    }
576

577
    bool seen = g->u.sees( *z );
×
578
    if( !z->sees( *target ) ||
×
579
        !g->m.clear_path( z->pos(), target->pos(), 12, 1, 100 ) ) {
×
580
        return false; // Can't see/reach target, no attack
581
    }
582

583
    z->moves -= 50;   // It takes a while
×
584

585
    if( seen ) {
×
586
        auto msg_type = target == &g->u ? m_bad : m_neutral;
×
587
        add_msg( msg_type, _( "A bolt of electricity arcs towards %s!" ), target->disp_name().c_str() );
×
588
    }
589
    sfx::play_variant_sound( "fire_gun", "bio_lightning", sfx::get_heard_volume( z->pos() ) );
×
590
    tripoint tarp( target->posx() + rng( -1, 1 ) + rng( -1, 1 ),
×
591
                   target->posy() + rng( -1, 1 ) + rng( -1, 1 ),
×
592
                   target->posz() );
×
593
    std::vector<tripoint> bolt = line_to( z->pos(), tarp, 0, 0 );
×
594
    for( auto &i : bolt ) { // Fill the LOS with electricity
×
595
        if( !one_in( 4 ) ) {
×
596
            g->m.add_field( i, fd_electricity, rng( 1, 3 ) );
×
597
        }
598
    }
599
    // 5x5 cloud of electricity at the square hit
600
    for( const auto &dest : g->m.points_in_radius( tarp, 2 ) ) {
×
601
        if( !one_in( 4 ) ) {
×
602
            g->m.add_field( dest, fd_electricity, rng( 1, 3 ) );
×
603
        }
604
    }
605

606
    return true;
×
607
}
608

609
bool mattack::shocking_reveal( monster *z )
×
610
{
611
    shockstorm( z );
×
612
    std::string WHAT_A_SCOOP = SNIPPET.random_from_category( "clickbait" );
×
613
    sounds::sound( z->pos(), 10, string_format( _( "the %s obnoxiously yelling \"%s!!!\"" ),
×
614
                   z->name().c_str(), WHAT_A_SCOOP ) );
×
615
    return true;
×
616
}
617

618
bool mattack::pull_metal_weapon( monster *z )
×
619
{
620
    ////////////////////////////////////////////////////////////////////////////////////////////////
621
    // Constants and Configuration
622

623
    // max distance that "pull_metal_weapon" can be applied to the target.
624
    constexpr auto max_distance = 12;
×
625

626
    // attack movement costs
627
    constexpr int att_cost_pull = 150;
×
628

629
    // minimum str to resist "pull_metal_weapon"
630
    constexpr int min_str = 4;
×
631

632
    Creature *target = z->attack_target();
×
633
    if( target == nullptr ) {
×
634
        return false;
635
    }
636

637
    if( !z->sees( *target ) || !g->m.clear_path( z->pos(), target->pos(),
×
638
            max_distance, 1, 100 ) ) {
×
639
        return false; // Can't see/reach target, no attack
640
    }
641
    player *foe = dynamic_cast< player * >( target );
×
642
    if( foe != nullptr ) {
×
643
        // Wielded steel or iron items except for built-in things like bionic claws or monomolecular blade
644
        if( !foe->weapon.has_flag( "NO_UNWIELD" ) &&
×
645
            ( foe->weapon.made_of( material_id( "iron" ) ) ||
×
646
              foe->weapon.made_of( material_id( "steel" ) ) ||
×
647
              foe->weapon.made_of( material_id( "budget_steel" ) ) ) ) {
×
648
            int wp_skill = foe->get_skill_level( skill_melee );
×
649
            z->moves -= att_cost_pull;   // It takes a while
×
650
            int success = 100;
×
651
            ///\EFFECT_STR increases resistance to pull_metal_weapon special attack
652
            if( foe->str_cur > min_str ) {
×
653
                ///\EFFECT_MELEE increases resistance to pull_metal_weapon special attack
654
                success = std::max( 100 - ( 6 * ( foe->str_cur - 6 ) ) - ( 6 * wp_skill ), 0 );
×
655
            }
656
            auto m_type = foe == &g->u ? m_bad : m_neutral;
×
657
            if( rng( 1, 100 ) <= success ) {
×
658
                target->add_msg_player_or_npc( m_type, _( "%s is pulled away from your hands!" ),
659
                                               _( "%s is pulled away from <npcname>'s hands!" ), foe->weapon.tname().c_str() );
×
660
                z->add_item( foe->remove_weapon() );
×
661
            } else {
662
                target->add_msg_player_or_npc( m_type,
663
                                               _( "The %s unsuccessfully attempts to pull your weapon away." ),
664
                                               _( "The %s unsuccessfully attempts to pull <npcname>'s weapon away." ), z->name().c_str() );
×
665
            }
666
        }
667
    }
668

669
    return true;
670
}
671

672
bool mattack::boomer( monster *z )
×
673
{
674
    if( !z->can_act() ) {
×
675
        return false;
676
    }
677

678
    Creature *target = z->attack_target();
×
679
    if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 3 || !z->sees( *target ) ) {
×
680
        return false;
681
    }
682

683
    std::vector<tripoint> line = g->m.find_clear_path( z->pos(), target->pos() );
×
684
    z->moves -= 250;   // It takes a while
×
685
    bool u_see = g->u.sees( *z );
×
686
    if( u_see ) {
×
687
        add_msg( m_warning, _( "The %s spews bile!" ), z->name().c_str() );
×
688
    }
689
    for( auto &i : line ) {
×
690
        g->m.add_field( i, fd_bile, 1 );
×
691
        // If bile hit a solid tile, return.
692
        if( g->m.impassable( i ) ) {
×
693
            g->m.add_field( i, fd_bile, 3 );
×
694
            if( g->u.sees( i ) )
×
695
                add_msg( _( "Bile splatters on the %s!" ),
696
                         g->m.tername( i ).c_str() );
×
697
            return true;
698
        }
699
    }
700
    if( !target->uncanny_dodge() ) {
×
701
        ///\EFFECT_DODGE increases chance to avoid boomer effect
702
        if( rng( 0, 10 ) > target->get_dodge() || one_in( target->get_dodge() ) ) {
×
703
            target->add_env_effect( effect_boomered, bp_eyes, 3, 12_turns );
×
704
        } else if( u_see ) {
×
705
            target->add_msg_player_or_npc( _( "You dodge it!" ),
706
                                           _( "<npcname> dodges it!" ) );
×
707
        }
708
        target->on_dodge( z, 10 );
×
709
    }
710

711
    return true;
712
}
713

714
bool mattack::boomer_glow( monster *z )
×
715
{
716
    if( !z->can_act() ) {
×
717
        return false;
718
    }
719

720
    Creature *target = z->attack_target();
×
721
    if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 3 || !z->sees( *target ) ) {
×
722
        return false;
723
    }
724

725
    std::vector<tripoint> line = g->m.find_clear_path( z->pos(), target->pos() );
×
726
    z->moves -= 250;   // It takes a while
×
727
    bool u_see = g->u.sees( *z );
×
728
    if( u_see ) {
×
729
        add_msg( m_warning, _( "The %s spews bile!" ), z->name().c_str() );
×
730
    }
731
    for( auto &i : line ) {
×
732
        g->m.add_field( i, fd_bile, 1 );
×
733
        if( g->m.impassable( i ) ) {
×
734
            g->m.add_field( i, fd_bile, 3 );
×
735
            if( g->u.sees( i ) ) {
×
736
                add_msg( _( "Bile splatters on the %s!" ), g->m.tername( i ).c_str() );
×
737
            }
738
            return true;
739
        }
740
    }
741
    if( !target->uncanny_dodge() ) {
×
742
        ///\EFFECT_DODGE increases chance to avoid glowing boomer effect
743
        if( rng( 0, 10 ) > target->get_dodge() || one_in( target->get_dodge() ) ) {
×
744
            target->add_env_effect( effect_boomered, bp_eyes, 5, 25_turns );
×
745
            target->on_dodge( z, 10 );
×
746
            for( int i = 0; i < rng( 2, 4 ); i++ ) {
×
747
                body_part bp = random_body_part();
×
748
                target->add_env_effect( effect_glowing, bp, 4, 4_minutes );
×
749
                if( target->has_effect( effect_glowing ) ) {
×
750
                    break;
751
                }
752
            }
753
        } else {
754
            target->add_msg_player_or_npc( _( "You dodge it!" ),
755
                                           _( "<npcname> dodges it!" ) );
×
756
        }
757
    }
758

759
    return true;
760
}
761

762
bool mattack::resurrect( monster *z )
×
763
{
764
    // Chance to recover some of our missing speed (yes this will regain
765
    // loses from being revived ourselves as well).
766
    // Multiplying by (current base speed / max speed) means that the
767
    // rate of speed regaining is unaffected by what our current speed is, i.e.
768
    // we will regain the same amount per minute at speed 50 as speed 200.
769
    if( one_in( int( 15 * double( z->get_speed_base() ) / double( z->type->speed ) ) ) ) {
×
770
        // Restore 10% of our current speed, capping at our type maximum
771
        z->set_speed_base( std::min( z->type->speed, int( z->get_speed_base() + .1 * z->type->speed ) ) );
×
772
    }
773

774
    int raising_level = 0;
×
775
    if( z->has_effect( effect_raising ) ) {
×
776
        raising_level = z->get_effect_int( effect_raising ) * 40;
×
777
    }
778

779
    bool sees_necromancer = g->u.sees( *z );
×
780
    std::vector<std::pair<tripoint, item *>> corpses;
781
    // Find all corpses that we can see within 10 tiles.
782
    int range = 10;
×
783
    bool found_eligible_corpse = false;
×
784
    int lowest_raise_score = INT_MAX;
×
785
    for( const tripoint &p : g->m.points_in_radius( z->pos(), range ) ) {
×
786
        if( !g->is_empty( p ) || g->m.get_field_strength( p, fd_fire ) > 1 ||
×
787
            !g->m.sees( z->pos(), p, -1 ) ) {
×
788
            continue;
789
        }
790

791
        for( auto &i : g->m.i_at( p ) ) {
×
792
            const mtype *mt = i.get_mtype();
×
793
            if( !( i.is_corpse() && i.active && mt->has_flag( MF_REVIVES ) &&
×
794
                   mt->in_species( ZOMBIE ) && !mt->has_flag( "NO_NECRO" ) ) ) {
×
795
                continue;
×
796
            }
797

798
            found_eligible_corpse = true;
×
799
            if( raising_level == 0 ) {
×
800
                // Since we have a target, start charging to raise it.
801
                if( sees_necromancer ) {
×
802
                    add_msg( m_info, _( "The %s throws its arms wide." ), z->name().c_str() );
×
803
                }
804
                while( z->moves >= 0 ) {
×
805
                    z->add_effect( effect_raising, 1_minutes );
×
806
                    z->moves -= 100;
×
807
                }
808
                return false;
×
809
            }
810
            int raise_score = ( i.damage_level( 4 ) + 1 ) * mt->hp + i.burnt;
×
811
            lowest_raise_score = std::min( lowest_raise_score, raise_score );
×
812
            if( raise_score <= raising_level ) {
×
813
                corpses.push_back( std::make_pair( p, &i ) );
×
814
            }
815
        }
816
    }
817

818
    if( corpses.empty() ) { // No nearby corpses
×
819
        if( found_eligible_corpse ) {
×
820
            // There was a corpse, but we haven't charged enough.
821
            if( sees_necromancer && one_in( sqrt( lowest_raise_score / 30 ) ) ) {
×
822
                add_msg( m_info, _( "The %s gesticulates wildly." ), z->name().c_str() );
×
823
            }
824
            while( z->moves >= 0 ) {
×
825
                z->add_effect( effect_raising, 1_minutes );
×
826
                z->moves -= 100;
×
827
                return false;
×
828
            }
829
        } else if( raising_level != 0 ) {
×
830
            z->remove_effect( effect_raising );
×
831
        }
832
        // Check to see if there are any nearby living zombies to see if we should get angry
833
        const bool allies = g->get_creature_if( [&]( const Creature & critter ) {
×
834
            const monster *const zed = dynamic_cast<const monster *>( &critter );
×
835
            if( zed && zed != z && zed->type->has_flag( MF_REVIVES ) && zed->type->in_species( ZOMBIE ) &&
×
836
                z->attitude_to( *zed ) == Creature::Attitude::A_FRIENDLY  &&
×
837
                within_target_range( z, zed, 10 ) ) {
×
838
                return true;
839
            }
840
            return false;
×
841
        } );
×
842
        if( !allies ) {
×
843
            // Nobody around who we could revive, get angry
844
            z->anger = 100;
×
845
        } else {
846
            // Someone is around who might die and we could revive,
847
            // calm down.
848
            z->anger = 5;
×
849
        }
850
        return false;
851
    } else {
852
        // We're reviving someone/could revive someone, calm down.
853
        z->anger = 5;
×
854
    }
855

856
    if( z->get_speed_base() <= z->type->speed / 2 ) {
×
857
        // We can only resurrect so many times in a time period
858
        // and we're currently out
859
        return false;
860
    }
861

862
    std::pair<tripoint, item *> raised = random_entry( corpses );
×
863
    float corpse_damage = raised.second->damage_level( 4 );
×
864
    // Did we successfully raise something?
865
    if( g->revive_corpse( raised.first, *raised.second ) ) {
×
866
        g->m.i_rem( raised.first, raised.second );
×
867
        if( sees_necromancer ) {
×
868
            add_msg( m_info, _( "The %s gestures at a nearby corpse." ), z->name().c_str() );
×
869
        }
870
        z->remove_effect( effect_raising );
×
871
        z->moves -= z->type->speed; // Takes one turn
×
872
        // Penalize speed by between 10% and 50% based on how damaged the corpse is.
873
        float speed_penalty = 0.1 + ( corpse_damage * 0.1 );
×
874
        z->set_speed_base( z->get_speed_base() - speed_penalty * z->type->speed );
×
875
        monster *const zed = g->critter_at<monster>( raised.first );
×
876
        if( !zed ) {
×
877
            debugmsg( "Misplaced or failed to revive a zombie corpse" );
×
878
            return true;
879
        }
880

881
        zed->make_ally( *z );
×
882
        if( g->u.sees( *zed ) ) {
×
883
            add_msg( m_warning, _( "A nearby %s rises from the dead!" ), zed->name().c_str() );
×
884
        } else if( sees_necromancer ) {
×
885
            // We saw the necromancer but not the revival
886
            add_msg( m_info, _( "But nothing seems to happen." ) );
×
887
        }
888
    }
889

890
    return true;
891
}
892

893
bool mattack::smash( monster *z )
×
894
{
895
    if( !z->can_act() ) {
×
896
        return false;
897
    }
898

899
    Creature *target = z->attack_target();
×
900
    if( target == nullptr || !is_adjacent( z, target, false ) ) {
×
901
        return false;
902
    }
903

904
    // Costs lots of moves to give you a little bit of a chance to get away.
905
    z->moves -= 400;
×
906

907
    if( target->uncanny_dodge() ) {
×
908
        return true;
909
    }
910

911
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
912
    if( dodge_check( z, target ) ) {
×
913
        target->add_msg_player_or_npc( _( "The %s takes a powerful swing at you, but you dodge it!" ),
914
                                       _( "The %s takes a powerful swing at <npcname>, who dodges it!" ),
915
                                       z->name().c_str() );
×
916
        target->on_dodge( z, z->type->melee_skill * 2 );
×
917
        return true;
×
918
    }
919

920
    target->add_msg_player_or_npc( _( "A blow from the %1$s sends %2$s flying!" ),
921
                                   _( "A blow from the %s sends <npcname> flying!" ),
922
                                   z->name().c_str(), target->disp_name().c_str() );
×
923
    // TODO: Make this parabolic
924
    g->fling_creature( target, g->m.coord_to_angle( z->posx(), z->posy(), target->posx(),
×
925
                       target->posy() ),
×
926
                       z->type->melee_sides * z->type->melee_dice * 3 );
×
927

928
    return true;
×
929
}
930

931
//--------------------------------------------------------------------------------------------------
932
// TODO: move elsewhere
933
//--------------------------------------------------------------------------------------------------
934

935
//--------------------------------------------------------------------------------------------------
936
/**
937
 * Find empty spaces around origin within a radius of N.
938
 *
939
 * @returns a pair with first  = array<tripoint, area>; area = (2*N + 1)^2.
940
 *                      second = the number of empty spaces found.
941
 */
942
template <size_t N = 1>
943
std::pair < std::array < tripoint, ( 2 * N + 1 ) * ( 2 * N + 1 ) >, size_t >
944
find_empty_neighbors( const tripoint &origin )
×
945
{
946
    constexpr auto r = static_cast<int>( N );
×
947

948
    const int x_min = origin.x - r;
×
949
    const int x_max = origin.x + r;
×
950
    const int y_min = origin.y - r;
×
951
    const int y_max = origin.y + r;
×
952

953
    std::pair < std::array < tripoint, ( 2 * N + 1 )*( 2 * N + 1 ) >, size_t > result;
×
954

955
    tripoint tmp;
×
956
    tmp.z = origin.z;
×
957
    for( tmp.x = x_min; tmp.x <= x_max; ++tmp.x ) {
×
958
        for( tmp.y = y_min; tmp.y <= y_max; ++tmp.y ) {
×
959
            if( g->is_empty( tmp ) ) {
×
960
                result.first[result.second++] = tmp;
×
961
            }
962
        }
963
    }
964

965
    return result;
×
966
}
967

968
//--------------------------------------------------------------------------------------------------
969
/**
970
 * Find empty spaces around a creature within a radius of N.
971
 *
972
 * @see find_empty_neighbors
973
 */
974
template <size_t N = 1>
975
std::pair < std::array < tripoint, ( 2 * N + 1 ) * ( 2 * N + 1 ) >, size_t >
976
find_empty_neighbors( Creature const &c )
×
977
{
978
    return find_empty_neighbors<N>( c.pos() );
×
979
}
980

981
//--------------------------------------------------------------------------------------------------
982
/**
983
 * Get a size_t value in the closed interval [0, size]; a convenience to avoid messy casting.
984
  */
985
size_t get_random_index( size_t const size )
×
986
{
987
    return static_cast<size_t>( rng( 0, static_cast<long>( size - 1 ) ) );
×
988
}
989

990
//--------------------------------------------------------------------------------------------------
991
/**
992
 * Get a size_t value in the closed interval [0, c.size() - 1]; a convenience to avoid messy casting.
993
 */
994
template <typename Container>
995
size_t get_random_index( Container const &c )
×
996
{
997
    return get_random_index( c.size() );
×
998
}
999

1000
bool mattack::science( monster *const z ) // I said SCIENCE again!
×
1001
{
1002
    ////////////////////////////////////////////////////////////////////////////////////////////////
1003
    // Constants and Configuration
1004

1005
    // attack types
1006
    enum : int {
1007
        att_shock,
1008
        att_radiation,
1009
        att_manhack,
1010
        att_acid_pool,
1011
        att_flavor,
1012
        att_enum_size
1013
    };
1014

1015
    // max distance that "science" can be applied to the target.
1016
    constexpr auto max_distance = 5;
×
1017

1018
    // attack movement costs
1019
    constexpr int att_cost_shock   = 0;
×
1020
    constexpr int att_cost_rad     = 400;
×
1021
    constexpr int att_cost_manhack = 200;
×
1022
    constexpr int att_cost_acid    = 100;
×
1023
    constexpr int att_cost_flavor  = 80;
×
1024

1025
    // radiation attack behavior
1026
    constexpr int att_rad_dodge_diff    = 16; // how hard it is to dodge
×
1027
    constexpr int att_rad_mutate_chance = 6;  // (1/x) inverse chance to cause mutation.
×
1028
    constexpr int att_rad_dose_min      = 20; // min radiation
×
1029
    constexpr int att_rad_dose_max      = 50; // max radiation
×
1030

1031
    // acid attack behavior
1032
    constexpr int att_acid_density = 3;
×
1033

1034
    // flavor messages
1035
    static std::array<char const *, 4> const m_flavor = {{
1036
            _( "The %s gesticulates wildly!" ),
×
1037
            _( "The %s coughs up a strange dust." ),
×
1038
            _( "The %s moans softly." ),
×
1039
            _( "The %s's skin crackles with electricity." ), //special case; leave this last
×
1040
        }
1041
    };
1042

1043
    if( !z->can_act() ) {
×
1044
        return false;
1045
    }
1046

1047
    ////////////////////////////////////////////////////////////////////////////////////////////////
1048
    // Look for a valid target...
1049
    Creature *const target = z->attack_target();
×
1050
    if( !target ) {
×
1051
        return false;
1052
    }
1053

1054
    // too far
1055
    const int dist = rl_dist( z->pos(), target->pos() );
×
1056
    if( dist > max_distance ) {
×
1057
        return false;
1058
    }
1059

1060
    // can't attack what you can't see
1061
    if( !z->sees( *target ) ) {
×
1062
        return false;
1063
    }
1064

1065
    ////////////////////////////////////////////////////////////////////////////////////////////////
1066
    // okay, we have a valid target; populate valid attack options...
1067
    std::array<int, att_enum_size> valid_attacks;
1068
    size_t valid_attack_count = 0;
×
1069

1070
    // can only shock if adjacent
1071
    if( dist == 1 ) {
×
1072
        valid_attacks[valid_attack_count++] = att_shock;
×
1073
    }
1074

1075
    // TODO: mutate() doesn't like non-players right now
1076
    // It will mutate NPCs, but it will say it mutated the player
1077
    player *const foe = dynamic_cast<player *>( target );
×
1078
    if( ( foe == &g->u ) && dist <= 2 ) {
×
1079
        valid_attacks[valid_attack_count++] = att_radiation;
×
1080
    }
1081

1082
    // need an open space for these attacks
1083
    auto const empty_neighbors = find_empty_neighbors( *z );
×
1084
    size_t const empty_neighbor_count = empty_neighbors.second;
×
1085

1086
    if( empty_neighbor_count ) {
×
1087
        if( z->ammo["bot_manhack"] > 0 ) {
×
1088
            valid_attacks[valid_attack_count++] = att_manhack;
×
1089
        }
1090
        valid_attacks[valid_attack_count++] = att_acid_pool;
×
1091
    }
1092

1093
    // flavor is always okay
1094
    valid_attacks[valid_attack_count++] = att_flavor;
×
1095

1096
    ////////////////////////////////////////////////////////////////////////////////////////////////
1097
    // choose and do a valid attack
1098
    const int attack_index = get_random_index( valid_attack_count );
×
1099
    switch( valid_attacks[attack_index] ) {
×
1100
        default :
1101
            DebugLog( D_WARNING, D_GAME ) << "Bad enum value in science.";
×
1102
            break;
1103
        case att_shock :
1104
            z->moves -= att_cost_shock;
1105

1106
            // Just reuse the taze - it's a bit different (shocks torso vs all),
1107
            // but let's go for consistency here
1108
            taze( z, target );
×
1109
            break;
1110
        case att_radiation : {
1111
            z->moves -= att_cost_rad;
×
1112

1113
            // if the player can see it
1114
            if( g->u.sees( *z ) ) {
×
1115
                // TODO: mutate() doesn't like non-players right now
1116
                add_msg( m_bad, _( "The %1$s opens its mouth and a beam shoots towards %2$s!" ),
1117
                         z->name().c_str(), target->disp_name().c_str() );
×
1118
            }
1119

1120
            // (1) Give the target a chance at an uncanny_dodge.
1121
            // (2) If that fails, always fail to dodge 1 in dodge_skill times.
1122
            // (3) If okay, dodge if dodge_skill > att_rad_dodge_diff.
1123
            // (4) Otherwise, fail 1 in (att_rad_dodge_diff - dodge_skill) times.
1124
            if( foe->uncanny_dodge() ) {
×
1125
                break;
1126
            }
1127

1128
            int const  dodge_skill  = foe->get_dodge();
×
1129
            bool const critial_fail = one_in( dodge_skill );
×
1130
            bool const is_trivial   = dodge_skill > att_rad_dodge_diff;
×
1131

1132
            ///\EFFECT_DODGE increases chance to avoid science effect
1133
            if( !critial_fail && ( is_trivial || dodge_skill > rng( 0, att_rad_dodge_diff ) ) ) {
×
1134
                target->add_msg_player_or_npc( _( "You dodge the beam!" ),
1135
                                               _( "<npcname> dodges the beam!" ) );
×
1136
            } else if( g->u.is_rad_immune() ) {
×
1137
                target->add_msg_if_player( m_good, _( "Your armor protects you from the radiation!" ) );
×
1138
            } else if( one_in( att_rad_mutate_chance ) ) {
×
1139
                foe->mutate();
×
1140
            } else {
1141
                target->add_msg_if_player( m_bad, _( "You get pins and needles all over." ) );
×
1142
                foe->radiation += rng( att_rad_dose_min, att_rad_dose_max );
×
1143
            }
1144
        }
1145
        break;
1146
        case att_manhack : {
1147
            z->moves -= att_cost_manhack;
×
1148
            z->ammo["bot_manhack"]--;
×
1149

1150
            // if the player can see it
1151
            if( g->u.sees( *z ) ) {
×
1152
                add_msg( m_warning, _( "The %s opens its coat, and a manhack flies out!" ),
1153
                         z->name().c_str() );
×
1154
            }
1155

1156
            const tripoint where = empty_neighbors.first[get_random_index( empty_neighbor_count )];
×
1157
            if( monster *const manhack = g->summon_mon( mon_manhack, where ) ) {
×
1158
                manhack->make_ally( *z );
×
1159
            }
1160
        }
1161
        break;
×
1162
        case att_acid_pool :
1163
            z->moves -= att_cost_acid;
×
1164

1165
            // if the player can see it
1166
            if( g->u.sees( *z ) ) {
×
1167
                add_msg( m_warning, _( "The %s drops a flask of acid!" ), z->name().c_str() );
×
1168
            }
1169

1170
            // fill empty tiles with acid
1171
            for( size_t i = 0; i < empty_neighbor_count; ++i ) {
×
1172
                const tripoint &p = empty_neighbors.first[i];
×
1173
                g->m.add_field( p, fd_acid, att_acid_density );
×
1174
            }
1175

1176
            break;
1177
        case att_flavor : {
1178
            const size_t i = get_random_index( m_flavor );
×
1179

1180
            // the special case; see above
1181
            if( i == m_flavor.size() - 1 ) {
×
1182
                z->moves -= att_cost_flavor;
×
1183
            }
1184

1185
            // if the player can see it, else forget about it
1186
            if( g->u.sees( *z ) ) {
×
1187
                add_msg( m_warning, m_flavor[i], z->name().c_str() );
×
1188
            }
1189
        }
1190
        break;
1191
    }
1192

1193
    return true;
1194
}
1195

1196
body_part body_part_hit_by_plant()
×
1197
{
1198
    body_part hit = num_bp;
×
1199
    if( one_in( 2 ) ) {
×
1200
        hit = bp_leg_l;
1201
    } else {
1202
        hit = bp_leg_r;
×
1203
    }
1204
    if( one_in( 4 ) ) {
×
1205
        hit = bp_torso;
1206
    } else if( one_in( 2 ) ) {
×
1207
        if( one_in( 2 ) ) {
×
1208
            hit = bp_foot_l;
1209
        } else {
1210
            hit = bp_foot_r;
×
1211
        }
1212
    }
1213
    return hit;
×
1214
}
1215

1216
bool mattack::growplants( monster *z )
×
1217
{
1218
    for( const auto &p : g->m.points_in_radius( z->pos(), 3 ) ) {
×
1219
        // TODO: Make this sensible - it can destroy EVERYTHING
1220
        if( !g->m.has_flag( "DIGGABLE", p ) && one_in( 4 ) ) {
×
1221
            g->m.ter_set( p, t_dirt );
×
1222
            continue;
1223
        }
1224

1225
        if( g->m.is_bashable( p ) && one_in( 3 ) ) {
×
1226
            // Destroy everything
1227
            g->m.destroy( p );
×
1228
            // And then make the ground fertile
1229
            g->m.ter_set( p, t_dirtmound );
×
1230
            continue;
1231
        }
1232

1233
        // 1 in 4 chance to grow a tree
1234
        if( !one_in( 4 ) ) {
×
1235
            if( one_in( 3 ) ) {
×
1236
                // If no tree, perhaps underbrush
1237
                g->m.ter_set( p, t_underbrush );
×
1238
            }
1239

1240
            continue;
1241
        }
1242

1243
        // Grow a tree and pierce stuff with it
1244
        Creature *critter = g->critter_at( p );
×
1245
        // Don't grow under friends (and self)
1246
        if( critter != nullptr &&
×
1247
            z->attitude_to( *critter ) == Creature::A_FRIENDLY ) {
×
1248
            continue;
1249
        }
1250

1251
        g->m.ter_set( p, t_tree_young );
×
1252
        if( critter == nullptr || critter->uncanny_dodge() ) {
×
1253
            continue;
1254
        }
1255

1256
        const body_part hit = body_part_hit_by_plant();
×
1257
        //~ %s is bodypart name in accusative.
1258
        critter->add_msg_player_or_npc( m_bad,
1259
                                        _( "A tree bursts forth from the earth and pierces your %s!" ),
1260
                                        _( "A tree bursts forth from the earth and pierces <npcname>'s %s!" ),
1261
                                        body_part_name_accusative( hit ).c_str() );
×
1262
        critter->deal_damage( z, hit, damage_instance( DT_STAB, rng( 10, 30 ) ) );
×
1263
    }
1264

1265
    // 1 in 5 chance of making existing vegetation grow larger
1266
    if( !one_in( 5 ) ) {
×
1267
        return true;
1268
    }
1269
    for( const tripoint &p : g->m.points_in_radius( z->pos(), 5 ) ) {
×
1270
        const auto ter = g->m.ter( p );
×
1271
        if( ter != t_tree_young && ter != t_underbrush ) {
×
1272
            // Skip as soon as possible to avoid all the checks
1273
            continue;
×
1274
        }
1275

1276
        Creature *critter = g->critter_at( p );
×
1277
        if( critter != nullptr && z->attitude_to( *critter ) == Creature::A_FRIENDLY ) {
×
1278
            // Don't buff terrain below friends (and self)
1279
            continue;
1280
        }
1281

1282
        if( ter == t_tree_young ) {
×
1283
            // Young tree => tree
1284
            // TODO: Make this deal damage too - young tree can be walked on, tree can't
1285
            g->m.ter_set( p, t_tree );
×
1286
        } else if( ter == t_underbrush ) {
×
1287
            // Underbrush => young tree
1288
            g->m.ter_set( p, t_tree_young );
×
1289
            if( critter != nullptr && !critter->uncanny_dodge() ) {
×
1290
                const body_part hit = body_part_hit_by_plant();
×
1291
                //~ %s is bodypart name in accusative.
1292
                critter->add_msg_player_or_npc( m_bad,
1293
                                                _( "The underbrush beneath your feet grows and pierces your %s!" ),
1294
                                                _( "Underbrush grows into a tree, and it pierces <npcname>'s %s!" ),
1295
                                                body_part_name_accusative( hit ).c_str() );
×
1296
                critter->deal_damage( z, hit, damage_instance( DT_STAB, rng( 10, 30 ) ) );
×
1297
            }
1298
        }
1299
    }
1300

1301
    return true; // added during refactor, previously had no cooldown reset
×
1302
}
1303

1304
bool mattack::grow_vine( monster *z )
×
1305
{
1306
    if( z->friendly ) {
×
1307
        if( rl_dist( g->u.pos(), z->pos() ) <= 3 ) {
×
1308
            // Friendly vines keep the area around you free, so you can move.
1309
            return false;
1310
        }
1311
    }
1312
    z->moves -= 100;
×
1313
    int xshift = rng( 0, 2 ), yshift = rng( 0, 2 );
×
1314
    for( int x = 0; x < 3; x++ ) {
×
1315
        for( int y = 0; y < 3; y++ ) {
×
1316
            tripoint dest( z->posx() + ( x + xshift ) % 3 - 1,
×
1317
                           z->posy() + ( y + yshift ) % 3 - 1,
×
1318
                           z->posz() );
×
1319
            if( !g->is_empty( dest ) ) {
×
1320
                continue;
×
1321
            }
1322

1323
            if( monster *const vine = g->summon_mon( mon_creeper_vine, dest ) ) {
×
1324
                vine->make_ally( *z );
×
1325
            }
1326
        }
1327
    }
1328

1329
    return true;
1330
}
1331

1332
bool mattack::vine( monster *z )
×
1333
{
1334
    std::vector<tripoint> grow;
1335
    int vine_neighbors = 0;
×
1336
    z->moves -= 100;
×
1337
    for( const tripoint &dest : g->m.points_in_radius( z->pos(), 1 ) ) {
×
1338
        Creature *critter = g->critter_at( dest );
×
1339
        if( critter != nullptr && z->attitude_to( *critter ) == Creature::Attitude::A_HOSTILE ) {
×
1340
            if( critter->uncanny_dodge() ) {
×
1341
                return true;
1342
            }
1343

1344
            body_part bphit = critter->get_random_body_part();
×
1345
            critter->add_msg_player_or_npc( m_bad,
1346
                                            //~ 1$s monster name(vine), 2$s bodypart in accusative
1347
                                            _( "The %1$s lashes your %2$s!" ),
1348
                                            _( "The %1$s lashes <npcname>'s %2$s!" ),
1349
                                            z->name().c_str(),
×
1350
                                            body_part_name_accusative( bphit ).c_str() );
×
1351
            damage_instance d;
×
1352
            // TODO: Buff it to more "modern" numbers - 4+4 is nothing
1353
            d.add_damage( DT_CUT, 4 );
×
1354
            d.add_damage( DT_BASH, 4 );
×
1355
            critter->deal_damage( z, bphit, d );
×
1356
            critter->check_dead_state();
×
1357
            z->moves -= 100;
×
1358
            return true;
×
1359
        }
1360

1361
        if( g->is_empty( dest ) ) {
×
1362
            grow.push_back( dest );
×
1363
        } else if( monster *const z = g->critter_at<monster>( dest ) ) {
×
1364
            if( z->type->id == mon_creeper_vine ) {
×
1365
                vine_neighbors++;
×
1366
            }
1367
        }
1368
    }
1369
    // Calculate distance from nearest hub
1370
    int dist_from_hub = 999;
×
1371
    for( monster &critter : g->all_monsters() ) {
×
1372
        if( critter.type->id == mon_creeper_hub ) {
×
1373
            int dist = rl_dist( z->pos(), critter.pos() );
×
1374
            if( dist < dist_from_hub ) {
×
1375
                dist_from_hub = dist;
×
1376
            }
1377
        }
1378
    }
1379
    if( grow.empty() || vine_neighbors > 5 || one_in( 7 - vine_neighbors ) ||
×
1380
        !one_in( dist_from_hub ) ) {
×
1381
        return true;
1382
    }
1383
    const tripoint target = random_entry( grow );
×
1384
    if( monster *const vine = g->summon_mon( mon_creeper_vine, target ) ) {
×
1385
        vine->make_ally( *z );
×
1386
        vine->reset_special( "VINE" );
×
1387
    }
1388

1389
    return true;
1390
}
1391

1392
bool mattack::spit_sap( monster *z )
×
1393
{
1394
    if( !z->can_act() ) {
×
1395
        return false;
1396
    }
1397

1398
    Creature *target = z->attack_target();
×
1399
    if( target == nullptr ||
×
1400
        rl_dist( z->pos(), target->pos() ) > 12 ||
×
1401
        !z->sees( *target ) ) {
×
1402
        return false;
1403
    }
1404

1405
    z->moves -= 150;
×
1406

1407
    projectile proj;
×
1408
    proj.speed = 10;
×
1409
    proj.range = 12;
×
1410
    proj.proj_effects.insert( "APPLY_SAP" );
×
1411
    proj.impact.add_damage( DT_ACID, rng( 5, 10 ) );
×
1412
    projectile_attack( proj, z->pos(), target->pos(), { 150 }, z );
×
1413

1414
    return true;
×
1415
}
1416

1417
bool mattack::triffid_heartbeat( monster *z )
×
1418
{
1419
    sounds::sound( z->pos(), 14, _( "thu-THUMP." ) );
×
1420
    z->moves -= 300;
×
1421
    if( z->friendly != 0 ) {
×
1422
        return true;
1423
        // TODO: when friendly: open a way to the stairs, don't spawn monsters
1424
    }
1425
    if( g->u.posz() != z->posz() ) {
×
1426
        // Maybe remove this and allow spawning monsters above?
1427
        return true;
1428
    }
1429

1430
    static pathfinding_settings root_pathfind( 10, 20, 50, 0, false, false, false, false );
1431
    if( rl_dist( z->pos(), g->u.pos() ) > 5 &&
×
1432
        !g->m.route( g->u.pos(), z->pos(), root_pathfind ).empty() ) {
×
1433
        add_msg( m_warning, _( "The root walls creak around you." ) );
×
1434
        for( const tripoint &dest : g->m.points_in_radius( z->pos(), 3 ) ) {
×
1435
            if( g->is_empty( dest ) && one_in( 4 ) ) {
×
1436
                g->m.ter_set( dest, t_root_wall );
×
1437
            } else if( g->m.ter( dest ) == t_root_wall && one_in( 10 ) ) {
×
1438
                g->m.ter_set( dest, t_dirt );
×
1439
            }
1440
        }
1441
        // Open blank tiles as long as there's no possible route
1442
        int tries = 0;
×
1443
        while( g->m.route( g->u.pos(), z->pos(), root_pathfind ).empty() &&
×
1444
               tries < 20 ) {
1445
            int x = rng( g->u.posx(), z->posx() - 3 ), y = rng( g->u.posy(), z->posy() - 3 );
×
1446
            tripoint dest( x, y, z->posz() );
×
1447
            tries++;
×
1448
            g->m.ter_set( dest, t_dirt );
×
1449
            if( rl_dist( dest, g->u.pos() ) > 3 && g->num_creatures() < 30 &&
×
1450
                !g->critter_at( dest ) && one_in( 20 ) ) { // Spawn an extra monster
×
1451
                mtype_id montype = mon_triffid;
×
1452
                if( one_in( 4 ) ) {
×
1453
                    montype = mon_creeper_hub;
×
1454
                } else if( one_in( 3 ) ) {
×
1455
                    montype = mon_biollante;
×
1456
                }
1457
                if( monster *const plant = g->summon_mon( montype, dest ) ) {
×
1458
                    plant->make_ally( *z );
×
1459
                }
1460
            }
1461
        }
1462

1463
    } else { // The player is close enough for a fight!
1464

1465
        for( const tripoint &dest : g->m.points_in_radius( z->pos(), 1 ) ) {
×
1466
            if( g->is_empty( dest ) && one_in( 2 ) ) {
×
1467
                if( monster *const  triffid = g->summon_mon( mon_triffid, dest ) ) {
×
1468
                    triffid->make_ally( *z );
×
1469
                }
1470
            }
1471
        }
1472
    }
1473

1474
    return true;
1475
}
1476

1477
bool mattack::fungus( monster *z )
×
1478
{
1479
    // TODO: Infect NPCs?
1480
    z->moves -= 200;   // It takes a while
×
1481
    if( g->u.has_trait( trait_THRESH_MYCUS ) ) {
×
1482
        z->friendly = 100;
×
1483
    }
1484
    //~ the sound of a fungus releasing spores
1485
    sounds::sound( z->pos(), 10, _( "Pouf!" ) );
×
1486
    if( g->u.sees( *z ) ) {
×
1487
        add_msg( m_warning, _( "Spores are released from the %s!" ), z->name().c_str() );
×
1488
    }
1489

1490
    // Use less laggy methods of reproduction when there is a lot of mons around
1491
    double spore_chance = 0.25;
×
1492
    int radius = 1;
×
1493
    if( g->num_creatures() > 25 ) {
×
1494
        // Number of creatures in the bubble and the resulting average number of spores per "Pouf!":
1495
        // 0-25: 2
1496
        // 50  : 0.5
1497
        // 75  : 0.22
1498
        // 100 : 0.125
1499
        // Assuming all creatures in the bubble were fungaloids (unlikely), the average number of spores per generation:
1500
        // 25  : 50
1501
        // 50  : 25
1502
        // 75  : 17
1503
        // 100 : 13
1504
        spore_chance *= ( 25.0 / g->num_creatures() ) * ( 25.0 / g->num_creatures() );
×
1505
        if( x_in_y( g->num_creatures(), 100 ) ) {
×
1506
            // Don't make the increased radius spawn more spores
1507
            const double old_area = ( ( 2 * radius + 1 ) * ( 2 * radius + 1 ) ) - 1;
×
1508
            radius++;
×
1509
            const double new_area = ( ( 2 * radius + 1 ) * ( 2 * radius + 1 ) ) - 1;
×
1510
            spore_chance *= old_area / new_area;
×
1511
        }
1512
    }
1513

1514
    fungal_effects fe( *g, g->m );
×
1515
    for( const tripoint &sporep : g->m.points_in_radius( z->pos(), radius ) ) {
×
1516
        if( sporep == z->pos() ) {
×
1517
            continue;
1518
        }
1519
        const int dist = rl_dist( z->pos(), sporep );
×
1520
        if( !one_in( dist ) ||
×
1521
            g->m.impassable( sporep ) ||
×
1522
            ( dist > 1 && !g->m.clear_path( z->pos(), sporep, 2, 1, 10 ) ) ) {
×
1523
            continue;
1524
        }
1525

1526
        fe.fungalize( sporep, z, spore_chance );
×
1527
    }
1528

1529
    return true;
×
1530
}
1531

1532
bool mattack::fungus_corporate( monster *z )
×
1533
{
1534
    if( x_in_y( 1, 20 ) ) {
×
1535
        sounds::sound( z->pos(), 10, _( "\"Buy SpOreos(tm) now!\"" ) );
×
1536
        if( g->u.sees( *z ) ) {
×
1537
            add_msg( m_warning, _( "Delicious snacks are released from the %s!" ), z->name().c_str() );
×
1538
            g->m.add_item( z->pos(), item( "sporeos" ) );
×
1539
        } // only spawns SpOreos if the player is near; can't have the COMMONERS stealing our product from good customers
1540
        return true;
1541
    } else {
1542
        return fungus( z );
×
1543
    }
1544
}
1545

1546
bool mattack::fungus_haze( monster *z )
×
1547
{
1548
    //~ That spore sound again
1549
    sounds::sound( z->pos(), 10, _( "Pouf!" ) );
×
1550
    if( g->u.sees( *z ) ) {
×
1551
        add_msg( m_info, _( "The %s pulses, and fresh fungal material bursts forth." ), z->name().c_str() );
×
1552
    }
1553
    z->moves -= 150;
×
1554
    for( const tripoint &dest : g->m.points_in_radius( z->pos(), 3 ) ) {
×
1555
        g->m.add_field( dest, fd_fungal_haze, rng( 1, 2 ) );
×
1556
    }
1557

1558
    return true;
×
1559
}
1560

1561
bool mattack::fungus_big_blossom( monster *z )
×
1562
{
1563
    bool firealarm = false;
×
1564
    const auto u_see = g->u.sees( *z );
×
1565
    // Fungal fire-suppressor! >:D
1566
    for( const tripoint &dest : g->m.points_in_radius( z->pos(), 6 ) ) {
×
1567
        if( g->m.get_field_strength( dest, fd_fire ) != 0 ) {
×
1568
            firealarm = true;
×
1569
        }
1570
        if( firealarm ) {
×
1571
            g->m.remove_field( dest, fd_fire );
×
1572
            g->m.remove_field( dest, fd_smoke );
×
1573
            g->m.add_field( dest, fd_fungal_haze, 3 );
×
1574
        }
1575
    }
1576
    // Special effects handled outside the loop
1577
    if( firealarm ) {
×
1578
        if( u_see ) {
×
1579
            // Sucks up all the smoke
1580
            add_msg( m_warning, _( "The %s suddenly inhales!" ), z->name().c_str() );
×
1581
        }
1582
        //~Sound of a giant fungal blossom inhaling
1583
        sounds::sound( z->pos(), 20, _( "WOOOSH!" ) );
×
1584
        if( u_see ) {
×
1585
            add_msg( m_bad, _( "The %s discharges an immense flow of spores, smothering the flames!" ),
1586
                     z->name().c_str() );
×
1587
        }
1588
        //~Sound of a giant fungal blossom blowing out the dangerous fire!
1589
        sounds::sound( z->pos(), 20, _( "POUFF!" ) );
×
1590
        return true;
×
1591
    } else {
1592
        // No fire detected, routine haze-emission
1593
        //~ That spore sound, much louder
1594
        sounds::sound( z->pos(), 15, _( "POUF." ) );
×
1595
        if( u_see ) {
×
1596
            add_msg( m_info, _( "The %s pulses, and fresh fungal material bursts forth!" ), z->name().c_str() );
×
1597
        }
1598
        z->moves -= 150;
×
1599
        for( const tripoint &dest : g->m.points_in_radius( z->pos(), 12 ) ) {
×
1600
            g->m.add_field( dest, fd_fungal_haze, rng( 1, 2 ) );
×
1601
        }
1602
    }
1603

1604
    return true;
×
1605
}
1606

1607
bool mattack::fungus_inject( monster *z )
×
1608
{
1609
    Creature *target = &g->u; // For faster copy+paste
×
1610
    if( rl_dist( z->pos(), g->u.pos() ) > 1 ) {
×
1611
        return false;
1612
    }
1613

1614
    if( g->u.has_trait( trait_THRESH_MARLOSS ) || g->u.has_trait( trait_THRESH_MYCUS ) ) {
×
1615
        z->friendly = 1;
×
1616
        return true;
×
1617
    }
1618
    if( ( g->u.has_trait( trait_MARLOSS ) ) && ( g->u.has_trait( trait_MARLOSS_BLUE ) ) &&
×
1619
        !g->u.crossed_threshold() ) {
×
1620
        add_msg( m_info, _( "The %s seems to wave you toward the tower..." ), z->name().c_str() );
×
1621
        z->anger = 0;
×
1622
        return true;
×
1623
    }
1624
    if( z->friendly ) {
×
1625
        // TODO: attack other creatures, not just g->u, for now just skip the code below as it
1626
        // only attacks g->u but the monster is friendly.
1627
        return true;
1628
    }
1629
    add_msg( m_warning, _( "The %s jabs at you with a needlelike point!" ), z->name().c_str() );
×
1630
    z->moves -= 150;
×
1631

1632
    if( g->u.uncanny_dodge() ) {
×
1633
        return true;
1634
    }
1635

1636
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
1637
    if( dodge_check( z, target ) ) {
×
1638
        target->add_msg_player_or_npc( _( "You dodge it!" ),
1639
                                       _( "<npcname> dodges it!" ) );
×
1640
        target->on_dodge( z, z->type->melee_skill * 2 );
×
1641
        return true;
×
1642
    }
1643

1644
    body_part hit = target->get_random_body_part();
×
1645
    int dam = rng( 5, 11 );
×
1646
    dam = g->u.deal_damage( z, hit, damage_instance( DT_CUT, dam ) ).total_damage();
×
1647

1648
    if( dam > 0 ) {
×
1649
        //~ 1$s is monster name, 2$s bodypart in accusative
1650
        add_msg( m_bad, _( "The %1$s sinks its point into your %2$s!" ), z->name().c_str(),
×
1651
                 body_part_name_accusative( hit ).c_str() );
×
1652

1653
        if( one_in( 10 - dam ) ) {
×
1654
            g->u.add_effect( effect_fungus, 10_minutes, num_bp, true );
×
1655
            add_msg( m_warning, _( "You feel thousands of live spores pumping into you..." ) );
×
1656
        }
1657
    } else {
1658
        //~ 1$s is monster name, 2$s bodypart in accusative
1659
        add_msg( _( "The %1$s strikes your %2$s, but your armor protects you." ), z->name().c_str(),
×
1660
                 body_part_name_accusative( hit ).c_str() );
×
1661
    }
1662

1663
    target->on_hit( z, hit,  z->type->melee_skill );
×
1664
    g->u.check_dead_state();
×
1665

1666
    return true;
×
1667
}
1668

1669
bool mattack::fungus_bristle( monster *z )
×
1670
{
1671
    if( g->u.has_trait( trait_THRESH_MARLOSS ) || g->u.has_trait( trait_THRESH_MYCUS ) ) {
×
1672
        z->friendly = 1;
×
1673
    }
1674
    Creature *target = z->attack_target();
×
1675
    if( target == nullptr ||
×
1676
        !is_adjacent( z, target, true ) ||
×
1677
        !z->sees( *target ) ) {
×
1678
        return false;
1679
    }
1680

1681
    auto msg_type = target == &g->u ? m_warning : m_neutral;
×
1682

1683
    add_msg( msg_type, _( "The %1$s swipes at %2$s with a barbed tendril!" ), z->name().c_str(),
×
1684
             target->disp_name().c_str() );
×
1685
    z->moves -= 150;
×
1686

1687
    if( target->uncanny_dodge() ) {
×
1688
        return true;
1689
    }
1690

1691
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
1692
    if( dodge_check( z, target ) ) {
×
1693
        target->add_msg_player_or_npc( _( "You dodge it!" ),
1694
                                       _( "<npcname> dodges it!" ) );
×
1695
        target->on_dodge( z, z->type->melee_skill * 2 );
×
1696
        return true;
×
1697
    }
1698

1699
    body_part hit = target->get_random_body_part();
×
1700
    int dam = rng( 7, 16 );
×
1701
    dam = target->deal_damage( z, hit, damage_instance( DT_CUT, dam ) ).total_damage();
×
1702

1703
    if( dam > 0 ) {
×
1704
        //~ 1$s is monster name, 2$s bodypart in accusative
1705
        target->add_msg_if_player( m_bad, _( "The %1$s sinks several needlelike barbs into your %2$s!" ),
1706
                                   z->name().c_str(),
×
1707
                                   body_part_name_accusative( hit ).c_str() );
×
1708

1709
        if( one_in( 15 - dam ) ) {
×
1710
            target->add_effect( effect_fungus, 20_minutes, num_bp, true );
×
1711
            target->add_msg_if_player( m_warning,
1712
                                       _( "You feel thousands of live spores pumping into you..." ) );
×
1713
        }
1714
    } else {
1715
        //~ 1$s is monster name, 2$s bodypart in accusative
1716
        target->add_msg_if_player( _( "The %1$s slashes your %2$s, but your armor protects you." ),
1717
                                   z->name().c_str(),
×
1718
                                   body_part_name_accusative( hit ).c_str() );
×
1719
    }
1720

1721
    target->on_hit( z, hit,  z->type->melee_skill );
×
1722

1723
    return true;
×
1724
}
1725

1726
bool mattack::fungus_growth( monster *z )
×
1727
{
1728
    // Young fungaloid growing into an adult
1729
    if( g->u.sees( *z ) ) {
×
1730
        add_msg( m_warning, _( "The %s grows into an adult!" ),
1731
                 z->name().c_str() );
×
1732
    }
1733

1734
    z->poly( mon_fungaloid );
×
1735

1736
    return false;
×
1737
}
1738

1739
bool mattack::fungus_sprout( monster *z )
×
1740
{
1741
    bool push_player = false; // To avoid map shift weirdness
×
1742
    for( const tripoint &dest : g->m.points_in_radius( z->pos(), 1 ) ) {
×
1743
        if( g->u.pos() == dest ) {
×
1744
            push_player = true;
×
1745
        }
1746
        if( g->is_empty( dest ) ) {
×
1747
            if( monster *const wall = g->summon_mon( mon_fungal_wall, dest ) ) {
×
1748
                wall->make_ally( *z );
×
1749
            }
1750
        }
1751
    }
1752

1753
    if( push_player ) {
×
1754
        const int angle = g->m.coord_to_angle( z->posx(), z->posy(), g->u.posx(), g->u.posy() );
×
1755
        add_msg( m_bad, _( "You're shoved away as a fungal wall grows!" ) );
×
1756
        g->fling_creature( &g->u, angle, rng( 10, 50 ) );
×
1757
    }
1758

1759
    return true;
×
1760
}
1761

1762
bool mattack::fungus_fortify( monster *z )
×
1763
{
1764
    if( z->friendly ) {
×
1765
        return false; // TODO: handle friendly monsters
1766
    }
1767
    Creature *target = &g->u;
×
1768
    bool mycus = false;
×
1769
    bool peaceful = true;
×
1770
    if( g->u.has_trait( trait_THRESH_MARLOSS ) || g->u.has_trait( trait_THRESH_MYCUS ) ) {
×
1771
        mycus = true; //No nifty support effects.  Yet.  This lets it rebuild hedges.
×
1772
    }
1773
    if( ( g->u.has_trait( trait_MARLOSS ) ) && ( g->u.has_trait( trait_MARLOSS_BLUE ) ) &&
×
1774
        !g->u.crossed_threshold() && !mycus ) {
×
1775
        // You have the other two.  Is it really necessary for us to fight?
1776
        add_msg( m_info, _( "The %s spreads its tendrils.  It seems as though it's expecting you..." ),
1777
                 z->name().c_str() );
×
1778
        if( rl_dist( z->pos(), g->u.pos() ) < 3 ) {
×
1779
            if( query_yn( _( "The tower extends and aims several tendrils from its depths.  Hold still?" ) ) ) {
×
1780
                add_msg( m_warning,
1781
                         _( "The %s works several tendrils into your arms, legs, torso, and even neck..." ),
1782
                         z->name().c_str() );
×
1783
                g->u.hurtall( 1, z );
×
1784
                add_msg( m_warning,
1785
                         _( "You see a clear golden liquid pump through the tendrils--and then lose consciousness." ) );
×
1786
                g->u.unset_mutation( trait_MARLOSS );
×
1787
                g->u.unset_mutation( trait_MARLOSS_BLUE );
×
1788
                g->u.set_mutation( trait_THRESH_MARLOSS );
×
1789
                g->m.ter_set( g->u.pos(),
×
1790
                              t_marloss ); // We only show you the door.  You walk through it on your own.
×
1791
                g->u.add_memorial_log( pgettext( "memorial_male", "Was shown to the Marloss Gateway." ),
1792
                                       pgettext( "memorial_female", "Was shown to the Marloss Gateway." ) );
×
1793
                g->u.add_msg_if_player( m_good,
1794
                                        _( "You wake up in a marloss bush.  Almost *cradled* in it, actually, as though it grew there for you." ) );
×
1795
                //~ Beginning to hear the Mycus while conscious: this is it speaking
1796
                g->u.add_msg_if_player( m_good,
1797
                                        _( "assistance, on an arduous quest. unity. together we have reached the door. now to pass through..." ) );
×
1798
                return true;
×
1799
            } else {
1800
                peaceful = false; // You declined the offer.  Fight!
1801
            }
1802
        }
1803
    } else {
1804
        peaceful = false; // You weren't eligible.  Fight!
1805
    }
1806

1807
    bool fortified = false;
×
1808
    bool push_player = false; // To avoid map shift weirdness
×
1809
    for( const tripoint &dest : g->m.points_in_radius( z->pos(), 1 ) ) {
×
1810
        if( g->u.pos() == dest ) {
×
1811
            push_player = true;
×
1812
        }
1813
        if( g->is_empty( dest ) ) {
×
1814
            if( monster *const wall = g->summon_mon( mon_fungal_hedgerow, dest ) ) {
×
1815
                wall->make_ally( *z );
×
1816
            }
1817
            fortified = true;
1818
        }
1819
    }
1820
    if( push_player ) {
×
1821
        add_msg( m_bad, _( "You're shoved away as a fungal hedgerow grows!" ) );
×
1822
        g->fling_creature( &g->u, g->m.coord_to_angle( z->posx(), z->posy(), g->u.posx(),
×
1823
                           g->u.posy() ), rng( 10, 50 ) );
×
1824
    }
1825
    if( fortified || mycus || peaceful ) {
×
1826
        return true;
1827
    }
1828

1829
    // TODO: De-playerize the whole block
1830
    const int dist = rl_dist( z->pos(), g->u.pos() );
×
1831
    if( dist >= 12 ) {
×
1832
        return false;
1833
    }
1834

1835
    if( dist > 3 ) {
×
1836
        // Oops, can't reach. ):
1837
        // How's about we spawn more tendrils? :)
1838
        // Aimed at the player, too?  Sure!
1839
        const tripoint hit_pos = target->pos() + point( rng( -1, 1 ), rng( -1, 1 ) );
×
1840
        if( hit_pos == target->pos() && !target->uncanny_dodge() ) {
×
1841
            const body_part hit = body_part_hit_by_plant();
×
1842
            //~ %s is bodypart name in accusative.
1843
            add_msg( m_bad, _( "A fungal tendril bursts forth from the earth and pierces your %s!" ),
1844
                     body_part_name_accusative( hit ).c_str() );
×
1845
            g->u.deal_damage( z, hit, damage_instance( DT_CUT, rng( 5, 11 ) ) );
×
1846
            g->u.check_dead_state();
×
1847
            // Probably doesn't have spores available *just* yet.  Let's be nice.
1848
        } else if( g->is_empty( hit_pos ) ) {
×
1849
            add_msg( m_bad, _( "A fungal tendril bursts forth from the earth!" ) );
×
1850
            if( monster *const tendril = g->summon_mon( mon_fungal_tendril, hit_pos ) ) {
×
1851
                tendril->make_ally( *z );
×
1852
            }
1853
        }
1854
        return true;
1855
    }
1856

1857
    add_msg( m_warning, _( "The %s takes aim, and spears at you with a massive tendril!" ),
1858
             z->name().c_str() );
×
1859
    z->moves -= 150;
×
1860

1861
    if( g->u.uncanny_dodge() ) {
×
1862
        return true;
1863
    }
1864
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
1865
    if( dodge_check( z, target ) ) {
×
1866
        target->add_msg_player_or_npc( _( "You dodge it!" ),
1867
                                       _( "<npcname> dodges it!" ) );
×
1868
        target->on_dodge( z, z->type->melee_skill * 2 );
×
1869
        return true;
×
1870
    }
1871

1872
    // TODO: 21 damage with no chance to critical isn't scary
1873
    body_part hit = target->get_random_body_part();
×
1874
    int dam = rng( 15, 21 );
×
1875
    dam = g->u.deal_damage( z, hit, damage_instance( DT_STAB, dam ) ).total_damage();
×
1876

1877
    if( dam > 0 ) {
×
1878
        //~ 1$s is monster name, 2$s bodypart in accusative
1879
        add_msg( m_bad, _( "The %1$s sinks its point into your %2$s!" ), z->name().c_str(),
×
1880
                 body_part_name_accusative( hit ).c_str() );
×
1881
        g->u.add_effect( effect_fungus, 40_minutes, num_bp, true );
×
1882
        add_msg( m_warning, _( "You feel millions of live spores pumping into you..." ) );
×
1883
    } else {
1884
        //~ 1$s is monster name, 2$s bodypart in accusative
1885
        add_msg( _( "The %1$s strikes your %2$s, but your armor protects you." ), z->name().c_str(),
×
1886
                 body_part_name_accusative( hit ).c_str() );
×
1887
    }
1888

1889
    target->on_hit( z, hit,  z->type->melee_skill );
×
1890
    g->u.check_dead_state();
×
1891
    return true;
×
1892
}
1893

1894
bool mattack::impale( monster *z )
×
1895
{
1896
    if( !z->can_act() ) {
×
1897
        return false;
1898
    }
1899
    Creature *target = z->attack_target();
×
1900
    if( target == nullptr || !is_adjacent( z, target, false ) ) {
×
1901
        return false;
1902
    }
1903

1904
    z->moves -= 80;
×
1905
    bool uncanny = target->uncanny_dodge();
×
1906
    if( uncanny || dodge_check( z, target ) ) {
×
1907
        auto msg_type = target == &g->u ? m_warning : m_info;
×
1908
        target->add_msg_player_or_npc( msg_type, _( "The %s lunges at you, but you dodge!" ),
1909
                                       _( "The %s lunges at <npcname>, but they dodge!" ),
1910
                                       z->name().c_str() );
×
1911
        if( !uncanny ) {
×
1912
            target->on_dodge( z, z->type->melee_skill * 2 );
×
1913
        }
1914

1915
        return true;
1916
    }
1917

1918
    int dam = target->deal_damage( z, bp_torso, damage_instance( DT_STAB, rng( 10, 20 ), rng( 5, 15 ),
×
1919
                                   .5 ) ).total_damage();
×
1920
    if( dam > 0 ) {
×
1921
        auto msg_type = target == &g->u ? m_bad : m_info;
×
1922
        //~ 1$s is monster name, 2$s bodypart in accusative
1923
        target->add_msg_player_or_npc( msg_type,
1924
                                       _( "The %1$s impales your torso!" ),
1925
                                       _( "The %1$s impales <npcname>'s torso!" ),
1926
                                       z->name().c_str() );
×
1927

1928
        target->on_hit( z, bp_torso,  z->type->melee_skill );
×
1929
        if( one_in( 60 / ( dam + 20 ) ) ) {
×
1930
            target->add_effect( effect_bleed, rng( 75_turns, 125_turns ), bp_torso, true );
×
1931
        }
1932

1933
        if( rng( 0, 200 + dam ) > 100 ) {
×
1934
            target->add_effect( effect_downed, 3_turns );
×
1935
        }
1936
        z->moves -= 80; //Takes extra time for the creature to pull out the protrusion
×
1937
    } else {
1938
        target->add_msg_player_or_npc(
1939
            _( "The %1$s tries to impale your torso, but fails to penetrate your armor!" ),
1940
            _( "The %1$s tries to impale <npcname>'s torso, but fails to penetrate their armor!" ),
1941
            z->name().c_str() );
×
1942
    }
1943

1944
    target->check_dead_state();
×
1945

1946
    return true;
×
1947
}
1948

1949
bool mattack::dermatik( monster *z )
×
1950
{
1951
    if( !z->can_act() ) {
×
1952
        return false;
1953
    }
1954

1955
    Creature *target = z->attack_target();
×
1956
    if( target == nullptr ||
×
1957
        !is_adjacent( z, target, true ) ||
×
1958
        !z->sees( *target ) ) {
×
1959
        return false;
1960
    }
1961

1962
    if( target->uncanny_dodge() ) {
×
1963
        return true;
1964
    }
1965
    player *foe = dynamic_cast< player * >( target );
×
1966
    if( foe == nullptr ) {
×
1967
        return true; // No implanting monsters for now
1968
    }
1969
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
1970
    if( dodge_check( z, target ) ) {
×
1971
        if( target == &g->u ) {
×
1972
            add_msg( _( "The %s tries to land on you, but you dodge." ), z->name().c_str() );
×
1973
        }
1974
        z->stumble();
×
1975
        target->on_dodge( z, z->type->melee_skill * 2 );
×
1976
        return true;
×
1977
    }
1978

1979
    // Can we swat the bug away?
1980
    int dodge_roll = z->dodge_roll();
×
1981
    ///\EFFECT_MELEE increases chance to deflect dermatik attack
1982

1983
    ///\EFFECT_UNARMED increases chance to deflect dermatik attack
1984
    int swat_skill = ( foe->get_skill_level( skill_melee ) + foe->get_skill_level(
×
1985
                           skill_unarmed ) * 2 ) / 3;
×
1986
    int player_swat = dice( swat_skill, 10 );
×
1987
    if( foe->has_trait( trait_TAIL_CATTLE ) ) {
×
1988
        target->add_msg_if_player( _( "You swat at the %s with your tail!" ), z->name().c_str() );
×
1989
        ///\EFFECT_DEX increases chance of deflecting dermatik attack with TAIL_CATTLE
1990

1991
        ///\EFFECT_UNARMED increases chance of deflecting dermatik attack with TAIL_CATTLE
1992
        player_swat += ( ( foe->dex_cur + foe->get_skill_level( skill_unarmed ) ) / 2 );
×
1993
    }
1994
    if( player_swat > dodge_roll ) {
×
1995
        target->add_msg_if_player( _( "The %s lands on you, but you swat it off." ), z->name().c_str() );
×
1996
        if( z->get_hp() >= z->get_hp_max() / 2 ) {
×
1997
            z->apply_damage( &g->u, bp_torso, 1 );
×
1998
            z->check_dead_state();
×
1999
        }
2000
        if( player_swat > dodge_roll * 1.5 ) {
×
2001
            z->stumble();
×
2002
        }
2003
        return true;
2004
    }
2005

2006
    // Can the bug penetrate our armor?
2007
    body_part targeted = target->get_random_body_part();
×
2008
    if( 4 < g->u.get_armor_cut( targeted ) / 3 ) {
×
2009
        //~ 1$s monster name(dermatik), 2$s bodypart name in accusative.
2010
        target->add_msg_if_player( _( "The %1$s lands on your %2$s, but can't penetrate your armor." ),
2011
                                   z->name().c_str(), body_part_name_accusative( targeted ).c_str() );
×
2012
        z->moves -= 150; // Attempted laying takes a while
×
2013
        return true;
×
2014
    }
2015

2016
    // Success!
2017
    z->moves -= 500; // Successful laying takes a long time
×
2018
    //~ 1$s monster name(dermatik), 2$s bodypart name in accusative.
2019
    target->add_msg_if_player( m_bad, _( "The %1$s sinks its ovipositor into your %2$s!" ),
2020
                               z->name().c_str(),
×
2021
                               body_part_name_accusative( targeted ).c_str() );
×
2022
    if( !foe->has_trait( trait_PARAIMMUNE ) || !foe->has_trait( trait_ACIDBLOOD ) ) {
×
2023
        foe->add_effect( effect_dermatik, 1_turns, targeted, true );
×
2024
        foe->add_memorial_log( pgettext( "memorial_male", "Injected with dermatik eggs." ),
2025
                               pgettext( "memorial_female", "Injected with dermatik eggs." ) );
×
2026
    }
2027

2028
    return true;
2029
}
2030

2031
bool mattack::dermatik_growth( monster *z )
×
2032
{
2033
    // Dermatik larva growing into an adult
2034
    if( g->u.sees( *z ) ) {
×
2035
        add_msg( m_warning, _( "The %s dermatik larva grows into an adult!" ),
2036
                 z->name().c_str() );
×
2037
    }
2038
    z->poly( mon_dermatik );
×
2039

2040
    return false;
×
2041
}
2042

2043
bool mattack::plant( monster *z )
×
2044
{
2045
    fungal_effects fe( *g, g->m );
×
2046
    // Spores taking seed and growing into a fungaloid
2047
    if( !fe.spread_fungus( z->pos() ) && one_in( 10 + g->num_creatures() / 5 ) ) {
×
2048
        if( g->u.sees( *z ) ) {
×
2049
            add_msg( m_warning, _( "The %s takes seed and becomes a young fungaloid!" ),
2050
                     z->name().c_str() );
×
2051
        }
2052

2053
        z->poly( mon_fungaloid_young );
×
2054
        z->moves -= 1000; // It takes a while
×
2055
        return false;
×
2056
    } else {
2057
        if( g->u.sees( *z ) ) {
×
2058
            add_msg( _( "The %s falls to the ground and bursts!" ),
2059
                     z->name().c_str() );
×
2060
        }
2061
        z->set_hp( 0 );
×
2062
        // Try fungifying once again
2063
        fe.spread_fungus( z->pos() );
×
2064
        return true;
2065
    }
2066
}
2067

2068
bool mattack::disappear( monster *z )
×
2069
{
2070
    z->set_hp( 0 );
×
2071
    return true;
×
2072
}
2073

2074
static void poly_keep_speed( monster &mon, const mtype_id &id )
2075
{
2076
    // Retain old speed after polymorph
2077
    // This prevents blobs regenerating speed through polymorphs
2078
    // and thus replicating indefinitely, covering entire map
2079
    const int old_speed = mon.get_speed_base();
×
2080
    mon.poly( id );
×
2081
    mon.set_speed_base( old_speed );
×
2082
}
2083

2084
static bool blobify( monster &blob, monster &target )
2085
{
2086
    if( g->u.sees( target ) ) {
×
2087
        add_msg( m_warning, _( "%s is engulfed by %s!" ),
2088
                 target.disp_name().c_str(), blob.disp_name().c_str() );
×
2089
    }
2090

2091
    switch( target.get_size() ) {
×
2092
        case MS_TINY:
2093
            // Just consume it
2094
            target.set_hp( 0 );
×
2095
            blob.set_speed_base( blob.get_speed_base() + 5 );
×
2096
            return false;
×
2097
        case MS_SMALL:
2098
            target.poly( mon_blob_small );
×
2099
            break;
×
2100
        case MS_MEDIUM:
2101
            target.poly( mon_blob );
×
2102
            break;
×
2103
        case MS_LARGE:
2104
            target.poly( mon_blob_large );
×
2105
            break;
×
2106
        case MS_HUGE:
2107
            // No polymorphing huge stuff
2108
            target.add_effect( effect_slimed, rng( 2_turns, 10_turns ) );
×
2109
            break;
×
2110
        default:
2111
            debugmsg( "Tried to blobify %s with invalid size: %d",
×
2112
                      target.disp_name().c_str(), static_cast<int>( target.get_size() ) );
×
2113
            return false;
×
2114
    }
2115

2116
    target.make_ally( blob );
×
2117
    return true;
×
2118
}
2119

2120
bool mattack::formblob( monster *z )
×
2121
{
2122
    if( z->friendly ) {
×
2123
        return false; // TODO: handle friendly monsters
2124
    }
2125

2126
    bool didit = false;
×
2127
    auto pts = closest_tripoints_first( 1, z->pos() );
×
2128
    // Don't check own tile
2129
    pts.erase( pts.begin() );
×
2130
    for( const tripoint &dest : pts ) {
×
2131
        Creature *critter = g->critter_at( dest );
×
2132
        if( critter == nullptr ) {
×
2133
            if( z->get_speed_base() > 85 && rng( 0, 250 ) < z->get_speed_base() ) {
×
2134
                // If we're big enough, spawn a baby blob.
2135
                didit = true;
×
2136
                z->set_speed_base( z->get_speed_base() - 15 );
×
2137
                if( monster *const blob = g->summon_mon( mon_blob_small, dest ) ) {
×
2138
                    blob->make_ally( *z );
×
2139
                }
2140

2141
                break;
2142
            }
2143

2144
            continue;
2145
        }
2146

2147
        if( critter->is_player() || critter->is_npc() ) {
×
2148
            // If we hit the player or some NPC, cover them with slime
2149
            didit = true;
×
2150
            // TODO: Add some sort of a resistance/dodge roll
2151
            critter->add_effect( effect_slimed, rng( 0_turns, 1_turns * z->get_hp() ) );
×
2152
            break;
2153
        }
2154

2155
        monster &othermon = *( dynamic_cast<monster *>( critter ) );
×
2156
        // Hit a monster.  If it's a blob, give it our speed.  Otherwise, blobify it?
2157
        if( z->get_speed_base() > 40 && othermon.type->in_species( BLOB ) ) {
×
2158
            if( othermon.type->id == mon_blob_brain ) {
×
2159
                // Brain blobs don't get sped up, they heal at the cost of the other blob.
2160
                // But only if they are hurt badly.
2161
                if( othermon.get_hp() < othermon.get_hp_max() / 2 ) {
×
2162
                    didit = true;
×
2163
                    othermon.heal( z->get_speed_base(), true );
×
2164
                    z->set_hp( 0 );
×
2165
                    return true;
2166
                }
2167
                continue;
2168
            }
2169
            didit = true;
×
2170
            othermon.set_speed_base( othermon.get_speed_base() + 5 );
×
2171
            z->set_speed_base( z->get_speed_base() - 5 );
×
2172
            if( othermon.type->id == mon_blob_small && othermon.get_speed_base() >= 60 ) {
×
2173
                poly_keep_speed( othermon, mon_blob );
×
2174
            } else if( othermon.type->id == mon_blob && othermon.get_speed_base() >= 80 ) {
×
2175
                poly_keep_speed( othermon, mon_blob_large );
×
2176
            }
2177
        } else if( ( othermon.made_of( material_id( "flesh" ) ) ||
×
2178
                     othermon.made_of( material_id( "veggy" ) ) ||
×
2179
                     othermon.made_of( material_id( "iflesh" ) ) ) &&
×
2180
                   rng( 0, z->get_hp() ) > rng( othermon.get_hp() / 2, othermon.get_hp() ) ) {
×
2181
            didit = blobify( *z, othermon );
×
2182
        }
2183
    }
2184

2185
    if( didit ) { // We did SOMEthing.
×
2186
        if( z->type->id == mon_blob && z->get_speed_base() <= 50 ) {
×
2187
            // We shrank!
2188
            poly_keep_speed( *z, mon_blob_small );
×
2189
        } else if( z->type->id == mon_blob_large && z->get_speed_base() <= 70 ) {
×
2190
            // We shrank!
2191
            poly_keep_speed( *z, mon_blob );
×
2192
        }
2193

2194
        z->moves = 0;
×
2195
        return true;
×
2196
    }
2197

2198
    return true; // consider returning false to try again immediately if nothing happened?
2199
}
2200

2201
bool mattack::callblobs( monster *z )
×
2202
{
2203
    if( z->friendly ) {
×
2204
        return false; // TODO: handle friendly monsters
2205
    }
2206
    // The huge brain blob interposes other blobs between it and any threat.
2207
    // For the moment just target the player, this gets a bit more complicated
2208
    // if we want to deal with NPCS and friendly monsters as well.
2209
    // The strategy is to send about 1/3 of the available blobs after the player,
2210
    // and keep the rest near the brain blob for protection.
2211
    tripoint enemy = g->u.pos();
×
2212
    std::list<monster *> allies;
2213
    std::vector<tripoint> nearby_points = closest_tripoints_first( 3, z->pos() );
×
2214
    for( monster &candidate : g->all_monsters() ) {
×
2215
        if( candidate.type->in_species( BLOB ) && candidate.type->id != mon_blob_brain ) {
×
2216
            // Just give the allies consistent assignments.
2217
            // Don't worry about trying to make the orders optimal.
2218
            allies.push_back( &candidate );
×
2219
        }
2220
    }
2221
    // 1/3 of the available blobs, unless they would fill the entire area near the brain.
2222
    const int num_guards = std::min( allies.size() / 3, nearby_points.size() );
×
2223
    int guards = 0;
×
2224
    for( std::list<monster *>::iterator ally = allies.begin();
×
2225
         ally != allies.end(); ++ally, ++guards ) {
×
2226
        tripoint post = enemy;
×
2227
        if( guards < num_guards ) {
×
2228
            // Each guard is assigned a spot in the nearby_points vector based on their order.
2229
            int assigned_spot = ( nearby_points.size() * guards ) / num_guards;
×
2230
            post = nearby_points[ assigned_spot ];
×
2231
        }
2232
        ( *ally )->set_dest( post );
×
2233
        if( !( *ally )->has_effect( effect_controlled ) ) {
×
2234
            ( *ally )->add_effect( effect_controlled, 1_turns, num_bp, true );
×
2235
        }
2236
    }
2237
    // This is telepathy, doesn't take any moves.
2238

2239
    return true;
×
2240
}
2241

2242
bool mattack::jackson( monster *z )
×
2243
{
2244
    // Jackson draws nearby zombies into the dance.
2245
    std::list<monster *> allies;
2246
    std::vector<tripoint> nearby_points = closest_tripoints_first( 3, z->pos() );
×
2247
    for( monster &candidate : g->all_monsters() ) {
×
2248
        if( candidate.type->in_species( ZOMBIE ) && candidate.type->id != mon_zombie_jackson ) {
×
2249
            // Just give the allies consistent assignments.
2250
            // Don't worry about trying to make the orders optimal.
2251
            allies.push_back( &candidate );
×
2252
        }
2253
    }
2254
    const int num_dancers = std::min( allies.size(), nearby_points.size() );
×
2255
    int dancers = 0;
×
2256
    bool converted = false;
×
2257
    for( auto ally = allies.begin(); ally != allies.end(); ++ally, ++dancers ) {
×
2258
        tripoint post = z->pos();
×
2259
        if( dancers < num_dancers ) {
×
2260
            // Each dancer is assigned a spot in the nearby_points vector based on their order.
2261
            int assigned_spot = ( nearby_points.size() * dancers ) / num_dancers;
×
2262
            post = nearby_points[ assigned_spot ];
×
2263
        }
2264
        if( ( *ally )->type->id != mon_zombie_dancer ) {
×
2265
            ( *ally )->poly( mon_zombie_dancer );
×
2266
            converted = true;
2267
        }
2268
        ( *ally )->set_dest( post );
×
2269
        if( !( *ally )->has_effect( effect_controlled ) ) {
×
2270
            ( *ally )->add_effect( effect_controlled, 1_turns, num_bp, true );
×
2271
        }
2272
    }
2273
    // Did we convert anybody?
2274
    if( converted ) {
×
2275
        if( g->u.sees( *z ) ) {
×
2276
            add_msg( m_warning, _( "The %s lets out a high-pitched cry!" ), z->name().c_str() );
×
2277
        }
2278
    }
2279
    // This is telepathy, doesn't take any moves.
2280
    return true;
×
2281
}
2282

2283
bool mattack::dance( monster *z )
×
2284
{
2285
    if( g->u.sees( *z ) ) {
×
2286
        switch( rng( 1, 10 ) ) {
×
2287
            case 1:
2288
                add_msg( m_neutral, _( "The %s swings its arms from side to side!" ), z->name().c_str() );
×
2289
                break;
×
2290
            case 2:
2291
                add_msg( m_neutral, _( "The %s does some fancy footwork!" ), z->name().c_str() );
×
2292
                break;
×
2293
            case 3:
2294
                add_msg( m_neutral, _( "The %s shrugs its shoulders!" ), z->name().c_str() );
×
2295
                break;
×
2296
            case 4:
2297
                add_msg( m_neutral, _( "The %s spins in place!" ), z->name().c_str() );
×
2298
                break;
×
2299
            case 5:
2300
                add_msg( m_neutral, _( "The %s crouches on the ground!" ), z->name().c_str() );
×
2301
                break;
×
2302
            case 6:
2303
                add_msg( m_neutral, _( "The %s looks left and right!" ), z->name().c_str() );
×
2304
                break;
×
2305
            case 7:
2306
                add_msg( m_neutral, _( "The %s jumps back and forth!" ), z->name().c_str() );
×
2307
                break;
×
2308
            case 8:
2309
                add_msg( m_neutral, _( "The %s raises its arms in the air!" ), z->name().c_str() );
×
2310
                break;
×
2311
            case 9:
2312
                add_msg( m_neutral, _( "The %s swings its hips!" ), z->name().c_str() );
×
2313
                break;
×
2314
            case 10:
2315
                add_msg( m_neutral, _( "The %s claps!" ), z->name().c_str() );
×
2316
                break;
×
2317
        }
2318
    }
2319

2320
    return true;
×
2321
}
2322

2323
bool mattack::dogthing( monster *z )
×
2324
{
2325
    if( z == nullptr ) {
×
2326
        return false; // TODO: replace pointers with references
2327
    }
2328

2329
    if( !one_in( 3 ) || !g->u.sees( *z ) ) {
×
2330
        return false;
2331
    }
2332

2333
    add_msg( _( "The %s's head explodes in a mass of roiling tentacles!" ),
2334
             z->name().c_str() );
×
2335

2336
    g->m.add_splash( z->bloodType(), z->pos(), 2, 3 );
×
2337

2338
    z->friendly = 0;
×
2339
    z->poly( mon_headless_dog_thing );
×
2340

2341
    return false;
×
2342
}
2343

2344
bool mattack::tentacle( monster *z )
×
2345
{
2346
    if( z->friendly ) {
×
2347
        return false; // TODO: handle friendly monsters
2348
    }
2349
    Creature *target = &g->u;
×
2350
    if( !z->sees( g->u ) ) {
×
2351
        return false;
2352
    }
2353
    add_msg( m_bad, _( "The %s lashes its tentacle at you!" ), z->name().c_str() );
×
2354
    z->moves -= 100;
×
2355

2356
    if( g->u.uncanny_dodge() ) {
×
2357
        return true;
2358
    }
2359
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
2360
    if( dodge_check( z, target ) ) {
×
2361
        target->add_msg_player_or_npc( _( "You dodge it!" ),
2362
                                       _( "<npcname> dodges it!" ) );
×
2363
        target->on_dodge( z, z->type->melee_skill * 2 );
×
2364
        return true;
2365
    }
2366

2367
    body_part hit = target->get_random_body_part();
×
2368
    int dam = rng( 10, 20 );
×
2369
    //~ 1$s is bodypart name, 2$d is damage value.
2370
    add_msg( m_bad, _( "Your %1$s is hit for %2$d damage!" ), body_part_name( hit ).c_str(), dam );
×
2371
    g->u.deal_damage( z, hit, damage_instance( DT_BASH, dam ) );
×
2372
    target->on_hit( z, hit,  z->type->melee_skill );
×
2373
    g->u.check_dead_state();
×
2374

2375
    return true;
2376
}
2377

2378
bool mattack::ranged_pull( monster *z )
×
2379
{
2380
    Creature *target = z->attack_target();
×
2381
    if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 3 ||
×
2382
        rl_dist( z->pos(), target->pos() ) <= 1 || !z->sees( *target ) ) {
×
2383
        return false;
2384
    }
2385

2386
    player *foe = dynamic_cast< player * >( target );
×
2387
    std::vector<tripoint> line = g->m.find_clear_path( z->pos(), target->pos() );
×
2388
    bool seen = g->u.sees( *z );
×
2389

2390
    for( auto &i : line ) {
×
2391
        // Player can't be pulled though bars, furniture, cars or creatures
2392
        // TODO: Add bashing? Currently a window is enough to prevent grabbing
2393
        if( !g->is_empty( i ) && i != z->pos() && i != target->pos() ) {
×
2394
            return false;
2395
        }
2396
    }
2397

2398
    z->moves -= 150;
×
2399

2400
    const bool uncanny = target->uncanny_dodge();
×
2401
    if( uncanny || dodge_check( z, target ) ) {
×
2402
        z->moves -= 200;
×
2403
        auto msg_type = foe == &g->u ? m_warning : m_info;
×
2404
        target->add_msg_player_or_npc( msg_type, _( "The %s's arms fly out at you, but you dodge!" ),
2405
                                       _( "The %s's arms fly out at <npcname>, but they dodge!" ),
2406
                                       z->name().c_str() );
×
2407

2408
        if( !uncanny ) {
×
2409
            target->on_dodge( z, z->type->melee_skill * 2 );
×
2410
        }
2411

2412
        return true;
2413
    }
2414

2415
    // Limit the range in case some weird math thing would cause the target to fly past us
2416
    int range = std::min( ( z->type->melee_sides * z->type->melee_dice ) / 10,
×
2417
                          rl_dist( z->pos(), target->pos() ) + 1 );
×
2418
    tripoint pt = target->pos();
×
2419
    while( range > 0 ) {
×
2420
        // Recalculate the ray each step
2421
        // We can't depend on either the target position being constant (obviously),
2422
        // but neither on z pos staying constant, because we may want to shift the map mid-pull
2423
        const int dir = g->m.coord_to_angle( target->posx(), target->posy(), z->posx(), z->posy() );
×
2424
        tileray tdir( dir );
×
2425
        tdir.advance();
×
2426
        pt.x = target->posx() + tdir.dx();
×
2427
        pt.y = target->posy() + tdir.dy();
×
2428
        if( !g->is_empty( pt ) ) { //Cancel the grab if the space is occupied by something
×
2429
            break;
2430
        }
2431

2432
        if( foe != nullptr ) {
×
2433
            if( foe->in_vehicle ) {
×
2434
                g->m.unboard_vehicle( foe->pos() );
×
2435
            }
2436

2437
            if( target->is_player() && ( pt.x < SEEX * int( MAPSIZE / 2 ) || pt.y < SEEY * int( MAPSIZE / 2 ) ||
×
2438
                                         pt.x >= SEEX * ( 1 + int( MAPSIZE / 2 ) ) || pt.y >= SEEY * ( 1 + int( MAPSIZE / 2 ) ) ) ) {
×
2439
                g->update_map( pt.x, pt.y );
×
2440
            }
2441
        }
2442

2443
        target->setpos( pt );
×
2444
        range--;
×
2445
        if( target->is_player() && seen ) {
×
2446
            g->draw();
×
2447
        }
2448
    }
2449
    if( seen ) {
×
2450
        add_msg( _( "The %1$s's arms fly out and pull and grab %2$s!" ), z->name().c_str(),
×
2451
                 target->disp_name().c_str() );
×
2452
    }
2453

2454
    const int prev_effect = target->get_effect_int( effect_grabbed );
×
2455
    target->add_effect( effect_grabbed, 2_turns, bp_torso, false,
2456
                        prev_effect + 4 ); //Duration needs to be at least 2, or grab will immediately be removed
×
2457

2458
    return true;
2459
}
2460

2461
bool mattack::grab( monster *z )
5,991✔
2462
{
2463
    if( !z->can_act() ) {
5,991✔
2464
        return false;
2465
    }
2466
    Creature *target = z->attack_target();
5,988✔
2467
    if( target == nullptr || !is_adjacent( z, target, false ) ) {
5,988✔
2468
        return false;
2469
    }
2470

2471
    z->moves -= 80;
×
2472
    const bool uncanny = target->uncanny_dodge();
×
2473
    const auto msg_type = target == &g->u ? m_warning : m_info;
×
2474
    if( uncanny || dodge_check( z, target ) ) {
×
2475
        target->add_msg_player_or_npc( msg_type, _( "The %s gropes at you, but you dodge!" ),
2476
                                       _( "The %s gropes at <npcname>, but they dodge!" ),
2477
                                       z->name().c_str() );
×
2478

2479
        if( !uncanny ) {
×
2480
            target->on_dodge( z, z->type->melee_skill * 2 );
×
2481
        }
2482

2483
        return true;
2484
    }
2485

2486
    player *pl = dynamic_cast<player *>( target );
×
2487
    if( pl == nullptr ) {
×
2488
        return true;
2489
    }
2490

2491
    ///\EFFECT_DEX increases chance to avoid being grabbed if DEX>STR
2492

2493
    ///\EFFECT_STR increases chance to avoid being grabbed if STR>DEX
2494
    if( pl->has_grab_break_tec() && pl->get_grab_resist() > 0 && pl->get_dex() > pl->get_str() ?
×
2495
        rng( 0, pl->get_dex() ) : rng( 0, pl->get_str() ) > rng( 0,
×
2496
                z->type->melee_sides + z->type->melee_dice ) ) {
×
2497
        if( target->has_effect( effect_grabbed ) ) {
×
2498
            target->add_msg_if_player( m_info, _( "The %s tries to grab you as well, but you bat it away!" ),
2499
                                       z->name().c_str() );
×
2500
        } else {
2501
            target->add_msg_player_or_npc( m_info, _( "The %s tries to grab you, but you break its grab!" ),
2502
                                           _( "The %s tries to grab <npcname>, but they break its grab!" ),
2503
                                           z->name().c_str() );
×
2504
        }
2505
        return true;
2506
    }
2507

2508
    const int prev_effect = target->get_effect_int( effect_grabbed );
×
2509
    target->add_effect( effect_grabbed, 2_turns, bp_torso, false, prev_effect + 1 );
×
2510
    target->add_msg_player_or_npc( m_bad, _( "The %s grabs you!" ), _( "The %s grabs <npcname>!" ),
2511
                                   z->name().c_str() );
×
2512

2513
    return true;
×
2514
}
2515

2516
bool mattack::grab_drag( monster *z )
×
2517
{
2518
    if( !z->can_act() ) {
×
2519
        return false;
2520
    }
2521
    Creature *target = z->attack_target();
×
2522
    monster *zz = dynamic_cast<monster *>( target );
×
2523
    if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 1 ) {
×
2524
        return false;
2525
    }
2526

2527
    player *foe = dynamic_cast< player * >( target );
×
2528

2529
    grab( z ); //First, grab the target
×
2530

2531
    if( !target->has_effect( effect_grabbed ) ) { //Can't drag if isn't grabbed, otherwise try and move
×
2532
        return false;
2533
    }
2534
    tripoint target_square = z->pos() - ( target->pos() - z->pos() );
×
2535
    if( z->can_move_to( target_square ) &&
×
2536
        target->stability_roll() < dice( z->type->melee_sides, z->type->melee_dice ) ) {
×
2537
        tripoint zpt = z->pos();
×
2538
        z->move_to( target_square );
×
2539
        if( !g->is_empty( zpt ) ) { //Cancel the grab if the space is occupied by something
×
2540
            return false;
×
2541
        }
2542
        if( target->is_player() && ( zpt.x < SEEX * int( MAPSIZE / 2 ) ||
×
2543
                                     zpt.y < SEEY * int( MAPSIZE / 2 ) ||
×
2544
                                     zpt.x >= SEEX * ( 1 + int( MAPSIZE / 2 ) ) || zpt.y >= SEEY * ( 1 + int( MAPSIZE / 2 ) ) ) ) {
×
2545
            g->update_map( zpt.x, zpt.y );
×
2546
        }
2547
        if( foe != nullptr ) {
×
2548
            if( foe->in_vehicle ) {
×
2549
                g->m.unboard_vehicle( foe->pos() );
×
2550
            }
2551
            foe->setpos( zpt );
×
2552
        } else {
2553
            zz->setpos( zpt );
×
2554
        }
2555
        target->add_msg_player_or_npc( m_good, _( "You are dragged behind the %s!" ),
2556
                                       _( "<npcname> gets dragged behind the %s!" ), z->name().c_str() );
×
2557
    } else {
2558
        target->add_msg_player_or_npc( m_good, _( "You resist the %s as it tries to drag you!" ),
2559
                                       _( "<npcname> resist the %s as it tries to drag them!" ), z->name().c_str() );
×
2560
    }
2561
    int prev_effect = target->get_effect_int( effect_grabbed );
×
2562
    target->add_effect( effect_grabbed, 2_turns, bp_torso, false, prev_effect + 3 );
×
2563

2564
    return true; // cooldown was not reset prior to refactor here
2565
}
2566

2567
bool mattack::gene_sting( monster *z )
×
2568
{
2569
    if( z->friendly ) {
×
2570
        return false; // TODO: handle friendly monsters
2571
    }
2572
    if( within_visual_range( z, 7 ) < 0 ) {
×
2573
        return false;
2574
    }
2575

2576
    z->moves -= 150;
×
2577

2578
    if( g->u.uncanny_dodge() ) {
×
2579
        return true;
2580
    }
2581
    add_msg( m_bad, _( "The %s shoots a dart into you!" ), z->name().c_str() );
×
2582
    g->u.mutate();
×
2583

2584
    return true;
×
2585
}
2586

2587
bool mattack::para_sting( monster *z )
×
2588
{
2589
    Creature *target = z->attack_target();
×
2590
    if( target == nullptr ) {
×
2591
        return false;
2592
    }
2593
    if( rl_dist( z->pos(), target->pos() ) > 4 ) {
×
2594
        return false;
2595
    }
2596

2597
    z->moves -= 150;
×
2598

2599
    if( target->uncanny_dodge() ) {
×
2600
        return true;
2601
    }
2602
    target->add_msg_if_player( m_bad, _( "The %s shoots a dart into you!" ), z->name().c_str() );
×
2603
    target->add_msg_if_player( m_bad, _( "You feel poison enter your body!" ) );
×
2604
    target->add_effect( effect_paralyzepoison, 5_minutes );
×
2605

2606
    return true;
×
2607
}
2608

2609
bool mattack::triffid_growth( monster *z )
×
2610
{
2611
    // Young triffid growing into an adult
2612
    if( g->u.sees( *z ) ) {
×
2613
        add_msg( m_warning, _( "The %s young triffid grows into an adult!" ),
2614
                 z->name().c_str() );
×
2615
    }
2616
    z->poly( mon_triffid );
×
2617

2618
    return false;
×
2619
}
2620

2621
bool mattack::stare( monster *z )
×
2622
{
2623
    if( z->friendly ) {
×
2624
        return false; // TODO: handle friendly monsters
2625
    }
2626
    z->moves -= 200;
×
2627
    if( z->sees( g->u ) ) {
×
2628
        if( g->u.sees( *z ) ) {
×
2629
            add_msg( m_bad, _( "The %s stares at you, and you shudder." ), z->name().c_str() );
×
2630
        } else {
2631
            add_msg( m_bad, _( "You feel like you're being watched, it makes you sick." ) );
×
2632
        }
2633
        g->u.add_effect( effect_teleglow, 80_minutes );
×
2634
    }
2635

2636
    return true;
2637
}
2638

2639
bool mattack::fear_paralyze( monster *z )
×
2640
{
2641
    if( z->friendly ) {
×
2642
        return false; // TODO: handle friendly monsters
2643
    }
2644
    if( g->u.sees( *z ) && !g->u.has_effect( effect_fearparalyze ) ) {
×
2645
        if( g->u.has_artifact_with( AEP_PSYSHIELD ) || ( g->u.worn_with_flag( "PSYSHIELD_PARTIAL" ) &&
×
2646
                one_in( 4 ) ) ) {
×
2647
            add_msg( _( "The %s probes your mind, but is rebuffed!" ), z->name().c_str() );
×
2648
            ///\EFFECT_INT decreases chance of being paralyzed by fear attack
2649
        } else if( rng( 0, 20 ) > g->u.get_int() ) {
×
2650
            add_msg( m_bad, _( "The terrifying visage of the %s paralyzes you." ), z->name().c_str() );
×
2651
            g->u.add_effect( effect_fearparalyze, 5_turns );
×
2652
            g->u.moves -= 400;
×
2653
        } else {
2654
            add_msg( _( "You manage to avoid staring at the horrendous %s." ), z->name().c_str() );
×
2655
        }
2656
    }
2657

2658
    return true;
2659
}
2660

2661
bool mattack::photograph( monster *z )
×
2662
{
2663
    if( within_visual_range( z, 6 ) < 0 ) {
×
2664
        return false;
2665
    }
2666

2667
    // Badges should NOT be swappable between roles.
2668
    // Hence separate checking.
2669
    // If you are in fact listed as a police officer
2670
    if( g->u.has_trait( trait_id( "PROF_POLICE" ) ) ) {
×
2671
        // And you're wearing your badge
2672
        if( g->u.is_wearing( "badge_deputy" ) ) {
×
2673
            if( one_in( 3 ) ) {
×
2674
                add_msg( m_info, _( "The %s flashes a LED and departs.  Human officer on scene." ),
2675
                         z->name().c_str() );
×
2676
                z->no_corpse_quiet = true;
×
2677
                z->no_extra_death_drops = true;
×
2678
                z->die( nullptr );
×
2679
                return false;
×
2680
            } else {
2681
                add_msg( m_info,
2682
                         _( "The %s acknowledges you as an officer responding, but hangs around to watch." ),
2683
                         z->name().c_str() );
×
2684
                add_msg( m_info, _( "Probably some now-obsolete Internal Affairs subroutine..." ) );
×
2685
                return true;
×
2686
            }
2687
        }
2688
    }
2689

2690
    if( g->u.has_trait( trait_id( "PROF_PD_DET" ) ) ) {
×
2691
        // And you have your shield on
2692
        if( g->u.is_wearing( "badge_detective" ) ) {
×
2693
            if( one_in( 4 ) ) {
×
2694
                add_msg( m_info, _( "The %s flashes a LED and departs.  Human officer on scene." ),
2695
                         z->name().c_str() );
×
2696
                z->no_corpse_quiet = true;
×
2697
                z->no_extra_death_drops = true;
×
2698
                z->die( nullptr );
×
2699
                return false;
×
2700
            } else {
2701
                add_msg( m_info,
2702
                         _( "The %s acknowledges you as an officer responding, but hangs around to watch." ),
2703
                         z->name().c_str() );
×
2704
                add_msg( m_info, _( "Ops used to do that in case you needed backup..." ) );
×
2705
                return true;
×
2706
            }
2707
        }
2708
    } else if( g->u.has_trait( trait_id( "PROF_SWAT" ) ) ) {
×
2709
        // And you're wearing your badge
2710
        if( g->u.is_wearing( "badge_swat" ) ) {
×
2711
            if( one_in( 3 ) ) {
×
2712
                add_msg( m_info, _( "The %s flashes a LED and departs.  SWAT's working the area." ),
2713
                         z->name().c_str() );
×
2714
                z->no_corpse_quiet = true;
×
2715
                z->no_extra_death_drops = true;
×
2716
                z->die( nullptr );
×
2717
                return false;
×
2718
            } else {
2719
                add_msg( m_info, _( "The %s acknowledges you as SWAT onsite, but hangs around to watch." ),
2720
                         z->name().c_str() );
×
2721
                add_msg( m_info, _( "Probably some now-obsolete Internal Affairs subroutine..." ) );
×
2722
                return true;
×
2723
            }
2724
        }
2725
    } else if( g->u.has_trait( trait_id( "PROF_CYBERCOP" ) ) ) {
×
2726
        // And you're wearing your badge
2727
        if( g->u.is_wearing( "badge_cybercop" ) ) {
×
2728
            if( one_in( 3 ) ) {
×
2729
                add_msg( m_info, _( "The %s winks a LED and departs.  One machine to another?" ),
2730
                         z->name().c_str() );
×
2731
                z->no_corpse_quiet = true;
×
2732
                z->no_extra_death_drops = true;
×
2733
                z->die( nullptr );
×
2734
                return false;
×
2735
            } else {
2736
                add_msg( m_info,
2737
                         _( "The %s acknowledges you as an officer responding, but hangs around to watch." ),
2738
                         z->name().c_str() );
×
2739
                add_msg( m_info, _( "Apparently yours aren't the only systems kept alive post-apocalypse." ) );
×
2740
                return true;
×
2741
            }
2742
        }
2743
    }
2744

2745
    if( g->u.has_trait( trait_id( "PROF_FED" ) ) ) {
×
2746
        // And you're wearing your badge
2747
        if( g->u.is_wearing( "badge_marshal" ) ) {
×
2748
            add_msg( m_info, _( "The %s flashes a LED and departs.  The Feds have this." ), z->name().c_str() );
×
2749
            z->no_corpse_quiet = true;
×
2750
            z->no_extra_death_drops = true;
×
2751
            z->die( nullptr );
×
2752
            return false;
×
2753
        }
2754
    }
2755

2756
    if( z->friendly ) {
×
2757
        // Friendly (hacked?) bot ignore the player.
2758
        // TODO: might need to be revisited when it can target npcs.
2759
        return false;
2760
    }
2761
    z->moves -= 150;
×
2762
    add_msg( m_warning, _( "The %s takes your picture!" ), z->name().c_str() );
×
2763
    // TODO: Make the player known to the faction
2764
    g->events.add( EVENT_ROBOT_ATTACK, calendar::turn + rng( 15_turns, 30_turns ), 0,
×
2765
                   g->u.global_sm_location() );
×
2766

2767
    return true;
×
2768
}
2769

2770
bool mattack::tazer( monster *z )
×
2771
{
2772
    Creature *target = z->attack_target();
×
2773
    if( target == nullptr || !is_adjacent( z, target, false ) ) {
×
2774
        return false;
2775
    }
2776

2777
    taze( z, target );
×
2778
    return true;
×
2779
}
2780

2781
void mattack::taze( monster *z, Creature *target )
×
2782
{
2783
    z->moves -= 200;   // It takes a while
×
2784
    if( target == nullptr || target->uncanny_dodge() ) {
×
2785
        return;
2786
    }
2787

2788
    int dam = target->deal_damage( z, bp_torso, damage_instance( DT_ELECTRIC, rng( 1,
2789
                                   5 ) ) ).total_damage();
×
2790
    if( dam == 0 ) {
×
2791
        target->add_msg_player_or_npc( _( "The %s unsuccessfully attempts to shock you." ),
2792
                                       _( "The %s unsuccessfully attempts to shock <npcname>." ),
2793
                                       z->name().c_str() );
×
2794
        return;
×
2795
    }
2796

2797
    auto m_type = target->attitude_to( g->u ) == Creature::A_FRIENDLY ? m_bad : m_neutral;
×
2798
    target->add_msg_player_or_npc( m_type,
2799
                                   _( "The %s shocks you!" ),
2800
                                   _( "The %s shocks <npcname>!" ),
2801
                                   z->name().c_str() );
×
2802
    target->check_dead_state();
×
2803
}
2804

2805
void mattack::rifle( monster *z, Creature *target )
×
2806
{
2807
    const std::string ammo_type( "556" );
×
2808
    // Make sure our ammo isn't weird.
2809
    if( z->ammo[ammo_type] > 3000 ) {
×
2810
        debugmsg( "Generated too much ammo (%d) for %s in mattack::rifle", z->ammo[ammo_type],
×
2811
                  z->name().c_str() );
×
2812
        z->ammo[ammo_type] = 3000;
×
2813
    }
2814

2815
    npc tmp = make_fake_npc( z, 16, 10, 8, 12 );
×
2816
    tmp.set_skill_level( skill_rifle, 8 );
×
2817
    tmp.set_skill_level( skill_gun, 6 );
×
2818
    tmp.recoil = 0; // no need to aim
×
2819

2820
    if( target == &g->u ) {
×
2821
        if( !z->has_effect( effect_targeted ) ) {
×
2822
            sounds::sound( z->pos(), 8, _( "beep-beep." ) );
×
2823
            z->add_effect( effect_targeted, 8_turns );
×
2824
            z->moves -= 100;
×
2825
            return;
×
2826
        }
2827
    }
2828
    z->moves -= 150;   // It takes a while
×
2829

2830
    if( z->ammo[ammo_type] <= 0 ) {
×
2831
        if( one_in( 3 ) ) {
×
2832
            sounds::sound( z->pos(), 2, _( "a chk!" ) );
×
2833
        } else if( one_in( 4 ) ) {
×
2834
            sounds::sound( z->pos(), 6, _( "boop!" ) );
×
2835
        }
2836
        return;
2837
    }
2838
    if( g->u.sees( *z ) ) {
×
2839
        add_msg( m_warning, _( "The %s opens up with its rifle!" ), z->name().c_str() );
×
2840
    }
2841

2842
    tmp.weapon = item( "m4a1" ).ammo_set( ammo_type, z->ammo[ ammo_type ] );
×
2843
    int burst = std::max( tmp.weapon.gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 );
×
2844

2845
    z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required();
×
2846

2847
    if( target == &g->u ) {
×
2848
        z->add_effect( effect_targeted, 3_turns );
×
2849
    }
2850
}
2851

2852
void mattack::frag( monster *z, Creature *target ) // This is for the bots, not a standalone turret
×
2853
{
2854
    const std::string ammo_type( "40mm_frag" );
×
2855
    // Make sure our ammo isn't weird.
2856
    if( z->ammo[ammo_type] > 200 ) {
×
2857
        debugmsg( "Generated too much ammo (%d) for %s in mattack::frag", z->ammo[ammo_type],
×
2858
                  z->name().c_str() );
×
2859
        z->ammo[ammo_type] = 200;
×
2860
    }
2861

2862
    if( target == &g->u ) {
×
2863
        if( !z->has_effect( effect_targeted ) ) {
×
2864
            //~Potential grenading detected.
2865
            add_msg( m_warning, _( "Those laser dots don't seem very friendly..." ) );
×
2866
            g->u.add_effect( effect_laserlocked,
2867
                             3_turns ); // Effect removed in game.cpp, duration doesn't much matter
×
2868
            sounds::sound( z->pos(), 10, _( "Targeting." ) );
×
2869
            z->add_effect( effect_targeted, 5_turns );
×
2870
            z->moves -= 150;
×
2871
            // Should give some ability to get behind cover,
2872
            // even though it's patently unrealistic.
2873
            return;
×
2874
        }
2875
    }
2876
    npc tmp = make_fake_npc( z, 16, 10, 8, 12 );
×
2877
    tmp.set_skill_level( skill_launcher, 8 );
×
2878
    tmp.set_skill_level( skill_gun, 6 );
×
2879
    tmp.recoil = 0; // no need to aim
×
2880
    z->moves -= 150;   // It takes a while
×
2881

2882
    if( z->ammo[ammo_type] <= 0 ) {
×
2883
        if( one_in( 3 ) ) {
×
2884
            sounds::sound( z->pos(), 2, _( "a chk!" ) );
×
2885
        } else if( one_in( 4 ) ) {
×
2886
            sounds::sound( z->pos(), 6, _( "boop!" ) );
×
2887
        }
2888
        return;
×
2889
    }
2890
    if( g->u.sees( *z ) ) {
×
2891
        add_msg( m_warning, _( "The %s's grenade launcher fires!" ), z->name().c_str() );
×
2892
    }
2893

2894
    tmp.weapon = item( "mgl" ).ammo_set( ammo_type, z->ammo[ ammo_type ] );
×
2895
    int burst = std::max( tmp.weapon.gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 );
×
2896

2897
    z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required();
×
2898

2899
    if( target == &g->u ) {
×
2900
        z->add_effect( effect_targeted, 3_turns );
×
2901
    }
2902
}
2903

2904
void mattack::tankgun( monster *z, Creature *target )
×
2905
{
2906
    const std::string ammo_type( "120mm_HEAT" );
×
2907
    // Make sure our ammo isn't weird.
2908
    if( z->ammo[ammo_type] > 40 ) {
×
2909
        debugmsg( "Generated too much ammo (%d) for %s in mattack::tankgun", z->ammo[ammo_type],
×
2910
                  z->name().c_str() );
×
2911
        z->ammo[ammo_type] = 40;
×
2912
    }
2913

2914
    int dist = rl_dist( z->pos(), target->pos() );
×
2915
    tripoint aim_point = target->pos();
×
2916
    if( dist > 50 ) {
×
2917
        return;
×
2918
    }
2919

2920
    if( !z->has_effect( effect_targeted ) ) {
×
2921
        //~ There will be a 120mm HEAT shell sent at high speed to your location next turn.
2922
        target->add_msg_if_player( m_warning, _( "You're not sure why you've got a laser dot on you..." ) );
×
2923
        //~ Sound of a tank turret swiveling into place
2924
        sounds::sound( z->pos(), 10, _( "whirrrrrclick." ) );
×
2925
        z->add_effect( effect_targeted, 1_minutes );
×
2926
        target->add_effect( effect_laserlocked, 1_minutes );
×
2927
        z->moves -= 200;
×
2928
        // Should give some ability to get behind cover,
2929
        // even though it's patently unrealistic.
2930
        return;
×
2931
    }
2932
    // Target the vehicle itself instead if there is one.
2933
    if( const optional_vpart_position vp = g->m.veh_at( target->pos() ) ) {
×
2934
        aim_point = vp->vehicle().global_pos3() + vp->vehicle().rotated_center_of_mass();
×
2935
    }
2936
    // kevingranade KA101: yes, but make it really inaccurate
2937
    // Sure thing.
2938
    npc tmp = make_fake_npc( z, 12, 8, 8, 8 );
×
2939
    tmp.set_skill_level( skill_launcher, 1 );
×
2940
    tmp.set_skill_level( skill_gun, 1 );
×
2941
    tmp.recoil = 0; // no need to aim
×
2942
    z->moves -= 150;   // It takes a while
×
2943

2944
    if( z->ammo[ammo_type] <= 0 ) {
×
2945
        if( one_in( 3 ) ) {
×
2946
            sounds::sound( z->pos(), 2, _( "a chk!" ) );
×
2947
        } else if( one_in( 4 ) ) {
×
2948
            sounds::sound( z->pos(), 6, _( "clank!" ) );
×
2949
        }
2950
        return;
×
2951
    }
2952
    if( g->u.sees( *z ) ) {
×
2953
        add_msg( m_warning, _( "The %s's 120mm cannon fires!" ), z->name().c_str() );
×
2954
    }
2955
    tmp.weapon = item( "TANK" ).ammo_set( ammo_type, z->ammo[ ammo_type ] );
×
2956
    int burst = std::max( tmp.weapon.gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 );
×
2957

2958
    z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required();
×
2959
}
2960

2961
bool mattack::searchlight( monster *z )
×
2962
{
2963

2964
    int max_lamp_count = 3;
×
2965
    if( z->get_hp() < z->get_hp_max() ) {
×
2966
        max_lamp_count--;
×
2967
    }
2968
    if( z->get_hp() < z->get_hp_max() / 3 ) {
×
2969
        max_lamp_count--;
×
2970
    }
2971

2972
    const int zposx = z->posx();
×
2973
    const int zposy = z->posy();
×
2974

2975
    //this searchlight is not initialized
2976
    if( z->inv.empty() ) {
×
2977

2978
        for( int i = 0; i < max_lamp_count; i++ ) {
×
2979

2980
            item settings( "processor", 0 );
×
2981

2982
            settings.set_var( "SL_PREFER_UP", "TRUE" );
×
2983
            settings.set_var( "SL_PREFER_DOWN", "TRUE" );
×
2984
            settings.set_var( "SL_PREFER_RIGHT", "TRUE" );
×
2985
            settings.set_var( "SL_PREFER_LEFT", "TRUE" );
×
2986

2987
            for( int x = zposx - 24; x < zposx + 24; x++ )
×
2988
                for( int y = zposy - 24; y < zposy + 24; y++ ) {
×
2989
                    tripoint dest( x, y, z->posz() );
×
2990
                    const monster *const mon = g->critter_at<monster>( dest );
×
2991
                    if( mon && mon->type->id == mon_turret_searchlight ) {
×
2992
                        if( x < zposx ) {
×
2993
                            settings.set_var( "SL_PREFER_LEFT", "FALSE" );
×
2994
                        }
2995
                        if( x > zposx ) {
×
2996
                            settings.set_var( "SL_PREFER_RIGHT", "FALSE" );
×
2997
                        }
2998
                        if( y < zposy ) {
×
2999
                            settings.set_var( "SL_PREFER_UP", "FALSE" );
×
3000
                        }
3001
                        if( y > zposy ) {
×
3002
                            settings.set_var( "SL_PREFER_DOWN", "FALSE" );
×
3003
                        }
3004
                    }
3005

3006
                }
3007

3008
            settings.set_var( "SL_SPOT_X", 0 );
×
3009
            settings.set_var( "SL_SPOT_Y", 0 );
×
3010

3011
            z->add_item( settings );
×
3012
        }
3013
    }
3014

3015
    //battery charge from the generator is enough for some time of work
3016
    if( calendar::once_every( 10_minutes ) ) {
×
3017

3018
        bool generator_ok = false;
×
3019

3020
        for( int x = zposx - 24; x < zposx + 24; x++ ) {
×
3021
            for( int y = zposy - 24; y < zposy + 24; y++ ) {
×
3022
                tripoint dest( x, y, z->posz() );
×
3023
                if( g->m.ter( dest ) == ter_str_id( "t_plut_generator" ) ) {
×
3024
                    generator_ok = true;
×
3025
                }
3026
            }
3027
        }
3028

3029
        if( !generator_ok ) {
×
3030
            for( auto &settings : z->inv ) {
×
3031
                settings.set_var( "SL_POWER", "OFF" );
×
3032
            }
3033

3034
            return true;
3035
        }
3036
    }
3037

3038
    for( int i = 0; i < max_lamp_count; i++ ) {
×
3039

3040
        item &settings = z->inv[i];
×
3041

3042
        if( settings.get_var( "SL_POWER" )  == "OFF" ) {
×
3043
            return true;
3044
        }
3045

3046
        const int rng_dir = rng( 0, 7 );
×
3047

3048
        if( one_in( 5 ) ) {
×
3049

3050
            if( !one_in( 5 ) ) {
×
3051
                settings.set_var( "SL_DIR", rng_dir );
×
3052
            } else {
3053
                const int rng_pref = rng( 0, 3 ) * 2;
×
3054
                if( rng_pref == 0 && settings.get_var( "SL_PREFER_UP" ) == "TRUE" ) {
×
3055
                    settings.set_var( "SL_DIR", rng_pref );
×
3056
                } else            if( rng_pref == 2 && settings.get_var( "SL_PREFER_RIGHT" ) == "TRUE" ) {
×
3057
                    settings.set_var( "SL_DIR", rng_pref );
×
3058
                } else            if( rng_pref == 4 && settings.get_var( "SL_PREFER_DOWN" ) == "TRUE" ) {
×
3059
                    settings.set_var( "SL_DIR", rng_pref );
×
3060
                } else            if( rng_pref == 6 && settings.get_var( "SL_PREFER_LEFT" ) == "TRUE" ) {
×
3061
                    settings.set_var( "SL_DIR", rng_pref );
×
3062
                }
3063
            }
3064
        }
3065

3066
        int x = zposx + settings.get_var( "SL_SPOT_X", 0 );
×
3067
        int y = zposy + settings.get_var( "SL_SPOT_Y", 0 );
×
3068
        int shift = 0;
×
3069

3070
        for( int i = 0; i < rng( 1, 2 ); i++ ) {
×
3071

3072
            if( !z->sees( g->u ) ) {
×
3073
                shift = settings.get_var( "SL_DIR", shift );
×
3074

3075
                switch( shift ) {
×
3076
                    case 0:
3077
                        y--;
×
3078
                        break;
×
3079
                    case 1:
3080
                        y--;
×
3081
                        x++;
×
3082
                        break;
×
3083
                    case 2:
3084
                        x++;
×
3085
                        break;
×
3086
                    case 3:
3087
                        x++;
×
3088
                        y++;
×
3089
                        break;
×
3090
                    case 4:
3091
                        y++;
×
3092
                        break;
×
3093
                    case 5:
3094
                        y++;
×
3095
                        x--;
×
3096
                        break;
×
3097
                    case 6:
3098
                        x--;
×
3099
                        break;
×
3100
                    case 7:
3101
                        x--;
×
3102
                        y--;
×
3103
                        break;
×
3104

3105
                    default:
3106
                        break;
3107
                }
3108

3109
            } else {
3110
                if( x < g->u.posx() ) {
×
3111
                    x++;
×
3112
                }
3113
                if( x > g->u.posx() ) {
×
3114
                    x--;
×
3115
                }
3116
                if( y < g->u.posy() ) {
×
3117
                    y++;
×
3118
                }
3119
                if( y > g->u.posy() ) {
×
3120
                    y--;
×
3121
                }
3122
            }
3123

3124
            if( rl_dist( x, y, zposx, zposy ) > 50 ) {
×
3125
                if( x > zposx ) {
×
3126
                    x--;
×
3127
                }
3128
                if( x < zposx ) {
×
3129
                    x++;
×
3130
                }
3131
                if( y > zposy ) {
×
3132
                    y--;
×
3133
                }
3134
                if( y < zposy ) {
×
3135
                    y++;
×
3136
                }
3137
            }
3138
        }
3139

3140
        settings.set_var( "SL_SPOT_X", x - zposx );
×
3141
        settings.set_var( "SL_SPOT_Y", y - zposy );
×
3142

3143
        g->m.add_field( tripoint( x, y, z->posz() ), fd_spotlight, 1 );
×
3144

3145
    }
3146

3147
    return true;
3148
}
3149

3150
bool mattack::flamethrower( monster *z )
×
3151
{
3152
    if( z->friendly ) {
×
3153
        return false; // TODO: handle friendly monsters
3154
    }
3155
    if( z->friendly != 0 ) { // @todo: that is always false!
×
3156
        // Attacking monsters, not the player!
3157
        int boo_hoo;
3158
        Creature *target = z->auto_find_hostile_target( 5, boo_hoo );
×
3159
        if( target == NULL ) { // Couldn't find any targets!
×
3160
            if( boo_hoo > 0 && g->u.sees( *z ) ) { // because that stupid oaf was in the way!
×
3161
                add_msg( m_warning, ngettext( "Pointed in your direction, the %s emits an IFF warning beep.",
3162
                                              "Pointed in your direction, the %s emits %d annoyed sounding beeps.",
3163
                                              boo_hoo ),
×
3164
                         z->name().c_str(), boo_hoo );
×
3165
            }
3166
            return false; // did reset before refactor, changed to match other turret behaviors
3167
        }
3168
        flame( z, target );
×
3169
        return true;
3170
    }
3171

3172
    int dist = within_visual_range( z, 5 );
×
3173
    if( dist < 0 ) {
×
3174
        return false;
3175
    }
3176

3177
    flame( z, &g->u );
×
3178

3179
    return true;
×
3180
}
3181

3182
void mattack::flame( monster *z, Creature *target )
×
3183
{
3184
    int dist = rl_dist( z->pos(), target->pos() );
×
3185
    if( target != &g->u ) {
×
3186
        // friendly
3187
        z->moves -= 500;   // It takes a while
×
3188
        if( !g->m.sees( z->pos(), target->pos(), dist ) ) {
×
3189
            // shouldn't happen
3190
            debugmsg( "mattack::flame invoked on invisible target" );
×
3191
        }
3192
        std::vector<tripoint> traj = g->m.find_clear_path( z->pos(), target->pos() );
×
3193

3194
        for( auto &i : traj ) {
×
3195
            // break out of attack if flame hits a wall
3196
            // TODO: Z
3197
            if( g->m.hit_with_fire( tripoint( i.x, i.y, z->posz() ) ) ) {
×
3198
                if( g->u.sees( i ) )
×
3199
                    add_msg( _( "The tongue of flame hits the %s!" ),
3200
                             g->m.tername( i.x, i.y ).c_str() );
×
3201
                return;
3202
            }
3203
            g->m.add_field( i, fd_fire, 1 );
×
3204
        }
3205
        target->add_effect( effect_onfire, 8_turns, bp_torso );
×
3206

3207
        return;
3208
    }
3209

3210
    z->moves -= 500;   // It takes a while
×
3211
    if( !g->m.sees( z->pos(), target->pos(), dist + 1 ) ) {
×
3212
        // shouldn't happen
3213
        debugmsg( "mattack::flame invoked on invisible target" );
×
3214
    }
3215
    std::vector<tripoint> traj = g->m.find_clear_path( z->pos(), target->pos() );
×
3216

3217
    for( auto &i : traj ) {
×
3218
        // break out of attack if flame hits a wall
3219
        if( g->m.hit_with_fire( tripoint( i.x, i.y, z->posz() ) ) ) {
×
3220
            if( g->u.sees( i ) )
×
3221
                add_msg( _( "The tongue of flame hits the %s!" ),
3222
                         g->m.tername( i.x, i.y ).c_str() );
×
3223
            return;
×
3224
        }
3225
        g->m.add_field( i, fd_fire, 1 );
×
3226
    }
3227
    if( !target->uncanny_dodge() ) {
×
3228
        target->add_effect( effect_onfire, 8_turns, bp_torso );
×
3229
    }
3230
}
3231

3232
bool mattack::copbot( monster *z )
×
3233
{
3234
    Creature *target = z->attack_target();
×
3235
    if( target == nullptr ) {
×
3236
        return false;
3237
    }
3238

3239
    // TODO: Make it recognize zeds as human, but ignore animals
3240
    player *foe = dynamic_cast<player *>( target );
×
3241
    bool sees_u = foe != nullptr && z->sees( *foe );
×
3242
    bool cuffed = foe != nullptr && foe->weapon.typeId() == "e_handcuffs";
×
3243
    // Taze first, then ask questions (simplifies later checks for non-humans)
3244
    if( !cuffed && is_adjacent( z, target, true ) ) {
×
3245
        taze( z, target );
×
3246
        return true;
×
3247
    }
3248

3249
    if( rl_dist( z->pos(), target->pos() ) > 2 || foe == nullptr || !z->sees( *target ) ) {
×
3250
        if( one_in( 3 ) ) {
×
3251
            if( sees_u ) {
×
3252
                if( foe->unarmed_attack() ) {
×
3253
                    sounds::sound( z->pos(), 18, _( "a robotic voice boom, \"Citizen, Halt!\"" ) );
×
3254
                } else if( !cuffed ) {
×
3255
                    sounds::sound( z->pos(), 18, _( "a robotic voice boom, \"\
×
3256
Please put down your weapon.\"" ) );
×
3257
                }
3258
            } else
3259
                sounds::sound( z->pos(), 18,
×
3260
                               _( "a robotic voice boom, \"Come out with your hands up!\"" ) );
×
3261
        } else {
3262
            sounds::sound( z->pos(), 18, _( "a police siren, whoop WHOOP" ) );
×
3263
        }
3264
        return true;
3265
    }
3266

3267
    // If cuffed don't attack the player, unless the bot is damaged
3268
    // presumably because of the player's actions
3269
    if( z->get_hp() == z->get_hp_max() ) {
×
3270
        z->anger = 1;
×
3271
    } else {
3272
        z->anger = z->type->agro;
×
3273
    }
3274

3275
    return true;
3276
}
3277

3278
bool mattack::chickenbot( monster *z )
×
3279
{
3280
    int mode = 0;
×
3281
    int boo_hoo = 0;
×
3282
    Creature *target;
3283
    if( z->friendly == 0 ) {
×
3284
        target = z->attack_target();
×
3285
        if( target == nullptr ) {
×
3286
            return false;
3287
        }
3288
    } else {
3289
        target = z->auto_find_hostile_target( 38, boo_hoo );
×
3290
        if( target == nullptr ) {
×
3291
            if( boo_hoo > 0 && g->u.sees( *z ) ) { // because that stupid oaf was in the way!
×
3292
                add_msg( m_warning, ngettext( "Pointed in your direction, the %s emits an IFF warning beep.",
3293
                                              "Pointed in your direction, the %s emits %d annoyed sounding beeps.",
3294
                                              boo_hoo ),
×
3295
                         z->name().c_str(), boo_hoo );
×
3296
            }
3297
            return false;
3298
        }
3299
    }
3300

3301
    int cap = target->power_rating() - 1;
×
3302
    monster *mon = dynamic_cast< monster * >( target );
×
3303
    // Their attitude to us and not ours to them, so that bobcats won't get gunned down
3304
    // Only monster-types for now - assuming humans are smart enough not to make it obvious
3305
    // Unless damaged - then everything is hostile
3306
    if( z->get_hp() <= z->get_hp_max() ||
×
3307
        ( mon != nullptr && mon->attitude_to( *z ) == Creature::Attitude::A_HOSTILE ) ) {
×
3308
        cap += 2;
×
3309
    }
3310

3311
    int dist = rl_dist( z->pos(), target->pos() );
×
3312
    int player_dist = rl_dist( target->pos(), g->u.pos() );
×
3313
    if( dist == 1 && one_in( 2 ) ) {
×
3314
        // Use tazer at point-blank range, and even then, not continuously.
3315
        mode = 1;
3316
    } else if( ( z->friendly == 0 || player_dist >= 6 ) &&
×
3317
               // Avoid shooting near player if we're friendly.
3318
               ( dist >= 12 || ( g->u.in_vehicle && dist >= 6 ) ) ) {
×
3319
        // Only use at long range, unless player is in a vehicle, then tolerate closer targeting.
3320
        mode = 3;
3321
    } else if( dist >= 4 ) {
×
3322
        // Don't use machine gun at very close range, under the assumption that targets at that range can dodge?
3323
        mode = 2;
×
3324
    }
3325

3326
    if( mode == 0 ) {
×
3327
        return false;    // No attacks were valid!
3328
    }
3329

3330
    if( mode > cap ) {
×
3331
        mode = cap;
×
3332
    }
3333
    switch( mode ) {
×
3334
        case 0:
3335
        case 1:
3336
            // If we downgraded to taze, but are out of range, don't act.
3337
            if( dist <= 1 ) {
×
3338
                taze( z, target );
×
3339
            }
3340
            break;
3341
        case 2:
3342
            if( dist <= 20 ) {
×
3343
                rifle( z, target );
×
3344
            }
3345
            break;
3346
        case 3:
3347
            if( dist <= 38 ) {
×
3348
                frag( z, target );
×
3349
            }
3350
            break;
3351
        default:
3352
            return false; // Weak stuff, shouldn't bother with
3353
    }
3354

3355
    return true;
3356
}
3357

3358
bool mattack::multi_robot( monster *z )
×
3359
{
3360
    int mode = 0;
×
3361
    int boo_hoo = 0;
×
3362
    Creature *target;
3363
    if( z->friendly == 0 ) {
×
3364
        target = z->attack_target();
×
3365
        if( target == nullptr ) {
×
3366
            return false;
3367
        }
3368
    } else {
3369
        target = z->auto_find_hostile_target( 48, boo_hoo );
×
3370
        if( target == nullptr ) {
×
3371
            if( boo_hoo > 0 && g->u.sees( *z ) ) { // because that stupid oaf was in the way!
×
3372
                add_msg( m_warning, ngettext( "Pointed in your direction, the %s emits an IFF warning beep.",
3373
                                              "Pointed in your direction, the %s emits %d annoyed sounding beeps.",
3374
                                              boo_hoo ),
×
3375
                         z->name().c_str(), boo_hoo );
×
3376
            }
3377
            return false;
3378
        }
3379
    }
3380

3381
    int cap = target->power_rating();
×
3382
    monster *mon = dynamic_cast< monster * >( target );
×
3383
    // Their attitude to us and not ours to them, so that bobcats won't get gunned down
3384
    // Only monster-types for now - assuming humans are smart enough not to make it obvious
3385
    // Unless damaged - then everything is hostile
3386
    if( z->get_hp() <= z->get_hp_max() ||
×
3387
        ( mon != nullptr && mon->attitude_to( *z ) == Creature::Attitude::A_HOSTILE ) ) {
×
3388
        cap += 2;
×
3389
    }
3390

3391
    int dist = rl_dist( z->pos(), target->pos() );
×
3392
    if( dist <= 15 ) {
×
3393
        mode = 1;
3394
    } else if( dist <= 30 ) {
×
3395
        mode = 2;
3396
    } else if( ( target == &g->u && g->u.in_vehicle ) ||
×
3397
               z->friendly != 0 ||
×
3398
               cap > 4 ) {
3399
        // Primary only kicks in if you're in a vehicle or are big enough to be mistaken for one.
3400
        // Or if you've hacked it so the turret's on your side.  ;-)
3401
        if( dist >= 30 && dist < 50 ) {
×
3402
            // Enforced max-range of 50.
3403
            mode = 5;
×
3404
            cap = 5;
×
3405
        }
3406
    }
3407

3408
    if( mode == 0 ) {
×
3409
        return false;    // No attacks were valid!
3410
    }
3411

3412
    if( mode > cap ) {
×
3413
        mode = cap;
×
3414
    }
3415
    switch( mode ) {
×
3416
        case 1:
3417
            if( dist <= 15 ) {
×
3418
                rifle( z, target );
×
3419
            }
3420
            break;
3421
        case 2:
3422
            if( dist <= 30 ) {
×
3423
                frag( z, target );
×
3424
            }
3425
            break;
3426
        default:
3427
            return false; // Weak stuff, shouldn't bother with
3428
    }
3429

3430
    return true;
3431
}
3432

3433
bool mattack::ratking( monster *z )
×
3434
{
3435
    if( z->friendly ) {
×
3436
        return false; // TODO: handle friendly monsters
3437
    }
3438
    // Disable z-level ratting or it can get silly
3439
    if( rl_dist( z->pos(), g->u.pos() ) > 50 || z->posz() != g->u.posz() ) {
×
3440
        return false;
3441
    }
3442

3443
    switch( rng( 1, 5 ) ) { // What do we say?
×
3444
        case 1:
3445
            add_msg( m_warning, _( "\"YOU... ARE FILTH...\"" ) );
×
3446
            break;
×
3447
        case 2:
3448
            add_msg( m_warning, _( "\"VERMIN... YOU ARE VERMIN...\"" ) );
×
3449
            break;
×
3450
        case 3:
3451
            add_msg( m_warning, _( "\"LEAVE NOW...\"" ) );
×
3452
            break;
×
3453
        case 4:
3454
            add_msg( m_warning, _( "\"WE... WILL FEAST... UPON YOU...\"" ) );
×
3455
            break;
×
3456
        case 5:
3457
            add_msg( m_warning, _( "\"FOUL INTERLOPER...\"" ) );
×
3458
            break;
×
3459
    }
3460
    if( rl_dist( z->pos(), g->u.pos() ) <= 10 ) {
×
3461
        g->u.add_effect( effect_rat, 3_minutes );
×
3462
    }
3463

3464
    return true;
3465
}
3466

3467
bool mattack::generator( monster *z )
×
3468
{
3469
    sounds::sound( z->pos(), 100, "" );
×
3470
    if( calendar::once_every( 1_minutes ) && z->get_hp() < z->get_hp_max() ) {
×
3471
        z->heal( 1 );
×
3472
    }
3473

3474
    return true;
×
3475
}
3476

3477
bool mattack::upgrade( monster *z )
×
3478
{
3479
    std::vector<monster *> targets;
3480
    for( monster &zed : g->all_monsters() ) {
×
3481
        // Check this first because it is a relatively cheap check
3482
        if( zed.can_upgrade() ) {
×
3483
            // Then do the more expensive ones
3484
            if( z->attitude_to( zed ) != Creature::Attitude::A_HOSTILE &&
×
3485
                within_target_range( z, &zed, 10 ) ) {
×
3486
                targets.push_back( &zed );
×
3487
            }
3488
        }
3489
    }
3490
    if( targets.empty() ) {
×
3491
        // Nobody to upgrade, get MAD!
3492
        z->anger = 100;
×
3493
        return false;
×
3494
    } else {
3495
        // We've got zombies to upgrade now, calm down again
3496
        z->anger = 5;
×
3497
    }
3498

3499
    z->moves -= z->type->speed; // Takes one turn
×
3500

3501
    monster *target = random_entry( targets );
×
3502

3503
    std::string old_name = target->name();
×
3504
    const auto could_see = g->u.sees( *target );
×
3505
    target->hasten_upgrade();
×
3506
    target->try_upgrade( false );
×
3507
    const auto can_see = g->u.sees( *target );
×
3508
    if( g->u.sees( *z ) ) {
×
3509
        if( could_see ) {
×
3510
            //~ %1$s is the name of the zombie upgrading the other, %2$s is the zombie being upgraded.
3511
            add_msg( m_warning, _( "A black mist floats from the %1$s around the %2$s." ),
3512
                     z->name().c_str(), old_name.c_str() );
×
3513
        } else {
3514
            add_msg( m_warning, _( "A black mist floats from the %s." ), z->name().c_str() );
×
3515
        }
3516
    }
3517
    if( target->name() != old_name ) {
×
3518
        if( could_see && can_see ) {
×
3519
            //~ %1$s is the pre-upgrade monster, %2$s is the post-upgrade monster.
3520
            add_msg( m_warning, _( "The %1$s becomes a %2$s!" ), old_name.c_str(),
3521
                     target->name().c_str() );
×
3522
        } else if( could_see ) {
×
3523
            add_msg( m_warning, _( "The %s vanishes!" ), old_name.c_str() );
×
3524
        } else if( can_see ) {
×
3525
            add_msg( m_warning, _( "A %s appears!" ), target->name().c_str() );
×
3526
        }
3527
    }
3528

3529
    return true;
3530
}
3531

3532
bool mattack::breathe( monster *z )
×
3533
{
3534
    z->moves -= 100;   // It takes a while
×
3535

3536
    bool able = ( z->type->id == mon_breather_hub );
×
3537
    if( !able ) {
×
3538
        for( const tripoint &dest : g->m.points_in_radius( z->pos(), 3 ) ) {
×
3539
            monster *const mon = g->critter_at<monster>( dest );
×
3540
            if( mon && mon->type->id == mon_breather_hub ) {
×
3541
                able = true;
3542
                break;
3543
            }
3544
        }
3545
    }
3546
    if( !able ) {
×
3547
        return true;
3548
    }
3549

3550
    std::vector<tripoint> valid;
3551
    for( const tripoint &dest : g->m.points_in_radius( z->pos(), 1 ) ) {
×
3552
        if( g->is_empty( dest ) ) {
×
3553
            valid.push_back( dest );
×
3554
        }
3555
    }
3556

3557
    if( !valid.empty() ) {
×
3558
        const tripoint pt = random_entry( valid );
×
3559
        if( monster *const spawned = g->summon_mon( mon_breather, pt ) ) {
×
3560
            spawned->reset_special( "BREATHE" );
×
3561
            spawned->make_ally( *z );
×
3562
        }
3563
    }
3564

3565
    return true;
×
3566
}
3567

3568
bool mattack::stretch_bite( monster *z )
×
3569
{
3570
    if( !z->can_act() ) {
×
3571
        return false;
3572
    }
3573

3574
    // Let it be used on non-player creatures
3575
    // can be used at close range too!
3576
    Creature *target = z->attack_target();
×
3577
    if( target == nullptr || rl_dist( z->pos(), target->pos() ) > 3 || !z->sees( *target ) ) {
×
3578
        return false;
3579
    }
3580

3581
    z->moves -= 150;
×
3582

3583
    for( auto &pnt : g->m.find_clear_path( z->pos(), target->pos() ) ) {
×
3584
        if( g->m.impassable( pnt ) ) {
×
3585
            z->add_effect( effect_stunned, 6_turns );
×
3586
            target->add_msg_player_or_npc( _( "The %1$s stretches its head at you, but bounces off the %2$s" ),
3587
                                           _( "The %1$s stretches its head at <npcname>, but bounces off the %2$s" ),
3588
                                           z->name().c_str(), g->m.obstacle_name( pnt ).c_str() );
×
3589
            return true;
×
3590
        }
3591
    }
3592
    bool uncanny = target->uncanny_dodge();
×
3593
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
3594
    if( uncanny || dodge_check( z, target ) ) {
×
3595
        z->moves -= 150;
×
3596
        z->add_effect( effect_stunned, 3_turns );
×
3597
        auto msg_type = target == &g->u ? m_warning : m_info;
×
3598
        target->add_msg_player_or_npc( msg_type,
3599
                                       _( "The %s's head extends to bite you, but you dodge and the head sails past!" ),
3600
                                       _( "The %s's head extends to bite <npcname>, but they dodge and the head sails past!" ),
3601
                                       z->name().c_str() );
×
3602
        if( !uncanny ) {
×
3603
            target->on_dodge( z, z->type->melee_skill * 2 );
×
3604
        }
3605
        return true;
3606
    }
3607

3608
    body_part hit = target->get_random_body_part();
×
3609
    int dam = rng( 5, 15 ); //more damage due to the speed of the moving head
×
3610
    dam = target->deal_damage( z, hit, damage_instance( DT_STAB, dam ) ).total_damage();
×
3611

3612
    if( dam > 0 ) {
×
3613
        auto msg_type = target == &g->u ? m_bad : m_info;
×
3614
        //~ 1$s is monster name, 2$s bodypart in accusative
3615
        target->add_msg_player_or_npc( msg_type,
3616
                                       _( "The %1$s's teeth sink into your %2$s!" ),
3617
                                       _( "The %1$s's teeth sink into <npcname>'s %2$s!" ),
3618
                                       z->name().c_str(),
×
3619
                                       body_part_name_accusative( hit ).c_str() );
×
3620

3621
        if( one_in( 16 - dam ) ) {
×
3622
            if( target->has_effect( effect_bite, hit ) ) {
×
3623
                target->add_effect( effect_bite, 40_minutes, hit, true );
×
3624
            } else if( target->has_effect( effect_infected, hit ) ) {
×
3625
                target->add_effect( effect_infected, 25_minutes, hit, true );
×
3626
            } else {
3627
                target->add_effect( effect_bite, 1_turns, hit, true );
×
3628
            }
3629
        }
3630
    } else {
3631
        target->add_msg_player_or_npc( _( "The %1$s's head hits your %2$s, but glances off your armor!" ),
3632
                                       _( "The %1$s's head hits <npcname>'s %2$s, but glances off armor!" ),
3633
                                       z->name().c_str(),
×
3634
                                       body_part_name_accusative( hit ).c_str() );
×
3635
    }
3636

3637
    target->on_hit( z, hit,  z->type->melee_skill );
×
3638

3639
    return true;
×
3640
}
3641

3642
bool mattack::brandish( monster *z )
×
3643
{
3644
    if( z->friendly ) {
×
3645
        return false; // TODO: handle friendly monsters
3646
    }
3647
    if( !z->sees( g->u ) ) {
×
3648
        return false; // Only brandish if we can see you!
3649
    }
3650
    add_msg( m_warning, _( "He's brandishing a knife!" ) );
×
3651
    add_msg( _( "Quiet, quiet" ) );
×
3652

3653
    return true;
×
3654
}
3655

3656
bool mattack::flesh_golem( monster *z )
×
3657
{
3658
    if( !z->can_act() ) {
×
3659
        return false;
3660
    }
3661

3662
    Creature *target = z->attack_target();
×
3663
    if( target == nullptr ) {
×
3664
        return false;
3665
    }
3666

3667
    int dist = rl_dist( z->pos(), target->pos() );
×
3668
    if( dist > 20 ||
×
3669
        !z->sees( *target ) ) {
×
3670
        return false;
3671
    }
3672

3673
    if( dist > 1 ) {
×
3674
        if( one_in( 12 ) ) {
×
3675
            z->moves -= 200;
×
3676
            // It doesn't "nearly deafen you" when it roars from the other side of bubble
3677
            sounds::sound( z->pos(), 80, _( "a terrifying roar!" ) );
×
3678
            return true;
×
3679
        }
3680
        return false;
3681
    }
3682
    if( !is_adjacent( z, target, true ) ) {
×
3683
        // No attacking through floor, even if we can see the target somehow
3684
        return false;
3685
    }
3686
    if( g->u.sees( *z ) ) {
×
3687
        add_msg( _( "The %1$s swings a massive claw at %2$s!" ), z->name().c_str(),
×
3688
                 target->disp_name().c_str() );
×
3689
    }
3690
    z->moves -= 100;
×
3691

3692
    if( target->uncanny_dodge() ) {
×
3693
        return true;
3694
    }
3695

3696
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
3697
    if( dodge_check( z, target ) ) {
×
3698
        target->add_msg_player_or_npc( _( "You dodge it!" ),
3699
                                       _( "<npcname> dodges it!" ) );
×
3700
        target->on_dodge( z, z->type->melee_skill * 2 );
×
3701
        return true;
3702
    }
3703
    body_part hit = target->get_random_body_part();
×
3704
    // TODO: 10 bashing damage doesn't sound like a "massive claw" but a mediocre punch
3705
    int dam = rng( 5, 10 );
×
3706
    //~ 1$s is bodypart name, 2$d is damage value.
3707
    target->deal_damage( z, hit, damage_instance( DT_BASH, dam ) );
×
3708
    if( one_in( 6 ) ) {
×
3709
        target->add_effect( effect_downed, 3_minutes );
×
3710
    }
3711

3712
    target->add_msg_if_player( m_bad, _( "Your %1$s is battered for %2$d damage!" ),
3713
                               body_part_name( hit ).c_str(), dam );
×
3714
    target->on_hit( z, hit,  z->type->melee_skill );
×
3715

3716
    return true;
3717
}
3718

3719
bool mattack::lunge( monster *z )
×
3720
{
3721
    if( !z->can_act() ) {
×
3722
        return false;
3723
    }
3724

3725
    Creature *target = z->attack_target();
×
3726
    if( target == nullptr ) {
×
3727
        return false;
3728
    }
3729

3730
    int dist = rl_dist( z->pos(), target->pos() );
×
3731
    if( dist > 20 ||
×
3732
        !z->sees( *target ) ) {
×
3733
        return false;
3734
    }
3735

3736
    bool seen = g->u.sees( *z );
×
3737
    if( dist > 1 ) {
×
3738
        if( one_in( 5 ) ) {
×
3739
            if( dist > 4 || !z->sees( *target ) ) {
×
3740
                return false; // Out of range
3741
            }
3742
            z->moves += 200;
×
3743
            if( seen ) {
×
3744
                add_msg( _( "The %1$s lunges for %2$s!" ), z->name().c_str(), target->disp_name().c_str() );
×
3745
            }
3746
            return true;
3747
        }
3748
        return false;
3749
    }
3750

3751
    if( !is_adjacent( z, target, false ) ) {
×
3752
        // No attacking up or down - lunging requires contact
3753
        // There could be a lunge down attack, though
3754
        return false;
3755
    }
3756

3757
    z->moves -= 100;
×
3758

3759
    if( target->uncanny_dodge() ) {
×
3760
        return true;
3761
    }
3762

3763
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
3764
    if( dodge_check( z, target ) ) {
×
3765
        target->add_msg_player_or_npc( _( "The %1$s lunges at you, but you sidestep it!" ),
3766
                                       _( "The %1$s lunges at <npcname>, but they sidestep it!" ), z->name().c_str() );
×
3767
        target->on_dodge( z, z->type->melee_skill * 2 );
×
3768
        return true;
3769
    }
3770
    body_part hit = target->get_random_body_part();
×
3771
    int dam = rng( 3, 7 );
×
3772
    dam = target->deal_damage( z, hit, damage_instance( DT_BASH, dam ) ).total_damage();
×
3773
    if( dam > 0 ) {
×
3774
        auto msg_type = target == &g->u ? m_bad : m_warning;
×
3775
        target->add_msg_player_or_npc( msg_type,
3776
                                       _( "The %1$s lunges at your %2$s, battering it for %3$d damage!" ),
3777
                                       _( "The %1$s lunges at <npcname>'s %2$s, battering it for %3$d damage!" ),
3778
                                       z->name().c_str(), body_part_name( hit ).c_str(), dam );
×
3779
    } else {
3780
        target->add_msg_player_or_npc( _( "The %1$s lunges at your %2$s, but your armor prevents injury!" ),
3781
                                       _( "The %1$s lunges at <npcname>'s %2$s, but their armor prevents injury!" ),
3782
                                       z->name().c_str(),
×
3783
                                       body_part_name_accusative( hit ).c_str() );
×
3784
    }
3785
    if( one_in( 6 ) ) {
×
3786
        target->add_effect( effect_downed, 3_turns );
×
3787
    }
3788
    target->on_hit( z, hit,  z->type->melee_skill );
×
3789
    target->check_dead_state();
×
3790
    return true;
3791
}
3792

3793
bool mattack::longswipe( monster *z )
×
3794
{
3795
    if( z->friendly ) {
×
3796
        return false; // TODO: handle friendly monsters
3797
    }
3798
    Creature *target = z->attack_target();
×
3799
    if( target == nullptr ) {
×
3800
        return false;
3801
    }
3802
    if( rl_dist( z->pos(), target->pos() ) > 3 || !z->sees( *target ) ) {
×
3803
        return false; //out of range
3804
    }
3805
    //Is there something impassable blocking the claw?
3806
    for( const auto &pnt : g->m.find_clear_path( z->pos(), target->pos() ) ) {
×
3807
        if( g->m.impassable( pnt ) ) {
×
3808
            //If we're here, it's an nonadjacent attack, which is only attempted 1/5 of the time.
3809
            if( !one_in( 5 ) ) {
×
3810
                return false;
3811
            }
3812
            target->add_msg_player_or_npc( _( "The %1$s thrusts a claw at you, but it bounces off the %2$s!" ),
3813
                                           _( "The %1$s thrusts a claw at <npcname>, but it bounces off the %2$s!" ),
3814
                                           z->name().c_str(), g->m.obstacle_name( pnt ).c_str() );
×
3815
            z->mod_moves( -150 );
×
3816
            return true;
3817
        }
3818
    }
3819

3820
    if( !is_adjacent( z, target, true ) ) {
×
3821
        if( one_in( 5 ) ) {
×
3822

3823
            z->moves -= 150;
×
3824

3825
            if( target->uncanny_dodge() ) {
×
3826
                return true;
3827
            }
3828
            // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
3829
            if( dodge_check( z, target ) ) {
×
3830
                target->add_msg_player_or_npc( _( "The %s thrusts a claw at you, but you evade it!" ),
3831
                                               _( "The %s thrusts a claw at <npcname>, but they evade it!" ),
3832
                                               z->name().c_str() );
×
3833
                target->on_dodge( z, z->type->melee_skill * 2 );
×
3834
                return true;
3835
            }
3836
            body_part hit = target->get_random_body_part();
×
3837
            int dam = rng( 3, 7 );
×
3838
            dam = target->deal_damage( z, hit, damage_instance( DT_CUT, dam ) ).total_damage();
×
3839
            if( dam > 0 ) {
×
3840
                auto msg_type = target == &g->u ? m_bad : m_warning;
×
3841
                //~ 1$s is bodypart name, 2$d is damage value.
3842
                target->add_msg_player_or_npc( msg_type,
3843
                                               _( "The %1$s thrusts a claw at your %2$s, slashing it for %3$d damage!" ),
3844
                                               _( "The %1$s thrusts a claw at <npcname>'s %2$s, slashing it for %3$d damage!" ),
3845
                                               z->name().c_str(), body_part_name( hit ).c_str(), dam );
×
3846
            } else {
3847
                target->add_msg_player_or_npc(
3848
                    _( "The %1$s thrusts a claw at your %2$s, but glances off your armor!" ),
3849
                    _( "The %1$s thrusts a claw at <npcname>'s %2$s, but glances off armor!" ),
3850
                    z->name().c_str(),
×
3851
                    body_part_name_accusative( hit ).c_str() );
×
3852
            }
3853
            target->on_hit( z, hit,  z->type->melee_skill );
×
3854
            return true;
3855
        }
3856
        return false;
3857
    }
3858
    z->moves -= 100;
×
3859

3860
    if( target->uncanny_dodge() ) {
×
3861
        return true;
3862
    }
3863

3864
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
3865
    if( dodge_check( z, target ) ) {
×
3866
        target->add_msg_player_or_npc( _( "The %s slashes at your neck! You duck!" ),
3867
                                       _( "The %s slashes at <npcname>'s neck! They duck!" ), z->name().c_str() );
×
3868
        target->on_dodge( z, z->type->melee_skill * 2 );
×
3869
        return true;
3870
    }
3871
    body_part hit = bp_head;
×
3872
    int dam = rng( 6, 10 );
×
3873
    dam = target->deal_damage( z, hit, damage_instance( DT_CUT, dam ) ).total_damage();
×
3874
    if( dam > 0 ) {
×
3875
        auto msg_type = target == &g->u ? m_bad : m_warning;
×
3876
        target->add_msg_player_or_npc( msg_type,
3877
                                       _( "The %1$s slashes at your neck, cutting your throat for %2$d damage!" ),
3878
                                       _( "The %1$s slashes at <npcname>'s neck, cutting their throat for %2$d damage!" ),
3879
                                       z->name().c_str(), dam );
×
3880
        target->add_effect( effect_bleed, 10_minutes, hit );
×
3881
    } else {
3882
        target->add_msg_player_or_npc( _( "The %1$s slashes at your %2$s, but glances off your armor!" ),
3883
                                       _( "The %1$s slashes at <npcname>'s %2$s, but glances off armor!" ),
3884
                                       z->name().c_str(),
×
3885
                                       body_part_name_accusative( hit ).c_str() );
×
3886
    }
3887
    target->on_hit( z, hit,  z->type->melee_skill );
×
3888
    target->check_dead_state();
×
3889

3890
    return true;
3891
}
3892

3893
bool mattack::parrot( monster *z )
×
3894
{
3895
    if( z->has_effect( effect_shrieking ) ) {
×
3896
        sounds::sound( z->pos(), 120, _( "a piercing wail!" ), true );
×
3897
        z->moves -= 40;
×
3898
        return false;
×
3899
    } else if( one_in( 20 ) ) {
×
3900
        z->moves -= 100;  // It takes a while
×
3901
        const SpeechBubble speech = get_speech( z->type->id.str() );
×
3902
        sounds::sound( z->pos(), speech.volume, speech.text );
×
3903
        return true;
×
3904
    }
3905

3906
    return false;
3907
}
3908

3909
bool mattack::darkman( monster *z )
×
3910
{
3911
    if( z->friendly ) {
×
3912
        return false; // TODO: handle friendly monsters
3913
    }
3914
    if( rl_dist( z->pos(), g->u.pos() ) > 40 ) {
×
3915
        return false;
3916
    }
3917
    std::vector<tripoint> free;
3918
    for( const tripoint &dest : g->m.points_in_radius( z->pos(), 1 ) ) {
×
3919
        if( g->is_empty( dest ) ) {
×
3920
            free.push_back( dest );
×
3921
        }
3922
    }
3923
    if( !free.empty() ) {
×
3924
        z->moves -= 10;
×
3925
        const tripoint target = random_entry( free );
×
3926
        if( monster *const shadow = g->summon_mon( mon_shadow, target ) ) {
×
3927
            shadow->make_ally( *z );
×
3928
        }
3929
        if( g->u.sees( *z ) ) {
×
3930
            add_msg( m_warning, _( "A shadow splits from the %s!" ),
3931
                     z->name().c_str() );
×
3932
        }
3933
    }
3934
    if( !z->sees( g->u ) ) {
×
3935
        return true; // Wont do the combat stuff unless it can see you
3936
    }
3937
    switch( rng( 1, 7 ) ) { // What do we say?
×
3938
        case 1:
3939
            add_msg( _( "\"Stop it please\"" ) );
×
3940
            break;
3941
        case 2:
3942
            add_msg( _( "\"Let us help you\"" ) );
×
3943
            break;
3944
        case 3:
3945
            add_msg( _( "\"We wish you no harm\"" ) );
×
3946
            break;
3947
        case 4:
3948
            add_msg( _( "\"Do not fear\"" ) );
×
3949
            break;
3950
        case 5:
3951
            add_msg( _( "\"We can help you\"" ) );
×
3952
            break;
3953
        case 6:
3954
            add_msg( _( "\"We are friendly\"" ) );
×
3955
            break;
3956
        case 7:
3957
            add_msg( _( "\"Please dont\"" ) );
×
3958
            break;
3959
    }
3960
    g->u.add_effect( effect_darkness, 1_turns, num_bp, true );
×
3961

3962
    return true;
3963
}
3964

3965
bool mattack::slimespring( monster *z )
×
3966
{
3967
    if( rl_dist( z->pos(), g->u.pos() ) > 30 ) {
×
3968
        return false;
3969
    }
3970

3971
    // This morale buff effect could get spammy
3972
    if( g->u.get_morale_level() <= 1 ) {
×
3973
        switch( rng( 1, 3 ) ) { //~ Your slimes try to cheer you up!
×
3974
            case 1:
3975
                //~ Lowercase is intended: they're small voices.
3976
                add_msg( m_good, _( "\"hey, it's gonna be all right!\"" ) );
×
3977
                g->u.add_morale( MORALE_SUPPORT, 10, 50 );
×
3978
                break;
×
3979
            case 2:
3980
                //~ Lowercase is intended: they're small voices.
3981
                add_msg( m_good, _( "\"we'll get through this!\"" ) );
×
3982
                g->u.add_morale( MORALE_SUPPORT, 10, 50 );
×
3983
                break;
×
3984
            case 3:
3985
                //~ Lowercase is intended: they're small voices.
3986
                add_msg( m_good, _( "\"i'm here for you!\"" ) );
×
3987
                g->u.add_morale( MORALE_SUPPORT, 10, 50 );
×
3988
                break;
×
3989
        }
3990
    }
3991
    if( rl_dist( z->pos(), g->u.pos() ) <= 3 && z->sees( g->u ) ) {
×
3992
        if( ( g->u.has_effect( effect_bleed ) ) || ( g->u.has_effect( effect_bite ) ) ) {
×
3993
            //~ Lowercase is intended: they're small voices.
3994
            add_msg( _( "\"let me help!\"" ) );
×
3995
            // Yes, your slimespring(s) handle/don't all Bad Damage at the same time.
3996
            if( g->u.has_effect( effect_bite ) ) {
×
3997
                if( one_in( 3 ) ) {
×
3998
                    g->u.remove_effect( effect_bite );
×
3999
                    add_msg( m_good, _( "The slime cleans you out!" ) );
×
4000
                } else {
4001
                    add_msg( _( "The slime flows over you, but your gouges still ache." ) );
×
4002
                }
4003
            }
4004
            if( g->u.has_effect( effect_bleed ) ) {
×
4005
                if( one_in( 2 ) ) {
×
4006
                    g->u.remove_effect( effect_bleed );
×
4007
                    add_msg( m_good, _( "The slime seals up your leaks!" ) );
×
4008
                } else {
4009
                    add_msg( _( "The slime flows over you, but your fluids are still leaking." ) );
×
4010
                }
4011
            }
4012
        }
4013
    }
4014

4015
    return true;
4016
}
4017

4018
bool mattack::thrown_by_judo( monster *z )
×
4019
{
4020
    Creature *target = z->attack_target();
×
4021
    if( target == nullptr ||
×
4022
        !is_adjacent( z, target, false ) ||
×
4023
        !z->sees( *target ) ) {
×
4024
        return false;
4025
    }
4026

4027
    player *foe = dynamic_cast< player * >( target );
×
4028
    if( foe == nullptr ) {
×
4029
        // No mons for now
4030
        return false;
4031
    }
4032
    // "Wimpy" Judo is about to pay off... :D
4033
    if( foe->is_throw_immune() ) {
×
4034
        // DX + Unarmed
4035
        ///\EFFECT_DEX increases chance judo-throwing a monster
4036

4037
        ///\EFFECT_UNARMED increases chance of judo-throwing monster, vs their melee skill
4038
        if( ( ( foe->dex_cur + foe->get_skill_level( skill_unarmed ) ) > ( z->type->melee_skill + rng( 0,
×
4039
                3 ) ) ) ) {
×
4040
            target->add_msg_if_player( m_good, _( "but you grab its arm and flip it to the ground!" ) );
×
4041

4042
            // most of the time, when not isolated
4043
            if( !one_in( 4 ) && !target->is_elec_immune() && z->type->sp_defense == &mdefense::zapback ) {
×
4044
                // If it all pans out, we're zap the player's arm as he flips the monster.
4045
                target->add_msg_if_player( _( "The flip does shock you..." ) );
×
4046
                // Discounted electric damage for quick flip
4047
                damage_instance shock;
×
4048
                shock.add_damage( DT_ELECTRIC, rng( 1, 3 ) );
×
4049
                foe->deal_damage( z, bp_arm_l, shock );
×
4050
                foe->deal_damage( z, bp_arm_r, shock );
×
4051
                foe->check_dead_state();
×
4052
            }
4053
            // Monster is down,
4054
            z->add_effect( effect_downed, 5_turns );
×
4055
            // Deal moderate damage
4056
            const auto damage = rng( 10, 20 );
×
4057
            z->apply_damage( foe, bp_torso, damage );
×
4058
            z->check_dead_state();
×
4059
        } else {
4060
            // Still avoids the major hit!
4061
            target->add_msg_if_player( _( "but you deftly spin out of its grasp!" ) );
×
4062
        }
4063
        return true;
4064
    } else {
4065
        return false;
4066
    }
4067
}
4068

4069
bool mattack::riotbot( monster *z )
×
4070
{
4071
    Creature *target = z->attack_target();
×
4072
    if( target == nullptr ) {
×
4073
        return false;
4074
    }
4075

4076
    player *foe = dynamic_cast<player *>( target );
×
4077

4078
    if( calendar::once_every( 1_minutes ) ) {
×
4079
        for( const tripoint &dest : g->m.points_in_radius( z->pos(), 4 ) ) {
×
4080
            if( g->m.passable( dest ) &&
×
4081
                g->m.clear_path( z->pos(), dest, 3, 1, 100 ) ) {
×
4082
                g->m.add_field( dest, fd_relax_gas, rng( 1, 3 ) );
×
4083
            }
4084
        }
4085
    }
4086

4087
    //already arrested?
4088
    //and yes, if the player has no hands, we are not going to arrest him.
4089
    if( foe != nullptr &&
×
4090
        ( foe->weapon.typeId() == "e_handcuffs" || !foe->has_two_arms() ) ) {
×
4091
        z->anger = 0;
×
4092

4093
        if( calendar::once_every( 25_turns ) ) {
×
4094
            sounds::sound( z->pos(), 10,
×
4095
                           _( "Halt and submit to arrest, citizen! The police will be here any moment." ) );
×
4096
        }
4097

4098
        return true;
4099
    }
4100

4101
    if( z->anger < z->type->agro ) {
×
4102
        z->anger += z->type->agro / 20;
×
4103
        return true;
×
4104
    }
4105

4106
    const int dist = rl_dist( z->pos(), target->pos() );
×
4107

4108
    //we need empty hands to arrest
4109
    if( foe == &g->u && !foe->is_armed() ) {
×
4110

4111
        sounds::sound( z->pos(), 15, _( "Please stay in place, citizen, do not make any movements!" ) );
×
4112

4113
        //we need to come closer and arrest
4114
        if( !is_adjacent( z, foe, false ) ) {
×
4115
            return true;
4116
        }
4117

4118
        //Strain the atmosphere, forcing the player to wait. Let him feel the power of law!
4119
        if( !one_in( 10 ) ) {
×
4120
            foe->add_msg_player_or_npc( _( "The robot carefully scans you." ),
4121
                                        _( "The robot carefully scans <npcname>." ) );
×
4122
            return true;
4123
        }
4124

4125
        enum {ur_arrest, ur_resist, ur_trick};
4126

4127
        //arrest!
4128
        uilist amenu;
×
4129
        amenu.allow_cancel = false;
×
4130
        amenu.text = _( "The riotbot orders you to present your hands and be cuffed." );
×
4131

4132
        amenu.addentry( ur_arrest, true, 'a', _( "Allow yourself to be arrested." ) );
×
4133
        amenu.addentry( ur_resist, true, 'r', _( "Resist arrest!" ) );
×
4134
        ///\EFFECT_INT >10 allows and increases chance whether you can feign death to avoid riot bot arrest
4135
        if( foe->int_cur > 12 || ( foe->int_cur > 10 && !one_in( foe->int_cur - 8 ) ) ) {
×
4136
            amenu.addentry( ur_trick, true, 't', _( "Feign death." ) );
×
4137
        }
4138

4139
        amenu.query();
×
4140
        const int choice = amenu.ret;
×
4141

4142
        if( choice == ur_arrest ) {
×
4143
            z->anger = 0;
×
4144

4145
            item handcuffs( "e_handcuffs", 0 );
×
4146
            handcuffs.charges = handcuffs.type->maximum_charges();
×
4147
            handcuffs.active = true;
×
4148
            handcuffs.set_var( "HANDCUFFS_X", foe->posx() );
×
4149
            handcuffs.set_var( "HANDCUFFS_Y", foe->posy() );
×
4150

4151
            const bool is_uncanny = foe->has_active_bionic( bionic_id( "bio_uncanny_dodge" ) ) &&
×
4152
                                    foe->power_level > 74 &&
×
4153
                                    !one_in( 3 );
×
4154
            ///\EFFECT_DEX >13 allows and increases chance to slip out of riot bot handcuffs
4155
            const bool is_dex = foe->dex_cur > 13 && !one_in( foe->dex_cur - 11 );
×
4156

4157
            if( is_uncanny || is_dex ) {
×
4158

4159
                if( is_uncanny ) {
×
4160
                    foe->charge_power( -75 );
×
4161
                }
4162

4163
                add_msg( m_good,
4164
                         _( "You deftly slip out of the handcuffs just as the robot closes them.  The robot didn't seem to notice!" ) );
×
4165
                foe->i_add( handcuffs );
×
4166
            } else {
4167
                handcuffs.item_tags.insert( "NO_UNWIELD" );
×
4168
                foe->wield( foe->i_add( handcuffs ) );
×
4169
                foe->moves -= 300;
×
4170
                add_msg( _( "The robot puts handcuffs on you." ) );
×
4171
            }
4172

4173
            sounds::sound( z->pos(), 5,
×
4174
                           _( "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." ) );
×
4175
            sounds::sound( z->pos(), 5,
×
4176
                           _( "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." ) );
×
4177
            sounds::sound( z->pos(), 5,
×
4178
                           _( "If you do not understand these rights, an officer will explain them in greater detail when taking you into custody." ) );
×
4179
            sounds::sound( z->pos(), 5,
×
4180
                           _( "Do not attempt to flee or to remove the handcuffs, citizen.  That can be dangerous to your health." ) );
×
4181

4182
            z->moves -= 300;
×
4183

4184
            return true;
×
4185
        }
4186

4187
        bool bad_trick = false;
×
4188

4189
        if( choice == ur_trick ) {
×
4190

4191
            ///\EFFECT_INT >10 allows and increases chance of successful feign death against riot bot
4192
            if( !one_in( foe->int_cur - 10 ) ) {
×
4193

4194
                add_msg( m_good,
4195
                         _( "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." ) );
×
4196

4197
                z->moves -= 300;
×
4198
                z->anger = -rng( 0, 50 );
×
4199
                return true;
×
4200
            } else {
4201
                add_msg( m_bad, _( "Your awkward movements do not fool the riotbot." ) );
×
4202
                foe->moves -= 100;
×
4203
                bad_trick = true;
×
4204
            }
4205
        }
4206

4207
        if( ( choice == ur_resist ) || bad_trick ) {
×
4208

4209
            add_msg( m_bad, _( "The robot sprays tear gas!" ) );
×
4210
            z->moves -= 200;
×
4211

4212
            for( const tripoint &dest : g->m.points_in_radius( z->pos(), 2 ) ) {
×
4213
                if( g->m.passable( dest ) &&
×
4214
                    g->m.clear_path( z->pos(), dest, 3, 1, 100 ) ) {
×
4215
                    g->m.add_field( dest, fd_tear_gas, rng( 1, 3 ) );
×
4216
                }
4217
            }
4218

4219
            return true;
×
4220
        }
4221

4222
        return true;
×
4223
    }
4224

4225
    if( calendar::once_every( 5_turns ) ) {
×
4226
        sounds::sound( z->pos(), 25, _( "Empty your hands and hold your position, citizen!" ) );
×
4227
    }
4228

4229
    if( dist > 5 && dist < 18 && one_in( 10 ) ) {
×
4230

4231
        z->moves -= 50;
×
4232

4233
        int delta = dist / 4 + 1;  //precautionary shot
×
4234
        if( z->get_hp() < z->get_hp_max() ) {
×
4235
            delta = 1;    //precision shot
×
4236
        }
4237

4238
        tripoint dest( target->posx() + rng( 0, delta ) - rng( 0, delta ),
×
4239
                       target->posy() + rng( 0, delta ) - rng( 0, delta ),
×
4240
                       target->posz() );
×
4241

4242
        //~ Sound of a riotbot using its blinding flash
4243
        sounds::sound( z->pos(), 3, _( "fzzzzzt" ) );
×
4244

4245
        std::vector<tripoint> traj = line_to( z->pos(), dest, 0, 0 );
×
4246
        for( auto &elem : traj ) {
×
4247
            if( !g->m.trans( elem ) ) {
×
4248
                break;
4249
            }
4250
            g->m.add_field( elem, fd_dazzling, 1 );
×
4251
        }
4252
        return true;
×
4253

4254
    }
4255

4256
    return true;
4257
}
4258

4259
bool mattack::bio_op_takedown( monster *z )
×
4260
{
4261
    if( !z->can_act() ) {
×
4262
        return false;
4263
    }
4264

4265
    Creature *target = z->attack_target();
×
4266
    // TODO: Allow drop-takedown form above
4267
    if( target == nullptr ||
×
4268
        !is_adjacent( z, target, false ) ||
×
4269
        !z->sees( *target ) ) {
×
4270
        return false;
4271
    }
4272

4273
    bool seen = g->u.sees( *z );
×
4274
    player *foe = dynamic_cast< player * >( target );
×
4275
    if( seen ) {
×
4276
        add_msg( _( "The %1$s mechanically grabs at %2$s!" ), z->name().c_str(),
×
4277
                 target->disp_name().c_str() );
×
4278
    }
4279
    z->moves -= 100;
×
4280

4281
    if( target->uncanny_dodge() ) {
×
4282
        return true;
4283
    }
4284

4285
    // Can we dodge the attack? Uses player dodge function % chance (melee.cpp)
4286
    if( dodge_check( z, target ) ) {
×
4287
        target->add_msg_player_or_npc( _( "You dodge it!" ),
4288
                                       _( "<npcname> dodges it!" ) );
×
4289
        target->on_dodge( z, z->type->melee_skill * 2 );
×
4290
        return true;
4291
    }
4292
    int dam = rng( 3, 9 );
×
4293
    if( foe == nullptr ) {
×
4294
        // Handle mons earlier - less to check for
4295
        dam = rng( 6, 18 ); // Always aim for the torso
×
4296
        target->deal_damage( z, bp_torso, damage_instance( DT_BASH, dam ) ); // Two hits - "leg" and torso
×
4297
        target->deal_damage( z, bp_torso, damage_instance( DT_BASH, dam ) );
×
4298
        target->add_effect( effect_downed, 3_turns );
×
4299
        if( seen ) {
×
4300
            add_msg( _( "%1$s slams %2$s to the ground!" ), z->name().c_str(), target->disp_name().c_str() );
×
4301
        }
4302
        target->check_dead_state();
×
4303
        return true;
4304
    }
4305
    // Yes, it has the CQC bionic.
4306
    body_part hit = num_bp;
×
4307
    if( one_in( 2 ) ) {
×
4308
        hit = bp_leg_l;
4309
    } else {
4310
        hit = bp_leg_r;
×
4311
    }
4312
    // Weak kick to start with, knocks you off your footing
4313

4314
    // Literally "The zombie kicks" vvvvv |  FIXME FIX message or comment why Literally.
4315
    //~ 1$s is bodypart name in accusative, 2$d is damage value.
4316
    target->add_msg_if_player( m_bad, _( "The zombie kicks your %1$s for %2$d damage..." ),
4317
                               body_part_name_accusative( hit ).c_str(), dam );
×
4318
    foe->deal_damage( z, hit, damage_instance( DT_BASH, dam ) );
×
4319
    // At this point, Judo or Tentacle Bracing can make this much less painful
4320
    if( !foe->is_throw_immune() ) {
×
4321
        if( !target->is_immune_effect( effect_downed ) ) {
×
4322
            if( one_in( 4 ) ) {
×
4323
                hit = bp_head;
×
4324
                dam = rng( 9, 21 ); // 50% damage buff for the headshot.
×
4325
                target->add_msg_if_player( m_bad, _( "and slams you, face first, to the ground for %d damage!" ),
4326
                                           dam );
×
4327
                foe->deal_damage( z, hit, damage_instance( DT_BASH, dam ) );
×
4328
            } else {
4329
                hit = bp_torso;
×
4330
                dam = rng( 6, 18 );
×
4331
                target->add_msg_if_player( m_bad, _( "and slams you to the ground for %d damage!" ), dam );
×
4332
                foe->deal_damage( z, hit, damage_instance( DT_BASH, dam ) );
×
4333
            }
4334
            foe->add_effect( effect_downed, 3_turns );
×
4335
        }
4336
    } else if( !thrown_by_judo( z ) ) {
×
4337
        // Saved by the tentacle-bracing! :)
4338
        hit = bp_torso;
×
4339
        dam = rng( 3, 9 );
×
4340
        target->add_msg_if_player( m_bad, _( "and slams you for %d damage!" ), dam );
×
4341
        foe->deal_damage( z, hit, damage_instance( DT_BASH, dam ) );
×
4342
    }
4343
    target->on_hit( z, hit,  z->type->melee_skill );
×
4344
    foe->check_dead_state();
×
4345

4346
    return true;
4347
}
4348

4349
bool mattack::suicide( monster *z )
×
4350
{
4351
    Creature *target = z->attack_target();
×
4352
    if( !within_target_range( z, target, 2 ) ) {
×
4353
        return false;
4354
    }
4355
    z->die( z );
×
4356

4357
    return false;
×
4358
}
4359

4360
bool mattack::kamikaze( monster *z )
×
4361
{
4362
    if( z->ammo.empty() ) {
×
4363
        // We somehow lost our ammo! Toggle this special off so we stop processing
4364
        add_msg( m_debug, "Missing ammo in kamikaze special for %s.", z->name().c_str() );
×
4365
        z->disable_special( "KAMIKAZE" );
×
4366
        return true;
×
4367
    }
4368

4369
    // Get the bomb type and it's data
4370
    const auto bomb_type = item::find_type( z->ammo.begin()->first );
×
4371
    const itype *act_bomb_type;
4372
    long charges;
4373
    // Hardcoded data for charge variant items
4374
    if( z->ammo.begin()->first == "mininuke" ) {
×
4375
        act_bomb_type = item::find_type( "mininuke_act" );
×
4376
        charges = 20;
×
4377
    } else if( z->ammo.begin()->first == "c4" ) {
×
4378
        act_bomb_type = item::find_type( "c4armed" );
×
4379
        charges = 10;
×
4380
    } else {
4381
        auto usage = bomb_type->get_use( "transform" );
×
4382
        if( usage == nullptr ) {
×
4383
            // Invalid item usage, Toggle this special off so we stop processing
4384
            add_msg( m_debug, "Invalid bomb transform use in kamikaze special for %s.", z->name().c_str() );
×
4385
            z->disable_special( "KAMIKAZE" );
×
4386
            return true;
×
4387
        }
4388
        const iuse_transform *actor = dynamic_cast<const iuse_transform *>( usage->get_actor_ptr() );
×
4389
        if( actor == nullptr ) {
×
4390
            // Invalid bomb item, Toggle this special off so we stop processing
4391
            add_msg( m_debug, "Invalid bomb type in kamikaze special for %s.", z->name().c_str() );
×
4392
            z->disable_special( "KAMIKAZE" );
×
4393
            return true;
×
4394
        }
4395
        act_bomb_type = item::find_type( actor->target );
×
4396
        charges = actor->ammo_qty;
×
4397
    }
4398

4399
    // HORRIBLE HACK ALERT! Remove the following code completely once we have working monster inventory processing
4400
    if( z->has_effect( effect_countdown ) ) {
×
4401
        if( z->get_effect( effect_countdown ).get_duration() == 1_turns ) {
×
4402
            z->die( nullptr );
×
4403
            // Timer is out, detonate
4404
            item i_explodes( act_bomb_type, calendar::turn, 0 );
×
4405
            i_explodes.active = true;
×
4406
            i_explodes.process( nullptr, z->pos(), false );
×
4407
            return false;
×
4408
        }
4409
        return false;
4410
    }
4411
    // END HORRIBLE HACK
4412

4413
    auto use = act_bomb_type->get_use( "explosion" );
×
4414
    if( use == nullptr ) {
×
4415
        // Invalid active bomb item usage, Toggle this special off so we stop processing
4416
        add_msg( m_debug, "Invalid active bomb explosion use in kamikaze special for %s.",
4417
                 z->name().c_str() );
×
4418
        z->disable_special( "KAMIKAZE" );
×
4419
        return true;
×
4420
    }
4421
    const explosion_iuse *exp_actor = dynamic_cast<const explosion_iuse *>( use->get_actor_ptr() );
×
4422
    if( exp_actor == nullptr ) {
×
4423
        // Invalid active bomb item, Toggle this special off so we stop processing
4424
        add_msg( m_debug, "Invalid active bomb type in kamikaze special for %s.", z->name().c_str() );
×
4425
        z->disable_special( "KAMIKAZE" );
×
4426
        return true;
×
4427
    }
4428

4429
    // Get our blast radius
4430
    int radius = -1;
×
4431
    if( exp_actor->fields_radius > radius ) {
×
4432
        radius = exp_actor->fields_radius;
×
4433
    }
4434
    if( exp_actor->emp_blast_radius > radius ) {
×
4435
        radius = exp_actor->emp_blast_radius;
×
4436
    }
4437
    // Extra check here to avoid sqrt if not needed
4438
    if( exp_actor->explosion.power > -1 ) {
×
4439
        int tmp = int( sqrt( double( exp_actor->explosion.power / 4 ) ) );
×
4440
        if( tmp > radius ) {
×
4441
            radius = tmp;
×
4442
        }
4443
    }
4444
    if( exp_actor->explosion.shrapnel.casing_mass > 0 ) {
×
4445
        // Actual factor is 2 * radius, but figure most pieces of shrapnel will miss
4446
        int tmp = int( sqrt( exp_actor->explosion.power ) );
×
4447
        if( tmp > radius ) {
×
4448
            radius = tmp;
×
4449
        }
4450
    }
4451
    // Flashbangs have a max range of 8
4452
    if( exp_actor->do_flashbang && radius < 8 ) {
×
4453
        radius = 8;
×
4454
    }
4455
    if( radius <= -1 ) {
×
4456
        // Not a valid explosion size, toggle this special off to stop processing
4457
        z->disable_special( "KAMIKAZE" );
×
4458
        return true;
×
4459
    }
4460

4461
    Creature *target = z->attack_target();
×
4462
    if( target == nullptr ) {
×
4463
        return false;
4464
    }
4465
    // Range is (radius + distance they expect to gain on you during the countdown)
4466
    // We double target speed because if the player is walking and then start to run their effective speed doubles
4467
    // .65 factor was determined experimentally to be about the factor required for players to be able to *just barely*
4468
    // outrun the explosion if they drop everything and run.
4469
    float factor = float( z->get_speed() ) / float( target->get_speed() * 2 );
×
4470
    int range = std::max( 1, int( .65 * ( radius + 1 + factor * charges ) ) );
×
4471

4472
    // Check if we are in range to begin the countdown
4473
    if( !within_target_range( z, target, range ) ) {
×
4474
        return false;
4475
    }
4476

4477
    // HORRIBLE HACK ALERT! Currently uses the amount of ammo as a pseudo-timer.
4478
    // Once we have proper monster inventory item processing replace the following
4479
    // line with the code below.
4480
    z->add_effect( effect_countdown, 1_turns * charges + 1_turns );
×
4481
    /* Replacement code here for once we have working monster inventories
4482

4483
    item i_explodes(act_bomb_type->id, 0);
4484
    i_explodes.charges = charges;
4485
    z->add_item(i_explodes);
4486
    z->disable_special("KAMIKAZE");
4487
    */
4488
    // END HORRIBLE HACK
4489

4490
    if( g->u.sees( z->pos() ) ) {
×
4491
        add_msg( m_bad, _( "The %s lights up menacingly." ), z->name().c_str() );
×
4492
    }
4493

4494
    return true;
4495
}
4496

4497
struct grenade_helper_struct {
×
4498
    std::string message;
4499
    int chance = 1;
4500
    float ammo_percentage = 1;
4501
};
4502

4503
// Returns 0 if this should be retired, 1 if it was successful, and -1 if something went horribly wrong
4504
int grenade_helper( monster *const z, Creature *const target, const int dist,
×
4505
                    const int moves, std::map<std::string, grenade_helper_struct> data )
4506
{
4507
    // Can't do anything if we can't act
4508
    if( !z->can_act() ) {
×
4509
        return 0;
4510
    }
4511
    // Too far or we can't target them
4512
    if( !within_target_range( z, target, dist ) ) {
×
4513
        return 0;
4514
    }
4515
    // We need an open space for these attacks
4516
    auto const empty_neighbors = find_empty_neighbors( *z );
×
4517
    size_t const empty_neighbor_count = empty_neighbors.second;
×
4518
    if( !empty_neighbor_count ) {
×
4519
        return 0;
4520
    }
4521

4522
    int total_ammo = 0;
×
4523
    // Sum up the ammo entries to get a ratio.
4524
    for( const auto &ammo_entry : z->type->starting_ammo ) {
×
4525
        total_ammo += ammo_entry.second;
×
4526
    }
4527
    if( total_ammo == 0 ) {
×
4528
        // Should never happen, but protect us from a div/0 if it does.
4529
        return -1;
4530
    }
4531

4532
    // Find how much ammo we currently have to get the total ratio
4533
    int curr_ammo = 0;
×
4534
    for( auto amm : z->ammo ) {
×
4535
        curr_ammo += amm.second;
×
4536
    }
4537
    float rat = curr_ammo / float( total_ammo );
×
4538

4539
    if( curr_ammo == 0 ) {
×
4540
        // We've run out of ammo, get angry and toggle the special off.
4541
        z->anger = 100;
×
4542
        return -1;
×
4543
    }
4544

4545
    // Hey look! another weighted list!
4546
    // Grab all attacks that pass their chance check and we've spent enough ammo for
4547
    weighted_float_list<std::string> possible_attacks;
×
4548
    for( auto amm : z->ammo ) {
×
4549
        if( amm.second > 0 && data[amm.first].ammo_percentage >= rat ) {
×
4550
            possible_attacks.add( amm.first, 1.0 / data[amm.first].chance );
×
4551
        }
4552
    }
4553
    std::string att = *possible_attacks.pick();
×
4554

4555
    z->moves -= moves;
×
4556
    z->ammo[att]--;
×
4557

4558
    // if the player can see it
4559
    if( g->u.sees( *z ) ) {
×
4560
        if( data[att].message.empty() ) {
×
4561
            add_msg( m_debug, "Invalid ammo message in grenadier special." );
×
4562
        } else {
4563
            add_msg( m_bad, data[att].message.c_str(), z->name().c_str() );
×
4564
        }
4565
    }
4566

4567
    // Get our monster type
4568
    auto bomb_type = item::find_type( att );
×
4569
    auto usage = bomb_type->get_use( "place_monster" );
×
4570
    if( usage == nullptr ) {
×
4571
        // Invalid bomb item usage, Toggle this special off so we stop processing
4572
        add_msg( m_debug, "Invalid bomb item usage in grenadier special for %s.", z->name().c_str() );
×
4573
        return -1;
×
4574
    }
4575
    auto *actor = dynamic_cast<const place_monster_iuse *>( usage->get_actor_ptr() );
×
4576
    if( actor == nullptr ) {
×
4577
        // Invalid bomb item, Toggle this special off so we stop processing
4578
        add_msg( m_debug, "Invalid bomb type in grenadier special for %s.", z->name().c_str() );
×
4579
        return -1;
×
4580
    }
4581

4582
    const tripoint where = empty_neighbors.first[get_random_index( empty_neighbor_count )];
×
4583

4584
    if( monster *const hack = g->summon_mon( actor->mtypeid, where ) ) {
×
4585
        hack->make_ally( *z );
×
4586
    }
4587
    return 1;
×
4588
}
4589

4590
bool mattack::grenadier( monster *const z )
×
4591
{
4592
    // Build our grenade map
4593
    std::map<std::string, grenade_helper_struct> grenades;
4594
    // Grenades
4595
    grenades["bot_grenade_hack"].message =
×
4596
        _( "The %s fumbles open a pouch and a grenade hack flies out!" );
×
4597
    // Flashbangs
4598
    grenades["bot_flashbang_hack"].message =
×
4599
        _( "The %s fumbles open a pouch and a flashbang hack flies out!" );
×
4600
    // Gasbombs
4601
    grenades["bot_gasbomb_hack"].message =
×
4602
        _( "The %s fumbles open a pouch and a tear gas hack flies out!" );
×
4603
    // C-4
4604
    grenades["bot_c4_hack"].message = _( "The %s fumbles open a pouch and a C-4 hack flies out!" );
×
4605
    grenades["bot_c4_hack"].chance = 8;
×
4606

4607
    // Only can actively target the player right now. Once we have the ability to grab targets that we aren't
4608
    // actively attacking change this to use that instead.
4609
    Creature *const target = static_cast<Creature *>( &g->u );
×
4610
    if( z->attitude_to( *target ) == Creature::A_FRIENDLY ) {
×
4611
        return false;
4612
    }
4613
    int ret = grenade_helper( z, target, 30, 60, grenades );
×
4614
    if( ret == -1 ) {
×
4615
        // Something broke badly, disable our special
4616
        z->disable_special( "GRENADIER" );
×
4617
    }
4618
    return true;
4619
}
4620

4621
bool mattack::grenadier_elite( monster *const z )
×
4622
{
4623
    // Build our grenade map
4624
    std::map<std::string, grenade_helper_struct> grenades;
4625
    // Grenades
4626
    grenades["bot_grenade_hack"].message = _( "The %s opens a pouch and a grenade hack flies out!" );
×
4627
    // Flashbangs
4628
    grenades["bot_flashbang_hack"].message =
×
4629
        _( "The %s opens a pouch and a flashbang hack flies out!" );
×
4630
    // Gasbombs
4631
    grenades["bot_gasbomb_hack"].message = _( "The %s opens a pouch and a tear gas hack flies out!" );
×
4632
    // C-4
4633
    grenades["bot_c4_hack"].message = _( "The %s cackles and opens a pouch; a C-4 hack flies out!" );
×
4634
    grenades["bot_c4_hack"].chance = 8;
×
4635
    grenades["bot_c4_hack"].ammo_percentage = .75;
×
4636
    // Mininuke
4637
    grenades["bot_mininuke_hack"].message =
×
4638
        _( "The %s opens its pack and spreads its hands, a mininuke hack floats out!" );
×
4639
    grenades["bot_mininuke_hack"].chance = 50;
×
4640
    grenades["bot_mininuke_hack"].ammo_percentage = .75;
×
4641

4642
    // Only can actively target the player right now. Once we have the ability to grab targets that we aren't
4643
    // actively attacking change this to use that instead.
4644
    Creature *const target = static_cast<Creature *>( &g->u );
×
4645
    if( z->attitude_to( *target ) == Creature::A_FRIENDLY ) {
×
4646
        return false;
4647
    }
4648
    int ret = grenade_helper( z, target, 30, 60, grenades );
×
4649
    if( ret == -1 ) {
×
4650
        // Something broke badly, disable our special
4651
        z->disable_special( "GRENADIER_ELITE" );
×
4652
    }
4653

4654
    return true;
4655
}
4656

4657
bool mattack::stretch_attack( monster *z )
×
4658
{
4659
    if( !z->can_act() ) {
×
4660
        return false;
4661
    }
4662

4663
    Creature *target = z->attack_target();
×
4664
    if( target == nullptr ) {
×
4665
        return false;
4666
    }
4667

4668
    int distance = rl_dist( z->pos(), target->pos() );
×
4669
    if( distance < 2 || distance > 3 || !z->sees( *target ) ) {
×
4670
        return false;
4671
    }
4672

4673
    int dam = rng( 5, 10 );
×
4674
    z->moves -= 100;
×
4675
    for( auto &pnt : g->m.find_clear_path( z->pos(), target->pos() ) ) {
×
4676
        if( g->m.impassable( pnt ) ) {
×
4677
            add_msg( _( "The %1$s thrusts its arm at you but bounces off the %2$s" ), z->name().c_str(),
×
4678
                     g->m.obstacle_name( pnt ).c_str() );
×
4679
            return true;
×
4680
        }
4681
    }
4682

4683
    auto msg_type = target == &g->u ? m_warning : m_info;
×
4684
    target->add_msg_player_or_npc( msg_type,
4685
                                   _( "The %s thrusts its arm at you, stretching to reach you from afar" ),
4686
                                   _( "The %s thrusts its arm at <npcname>" ),
4687
                                   z->name().c_str() );
×
4688
    if( dodge_check( z, target ) || g->u.uncanny_dodge() ) {
×
4689
        target->add_msg_player_or_npc( msg_type, _( "You evade the stretched arm and it sails past you!" ),
4690
                                       _( "<npcname> evades the stretched arm!" ) );
×
4691
        target->on_dodge( z, z->type->melee_skill * 2 );
×
4692
        //takes some time to retract the arm
4693
        z->moves -= 150;
×
4694
        return true;
×
4695
    }
4696

4697
    body_part hit = target->get_random_body_part();
×
4698
    dam = target->deal_damage( z, hit, damage_instance( DT_STAB, dam ) ).total_damage();
×
4699

4700
    if( dam > 0 ) {
×
4701
        auto msg_type = target == &g->u ? m_bad : m_info;
×
4702
        //~ 1$s is monster name, 2$s bodypart in accusative
4703
        target->add_msg_player_or_npc( msg_type,
4704
                                       _( "The %1$s's arm pierces your %2$s!" ),
4705
                                       _( "The %1$s arm pierces <npcname>'s %2$s!" ),
4706
                                       z->name().c_str(),
×
4707
                                       body_part_name_accusative( hit ).c_str() );
×
4708

4709
        target->check_dead_state();
×
4710
    } else {
4711
        target->add_msg_player_or_npc( _( "The %1$s arm hits your %2$s, but glances off your armor!" ),
4712
                                       _( "The %1$s hits <npcname>'s %2$s, but glances off armor!" ),
4713
                                       z->name().c_str(),
×
4714
                                       body_part_name_accusative( hit ).c_str() );
×
4715
    }
4716

4717
    target->on_hit( z, hit,  z->type->melee_skill );
×
4718

4719
    return true;
×
4720
}
4721

4722
bool mattack::doot( monster *z )
×
4723
{
4724
    z->moves -= 300;
×
4725
    if( g->u.sees( *z ) ) {
×
4726
        add_msg( _( "The %s doots its trumpet!" ), z->name().c_str() );
×
4727
    }
4728
    int spooks = 0;
×
4729
    for( const tripoint &spookyscary : g->m.points_in_radius( z->pos(), 2 ) ) {
×
4730
        if( spookyscary == z->pos() || g->m.impassable( spookyscary ) ) {
×
4731
            continue;
4732
        }
4733
        const int dist = rl_dist( z->pos(), spookyscary );
×
4734
        if( ( one_in( dist + 3 ) || spooks == 0 ) && spooks < 5 ) {
×
4735
            if( g->u.sees( *z ) ) {
×
4736
                add_msg( _( "A spooky skeleton rises from the ground!" ) );
×
4737
            }
4738
            g->summon_mon( mon_zombie_skeltal_minion, spookyscary );
×
4739
            spooks++;
×
4740
            continue;
×
4741
        }
4742
    }
4743
    sounds::sound( z->pos(), 200, _( "DOOT." ) );
×
4744
    return true;
×
4745
}
4746

4747
bool mattack::dodge_check( monster *z, Creature *target )
×
4748
{
4749
    ///\EFFECT_DODGE increases chance of dodging, vs their melee skill
4750
    float dodge = std::max( target->get_dodge() - rng( 0, z->get_hit() ), 0.0f );
×
4751
    return rng( 0, 10000 ) < 10000 / ( 1 + 99 * exp( -.6 * dodge ) );
×
4752
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc