From c663dfdc523e56ae1d522f7585de616c02c09631 Mon Sep 17 00:00:00 2001 From: anothersimulacrum Date: Thu, 9 Apr 2020 00:09:19 -0700 Subject: [PATCH] Add assign reader for damage instances, deprecate legacy damage code (#37329) * Add assign reader for damage instances This assign reader allows consistent and clear reading of damage instances for all items. Make the necessary changes to item factory and damage units to preserve existing functionality. * Cleanup damage parameter names These names weren't very clear, hopefully this is an improvement. The JSON changes are to fix errors this exposes. --- data/json/items/gunmod/muzzle.json | 1 - .../Dark-Skies-Above/monsters/mongun.json | 3 +- src/assign.h | 282 ++++++++++++++++++ src/creature.cpp | 2 +- src/damage.cpp | 102 ++++++- src/damage.h | 19 +- src/dump.cpp | 1 - src/game_inventory.cpp | 5 +- src/item.cpp | 41 +-- src/item_factory.cpp | 42 +-- src/itype.h | 12 - src/player.cpp | 11 +- 12 files changed, 413 insertions(+), 108 deletions(-) diff --git a/data/json/items/gunmod/muzzle.json b/data/json/items/gunmod/muzzle.json index c13316b0ed496..306394b3ea649 100644 --- a/data/json/items/gunmod/muzzle.json +++ b/data/json/items/gunmod/muzzle.json @@ -120,7 +120,6 @@ "price": 0, "material": [ "superalloy", "ceramic" ], "mod_targets": [ "rifle" ], - "damage_modifier": 0, "handling_modifier": 0, "proportional": { "loudness_modifier": 2 }, "flags": [ "IRREMOVABLE" ] diff --git a/data/mods/Dark-Skies-Above/monsters/mongun.json b/data/mods/Dark-Skies-Above/monsters/mongun.json index df0a1b5f77bfb..4a5c1a59c0c57 100644 --- a/data/mods/Dark-Skies-Above/monsters/mongun.json +++ b/data/mods/Dark-Skies-Above/monsters/mongun.json @@ -5,8 +5,7 @@ "type": "GUN", "name": "fake flamesword", "description": "a fake gun used by the consecrator. it's a bug if you find this in the wild", - "ranged_damage": { "damage_type": "heat", "amount": 10 }, - "pierce": 30, + "ranged_damage": { "damage_type": "heat", "amount": 10, "armor_penetration": 30 }, "range": 9, "loudness": 1, "ammo_effects": [ "FLAME", "STREAM", "INCENDIARY", "NEVER_MISFIRES" ] diff --git a/src/assign.h b/src/assign.h index 4c27671ea6ae0..c959d991bbbab 100644 --- a/src/assign.h +++ b/src/assign.h @@ -11,6 +11,7 @@ #include "calendar.h" #include "color.h" +#include "damage.h" #include "debug.h" #include "json.h" #include "units.h" @@ -589,4 +590,285 @@ inline bool assign( const JsonObject &jo, const std::string &name, cata::optiona return assign( jo, name, *val, strict ); } +static const float float_max = std::numeric_limits::max(); + +static void assign_dmg_relative( damage_instance &out, const damage_instance &val, + damage_instance relative, bool &strict ) +{ + for( const damage_unit &val_dmg : val.damage_units ) { + for( damage_unit &tmp : relative.damage_units ) { + if( tmp.type != val_dmg.type ) { + continue; + } + + // Do not require strict parsing for relative and proportional values as rules + // such as +10% are well-formed independent of whether they affect base value + strict = false; + + // res_mult is set to 1 if it's not specified. Set it to zero so we don't accidentally add to it + if( tmp.res_mult == 1.0f ) { + tmp.res_mult = 0; + } + // Same for damage_multiplier + if( tmp.damage_multiplier == 1.0f ) { + tmp.damage_multiplier = 0; + } + + // As well as the unconditional versions + if( tmp.unconditional_res_mult == 1.0f ) { + tmp.unconditional_res_mult = 0; + } + + if( tmp.unconditional_damage_mult == 1.0f ) { + tmp.unconditional_damage_mult = 0; + } + + damage_unit out_dmg( tmp.type, 0.0f ); + + out_dmg.amount = tmp.amount + val_dmg.amount; + out_dmg.res_pen = tmp.res_pen + val_dmg.res_pen; + + out_dmg.res_mult = tmp.res_mult + val_dmg.res_mult; + out_dmg.damage_multiplier = tmp.damage_multiplier + val_dmg.damage_multiplier; + + out_dmg.unconditional_res_mult = tmp.unconditional_res_mult + val_dmg.unconditional_res_mult; + out_dmg.unconditional_damage_mult = tmp.unconditional_damage_mult + + val_dmg.unconditional_damage_mult; + + out.add( out_dmg ); + } + } +} + +static void assign_dmg_proportional( const JsonObject &jo, const std::string &name, + damage_instance &out, + const damage_instance &val, + damage_instance proportional, bool &strict ) +{ + for( const damage_unit &val_dmg : val.damage_units ) { + for( damage_unit &scalar : proportional.damage_units ) { + if( scalar.type != val_dmg.type ) { + continue; + } + + // Do not require strict parsing for relative and proportional values as rules + // such as +10% are well-formed independent of whether they affect base value + strict = false; + + // Can't have negative percent, and 100% is pointless + // If it's 0, it wasn't loaded + if( scalar.amount == 1 || scalar.amount < 0 ) { + jo.throw_error( "Proportional damage amount is not a valid scalar", name ); + } + + // If it's 0, it wasn't loaded + if( scalar.res_pen < 0 || scalar.res_pen == 1 ) { + jo.throw_error( "Proportional armor penetration is not a valid scalar", name ); + } + + // It wasn't loaded, so set it 100% + if( scalar.res_pen == 0 ) { + scalar.res_pen = 1.0f; + } + + // Ditto + if( scalar.amount == 0 ) { + scalar.amount = 1.0f; + } + + // If it's 1, it wasn't loaded (or was loaded as 1) + if( scalar.res_mult <= 0 ) { + jo.throw_error( "Proportional armor penetration multiplier is not a valid scalar", name ); + } + + // If it's 1, it wasn't loaded (or was loaded as 1) + if( scalar.damage_multiplier <= 0 ) { + jo.throw_error( "Proportional damage multipler is not a valid scalar", name ); + } + + // If it's 1, it wasn't loaded (or was loaded as 1) + if( scalar.unconditional_res_mult <= 0 ) { + jo.throw_error( "Proportional unconditional armor penetration multiplier is not a valid scalar", + name ); + } + + // It's it's 1, it wasn't loaded (or was loaded as 1) + if( scalar.unconditional_damage_mult <= 0 ) { + jo.throw_error( "Proportional unconditional damage multiplier is not a valid scalar", name ); + } + + damage_unit out_dmg( scalar.type, 0.0f ); + + out_dmg.amount = val_dmg.amount * scalar.amount; + out_dmg.res_pen = val_dmg.res_pen * scalar.res_pen; + + out_dmg.res_mult = val_dmg.res_mult * scalar.res_mult; + out_dmg.damage_multiplier = val_dmg.damage_multiplier * scalar.damage_multiplier; + + out_dmg.unconditional_res_mult = val_dmg.unconditional_res_mult * scalar.unconditional_res_mult; + out_dmg.unconditional_damage_mult = val_dmg.unconditional_damage_mult * + scalar.unconditional_damage_mult; + + out.add( out_dmg ); + } + } +} + +static void check_assigned_dmg( const JsonObject &err, const std::string &name, + const damage_instance &out, const damage_instance &lo_inst, const damage_instance &hi_inst ) +{ + for( const damage_unit &out_dmg : out.damage_units ) { + auto lo_iter = std::find_if( lo_inst.damage_units.begin(), + lo_inst.damage_units.end(), [&out_dmg]( const damage_unit & du ) { + return du.type == out_dmg.type || du.type == DT_NULL; + } ); + + auto hi_iter = std::find_if( hi_inst.damage_units.begin(), + hi_inst.damage_units.end(), [&out_dmg]( const damage_unit & du ) { + return du.type == out_dmg.type || du.type == DT_NULL; + } ); + + if( lo_iter == lo_inst.damage_units.end() ) { + err.throw_error( "Min damage type used in assign does not match damage type assigned", name ); + } + if( hi_iter == hi_inst.damage_units.end() ) { + err.throw_error( "Max damage type used in assign does not match damage type assigned", name ); + } + + const damage_unit &hi_dmg = *hi_iter; + const damage_unit &lo_dmg = *lo_iter; + + if( out_dmg.amount < lo_dmg.amount || out_dmg.amount > hi_dmg.amount ) { + err.throw_error( "value for damage outside supported range", name ); + } + if( out_dmg.res_pen < lo_dmg.res_pen || out_dmg.res_pen > hi_dmg.res_pen ) { + err.throw_error( "value for armor penetration outside supported range", name ); + } + if( out_dmg.res_mult < lo_dmg.res_mult || out_dmg.res_mult > hi_dmg.res_mult ) { + err.throw_error( "value for armor penetration multiplier outside supported range", name ); + } + if( out_dmg.damage_multiplier < lo_dmg.damage_multiplier || + out_dmg.damage_multiplier > hi_dmg.damage_multiplier ) { + err.throw_error( "value for damage multiplier outside supported range", name ); + } + } +} + +inline bool assign( const JsonObject &jo, const std::string &name, damage_instance &val, + bool strict = false, + const damage_instance &lo = damage_instance( DT_NULL, 0.0f, 0.0f, 0.0f, 0.0f ), + const damage_instance &hi = damage_instance( DT_NULL, float_max, float_max, float_max, + float_max ) ) +{ + // What we'll eventually be returning for the damage instance + damage_instance out; + + if( jo.has_array( name ) ) { + out = load_damage_instance_inherit( jo.get_array( name ), val ); + } else if( jo.has_object( name ) ) { + out = load_damage_instance_inherit( jo.get_object( name ), val ); + } else { + // Legacy: remove after 0.F + float amount = 0.0f; + float arpen = 0.0f; + float unc_dmg_mult = 1.0f; + + // There will always be either a prop_damage or damage (name) + if( jo.has_member( name ) ) { + amount = jo.get_float( name ); + } else if( jo.has_member( "prop_damage" ) ) { + unc_dmg_mult = jo.get_float( "prop_damage" ); + } + // And there may or may not be armor penetration + if( jo.has_member( "pierce" ) ) { + arpen = jo.get_float( "pierce" ); + } + + out.add_damage( DT_STAB, amount, arpen, 1.0f, 1.0f, 1.0f, unc_dmg_mult ); + } + + // Object via which to report errors which differs for proportional/relative values + const JsonObject &err = jo; + JsonObject relative = jo.get_object( "relative" ); + relative.allow_omitted_members(); + JsonObject proportional = jo.get_object( "proportional" ); + proportional.allow_omitted_members(); + + // Currently, we load only either relative or proportional when loading damage + // There's no good reason for this, but it's simple for now + if( relative.has_object( name ) ) { + assign_dmg_relative( out, val, load_damage_instance( relative.get_object( name ) ), strict ); + } else if( relative.has_array( name ) ) { + assign_dmg_relative( out, val, load_damage_instance( relative.get_array( name ) ), strict ); + } else if( proportional.has_object( name ) ) { + assign_dmg_proportional( proportional, name, out, val, + load_damage_instance( proportional.get_object( name ) ), + strict ); + } else if( proportional.has_array( name ) ) { + assign_dmg_proportional( proportional, name, out, val, + load_damage_instance( proportional.get_array( name ) ), + strict ); + } else if( relative.has_member( name ) || relative.has_member( "pierce" ) || + relative.has_member( "prop_damage" ) ) { + // Legacy: Remove after 0.F + // It is valid for relative to adjust any of pierce, prop_damage, or damage + // So check for what it's modifying, and modify that + float amt = 0.0f; + float arpen = 0.0f; + float unc_dmg_mul = 1.0f; + + if( relative.has_member( name ) ) { + amt = relative.get_float( name ); + } + if( relative.has_member( "pierce" ) ) { + arpen = relative.get_float( "pierce" ); + } + if( relative.has_member( "prop_damage" ) ) { + unc_dmg_mul = relative.get_float( "prop_damage" ); + } + + assign_dmg_relative( out, val, damage_instance( DT_STAB, amt, arpen, 1.0f, 1.0f, 1.0f, + unc_dmg_mul ), strict ); + } else if( proportional.has_member( name ) || proportional.has_member( "pierce" ) || + proportional.has_member( "prop_damage" ) ) { + // Legacy: Remove after 0.F + // It is valid for proportional to adjust any of pierce, prop_damage, or damage + // So check if it's modifying any of the things before going on to modify it + float amt = 0.0f; + float arpen = 0.0f; + float unc_dmg_mul = 1.0f; + + if( proportional.has_member( name ) ) { + amt = proportional.get_float( name ); + } + if( proportional.has_member( "pierce" ) ) { + arpen = proportional.get_float( "pierce" ); + } + if( proportional.has_member( "prop_damage" ) ) { + unc_dmg_mul = proportional.get_float( "prop_damage" ); + } + + assign_dmg_proportional( proportional, name, out, val, damage_instance( DT_STAB, amt, arpen, 1.0f, + 1.0f, 1.0f, unc_dmg_mul ), strict ); + } else if( !jo.has_member( name ) && !jo.has_member( "prop_damage" ) ) { + // Straight copy-from, not modified by proportional or relative + out = val; + strict = false; + } + + check_assigned_dmg( err, name, out, lo, hi ); + + if( strict && out == val ) { + report_strict_violation( err, "assignment does not update value", name ); + } + + if( out.damage_units.empty() ) { + out = damage_instance( DT_STAB, 0.0f ); + } + + // Now that we've verified everything in out is all good, set val to it + val = out; + + return true; +} #endif diff --git a/src/creature.cpp b/src/creature.cpp index 29e6bf04e7814..4475db77d6a83 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -849,7 +849,7 @@ void Creature::deal_damage_handle_type( const damage_unit &du, body_part bp, int } // Apply damage multiplier from skill, critical hits or grazes after all other modifications. - const int adjusted_damage = du.amount * du.damage_multiplier; + const int adjusted_damage = du.amount * du.damage_multiplier * du.unconditional_damage_mult; if( adjusted_damage <= 0 ) { return; } diff --git a/src/damage.cpp b/src/damage.cpp index 6ce887eb23d40..38ffe715ecd21 100644 --- a/src/damage.cpp +++ b/src/damage.cpp @@ -20,7 +20,9 @@ bool damage_unit::operator==( const damage_unit &other ) const amount == other.amount && res_pen == other.res_pen && res_mult == other.res_mult && - damage_multiplier == other.damage_multiplier; + damage_multiplier == other.damage_multiplier && + unconditional_res_mult == other.unconditional_res_mult && + unconditional_damage_mult == other.unconditional_res_mult; } damage_instance::damage_instance() = default; @@ -32,14 +34,16 @@ damage_instance damage_instance::physical( float bash, float cut, float stab, fl d.add_damage( DT_STAB, stab, arpen ); return d; } -damage_instance::damage_instance( damage_type dt, float a, float rp, float rm, float mul ) +damage_instance::damage_instance( damage_type dt, float amt, float arpen, float arpen_mult, + float dmg_mult, float unc_arpen_mult, float unc_dmg_mult ) { - add_damage( dt, a, rp, rm, mul ); + add_damage( dt, amt, arpen, arpen_mult, dmg_mult, unc_arpen_mult, unc_dmg_mult ); } -void damage_instance::add_damage( damage_type dt, float a, float rp, float rm, float mul ) +void damage_instance::add_damage( damage_type dt, float amt, float arpen, float arpen_mult, + float dmg_mult, float unc_arpen_mult, float unc_dmg_mult ) { - damage_unit du( dt, a, rp, rm, mul ); + damage_unit du( dt, amt, arpen, arpen_mult, dmg_mult, unc_arpen_mult, unc_dmg_mult ); add( du ); } @@ -64,7 +68,7 @@ float damage_instance::type_damage( damage_type dt ) const float ret = 0; for( const auto &elem : damage_units ) { if( elem.type == dt ) { - ret += elem.amount * elem.damage_multiplier; + ret += elem.amount * elem.damage_multiplier * elem.unconditional_damage_mult; } } return ret; @@ -74,7 +78,7 @@ float damage_instance::total_damage() const { float ret = 0; for( const auto &elem : damage_units ) { - ret += elem.amount * elem.damage_multiplier; + ret += elem.amount * elem.damage_multiplier * elem.unconditional_damage_mult; } return ret; } @@ -111,6 +115,9 @@ void damage_instance::add( const damage_unit &added_du ) // Linearly interpolate armor multiplier based on damage proportion contributed float t = added_du.damage_multiplier / ( added_du.damage_multiplier + du.damage_multiplier ); du.res_mult = lerp( du.res_mult, added_du.damage_multiplier, t ); + + du.unconditional_res_mult *= added_du.unconditional_res_mult; + du.unconditional_damage_mult *= added_du.unconditional_damage_mult; } } @@ -212,7 +219,8 @@ float resistances::type_resist( damage_type dt ) const } float resistances::get_effective_resist( const damage_unit &du ) const { - return std::max( type_resist( du.type ) - du.res_pen, 0.0f ) * du.res_mult; + return std::max( type_resist( du.type ) - du.res_pen, + 0.0f ) * du.res_mult * du.unconditional_res_mult; } resistances &resistances::operator+=( const resistances &other ) @@ -292,32 +300,96 @@ static damage_unit load_damage_unit( const JsonObject &curr ) curr.throw_error( "Invalid damage type" ); } - float amount = curr.get_float( "amount" ); - int arpen = curr.get_int( "armor_penetration", 0 ); + float amount = curr.get_float( "amount", 0 ); + float arpen = curr.get_float( "armor_penetration", 0 ); float armor_mul = curr.get_float( "armor_multiplier", 1.0f ); float damage_mul = curr.get_float( "damage_multiplier", 1.0f ); - return damage_unit( dt, amount, arpen, armor_mul, damage_mul ); + + float unc_armor_mul = curr.get_float( "constant_armor_multiplier", 1.0f ); + float unc_damage_mul = curr.get_float( "constant_damage_multiplier", 1.0f ); + + return damage_unit( dt, amount, arpen, armor_mul, damage_mul, unc_armor_mul, unc_damage_mul ); +} + +static damage_unit load_damage_unit_inherit( const JsonObject &curr, const damage_instance &parent ) +{ + damage_unit ret = load_damage_unit( curr ); + + const std::vector &parent_damage = parent.damage_units; + auto du_iter = std::find_if( parent_damage.begin(), + parent_damage.end(), [&ret]( const damage_unit & dmg ) { + return dmg.type == ret.type; + } ); + + if( du_iter == parent_damage.end() ) { + return ret; + } + + const damage_unit &parent_du = *du_iter; + + if( !curr.has_float( "amount" ) ) { + ret.amount = parent_du.amount; + } + if( !curr.has_float( "armor_penetration" ) ) { + ret.res_pen = parent_du.res_pen; + } + if( !curr.has_float( "armor_multiplier" ) ) { + ret.res_mult = parent_du.res_mult; + } + if( !curr.has_float( "damage_multiplier" ) ) { + ret.damage_multiplier = parent_du.damage_multiplier; + } + if( !curr.has_float( "constant_armor_multiplier" ) ) { + ret.unconditional_res_mult = parent_du.unconditional_res_mult; + } + if( !curr.has_float( "constant_damage_multiplier" ) ) { + ret.unconditional_damage_mult = parent_du.unconditional_damage_mult; + } + + return ret; +} + +static damage_instance blank_damage_instance() +{ + damage_instance ret; + + for( int i = 0; i < NUM_DT; ++i ) { + ret.add_damage( static_cast( i ), 0.0f ); + } + + return ret; } damage_instance load_damage_instance( const JsonObject &jo ) +{ + return load_damage_instance_inherit( jo, blank_damage_instance() ); +} + +damage_instance load_damage_instance( const JsonArray &jarr ) +{ + return load_damage_instance_inherit( jarr, blank_damage_instance() ); +} + + +damage_instance load_damage_instance_inherit( const JsonObject &jo, const damage_instance &parent ) { damage_instance di; if( jo.has_array( "values" ) ) { for( const JsonObject curr : jo.get_array( "values" ) ) { - di.damage_units.push_back( load_damage_unit( curr ) ); + di.damage_units.push_back( load_damage_unit_inherit( curr, parent ) ); } } else if( jo.has_string( "damage_type" ) ) { - di.damage_units.push_back( load_damage_unit( jo ) ); + di.damage_units.push_back( load_damage_unit_inherit( jo, parent ) ); } return di; } -damage_instance load_damage_instance( const JsonArray &jarr ) +damage_instance load_damage_instance_inherit( const JsonArray &jarr, const damage_instance &parent ) { damage_instance di; for( const JsonObject curr : jarr ) { - di.damage_units.push_back( load_damage_unit( curr ) ); + di.damage_units.push_back( load_damage_unit_inherit( curr, parent ) ); } return di; diff --git a/src/damage.h b/src/damage.h index 2c63574bb02fa..c5b5c277ed430 100644 --- a/src/damage.h +++ b/src/damage.h @@ -38,8 +38,13 @@ struct damage_unit { float res_mult; float damage_multiplier; - damage_unit( damage_type dt, float a, float rp = 0.0f, float rm = 1.0f, float mul = 1.0f ) : - type( dt ), amount( a ), res_pen( rp ), res_mult( rm ), damage_multiplier( mul ) { } + float unconditional_res_mult; + float unconditional_damage_mult; + + damage_unit( damage_type dt, float amt, float arpen = 0.0f, float arpen_mult = 1.0f, + float dmg_mult = 1.0f, float unc_arpen_mult = 1.0f, float unc_dmg_mult = 1.0f ) : + type( dt ), amount( amt ), res_pen( arpen ), res_mult( arpen_mult ), damage_multiplier( dmg_mult ), + unconditional_res_mult( unc_arpen_mult ), unconditional_damage_mult( unc_dmg_mult ) { } bool operator==( const damage_unit &other ) const; }; @@ -50,7 +55,8 @@ struct damage_instance { std::vector damage_units; damage_instance(); static damage_instance physical( float bash, float cut, float stab, float arpen = 0.0f ); - damage_instance( damage_type dt, float a, float rp = 0.0f, float rm = 1.0f, float mul = 1.0f ); + damage_instance( damage_type dt, float amt, float arpen = 0.0f, float arpen_mult = 1.0f, + float dmg_mult = 1.0f, float unc_arpen_mult = 1.0f, float unc_dmg_mult = 1.0f ); void mult_damage( double multiplier, bool pre_armor = false ); float type_damage( damage_type dt ) const; float total_damage() const; @@ -70,7 +76,8 @@ struct damage_instance { * The normalization means that the effective damage can actually decrease (depending on target's armor). */ /*@{*/ - void add_damage( damage_type dt, float a, float rp = 0.0f, float rm = 1.0f, float mul = 1.0f ); + void add_damage( damage_type dt, float amt, float arpen = 0.0f, float arpen_mult = 1.0f, + float dmg_mult = 1.0f, float unc_arpen_mult = 1.0f, float unc_dmg_mult = 1.0f ); void add( const damage_instance &added_di ); void add( const damage_unit &added_du ); /*@}*/ @@ -113,6 +120,10 @@ const skill_id &skill_by_dt( damage_type dt ); damage_instance load_damage_instance( const JsonObject &jo ); damage_instance load_damage_instance( const JsonArray &jarr ); +damage_instance load_damage_instance_inherit( const JsonObject &jo, const damage_instance &parent ); +damage_instance load_damage_instance_inherit( const JsonArray &jarr, + const damage_instance &parent ); + resistances load_resistances_instance( const JsonObject &jo ); // Returns damage or resistance data diff --git a/src/dump.cpp b/src/dump.cpp index 46e46883a5a1b..4d010fd3a4a28 100644 --- a/src/dump.cpp +++ b/src/dump.cpp @@ -88,7 +88,6 @@ bool game::dump_stats( const std::string &what, dump_mode mode, damage_instance damage = obj.type->ammo->damage; r.push_back( to_string( damage.total_damage() ) ); r.push_back( to_string( damage.empty() ? 0 : ( *damage.begin() ).res_pen ) ); - r.push_back( obj.type->ammo->prop_damage ? to_string( *obj.type->ammo->prop_damage ) : "---" ); rows.push_back( r ); }; for( const itype *e : item_controller->all() ) { diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index 8557d1610906d..14788fe704821 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -1151,8 +1151,9 @@ class weapon_inventory_preset: public inventory_selector_preset if( loc->ammo_data() && loc->ammo_remaining() ) { const int basic_damage = loc->gun_damage( false ).total_damage(); - if( loc->ammo_data()->ammo->prop_damage ) { - const float ammo_mult = *loc->ammo_data()->ammo->prop_damage; + if( loc->ammo_data()->ammo->damage.damage_units.front().unconditional_damage_mult != 1.0f ) { + const float ammo_mult = + loc->ammo_data()->ammo->damage.damage_units.front().unconditional_damage_mult; return string_format( "%s*%s = %s", get_damage_string( basic_damage, true ), diff --git a/src/item.cpp b/src/item.cpp index acf767eff3fbf..b992eb24dda70 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -1036,12 +1036,9 @@ void item::clear_vars() static int get_ranged_pierce( const common_ranged_data &ranged ) { if( ranged.damage.empty() ) { - if( ranged.legacy_pierce ) { - return ranged.legacy_pierce; - } else { - return 0; - } + return 0; } + return ranged.damage.damage_units.front().res_pen; } @@ -1677,21 +1674,18 @@ void item::ammo_info( std::vector &info, const iteminfo_query *parts, } const islot_ammo &ammo = *ammo_data()->ammo; - if( !ammo.damage.empty() || ammo.prop_damage || ammo.force_stat_display ) { - if( !ammo.damage.empty() ) { + if( !ammo.damage.empty() || ammo.force_stat_display ) { + if( !ammo.damage.empty() && ammo.damage.damage_units.front().amount > 0 ) { if( parts->test( iteminfo_parts::AMMO_DAMAGE_VALUE ) ) { info.emplace_back( "AMMO", _( "Damage: " ), "", iteminfo::no_newline, ammo.damage.total_damage() ); } - } else if( ammo.prop_damage ) { + } else { if( parts->test( iteminfo_parts::AMMO_DAMAGE_PROPORTIONAL ) ) { info.emplace_back( "AMMO", _( "Damage multiplier: " ), "", iteminfo::no_newline | iteminfo::is_decimal, - *ammo.prop_damage ); + ammo.damage.damage_units.front().unconditional_damage_mult ); } - } else { - info.emplace_back( "AMMO", _( "Damage multiplier: " ), "", - iteminfo::no_newline | iteminfo::is_decimal, 1.0 ); } if( parts->test( iteminfo_parts::AMMO_DAMAGE_AP ) ) { info.emplace_back( "AMMO", space + _( "Armor-pierce: " ), get_ranged_pierce( ammo ) ); @@ -1770,12 +1764,14 @@ void item::gun_info( const item *mod, std::vector &info, const iteminf if( mod->ammo_required() ) { // ammo_damage, sum_of_damage, and ammo_mult not shown so don't need to translate. - if( curammo->ammo->prop_damage ) { + float dmg_mult = 1.0f; + for( const damage_unit &dmg : curammo->ammo->damage.damage_units ) { + dmg_mult *= dmg.unconditional_damage_mult; + } + if( dmg_mult != 1.0f ) { if( parts->test( iteminfo_parts::GUN_DAMAGE_AMMOPROP ) ) { - info.push_back( - iteminfo( "GUN", "ammo_mult", "*", - iteminfo::no_newline | iteminfo::no_name | iteminfo::is_decimal, - *curammo->ammo->prop_damage ) ); + info.push_back( iteminfo( "GUN", "ammo_mult", "*", + iteminfo::no_newline | iteminfo::no_name | iteminfo::is_decimal, dmg_mult ) ); } } else { if( parts->test( iteminfo_parts::GUN_DAMAGE_LOADEDAMMO ) ) { @@ -6690,16 +6686,7 @@ damage_instance item::gun_damage( bool with_ammo ) const } if( with_ammo && ammo_data() ) { - if( ammo_data()->ammo->prop_damage ) { - for( damage_unit &elem : ret.damage_units ) { - if( elem.type == DT_STAB ) { - elem.amount *= *ammo_data()->ammo->prop_damage; - elem.res_pen = ammo_data()->ammo->legacy_pierce; - } - } - } else { - ret.add( ammo_data()->ammo->damage ); - } + ret.add( ammo_data()->ammo->damage ); } int item_damage = damage_level( 4 ); diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 2fdce6ab5f35a..b5b45b4d667e9 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -144,18 +144,6 @@ void Item_factory::finalize_pre( itype &obj ) } } - static const auto handle_legacy_ranged = []( common_ranged_data & ranged ) { - if( ranged.legacy_damage != 0 ) { - ranged.damage.add( damage_instance::physical( 0, 0, ranged.legacy_damage, ranged.legacy_pierce ) ); - ranged.legacy_damage = 0; - ranged.legacy_pierce = 0; - } - }; - - if( obj.gunmod ) { - handle_legacy_ranged( *obj.gunmod ); - } - if( obj.mod ) { std::string func = obj.gunmod ? "GUNMOD_ATTACH" : "TOOLMOD_ATTACH"; obj.use_methods.emplace( func, usage_from_string( func ) ); @@ -216,8 +204,6 @@ void Item_factory::finalize_pre( itype &obj ) // for ammo not specifying loudness (or an explicit zero) derive value from other properties if( obj.ammo ) { - handle_legacy_ranged( *obj.ammo ); - if( obj.ammo->loudness < 0 ) { obj.ammo->loudness = obj.ammo->range * 2; for( const damage_unit &du : obj.ammo->damage ) { @@ -348,7 +334,6 @@ void Item_factory::finalize_pre( itype &obj ) } } - handle_legacy_ranged( *obj.gun ); // TODO: add explicit action field to gun definitions const auto defmode_name = [&]() { if( obj.gun->clip == 1 ) { @@ -1494,20 +1479,15 @@ void Item_factory::load( islot_ammo &slot, const JsonObject &jo, const std::stri assign( jo, "drop", slot.drop, strict ); assign( jo, "drop_chance", slot.drop_chance, strict, 0.0f, 1.0f ); assign( jo, "drop_active", slot.drop_active, strict ); - if( jo.has_object( "damage" ) ) { - assign( jo, "damage", slot.damage, strict ); - } else { - assign( jo, "damage", slot.legacy_damage, strict, 0 ); - } - - assign( jo, "pierce", slot.legacy_pierce, strict, 0 ); + // Damage instance assign reader handles pierce and prop_damage + assign( jo, "damage", slot.damage, strict ); assign( jo, "range", slot.range, strict, 0 ); assign( jo, "dispersion", slot.dispersion, strict, 0 ); assign( jo, "recoil", slot.recoil, strict, 0 ); assign( jo, "count", slot.def_charges, strict, 1 ); assign( jo, "loudness", slot.loudness, strict, 0 ); assign( jo, "effects", slot.ammo_effects, strict ); - assign( jo, "prop_damage", slot.prop_damage, strict ); + assign( jo, "show_stats", slot.force_stat_display, strict ); } @@ -1597,13 +1577,8 @@ void Item_factory::load( islot_gun &slot, const JsonObject &jo, const std::strin slot.ammo.insert( ammotype( jo.get_string( "ammo" ) ) ); } assign( jo, "range", slot.range, strict ); - if( jo.has_object( "ranged_damage" ) || jo.has_array( "ranged_damage" ) ) { - assign( jo, "ranged_damage", slot.damage, strict ); - } else { - assign( jo, "ranged_damage", slot.legacy_damage, strict ); - } - - assign( jo, "pierce", slot.legacy_pierce, strict, 0 ); + // Damage instance assign reader handles pierce + assign( jo, "ranged_damage", slot.damage, strict, damage_instance( DT_NULL, -20, -20, -20, -20 ) ); assign( jo, "dispersion", slot.dispersion, strict ); assign( jo, "sight_dispersion", slot.sight_dispersion, strict, 0, static_cast( MAX_RECOIL ) ); assign( jo, "recoil", slot.recoil, strict, 0 ); @@ -1995,11 +1970,8 @@ void Item_factory::load( islot_gunmod &slot, const JsonObject &jo, const std::st { bool strict = src == "dda"; - if( jo.has_object( "damage_modifier" ) ) { - assign( jo, "damage_modifier", slot.damage ); - } else { - assign( jo, "damage_modifier", slot.legacy_damage ); - } + assign( jo, "damage_modifier", slot.damage, strict, damage_instance( DT_NULL, -20, -20, -20, + -20 ) ); assign( jo, "loudness_modifier", slot.loudness ); assign( jo, "location", slot.location ); assign( jo, "dispersion_modifier", slot.dispersion ); diff --git a/src/itype.h b/src/itype.h index fe49507a219d7..80e7ec8c23d6e 100644 --- a/src/itype.h +++ b/src/itype.h @@ -422,12 +422,6 @@ struct common_ranged_data { * Dispersion "bonus" from gun. */ int dispersion = 0; - /** - * Legacy pierce and damage values, used if @ref damage isn't set. - *@{*/ - int legacy_pierce = 0; - int legacy_damage = 0; - /*@}*/ }; struct islot_engine { @@ -716,12 +710,6 @@ struct islot_ammo : common_ranged_data { * */ bool special_cookoff = false; - /** - * If set, ammo does not give a flat damage, instead it multiplies the base - * damage of the gun by this value. - */ - cata::optional prop_damage; - /** * Some combat ammo might not have a damage or prop_damage value * Set this to make it show as combat ammo anyway diff --git a/src/player.cpp b/src/player.cpp index f6cb570421e13..c445c70d6ddc6 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -2773,14 +2773,9 @@ item::reload_option player::select_ammo( const item &base, const itype *ammo = sel.ammo->is_ammo_container() ? sel.ammo->contents.front().ammo_data() : sel.ammo->ammo_data(); if( ammo ) { - if( ammo->ammo->prop_damage ) { - row += string_format( "| *%-6.2f | %-7d", static_cast( *ammo->ammo->prop_damage ), - ammo->ammo->legacy_pierce ); - } else { - const damage_instance &dam = ammo->ammo->damage; - row += string_format( "| %-7d | %-7d", static_cast( dam.total_damage() ), - static_cast( dam.empty() ? 0.0f : ( *dam.begin() ).res_pen ) ); - } + const damage_instance &dam = ammo->ammo->damage; + row += string_format( "| %-7d | %-7d", static_cast( dam.total_damage() ), + static_cast( dam.empty() ? 0.0f : ( *dam.begin() ).res_pen ) ); } else { row += "| | "; }