From ffd3acabbc31a0943c043c666b8bbbd4e0c5f592 Mon Sep 17 00:00:00 2001 From: Hymore246 Date: Wed, 15 Jan 2020 20:29:46 -0600 Subject: [PATCH] Adds Elite Zombie Bio-Operator monster --- data/json/harvest.json | 27 ++++ data/json/itemgroups/bionics.json | 62 +++++++++ data/json/monstergroups/lab.json | 4 + data/json/monstergroups/military.json | 1 + data/json/monsters/zed_soldiers.json | 16 +++ doc/JSON_FLAGS.md | 5 +- src/monattack.cpp | 191 ++++++++++++++++++++++++++ src/monattack.h | 3 + src/monstergenerator.cpp | 3 + 9 files changed, 311 insertions(+), 1 deletion(-) diff --git a/data/json/harvest.json b/data/json/harvest.json index 14ca9a94e364f..81ca74264b0ac 100644 --- a/data/json/harvest.json +++ b/data/json/harvest.json @@ -707,6 +707,33 @@ { "drop": "bone_tainted", "type": "bone", "mass_ratio": 0.1 } ] }, + { + "id": "CBM_OP2", + "type": "harvest", + "entries": [ + { + "drop": "bionics_op2_off", + "type": "bionic_group", + "flags": [ "FILTHY", "NO_STERILE", "NO_PACKED" ], + "faults": [ "fault_bionic_salvaged" ] + }, + { + "drop": "bionics_op2_def", + "type": "bionic_group", + "flags": [ "FILTHY", "NO_STERILE", "NO_PACKED" ], + "faults": [ "fault_bionic_salvaged" ] + }, + { + "drop": "bionics_op2_utl", + "type": "bionic_group", + "flags": [ "FILTHY", "NO_STERILE", "NO_PACKED" ], + "faults": [ "fault_bionic_salvaged" ] + }, + { "drop": "meat_tainted", "type": "flesh", "mass_ratio": 0.25 }, + { "drop": "fat_tainted", "type": "flesh", "mass_ratio": 0.08 }, + { "drop": "bone_tainted", "type": "bone", "mass_ratio": 0.1 } + ] + }, { "id": "CBM_POWER", "type": "harvest", diff --git a/data/json/itemgroups/bionics.json b/data/json/itemgroups/bionics.json index d2fc5c7e6f34b..f8a00df5ee45c 100644 --- a/data/json/itemgroups/bionics.json +++ b/data/json/itemgroups/bionics.json @@ -246,6 +246,68 @@ [ "bio_shotgun", 10 ] ] }, + { + "type": "item_group", + "id": "bionics_op2_off", + "subtype": "distribution", + "//": "Elite zombie bio-operator offense CBMs.", + "items": [ + [ "bio_blade", 10 ], + [ "bio_chain_lightning", 10 ], + [ "bio_claws", 10 ], + [ "bio_cqb", 10 ], + [ "bio_emp", 10 ], + [ "bio_emp_armgun", 10 ], + [ "bio_flashbang", 10 ], + [ "bio_heat_absorb", 10 ], + [ "bio_laser", 10 ], + [ "bio_railgun", 10 ], + [ "bio_shotgun", 10 ], + [ "bio_shock", 10 ], + [ "bio_targeting", 10 ] + ] + }, + { + "type": "item_group", + "id": "bionics_op2_def", + "subtype": "distribution", + "//": "Elite zombie bio-operator defense CBMs.", + "items": [ + [ "bio_ads", 10 ], + [ "bio_armor_arms", 10 ], + [ "bio_armor_eyes", 10 ], + [ "bio_armor_head", 10 ], + [ "bio_armor_legs", 10 ], + [ "bio_armor_torso", 10 ], + [ "bio_carbon", 10 ], + [ "bio_cloak", 10 ], + [ "bio_faraday", 10 ], + [ "bio_nanobots", 10 ], + [ "bio_ods", 10 ], + [ "bio_shock_absorber", 10 ], + [ "bio_uncanny_dodge", 10 ] + ] + }, + { + "type": "item_group", + "id": "bionics_op2_utl", + "subtype": "distribution", + "//": "Elite zombie bio-operator utility CBMs.", + "items": [ + [ "bio_eye_enhancer", 10 ], + [ "bio_dex_enhancer", 10 ], + [ "bio_int_enhancer", 10 ], + [ "bio_str_enhancer", 10 ], + [ "bio_eye_optic", 10 ], + [ "bio_gills", 10 ], + [ "bio_infrared", 10 ], + [ "bio_memory", 10 ], + [ "bio_night_vision", 10 ], + [ "bio_speed", 10 ], + [ "bio_ups", 10 ], + [ "bio_weight", 10 ] + ] + }, { "type": "item_group", "id": "bionics_tech", diff --git a/data/json/monstergroups/lab.json b/data/json/monstergroups/lab.json index 5b5fa4ebf6346..99724ebfa3145 100644 --- a/data/json/monstergroups/lab.json +++ b/data/json/monstergroups/lab.json @@ -48,6 +48,7 @@ { "monster": "mon_skitterbot", "freq": 1, "cost_multiplier": 0, "pack_size": [ 8, 12 ] }, { "monster": "mon_secubot", "freq": 1, "cost_multiplier": 10 }, { "monster": "mon_zombie_bio_op", "freq": 50, "cost_multiplier": 5 }, + { "monster": "mon_zombie_bio_op2", "freq": 25, "cost_multiplier": 5, "starts": 1440 }, { "monster": "mon_zombie_armored", "freq": 5, "cost_multiplier": 5 }, { "monster": "mon_zombie_electric", "freq": 50, "cost_multiplier": 3 }, { "monster": "mon_zombie_necro", "freq": 15, "cost_multiplier": 15 }, @@ -81,6 +82,7 @@ { "monster": "mon_skitterbot", "freq": 10, "cost_multiplier": 1, "pack_size": [ 8, 12 ] }, { "monster": "mon_blob_small", "freq": 10, "cost_multiplier": 1, "pack_size": [ 1, 4 ] }, { "monster": "mon_zombie_bio_op", "freq": 50, "cost_multiplier": 5 }, + { "monster": "mon_zombie_bio_op2", "freq": 25, "cost_multiplier": 5, "starts": 1440 }, { "monster": "mon_zombie_armored", "freq": 5, "cost_multiplier": 5 }, { "monster": "mon_zombie_brute_shocker", "freq": 5, "cost_multiplier": 5 } ] @@ -101,6 +103,7 @@ { "monster": "mon_secubot", "freq": 1, "cost_multiplier": 10 }, { "monster": "mon_talon_m202a1", "freq": 1, "cost_multiplier": 15 }, { "monster": "mon_zombie_bio_op", "freq": 50, "cost_multiplier": 5 }, + { "monster": "mon_zombie_bio_op2", "freq": 25, "cost_multiplier": 5, "starts": 1440 }, { "monster": "mon_zombie_armored", "freq": 5, "cost_multiplier": 8 }, { "monster": "mon_zombie_electric", "freq": 50, "cost_multiplier": 3 }, { "monster": "mon_zombie_necro", "freq": 15, "cost_multiplier": 15 }, @@ -147,6 +150,7 @@ { "monster": "mon_skitterbot", "freq": 10, "cost_multiplier": 0, "pack_size": [ 2, 3 ] }, { "monster": "mon_secubot", "freq": 2, "cost_multiplier": 5 }, { "monster": "mon_zombie_bio_op", "freq": 10, "cost_multiplier": 3 }, + { "monster": "mon_zombie_bio_op2", "freq": 25, "cost_multiplier": 5, "starts": 1440 }, { "monster": "mon_zombie", "freq": 40, "cost_multiplier": 1, "pack_size": [ 2, 7 ] }, { "monster": "mon_zombie_fat", "freq": 40, "cost_multiplier": 1, "pack_size": [ 2, 4 ] }, { "monster": "mon_zombie_tough", "freq": 20, "cost_multiplier": 2, "pack_size": [ 2, 4 ] }, diff --git a/data/json/monstergroups/military.json b/data/json/monstergroups/military.json index 459bbda296e02..235912d435406 100644 --- a/data/json/monstergroups/military.json +++ b/data/json/monstergroups/military.json @@ -33,6 +33,7 @@ { "monster": "mon_zombie_flamer", "freq": 100, "cost_multiplier": 30 }, { "monster": "mon_zombie_military_pilot", "freq": 5, "cost_multiplier": 1 }, { "monster": "mon_zombie_bio_op", "freq": 50, "cost_multiplier": 5 }, + { "monster": "mon_zombie_bio_op2", "freq": 25, "cost_multiplier": 5, "starts": 1440 }, { "monster": "mon_zombie_armored", "freq": 10, "cost_multiplier": 5 } ] }, diff --git a/data/json/monsters/zed_soldiers.json b/data/json/monsters/zed_soldiers.json index 13aec2b34a0e9..6bb12ac41f74c 100644 --- a/data/json/monsters/zed_soldiers.json +++ b/data/json/monsters/zed_soldiers.json @@ -465,5 +465,21 @@ "REVIVES", "FILTHY" ] + }, + { + "id": "mon_zombie_bio_op2", + "type": "MONSTER", + "name": "elite zombie bio-operator", + "description": "Once a highly trained soldier, various bionics cover this zombie's broken body. Strangely, it maintains a vague martial arts stance.", + "copy-from": "mon_zombie_bio_op", + "looks_like": "mon_zombie_bio_op", + "diff": 10, + "hp": 180, + "melee_skill": 8, + "dodge": 4, + "armor_bash": 15, + "armor_cut": 27, + "harvest": "CBM_OP2", + "special_attacks": [ [ "BIO_OP_BIOJUTSU", 15 ] ] } ] diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index bd02887e1824f..e97b131350e1c 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -1003,7 +1003,10 @@ Also see `monster_attacks.json` for more special attacks, for example, impale an - ```ACID_BARF``` Barfs corroding, blinding acid. - ```ACID``` Spit acid. - ```ANTQUEEN``` Hatches/grows: `egg > ant > soldier`. -- ```BIO_OP_TAKEDOWN``` Attack with special martial art takedown maneuverer. +- ```BIO_OP_BIOJUTSU``` Attack with a random special martial art maneuver. +- ```BIO_OP_TAKEDOWN``` Attack with special martial art takedown maneuver. +- ```BIO_OP_DISARM``` Attack with a special martial art disarm maneuver. +- ```BIO_OP_IMPALE``` Attack with a strong martial art maneuver. - ```BITE``` Bite attack that can cause deep infected wounds. - ```BMG_TUR``` Barrett .50BMG rifle fires. - ```BOOMER_GLOW``` Spit glowing bile. diff --git a/src/monattack.cpp b/src/monattack.cpp index a6e21c2bd8d84..8d9b296dbe2de 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -5047,6 +5047,50 @@ bool mattack::flesh_tendril( monster *z ) return false; } +bool mattack::bio_op_random_biojutsu( monster *z ) +{ + int choice; + int redo; + + if( !z->can_act() ) { + return false; + } + + Creature *target = z->attack_target(); + if( target == nullptr || + !is_adjacent( z, target, false ) || + !z->sees( *target ) ) { + return false; + } + + player *foe = dynamic_cast< player * >( target ); + + do { + choice = rng( 1, 3 ); + redo = false; + + // ignore disarm if the target isn't a "player" or isn't armed + if( choice == 3 && foe != nullptr && !foe->is_armed() ) { + redo = true; + } + + } while( redo ); + + switch( choice ) { + case 1: + bio_op_takedown( z ); + break; + case 2: + bio_op_impale( z ); + break; + case 3: + bio_op_disarm( z ); + break; + } + + return true; +} + bool mattack::bio_op_takedown( monster *z ) { if( !z->can_act() ) { @@ -5142,6 +5186,153 @@ bool mattack::bio_op_takedown( monster *z ) return true; } +bool mattack::bio_op_impale( monster *z ) +{ + if( !z->can_act() ) { + return false; + } + + Creature *target = z->attack_target(); + if( target == nullptr || + !is_adjacent( z, target, false ) || + !z->sees( *target ) ) { + return false; + } + + const bool seen = g->u.sees( *z ); + player *foe = dynamic_cast< player * >( target ); + if( seen ) { + add_msg( _( "The %1$s mechanically lunges at %2$s!" ), z->name(), + target->disp_name() ); + } + z->moves -= 100; + + if( target->uncanny_dodge() ) { + return true; + } + + // Can we dodge the attack? Uses player dodge function % chance (melee.cpp) + if( dodge_check( z, target ) ) { + target->add_msg_player_or_npc( _( "You dodge it!" ), + _( " dodges it!" ) ); + target->on_dodge( z, z->type->melee_skill * 2 ); + return true; + } + + // Yes, it has the CQC bionic. + int dam = rng( 8, 24 ); + bool do_bleed = false; + int t_dam; + + if( one_in( 4 ) ) { + dam = rng( 12, 36 ); // 50% damage buff for the crit. + do_bleed = true; + } + + if( foe == nullptr ) { + // Handle mons earlier - less to check for + target->deal_damage( z, bp_torso, damage_instance( DT_STAB, dam ) ); + if( do_bleed ) { + target->add_effect( effect_bleed, rng( 75_turns, 125_turns ), bp_torso, true ); + } + if( seen ) { + add_msg( _( "The %1$s impales %2$s!" ), z->name(), target->disp_name() ); + } + target->check_dead_state(); + return true; + } + + body_part hit = target->get_random_body_part(); + + t_dam = foe->deal_damage( z, hit, damage_instance( DT_STAB, dam ) ).total_damage(); + + target->add_msg_player_or_npc( _( "The %1$s tries to impale your %s…" ), + _( "The %1$s tries to impale 's %s…" ), + z->name(), body_part_name_accusative( hit ) ); + + if( t_dam > 0 ) { + target->add_msg_if_player( m_bad, _( "and deals %d damage!" ), t_dam ); + + if( do_bleed ) { + target->as_character()->make_bleed( hit, rng( 75_turns, 125_turns ), true ); + } + } else { + target->add_msg_player_or_npc( _( "but fails to penetrate your armor!" ), + _( "but fails to penetrate 's armor!" ) ); + } + + target->on_hit( z, hit, z->type->melee_skill ); + foe->check_dead_state(); + + return true; +} + +bool mattack::bio_op_disarm( monster *z ) +{ + if( !z->can_act() ) { + return false; + } + + Creature *target = z->attack_target(); + if( target == nullptr || + !is_adjacent( z, target, false ) || + !z->sees( *target ) ) { + return false; + } + + const bool seen = g->u.sees( *z ); + player *foe = dynamic_cast< player * >( target ); + + // disarm doesn't work on creatures or unarmed targets + if( foe == nullptr || ( foe != nullptr && !foe->is_armed() ) ) { + return false; + } + + if( seen ) { + add_msg( _( "The %1$s mechanically reaches for %2$s!" ), z->name(), + target->disp_name() ); + } + z->moves -= 100; + + if( target->uncanny_dodge() ) { + return true; + } + + // Can we dodge the attack? Uses player dodge function % chance (melee.cpp) + if( dodge_check( z, target ) ) { + target->add_msg_player_or_npc( _( "You dodge it!" ), + _( " dodges it!" ) ); + target->on_dodge( z, z->type->melee_skill * 2 ); + return true; + } + + int mon_stat = z->type->melee_dice * z->type->melee_sides; + int my_roll = dice( 3, 2 * mon_stat ); + my_roll += dice( 3, z->type->melee_skill ); + + /** @EFFECT_STR increases chance to avoid disarm, primary stat */ + /** @EFFECT_DEX increases chance to avoid disarm, secondary stat */ + /** @EFFECT_PER increases chance to avoid disarm, secondary stat */ + /** @EFFECT_MELEE increases chance to avoid disarm */ + int their_roll = dice( 3, 2 * foe->get_str() + foe->get_dex() ); + their_roll += dice( 3, foe->get_per() ); + their_roll += dice( 3, foe->get_skill_level( skill_melee ) ); + + item &it = foe->weapon; + + target->add_msg_if_player( m_bad, _( "The zombie grabs your %s…" ), it.tname() ); + + if( my_roll >= their_roll && !it.has_flag( "NO_UNWIELD" ) ) { + target->add_msg_if_player( m_bad, _( "and throws it to the ground!" ) ); + const tripoint tp = foe->pos() + tripoint( rng( -1, 1 ), rng( -1, 1 ), 0 ); + g->m.add_item_or_charges( tp, foe->i_rem( &it ) ); + } else { + target->add_msg_if_player( m_good, _( "but you break its grip!" ) ); + } + + return true; +} + bool mattack::suicide( monster *z ) { Creature *target = z->attack_target(); diff --git a/src/monattack.h b/src/monattack.h index 73d3735f14ae5..3501ea740b63f 100644 --- a/src/monattack.h +++ b/src/monattack.h @@ -89,7 +89,10 @@ bool leech_spawner( monster *z ); bool mon_leech_evolution( monster *z ); bool tindalos_teleport( monster *z ); bool flesh_tendril( monster *z ); +bool bio_op_random_biojutsu( monster *z ); bool bio_op_takedown( monster *z ); +bool bio_op_impale( monster *z ); +bool bio_op_disarm( monster *z ); bool ranged_pull( monster *z ); bool grab( monster *z ); bool grab_drag( monster *z ); diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index 45672102f20cb..2c45b4cb6faf1 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -581,7 +581,10 @@ void MonsterGenerator::init_attack() add_hardcoded_attack( "MON_LEECH_EVOLUTION", mattack::mon_leech_evolution ); add_hardcoded_attack( "TINDALOS_TELEPORT", mattack::tindalos_teleport ); add_hardcoded_attack( "FLESH_TENDRIL", mattack::flesh_tendril ); + add_hardcoded_attack( "BIO_OP_BIOJUTSU", mattack::bio_op_random_biojutsu ); add_hardcoded_attack( "BIO_OP_TAKEDOWN", mattack::bio_op_takedown ); + add_hardcoded_attack( "BIO_OP_IMPALE", mattack::bio_op_impale ); + add_hardcoded_attack( "BIO_OP_DISARM", mattack::bio_op_disarm ); add_hardcoded_attack( "SUICIDE", mattack::suicide ); add_hardcoded_attack( "KAMIKAZE", mattack::kamikaze ); add_hardcoded_attack( "GRENADIER", mattack::grenadier );