diff --git a/data/json/ammo_effects.json b/data/json/ammo_effects.json index 8bcf4e929f081..ef4c940db0edd 100644 --- a/data/json/ammo_effects.json +++ b/data/json/ammo_effects.json @@ -427,5 +427,10 @@ "type": "ammo_effect", "//": "runs EoC that cause you to mutate", "eoc": [ "EOC_random_mutate" ] + }, + { + "id": "LIQUID", + "type": "ammo_effect", + "//": "This is basically a flag for liquid ammo, it is set automatically when the material of the ammo is liquid" } ] diff --git a/data/json/monster_special_attacks/monster_ammo.json b/data/json/monster_special_attacks/monster_ammo.json index 924511693f875..1a2e6b39d79d3 100644 --- a/data/json/monster_special_attacks/monster_ammo.json +++ b/data/json/monster_special_attacks/monster_ammo.json @@ -23,7 +23,7 @@ "ammo_type": "bile", "range": 4, "count": 100, - "effects": [ "NEVER_MISFIRES", "JET" ] + "effects": [ "NEVER_MISFIRES", "JET", "BILE_GLOW" ] }, { "id": "boomer_stomach", @@ -53,5 +53,10 @@ "type": "ammo_effect", "//": "Creates a trail of bile", "trail": { "field_type": "fd_bile", "intensity_min": 1, "intensity_max": 2, "chance": 75 } + }, + { + "id": "BILE_GLOW", + "type": "ammo_effect", + "on_hit_effects": [ { "effect": "glowing", "duration": "5 m", "intensity": 1 } ] } -] \ No newline at end of file +] diff --git a/data/json/monster_special_attacks/monster_gun.json b/data/json/monster_special_attacks/monster_gun.json index c83f2b3a81b5c..453beabf544c3 100644 --- a/data/json/monster_special_attacks/monster_gun.json +++ b/data/json/monster_special_attacks/monster_gun.json @@ -305,7 +305,7 @@ "description": "The head of a boomer, if you see this item it's a bug.", "material": [ "flesh" ], "flags": [ "NEVER_JAMS", "NON_FOULING" ], - "ammo_effects": [ "PSEUDO", "NEVER_MISFIRES", "NO_PENETRATE_OBSTACLES", "BILE_BOMB", "BILE_JET" ], + "ammo_effects": [ "NEVER_MISFIRES", "NO_PENETRATE_OBSTACLES", "BILE_BOMB", "BILE_JET" ], "ammo": [ "bile" ], "skill": "rifle", "dispersion": 300, diff --git a/src/ammo_effect.cpp b/src/ammo_effect.cpp index ff0a1887d80b8..736694c2636e1 100644 --- a/src/ammo_effect.cpp +++ b/src/ammo_effect.cpp @@ -90,6 +90,19 @@ void ammo_effect::load( const JsonObject &jo, const std::string_view ) JsonObject joe = jo.get_object( "explosion" ); aoe_explosion_data = load_explosion_data( joe ); } + if( jo.has_member( "on_hit_effects" ) ) { + JsonArray json_arr = jo.get_array( "on_hit_effects" ); + for( JsonObject joe : json_arr ) { + on_hit_effect new_effect; + optional( joe, was_loaded, "bp_to_hit", new_effect.bp_to_hit, bodypart_str_id::NULL_ID() ); + optional( joe, was_loaded, "need_touch_skin", new_effect.need_touch_skin, false ); + optional( joe, was_loaded, "affected_bps", new_effect.affected_bps ); + mandatory( joe, was_loaded, "duration", new_effect.duration ); + mandatory( joe, was_loaded, "effect", new_effect.effect ); + mandatory( joe, was_loaded, "intensity", new_effect.intensity ); + on_hit_effects.push_back( new_effect ); + } + } optional( jo, was_loaded, "do_flashbang", do_flashbang, false ); optional( jo, was_loaded, "do_emp_blast", do_emp_blast, false ); optional( jo, was_loaded, "foamcrete_build", foamcrete_build, false ); diff --git a/src/ammo_effect.h b/src/ammo_effect.h index 4bebe763dc087..ec08d49d5e3cd 100644 --- a/src/ammo_effect.h +++ b/src/ammo_effect.h @@ -19,6 +19,15 @@ template class generic_factory; generic_factory &get_all_ammo_effects(); +struct on_hit_effect { + bodypart_id bp_to_hit; + bool need_touch_skin; + efftype_id effect; + time_duration duration; + int intensity; + std::vector affected_bps; +}; + struct ammo_effect { public: void load( const JsonObject &jo, std::string_view src ); @@ -55,6 +64,8 @@ struct ammo_effect { int trail_intensity_max = 0; int trail_chance = 100; + std::vector on_hit_effects; + // Used by generic_factory ammo_effect_str_id id; std::vector> src; diff --git a/src/creature.cpp b/src/creature.cpp index ac8e791108a29..2fbe87b66782c 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -12,6 +12,7 @@ #include #include +#include "ammo_effect.h" #include "anatomy.h" #include "body_part_set.h" #include "cached_options.h" @@ -85,6 +86,7 @@ static const ammo_effect_str_id ammo_effect_FOAMCRETE( "FOAMCRETE" ); static const ammo_effect_str_id ammo_effect_IGNITE( "IGNITE" ); static const ammo_effect_str_id ammo_effect_INCENDIARY( "INCENDIARY" ); static const ammo_effect_str_id ammo_effect_LARGE_BEANBAG( "LARGE_BEANBAG" ); +static const ammo_effect_str_id ammo_effect_LIQUID( "LIQUID" ); static const ammo_effect_str_id ammo_effect_MAGIC( "MAGIC" ); static const ammo_effect_str_id ammo_effect_NOGIB( "NOGIB" ); static const ammo_effect_str_id ammo_effect_NO_DAMAGE_SCALING( "NO_DAMAGE_SCALING" ); @@ -969,11 +971,34 @@ double Creature::accuracy_projectile_attack( dealt_projectile_attack &attack ) c return attack.missed_by + std::max( 0.0, std::min( 1.0, dodge_rescaled ) ); } -void projectile::apply_effects_nodamage( Creature &target, Creature *source ) const +void projectile::apply_effects_nodamage( Creature &target, Creature *source, + const dealt_damage_instance &dealt_dam, bool soaked_through ) const { + bool is_liquid = proj_effects.count( ammo_effect_LIQUID ); if( proj_effects.count( ammo_effect_BOUNCE ) ) { target.add_effect( effect_source( source ), effect_bounced, 1_turns ); } + + for( const ammo_effect_str_id &proj_effect : proj_effects ) { + for( const on_hit_effect &on_hit_eff : proj_effect->on_hit_effects ) { + if( on_hit_eff.need_touch_skin && is_liquid && !soaked_through ) { + continue; + } + if( on_hit_eff.bp_to_hit ) { + if( on_hit_eff.bp_to_hit != dealt_dam.bp_hit ) { + continue; + } + } + if( !on_hit_eff.affected_bps.empty() ) { + for( const bodypart_id &bp : on_hit_eff.affected_bps ) { + target.add_effect( on_hit_eff.effect, on_hit_eff.duration, bp, false, on_hit_eff.intensity ); + } + } else { + target.add_effect( on_hit_eff.effect, on_hit_eff.duration, false, on_hit_eff.intensity ); + } + + } + } } void projectile::apply_effects_damage( Creature &target, Creature *source, @@ -1316,8 +1341,6 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack return; } - proj.apply_effects_nodamage( *this, source ); - projectile_attack_results hit_selection = select_body_part_projectile_attack( proj, goodhit, missed_by ); // Create a copy that records whether the attack is a crit. @@ -1346,7 +1369,8 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack } } - if( attack.proj.proj_effects.count( "LIQUID" ) > 0 ) { + bool soaked_through = false; + if( attack.proj.proj_effects.count( ammo_effect_LIQUID ) > 0 ) { if( Character *char_target = as_character() ) { // clothing_wetness_mult returns the effective permeability of the armor on bp_hit // as a float between 0 and 1 @@ -1354,7 +1378,7 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack // 1 permeability means all liquid touches the skin and no damage is negated float permeability = char_target->worn.clothing_wetness_mult( hit_selection.bp_hit ); permeability = std::clamp( permeability, 0.0f, 1.0f ); - + soaked_through = permeability > 0; impact.mult_damage( permeability ); } } @@ -1367,6 +1391,8 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack proj.apply_effects_damage( *this, source, dealt_dam, goodhit < accuracy_critical ); + proj.apply_effects_nodamage( *this, source, dealt_dam, soaked_through ); + if( print_messages ) { messaging_projectile_attack( source, hit_selection, dealt_dam.total_damage() ); } diff --git a/src/projectile.h b/src/projectile.h index 48b15c1312100..2488b28fb2525 100644 --- a/src/projectile.h +++ b/src/projectile.h @@ -51,7 +51,8 @@ struct projectile { const dealt_damage_instance &dealt_dam, bool critical ) const; // pplies proj_effects to a creature that was hit but not damaged - void apply_effects_nodamage( Creature &target, Creature *source ) const; + void apply_effects_nodamage( Creature &target, Creature *source, + const dealt_damage_instance &dealt_dam, bool soaked_through = false ) const; projectile(); projectile( const projectile & ); diff --git a/src/ranged.cpp b/src/ranged.cpp index d9fe8b6c3365b..1f2881a159788 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -87,6 +87,7 @@ static const ammo_effect_str_id ammo_effect_HEAVY_HIT( "HEAVY_HIT" ); static const ammo_effect_str_id ammo_effect_IGNITE( "IGNITE" ); static const ammo_effect_str_id ammo_effect_LASER( "LASER" ); static const ammo_effect_str_id ammo_effect_LIGHTNING( "LIGHTNING" ); +static const ammo_effect_str_id ammo_effect_LIQUID( "LIQUID" ); static const ammo_effect_str_id ammo_effect_MATCHHEAD( "MATCHHEAD" ); static const ammo_effect_str_id ammo_effect_MULTI_EFFECTS( "MULTI_EFFECTS" ); static const ammo_effect_str_id ammo_effect_NON_FOULING( "NON_FOULING" ); @@ -2194,7 +2195,7 @@ static projectile make_gun_projectile( const item &gun ) proj.proj_effects = gun.ammo_effects(); if( gun.ammo_data()->phase == phase_id::LIQUID ) { - proj.proj_effects.insert( "LIQUID" ); + proj.proj_effects.insert( ammo_effect_LIQUID ); } auto &fx = proj.proj_effects;