From 0c7dd6a4c359663bd27a3f1c2ae113ffead4fd59 Mon Sep 17 00:00:00 2001 From: Fris0uman <41293484+Fris0uman@users.noreply.github.com> Date: Sat, 15 Jun 2024 04:44:11 +0200 Subject: [PATCH] Use ammo_effect_str_id instead of std::string (#73931) * Use ammo_effect_str_id instead of std::string * replace RECOVER_ flag with a new field * Update json * Update doc * remove unused ammo_effect * update mods * fix ids for clang --------- Co-authored-by: Fris0uman --- data/json/ammo_effects.json | 5 - data/json/items/ammo.json | 6 +- data/json/items/generic/toys_and_sports.json | 6 +- data/json/items/ranged/archery.json | 37 ++++--- data/json/items/ranged/atlatl.json | 6 +- data/json/items/ranged/ballista.json | 2 +- data/json/items/ranged/crossbows.json | 27 ++--- data/json/items/ranged/launchers.json | 2 +- data/json/items/ranged/spearguns.json | 6 +- data/json/items/resources/stone.json | 3 +- data/mods/Magiclysm/items/archery.json | 23 +++-- data/mods/Magiclysm/items/ethereal_items.json | 5 +- data/mods/TEST_DATA/items.json | 5 +- data/mods/Xedra_Evolved/items/ammo.json | 3 +- data/mods/Xedra_Evolved/items/range.json | 4 +- doc/JSON_INFO.md | 1 + src/ammo_effect.cpp | 10 +- src/ammo_effect.h | 4 +- src/ballistics.cpp | 54 ++++++---- src/bionics.cpp | 7 +- src/creature.cpp | 45 ++++++--- src/explosion.cpp | 4 +- src/item.cpp | 57 +++++------ src/item.h | 2 +- src/item_factory.cpp | 18 ++-- src/itype.h | 7 +- src/magic.cpp | 4 +- src/map.cpp | 20 ++-- src/monattack.cpp | 20 ++-- src/mondefense.cpp | 7 +- src/monster.cpp | 4 +- src/projectile.cpp | 9 +- src/projectile.h | 6 +- src/ranged.cpp | 98 +++++++++++-------- src/turret.cpp | 4 +- src/vehicle.h | 2 +- tests/coverage_test.cpp | 2 +- tests/explosion_balance_test.cpp | 4 +- tests/materials_test.cpp | 6 +- tests/vehicle_turrets_test.cpp | 5 +- 40 files changed, 320 insertions(+), 220 deletions(-) diff --git a/data/json/ammo_effects.json b/data/json/ammo_effects.json index 079515e28b464..82c3eccf89c7e 100644 --- a/data/json/ammo_effects.json +++ b/data/json/ammo_effects.json @@ -170,11 +170,6 @@ "type": "ammo_effect", "//": "Applies sensor-stunning effect to robots. Hardcoded" }, - { - "id": "RECOVER_X", - "type": "ammo_effect", - "//": " Has a [(X-1)/X] probability to create a single charge of the used ammo at the point of impact. Change the X with any number, like 'RECOVER_2' means [(2-1)/2]=50% chance. Hardcoded" - }, { "id": "RECYCLED", "type": "ammo_effect", diff --git a/data/json/items/ammo.json b/data/json/items/ammo.json index ece4fed043cfd..dbf44dc7c75f6 100644 --- a/data/json/items/ammo.json +++ b/data/json/items/ammo.json @@ -539,7 +539,8 @@ "dispersion": 14, "loudness": 0, "to_hit": -2, - "effects": [ "NEVER_MISFIRES", "NON_FOULING", "RECOVER_80" ], + "recovery_chance": 80, + "effects": [ "NEVER_MISFIRES", "NON_FOULING" ], "melee_damage": { "cut": 9 } }, { @@ -560,7 +561,8 @@ "dispersion": 14, "loudness": 0, "to_hit": -1, - "effects": [ "NEVER_MISFIRES", "NON_FOULING", "RECOVER_80", "ALLOWS_BODY_BLOCK" ], + "recovery_chance": 80, + "effects": [ "NEVER_MISFIRES", "NON_FOULING", "ALLOWS_BODY_BLOCK" ], "qualities": [ [ "HAMMER", 1 ] ], "melee_damage": { "bash": 7 } }, diff --git a/data/json/items/generic/toys_and_sports.json b/data/json/items/generic/toys_and_sports.json index 91bc534cd9151..c8bbcee2b2b24 100644 --- a/data/json/items/generic/toys_and_sports.json +++ b/data/json/items/generic/toys_and_sports.json @@ -122,7 +122,8 @@ "dispersion": 14, "loudness": 0, "to_hit": -3, - "effects": [ "NEVER_MISFIRES", "NON_FOULING", "RECOVER_60" ], + "recovery_chance": 60, + "effects": [ "NEVER_MISFIRES", "NON_FOULING" ], "melee_damage": { "bash": 8 } }, { @@ -163,7 +164,8 @@ "dispersion": 12, "loudness": 0, "to_hit": 3, - "effects": [ "NEVER_MISFIRES", "NON_FOULING", "RECOVER_80" ], + "recovery_chance": 80, + "effects": [ "NEVER_MISFIRES", "NON_FOULING" ], "melee_damage": { "bash": 6 } }, { diff --git a/data/json/items/ranged/archery.json b/data/json/items/ranged/archery.json index 2cbde31fda16f..a1f02fc4dea6c 100644 --- a/data/json/items/ranged/archery.json +++ b/data/json/items/ranged/archery.json @@ -12,7 +12,7 @@ "description": "A crude pointed wooden shaft with a notch at the back.", "material": [ "wood" ], "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.75 }, - "effects": [ "RECOVER_2" ] + "recovery_chance": 2 }, { "type": "AMMO", @@ -33,7 +33,7 @@ "dispersion": 150, "loudness": 0, "critical_multiplier": 10, - "effects": [ "RECOVER_30" ], + "recovery_chance": 30, "melee_damage": { "bash": 3 } }, { @@ -50,7 +50,8 @@ "material": [ { "type": "wood", "portion": 81 }, { "type": "rubber", "portion": 19 } ], "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.2 }, "critical_multiplier": 1, - "effects": [ "RECOVER_35", "BEANBAG" ] + "recovery_chance": 35, + "effects": [ "BEANBAG" ] }, { "type": "AMMO", @@ -63,7 +64,8 @@ "copy-from": "arrow_field_point_fletched", "price_postapoc": "15 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.5 }, - "effects": [ "RECOVER_10", "NOGIB" ] + "recovery_chance": 10, + "effects": [ "NOGIB" ] }, { "type": "AMMO", @@ -76,7 +78,7 @@ "relative": { "dispersion": -40 }, "price": "20 USD", "damage": { "damage_type": "stab", "armor_penetration": 1, "constant_damage_multiplier": 1.5 }, - "effects": [ "RECOVER_25" ], + "recovery_chance": 25, "melee_damage": { "bash": 2, "cut": 1 } }, { @@ -90,7 +92,7 @@ "material": [ "wood" ], "copy-from": "arrow_field_point_fletched", "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.75 }, - "effects": [ "RECOVER_6" ] + "recovery_chance": 6 }, { "type": "AMMO", @@ -103,7 +105,8 @@ "copy-from": "arrow_field_point_fletched", "price_postapoc": "25 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.5 }, - "effects": [ "RECOVER_35", "NOGIB" ] + "recovery_chance": 35, + "effects": [ "NOGIB" ] }, { "type": "AMMO", @@ -118,7 +121,7 @@ "price_postapoc": "15 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 1.25 }, "show_stats": true, - "effects": [ "RECOVER_8" ] + "recovery_chance": 8 }, { "type": "AMMO", @@ -132,7 +135,7 @@ "price_postapoc": "20 cent", "damage": { "damage_type": "stab", "armor_penetration": 1 }, "show_stats": true, - "effects": [ "RECOVER_20" ], + "recovery_chance": 20, "melee_damage": { "bash": 2 } }, { @@ -148,7 +151,7 @@ "price_postapoc": "40 cent", "damage": { "damage_type": "stab", "armor_penetration": 1, "constant_damage_multiplier": 1.5 }, "range": 2, - "effects": [ "RECOVER_35" ], + "recovery_chance": 35, "melee_damage": { "bash": 3, "cut": 2 } }, { @@ -164,7 +167,7 @@ "price_postapoc": "40 cent", "damage": { "damage_type": "stab", "armor_penetration": 3 }, "range": 2, - "effects": [ "RECOVER_40" ], + "recovery_chance": 40, "melee_damage": { "bash": 3, "cut": 2 } }, { @@ -181,7 +184,8 @@ "relative": { "dispersion": -75 }, "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.5 }, "range": 2, - "effects": [ "RECOVER_45", "NOGIB" ] + "recovery_chance": 45, + "effects": [ "NOGIB" ] }, { "type": "AMMO", @@ -197,7 +201,7 @@ "price_postapoc": "50 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 1.75 }, "range": 4, - "effects": [ "RECOVER_30" ], + "recovery_chance": 30, "melee_damage": { "bash": 1 } }, { @@ -220,7 +224,7 @@ "dispersion": 120, "loudness": 0, "critical_multiplier": 6, - "effects": [ "RECOVER_1000000" ], + "recovery_chance": 100, "to_hit": { "grip": "solid", "length": "long", "surface": "point", "balance": "good" }, "flags": [ "UNBREAKABLE_MELEE", "NONCONDUCTIVE" ], "melee_damage": { "bash": 10, "stab": 26 } @@ -245,7 +249,7 @@ "count": 1, "stack_size": 1, "critical_multiplier": 10, - "effects": [ "RECOVER_35" ], + "recovery_chance": 35, "use_action": { "target": "arrow_flamming", "target_timer": "20 seconds", @@ -266,7 +270,8 @@ "symbol": "=", "color": "brown", "revert_to": "arrow_field_point_fletched", - "effects": [ "IGNITE", "RECOVER_35" ], + "recovery_chance": 35, + "effects": [ "IGNITE" ], "flags": [ "TRADER_AVOID" ] }, { diff --git a/data/json/items/ranged/atlatl.json b/data/json/items/ranged/atlatl.json index 4b9e664ba27db..0777b590ef4f9 100644 --- a/data/json/items/ranged/atlatl.json +++ b/data/json/items/ranged/atlatl.json @@ -53,7 +53,7 @@ "dispersion": 35, "loudness": 0, "critical_multiplier": 15, - "effects": [ "RECOVER_30" ], + "recovery_chance": 30, "melee_damage": { "stab": 10 } }, { @@ -74,7 +74,7 @@ "dispersion": 35, "loudness": 0, "critical_multiplier": 15, - "effects": [ "RECOVER_25" ], + "recovery_chance": 25, "melee_damage": { "stab": 8 } }, { @@ -95,7 +95,7 @@ "dispersion": 50, "loudness": 0, "critical_multiplier": 8, - "effects": [ "RECOVER_6" ], + "recovery_chance": 6, "melee_damage": { "stab": 5 } } ] diff --git a/data/json/items/ranged/ballista.json b/data/json/items/ranged/ballista.json index 051d17576e55c..4d54a86aa7632 100644 --- a/data/json/items/ranged/ballista.json +++ b/data/json/items/ranged/ballista.json @@ -48,7 +48,7 @@ "dispersion": 35, "loudness": 5, "critical_multiplier": 2, - "effects": [ "RECOVER_50" ], + "recovery_chance": 50, "melee_damage": { "stab": 12 } } ] diff --git a/data/json/items/ranged/crossbows.json b/data/json/items/ranged/crossbows.json index 77eef424bd2c2..03f4dd10adcfb 100644 --- a/data/json/items/ranged/crossbows.json +++ b/data/json/items/ranged/crossbows.json @@ -11,7 +11,7 @@ "price": "0 cent", "price_postapoc": "20 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.75 }, - "effects": [ "RECOVER_2" ] + "recovery_chance": 2 }, { "type": "AMMO", @@ -26,7 +26,7 @@ "price": "5 USD", "price_postapoc": "25 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.75 }, - "effects": [ "RECOVER_6" ] + "recovery_chance": 6 }, { "type": "AMMO", @@ -41,7 +41,8 @@ "price": "6 USD 50 cent", "price_postapoc": "25 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.5 }, - "effects": [ "RECOVER_10", "NOGIB" ] + "recovery_chance": 10, + "effects": [ "NOGIB" ] }, { "type": "AMMO", @@ -57,7 +58,7 @@ "price_postapoc": "30 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 1.25 }, "show_stats": true, - "effects": [ "RECOVER_8" ] + "recovery_chance": 8 }, { "type": "AMMO", @@ -69,7 +70,7 @@ "copy-from": "bolt_wood_bodkin", "relative": { "dispersion": 10 }, "damage": { "damage_type": "stab", "constant_damage_multiplier": 1.5, "armor_penetration": 1 }, - "effects": [ "RECOVER_25" ] + "recovery_chance": 25 }, { "type": "AMMO", @@ -89,7 +90,7 @@ "dispersion": 100, "loudness": 0, "critical_multiplier": 10, - "effects": [ "RECOVER_30" ], + "recovery_chance": 30, "melee_damage": { "bash": 3 } }, { @@ -102,7 +103,8 @@ "copy-from": "bolt_wood_bodkin", "relative": { "dispersion": 30 }, "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.5 }, - "effects": [ "RECOVER_35", "NOGIB" ] + "recovery_chance": 35, + "effects": [ "NOGIB" ] }, { "type": "AMMO", @@ -115,7 +117,7 @@ "relative": { "dispersion": 80 }, "damage": { "damage_type": "stab", "constant_damage_multiplier": 1.25, "armor_penetration": 1 }, "show_stats": true, - "effects": [ "RECOVER_20" ] + "recovery_chance": 20 }, { "type": "AMMO", @@ -131,7 +133,7 @@ "price_postapoc": "50 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 1.5, "armor_penetration": 2 }, "range": 2, - "effects": [ "RECOVER_35" ] + "recovery_chance": 35 }, { "type": "AMMO", @@ -147,7 +149,7 @@ "price_postapoc": "50 cent", "damage": { "damage_type": "stab", "armor_penetration": 4 }, "range": 2, - "effects": [ "RECOVER_40" ] + "recovery_chance": 40 }, { "type": "AMMO", @@ -161,7 +163,8 @@ "relative": { "dispersion": -30 }, "damage": { "damage_type": "stab", "constant_damage_multiplier": 0.5 }, "range": 2, - "effects": [ "RECOVER_45", "NOGIB" ] + "recovery_chance": 45, + "effects": [ "NOGIB" ] }, { "type": "AMMO", @@ -177,7 +180,7 @@ "price_postapoc": "60 cent", "damage": { "damage_type": "stab", "constant_damage_multiplier": 1.75 }, "range": 4, - "effects": [ "RECOVER_30" ] + "recovery_chance": 30 }, { "id": "bullet_crossbow", diff --git a/data/json/items/ranged/launchers.json b/data/json/items/ranged/launchers.json index 86998ca5717c3..7b60e528be578 100644 --- a/data/json/items/ranged/launchers.json +++ b/data/json/items/ranged/launchers.json @@ -65,7 +65,7 @@ "price": "500 USD", "material": [ "steel" ], "flags": [ "NEVER_JAMS", "MOUNTED_GUN", "NO_RELOAD", "NON_FOULING", "WONT_TRAIN_MARKSMANSHIP" ], - "ammo_effects": [ "JET", "BEANBAG", "NEVER_MISFIRES", "RECOVER_10" ], + "ammo_effects": [ "JET", "BEANBAG", "NEVER_MISFIRES" ], "skill": "launcher", "ammo": [ "water" ], "weight": "24500 g", diff --git a/data/json/items/ranged/spearguns.json b/data/json/items/ranged/spearguns.json index fa399bb2ac38b..e9cd0814cc8e3 100644 --- a/data/json/items/ranged/spearguns.json +++ b/data/json/items/ranged/spearguns.json @@ -19,7 +19,7 @@ "dispersion": 150, "loudness": 0, "count": 4, - "effects": [ "RECOVER_5" ], + "recovery_chance": 5, "melee_damage": { "bash": 1 } }, { @@ -42,7 +42,7 @@ "dispersion": 90, "loudness": 0, "count": 4, - "effects": [ "RECOVER_3" ], + "recovery_chance": 3, "melee_damage": { "bash": 1 } }, { @@ -65,7 +65,7 @@ "dispersion": 120, "loudness": 0, "count": 4, - "effects": [ "RECOVER_15" ], + "recovery_chance": 15, "melee_damage": { "bash": 1 } }, { diff --git a/data/json/items/resources/stone.json b/data/json/items/resources/stone.json index 737b8268cee55..afdea6fd6d3c0 100644 --- a/data/json/items/resources/stone.json +++ b/data/json/items/resources/stone.json @@ -18,7 +18,8 @@ "dispersion": 14, "loudness": 0, "to_hit": -1, - "effects": [ "NEVER_MISFIRES", "NON_FOULING", "RECOVER_60" ], + "recovery_chance": 60, + "effects": [ "NEVER_MISFIRES", "NON_FOULING" ], "melee_damage": { "bash": 7 } }, { diff --git a/data/mods/Magiclysm/items/archery.json b/data/mods/Magiclysm/items/archery.json index 713d7398e4a32..e587c3aa332a7 100644 --- a/data/mods/Magiclysm/items/archery.json +++ b/data/mods/Magiclysm/items/archery.json @@ -74,7 +74,7 @@ "count": 15, "stack_size": 15, "critical_multiplier": 10, - "effects": [ "RECOVER_65" ], + "recovery_chance": 65, "melee_damage": { "bash": 12 } }, { @@ -98,7 +98,8 @@ "loudness": 0, "show_stats": true, "critical_multiplier": 10, - "effects": [ "TANGLE", "RECOVER_7" ] + "recovery_chance": 7, + "effects": [ "TANGLE" ] }, { "id": "arrow_stormshaper_crafted", @@ -106,8 +107,9 @@ "copy-from": "arrow_druid_crafted", "name": { "str": "thunderclap arrow" }, "color": "magenta", + "recovery_chance": 7, "description": "An arrow with a fragment of a stormshaper rune in the arrowhead. It trails lightning and explodes with a crash of thunder.", - "effects": [ "LIGHTNING", "FLASHBANG", "RECOVER_7" ] + "effects": [ "LIGHTNING", "FLASHBANG" ] }, { "id": "arrow_earthshaper_crafted", @@ -124,8 +126,9 @@ "copy-from": "arrow_druid_crafted", "name": { "str": "fireball arrow" }, "color": "red", + "recovery_chance": 7, "description": "An arrow with a fragment of a kelvinist rune in the arrowhead. It explodes into a firestorm.", - "effects": [ "NAPALM", "RECOVER_7" ] + "effects": [ "NAPALM" ] }, { "id": "arrow_biomancer_crafted", @@ -134,8 +137,9 @@ "name": { "str": "visceral arrow" }, "color": "green", "material": [ "bone" ], + "recovery_chance": 7, "description": "A disturbing bone arrow with a fragment of a biomancer rune in the arrowhead. It showers its target in acid.", - "effects": [ "ACIDBOMB", "RECOVER_7" ] + "effects": [ "ACIDBOMB" ] }, { "id": "arrow_technomancer_crafted", @@ -143,8 +147,9 @@ "copy-from": "arrow_druid_crafted", "name": { "str": "sparking arrow" }, "color": "white", + "recovery_chance": 7, "description": "A metallic arrow with a fragment of a technomancer rune in the arrowhead. It destroys electronic targets.", - "effects": [ "EMP", "DRAW_LASER_BEAM", "RECOVER_7" ] + "effects": [ "EMP", "DRAW_LASER_BEAM" ] }, { "id": "arrow_magus_crafted", @@ -153,8 +158,9 @@ "//": "I'd like this to cause massive knockback but I'm limited by the available ammo flags.", "name": { "str": "gravity arrow" }, "color": "black", + "recovery_chance": 7, "description": "An arrow with a fragment of a magus rune in the arrowhead. It reverses flight briefly after striking its target and then hits another one.", - "effects": [ "BOUNCE", "RECOVER_7" ] + "effects": [ "BOUNCE" ] }, { "id": "arrow_animist_crafted", @@ -163,8 +169,9 @@ "//": "I'd like this to drain life but see above.", "name": { "str": "enervating arrow" }, "color": "cyan", + "recovery_chance": 7, "description": "An arrow with a fragment of an animist rune in the arrowhead. It unleashes a cloud of enervating gas.", - "effects": [ "PARALYZEPOISON", "TOXICGAS", "RECOVER_7" ] + "effects": [ "PARALYZEPOISON", "TOXICGAS" ] }, { "id": "quiver_of_holding_plus_one", diff --git a/data/mods/Magiclysm/items/ethereal_items.json b/data/mods/Magiclysm/items/ethereal_items.json index bfaba83b270b6..7dce58f5511db 100644 --- a/data/mods/Magiclysm/items/ethereal_items.json +++ b/data/mods/Magiclysm/items/ethereal_items.json @@ -446,7 +446,7 @@ "description": "A magically conjured arrow made of twisted wood.", "damage": { "damage_type": "stab", "armor_penetration": 3 }, "count": 1, - "effects": [ "RECOVER_8" ] + "recovery_chance": 8 }, { "id": "arrow_druid", @@ -457,7 +457,8 @@ "description": "A arrow that looks more like it was grown than carved. Despite that, it flies straight and true. It explodes into a mass of entangling vegetation when it hits its target.", "damage": { "damage_type": "stab", "armor_penetration": 2 }, "count": 1, - "effects": [ "RECOVER_6", "TANGLE" ] + "recovery_chance": 6, + "effects": [ "TANGLE" ] }, { "type": "TOOL", diff --git a/data/mods/TEST_DATA/items.json b/data/mods/TEST_DATA/items.json index a9ac665dcf052..d33f28ddb8596 100644 --- a/data/mods/TEST_DATA/items.json +++ b/data/mods/TEST_DATA/items.json @@ -82,7 +82,8 @@ "dispersion": 14, "loudness": 0, "to_hit": -2, - "effects": [ "NEVER_MISFIRES", "NON_FOULING", "RECOVER_80" ], + "recovery_chance": 80, + "effects": [ "NEVER_MISFIRES", "NON_FOULING" ], "qualities": [ [ "HAMMER", 1 ] ], "melee_damage": { "bash": 7 }, "variant_type": "generic", @@ -2120,7 +2121,7 @@ "loudness": 0, "count": 10, "critical_multiplier": 10, - "effects": [ "RECOVER_25" ], + "recovery_chance": 25, "melee_damage": { "bash": 2, "cut": 1 } }, { diff --git a/data/mods/Xedra_Evolved/items/ammo.json b/data/mods/Xedra_Evolved/items/ammo.json index fea98aa4f7598..35cefedb94aa5 100644 --- a/data/mods/Xedra_Evolved/items/ammo.json +++ b/data/mods/Xedra_Evolved/items/ammo.json @@ -18,7 +18,8 @@ "range": 10, "dispersion": 14, "loudness": 0, - "effects": [ "NEVER_MISFIRES", "NON_FOULING", "RECOVER_80" ], + "recovery_chance": 80, + "effects": [ "NEVER_MISFIRES", "NON_FOULING" ], "qualities": [ [ "CUT", 1 ] ], "melee_damage": { "bash": 1, "cut": 10 } }, diff --git a/data/mods/Xedra_Evolved/items/range.json b/data/mods/Xedra_Evolved/items/range.json index 1d0369a79af57..d16942e4412d9 100644 --- a/data/mods/Xedra_Evolved/items/range.json +++ b/data/mods/Xedra_Evolved/items/range.json @@ -69,7 +69,7 @@ "loudness": 0, "show_stats": true, "critical_multiplier": 10, - "effects": [ "RECOVER_20" ], + "recovery_chance": 20, "melee_damage": { "bash": 2 } }, { @@ -129,7 +129,7 @@ "dispersion": 60, "loudness": 0, "critical_multiplier": 10, - "effects": [ "RECOVER_30" ], + "recovery_chance": 30, "melee_damage": { "bash": 1 } } ] diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 19ed86807891e..ae5fc10e484ac 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -3495,6 +3495,7 @@ See [GAME_BALANCE.md](GAME_BALANCE.md)'s `MELEE_WEAPONS` section for the criteri ] }, "range" : 5, // Range when fired +"recovery_chance": 6, // Percentage of chance to recover the ammo after firing "dispersion" : 0, // Inaccuracy of ammo, measured in 100ths of Minutes Of Angle (MOA) "shot_counter": 5, // Increases amount of shots produced by gun by this amount. `"shot_counter": 5` means each shot will be counted as 6 shots (1 you actually perform + 5); designed for using in suppressor mod breakage and for stuff like replaceable barrels, but not used anywhere at this moment "projectile_count": 5,// amount of pellets, that the ammo will shot, like in shotgun-like weapon; if used, shot_damage should be specified diff --git a/src/ammo_effect.cpp b/src/ammo_effect.cpp index 945338a42517f..d4848eb987052 100644 --- a/src/ammo_effect.cpp +++ b/src/ammo_effect.cpp @@ -30,35 +30,35 @@ const ammo_effect &int_id::obj() const /** @relates int_id */ template<> -const string_id &int_id::id() const +const ammo_effect_str_id &int_id::id() const { return get_all_ammo_effects().convert( *this ); } /** @relates string_id */ template<> -bool string_id::is_valid() const +bool ammo_effect_str_id::is_valid() const { return get_all_ammo_effects().is_valid( *this ); } /** @relates string_id */ template<> -const ammo_effect &string_id::obj() const +const ammo_effect &ammo_effect_str_id::obj() const { return get_all_ammo_effects().obj( *this ); } /** @relates string_id */ template<> -int_id string_id::id() const +int_id ammo_effect_str_id::id() const { return get_all_ammo_effects().convert( *this, AE_NULL ); } /** @relates int_id */ template<> -int_id::int_id( const string_id &id ) : _id( id.id() ) +int_id::int_id( const ammo_effect_str_id &id ) : _id( id.id() ) { } diff --git a/src/ammo_effect.h b/src/ammo_effect.h index 1e56ddd624845..34d2a6ad29841 100644 --- a/src/ammo_effect.h +++ b/src/ammo_effect.h @@ -54,8 +54,8 @@ struct ammo_effect { int trail_chance = 100; // Used by generic_factory - string_id id; - std::vector, mod_id>> src; + ammo_effect_str_id id; + std::vector> src; bool was_loaded = false; static size_t count(); diff --git a/src/ballistics.cpp b/src/ballistics.cpp index 063b5c5c82c5c..f12dc86af5ae9 100644 --- a/src/ballistics.cpp +++ b/src/ballistics.cpp @@ -38,6 +38,24 @@ #include "units.h" #include "vpart_position.h" +static const ammo_effect_str_id ammo_effect_ACT_ON_RANGED_HIT( "ACT_ON_RANGED_HIT" ); +static const ammo_effect_str_id ammo_effect_BOUNCE( "BOUNCE" ); +static const ammo_effect_str_id ammo_effect_BURST( "BURST" ); +static const ammo_effect_str_id ammo_effect_DRAW_AS_LINE( "DRAW_AS_LINE" ); +static const ammo_effect_str_id ammo_effect_HEAVY_HIT( "HEAVY_HIT" ); +static const ammo_effect_str_id ammo_effect_JET( "JET" ); +static const ammo_effect_str_id ammo_effect_MUZZLE_SMOKE( "MUZZLE_SMOKE" ); +static const ammo_effect_str_id ammo_effect_NO_EMBED( "NO_EMBED" ); +static const ammo_effect_str_id ammo_effect_NO_ITEM_DAMAGE( "NO_ITEM_DAMAGE" ); +static const ammo_effect_str_id ammo_effect_NO_OVERSHOOT( "NO_OVERSHOOT" ); +static const ammo_effect_str_id ammo_effect_NO_PENETRATE_OBSTACLES( "NO_PENETRATE_OBSTACLES" ); +static const ammo_effect_str_id ammo_effect_NULL_SOURCE( "NULL_SOURCE" ); +static const ammo_effect_str_id ammo_effect_SHATTER_SELF( "SHATTER_SELF" ); +static const ammo_effect_str_id ammo_effect_STREAM( "STREAM" ); +static const ammo_effect_str_id ammo_effect_STREAM_BIG( "STREAM_BIG" ); +static const ammo_effect_str_id ammo_effect_STREAM_TINY( "STREAM_TINY" ); +static const ammo_effect_str_id ammo_effect_TANGLE( "TANGLE" ); + static const efftype_id effect_bounced( "bounced" ); static const itype_id itype_glass_shard( "glass_shard" ); @@ -55,7 +73,7 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) const tripoint &pt = attack.end_point; - if( effects.count( "SHATTER_SELF" ) ) { + if( effects.count( ammo_effect_SHATTER_SELF ) ) { // Drop the contents, not the thrown item add_msg_if_player_sees( pt, _( "The %s shatters!" ), drop_item.tname() ); @@ -84,7 +102,7 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) return; } - if( effects.count( "BURST" ) ) { + if( effects.count( ammo_effect_BURST ) ) { // Drop the contents, not the thrown item add_msg_if_player_sees( pt, _( "The %s bursts!" ), drop_item.tname() ); @@ -103,7 +121,8 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) // We can only embed in monsters bool mon_there = mon != nullptr && !mon->is_dead_state(); // And if we actually want to embed - bool embed = mon_there && effects.count( "NO_EMBED" ) == 0 && effects.count( "TANGLE" ) == 0; + bool embed = mon_there && effects.count( ammo_effect_NO_EMBED ) == 0 && + effects.count( ammo_effect_TANGLE ) == 0; // Don't embed in small creatures if( embed ) { const creature_size critter_size = mon->get_size(); @@ -131,10 +150,10 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) // if they aren't friendly they will try and break out of the net/bolas/lasso // players and NPCs just get the downed effect, and item is dropped. // TODO: storing the item on player until they recover from downed - if( effects.count( "TANGLE" ) && mon_there ) { + if( effects.count( ammo_effect_TANGLE ) && mon_there ) { do_drop = false; } - if( effects.count( "ACT_ON_RANGED_HIT" ) ) { + if( effects.count( ammo_effect_ACT_ON_RANGED_HIT ) ) { // Don't drop if it exploded do_drop = !dropped_item.activate_thrown( attack.end_point ); } @@ -144,7 +163,7 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) here.add_item_or_charges( attack.end_point, dropped_item ); } - if( effects.count( "HEAVY_HIT" ) ) { + if( effects.count( ammo_effect_HEAVY_HIT ) ) { if( here.has_flag( ter_furn_flag::TFLAG_LIQUID, pt ) ) { sounds::sound( pt, 10, sounds::sound_t::combat, _( "splash!" ), false, "bullet_hit", "hit_water" ); } else { @@ -239,20 +258,21 @@ dealt_projectile_attack projectile_attack( const projectile &proj_arg, const tri projectile &proj = attack.proj; const auto &proj_effects = proj.proj_effects; - const bool stream = proj_effects.count( "STREAM" ) > 0 || - proj_effects.count( "STREAM_TINY" ) > 0 || - proj_effects.count( "STREAM_BIG" ) > 0 || - proj_effects.count( "JET" ) > 0; + const bool stream = proj_effects.count( ammo_effect_STREAM ) > 0 || + proj_effects.count( ammo_effect_STREAM_TINY ) > 0 || + proj_effects.count( ammo_effect_STREAM_BIG ) > 0 || + proj_effects.count( ammo_effect_JET ) > 0; const char bullet = stream ? '#' : '*'; - const bool no_item_damage = proj_effects.count( "NO_ITEM_DAMAGE" ) > 0; - const bool do_draw_line = proj_effects.count( "DRAW_AS_LINE" ) > 0; - const bool null_source = proj_effects.count( "NULL_SOURCE" ) > 0; + const bool no_item_damage = proj_effects.count( ammo_effect_NO_ITEM_DAMAGE ) > 0; + const bool do_draw_line = proj_effects.count( ammo_effect_DRAW_AS_LINE ) > 0; + const bool null_source = proj_effects.count( ammo_effect_NULL_SOURCE ) > 0; // Determines whether it can penetrate obstacles - const bool is_bullet = proj_arg.speed >= 200 && !proj_effects.count( "NO_PENETRATE_OBSTACLES" ); + const bool is_bullet = proj_arg.speed >= 200 && + !proj_effects.count( ammo_effect_NO_PENETRATE_OBSTACLES ); // If we were targeting a tile rather than a monster, don't overshoot // Unless the target was a wall, then we are aiming high enough to overshoot - const bool no_overshoot = proj_effects.count( "NO_OVERSHOOT" ) || + const bool no_overshoot = proj_effects.count( ammo_effect_NO_OVERSHOOT ) || ( creatures.creature_at( target_arg ) == nullptr && here.passable( target_arg ) ); double extend_to_range = no_overshoot ? range : proj_arg.range; @@ -313,7 +333,7 @@ dealt_projectile_attack projectile_attack( const projectile &proj_arg, const tri trajectory.insert( trajectory.begin(), source ); static emit_id muzzle_smoke( "emit_smaller_smoke_plume" ); - if( proj_effects.count( "MUZZLE_SMOKE" ) ) { + if( proj_effects.count( ammo_effect_MUZZLE_SMOKE ) ) { here.emit_field( trajectory.front(), muzzle_smoke ); } @@ -498,7 +518,7 @@ dealt_projectile_attack projectile_attack( const projectile &proj_arg, const tri } // TODO: Move this outside now that we have hit point in return values? - if( proj.proj_effects.count( "BOUNCE" ) ) { + if( proj.proj_effects.count( ammo_effect_BOUNCE ) ) { // Add effect so the shooter is not targeted itself. if( origin && !origin->has_effect( effect_bounced ) ) { origin->add_effect( effect_bounced, 1_turns ); diff --git a/src/bionics.cpp b/src/bionics.cpp index 09d96a2777ccb..048dd6dce8362 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -97,6 +97,11 @@ static const activity_id ACT_OPERATION( "ACT_OPERATION" ); +static const ammo_effect_str_id ammo_effect_DRAW_AS_LINE( "DRAW_AS_LINE" ); +static const ammo_effect_str_id ammo_effect_JET( "JET" ); +static const ammo_effect_str_id ammo_effect_NO_DAMAGE_SCALING( "NO_DAMAGE_SCALING" ); +static const ammo_effect_str_id ammo_effect_NO_ITEM_DAMAGE( "NO_ITEM_DAMAGE" ); + static const bionic_id afs_bio_dopamine_stimulators( "afs_bio_dopamine_stimulators" ); static const bionic_id bio_blood_anal( "bio_blood_anal" ); static const bionic_id bio_cqb( "bio_cqb" ); @@ -1018,7 +1023,7 @@ bool Character::activate_bionic( bionic &bio, bool eff_only, bool *close_bionics proj.impact.add_damage( STATIC( damage_type_id( "bash" ) ), pr.first.weight() / 250_gram ); // make the projectile stop one tile short to prevent hitting the player proj.range = rl_dist( pr.second, pos() ) - 1; - proj.proj_effects = {{ "NO_ITEM_DAMAGE", "DRAW_AS_LINE", "NO_DAMAGE_SCALING", "JET" }}; + proj.proj_effects = {{ ammo_effect_NO_ITEM_DAMAGE, ammo_effect_DRAW_AS_LINE, ammo_effect_NO_DAMAGE_SCALING, ammo_effect_JET }}; dealt_projectile_attack dealt = projectile_attack( proj, pr.second, pos(), dispersion_sources{ 0 }, this ); diff --git a/src/creature.cpp b/src/creature.cpp index d545e1522a001..6f6d124d2bf3f 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -77,6 +77,21 @@ struct mutation_branch; +static const ammo_effect_str_id ammo_effect_APPLY_SAP( "APPLY_SAP" ); +static const ammo_effect_str_id ammo_effect_BEANBAG( "BEANBAG" ); +static const ammo_effect_str_id ammo_effect_BLINDS_EYES( "BLINDS_EYES" ); +static const ammo_effect_str_id ammo_effect_BOUNCE( "BOUNCE" ); +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_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" ); +static const ammo_effect_str_id ammo_effect_PARALYZEPOISON( "PARALYZEPOISON" ); +static const ammo_effect_str_id ammo_effect_ROBOT_DAZZLE( "ROBOT_DAZZLE" ); +static const ammo_effect_str_id ammo_effect_TANGLE( "TANGLE" ); + static const anatomy_id anatomy_human_anatomy( "human_anatomy" ); static const damage_type_id damage_acid( "acid" ); @@ -926,7 +941,7 @@ double Creature::accuracy_projectile_attack( dealt_projectile_attack &attack ) c void projectile::apply_effects_nodamage( Creature &target, Creature *source ) const { - if( proj_effects.count( "BOUNCE" ) ) { + if( proj_effects.count( ammo_effect_BOUNCE ) ) { target.add_effect( effect_source( source ), effect_bounced, 1_turns ); } } @@ -935,7 +950,7 @@ void projectile::apply_effects_damage( Creature &target, Creature *source, const dealt_damage_instance &dealt_dam, bool critical ) const { // Apply ammo effects to target. - if( proj_effects.count( "TANGLE" ) ) { + if( proj_effects.count( ammo_effect_TANGLE ) ) { // if its a tameable animal, its a good way to catch them if they are running away, like them ranchers do! // we assume immediate success, then certain monster types immediately break free in monster.cpp move_effects() if( target.is_monster() ) { @@ -958,7 +973,7 @@ void projectile::apply_effects_damage( Creature &target, Creature *source, } Character &player_character = get_player_character(); - if( proj_effects.count( "INCENDIARY" ) ) { + if( proj_effects.count( ammo_effect_INCENDIARY ) ) { if( x_in_y( 1, 100 ) ) { // 1% chance if( target.made_of( material_veggy ) || target.made_of_any( Creature::cmat_flammable ) ) { target.add_effect( effect_source( source ), effect_onfire, rng( 2_turns, 6_turns ), @@ -976,7 +991,7 @@ void projectile::apply_effects_damage( Creature &target, Creature *source, player_character.rem_morale( morale_pyromania_nofire ); } } - } else if( proj_effects.count( "IGNITE" ) ) { + } else if( proj_effects.count( ammo_effect_IGNITE ) ) { if( x_in_y( 1, 2 ) ) { // 50% chance if( target.made_of( material_veggy ) || target.made_of_any( Creature::cmat_flammable ) ) { target.add_effect( effect_source( source ), effect_onfire, 10_turns, dealt_dam.bp_hit ); @@ -994,7 +1009,7 @@ void projectile::apply_effects_damage( Creature &target, Creature *source, } } - if( proj_effects.count( "ROBOT_DAZZLE" ) ) { + if( proj_effects.count( ammo_effect_ROBOT_DAZZLE ) ) { if( critical && target.in_species( species_ROBOT ) ) { time_duration duration = rng( 6_turns, 8_turns ); target.add_effect( effect_source( source ), effect_stunned, duration ); @@ -1007,31 +1022,31 @@ void projectile::apply_effects_damage( Creature &target, Creature *source, } if( dealt_dam.bp_hit->has_type( body_part_type::type::head ) && - proj_effects.count( "BLINDS_EYES" ) ) { + proj_effects.count( ammo_effect_BLINDS_EYES ) ) { // TODO: Change this to require bp_eyes target.add_env_effect( effect_blind, target.get_random_body_part_of_type( body_part_type::type::sensor ), 5, rng( 3_turns, 10_turns ) ); } - if( proj_effects.count( "APPLY_SAP" ) ) { + if( proj_effects.count( ammo_effect_APPLY_SAP ) ) { target.add_effect( effect_source( source ), effect_sap, 1_turns * dealt_dam.total_damage() ); } - if( proj_effects.count( "PARALYZEPOISON" ) && dealt_dam.total_damage() > 0 && + if( proj_effects.count( ammo_effect_PARALYZEPOISON ) && dealt_dam.total_damage() > 0 && !dealt_dam.bp_hit->has_flag( json_flag_BIONIC_LIMB ) ) { target.add_msg_if_player( m_bad, _( "You feel poison coursing through your body!" ) ); target.add_effect( effect_source( source ), effect_paralyzepoison, 5_minutes ); } - if( proj_effects.count( "FOAMCRETE" ) && effect_foamcrete_slow.is_valid() ) { + if( proj_effects.count( ammo_effect_FOAMCRETE ) && effect_foamcrete_slow.is_valid() ) { target.add_msg_if_player( m_bad, _( "The foamcrete stiffens around you!" ) ); target.add_effect( effect_source( source ), effect_foamcrete_slow, 5_minutes ); } int stun_strength = 0; - if( proj_effects.count( "BEANBAG" ) ) { + if( proj_effects.count( ammo_effect_BEANBAG ) ) { stun_strength = 4; } - if( proj_effects.count( "LARGE_BEANBAG" ) ) { + if( proj_effects.count( ammo_effect_LARGE_BEANBAG ) ) { stun_strength = 16; } if( stun_strength > 0 ) { @@ -1075,7 +1090,7 @@ projectile_attack_results Creature::select_body_part_projectile_attack( const projectile &proj, const double goodhit, const double missed_by ) const { projectile_attack_results ret( proj ); - const bool magic = proj.proj_effects.count( "MAGIC" ) > 0; + const bool magic = proj.proj_effects.count( ammo_effect_MAGIC ) > 0; double hit_value = missed_by + rng_float( -0.5, 0.5 ); if( magic ) { // Best possible hit @@ -1201,7 +1216,7 @@ void Creature::messaging_projectile_attack( const Creature *source, void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack &attack, bool print_messages, const weakpoint_attack &wp_attack ) { - const bool magic = attack.proj.proj_effects.count( "MAGIC" ) > 0; + const bool magic = attack.proj.proj_effects.count( ammo_effect_MAGIC ) > 0; const double missed_by = attack.missed_by; if( missed_by >= 1.0 && !magic ) { // Total miss @@ -1268,13 +1283,13 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack // copy it, since we're mutating. damage_instance impact = proj.impact; - if( hit_selection.damage_mult > 0.0f && proj_effects.count( "NO_DAMAGE_SCALING" ) ) { + if( hit_selection.damage_mult > 0.0f && proj_effects.count( ammo_effect_NO_DAMAGE_SCALING ) ) { hit_selection.damage_mult = 1.0f; } impact.mult_damage( hit_selection.damage_mult ); - if( proj_effects.count( "NOGIB" ) > 0 ) { + if( proj_effects.count( ammo_effect_NOGIB ) > 0 ) { float dmg_ratio = static_cast( impact.total_damage() ) / get_hp_max( hit_selection.bp_hit ); if( dmg_ratio > 1.25f ) { impact.mult_damage( 1.0f / dmg_ratio ); diff --git a/src/explosion.cpp b/src/explosion.cpp index 26379f84698b2..906fef90b1c6f 100644 --- a/src/explosion.cpp +++ b/src/explosion.cpp @@ -64,6 +64,8 @@ #include "vehicle.h" #include "vpart_position.h" +static const ammo_effect_str_id ammo_effect_NULL_SOURCE( "NULL_SOURCE" ); + static const damage_type_id damage_bash( "bash" ); static const damage_type_id damage_bullet( "bullet" ); static const damage_type_id damage_heat( "heat" ); @@ -399,7 +401,7 @@ static std::vector shrapnel( const Creature *source, const tripoint &s projectile proj; proj.speed = fragment_velocity; proj.range = range; - proj.proj_effects.insert( "NULL_SOURCE" ); + proj.proj_effects.insert( ammo_effect_NULL_SOURCE ); struct local_caches { cata::mdarray obstacle_cache; diff --git a/src/item.cpp b/src/item.cpp index e0292f486b85f..6e5abdcf67994 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -119,6 +119,13 @@ static const std::string GUN_MODE_VAR_NAME( "item::mode" ); static const std::string CLOTHING_MOD_VAR_PREFIX( "clothing_mod_" ); +static const ammo_effect_str_id ammo_effect_BLACKPOWDER( "BLACKPOWDER" ); +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_MATCHHEAD( "MATCHHEAD" ); +static const ammo_effect_str_id ammo_effect_NEVER_MISFIRES( "NEVER_MISFIRES" ); +static const ammo_effect_str_id ammo_effect_RECYCLED( "RECYCLED" ); + static const ammotype ammo_battery( "battery" ); static const ammotype ammo_bolt( "bolt" ); static const ammotype ammo_money( "money" ); @@ -3051,52 +3058,40 @@ void item::ammo_info( std::vector &info, const iteminfo_query *parts, } std::vector fx; - if( ammo.ammo_effects.count( "RECYCLED" ) && + if( ammo.ammo_effects.count( ammo_effect_RECYCLED ) && parts->test( iteminfo_parts::AMMO_FX_RECYCLED ) ) { fx.emplace_back( _( "This ammo has been hand-loaded." ) ); } - if( ammo.ammo_effects.count( "MATCHHEAD" ) && + if( ammo.ammo_effects.count( ammo_effect_MATCHHEAD ) && parts->test( iteminfo_parts::AMMO_FX_BLACKPOWDER ) ) { fx.emplace_back( _( "This ammo has been loaded with matchhead powder, and will quickly " "clog and rust most guns like blackpowder, but will also rarely cause the gun damage from pressure spikes." ) ); - } else if( ammo.ammo_effects.count( "BLACKPOWDER" ) && + } else if( ammo.ammo_effects.count( ammo_effect_BLACKPOWDER ) && parts->test( iteminfo_parts::AMMO_FX_BLACKPOWDER ) ) { fx.emplace_back( _( "This ammo has been loaded with blackpowder, and will quickly " "clog up most guns, and cause rust if the gun is not cleaned." ) ); } - if( ammo.ammo_effects.count( "NEVER_MISFIRES" ) && + if( ammo.ammo_effects.count( ammo_effect_NEVER_MISFIRES ) && parts->test( iteminfo_parts::AMMO_FX_CANTMISSFIRE ) ) { fx.emplace_back( _( "This ammo never misfires." ) ); } if( parts->test( iteminfo_parts::AMMO_FX_RECOVER ) ) { - for( const std::string &effect : ammo.ammo_effects ) { - if( string_starts_with( effect, "RECOVER_" ) ) { - ret_val try_recover_chance = - try_parse_integer( effect.substr( 8 ), false ); - if( !try_recover_chance.success() ) { - debugmsg( "Error parsing ammo RECOVER_ denominator: %s", - try_recover_chance.str() ); - break; - } - int recover_chance = try_recover_chance.value(); - if( recover_chance <= 5 ) { - fx.emplace_back( _( "Stands a very low chance of remaining intact once fired." ) ); - } else if( recover_chance <= 10 ) { - fx.emplace_back( _( "Stands a low chance of remaining intact once fired." ) ); - } else if( recover_chance <= 20 ) { - fx.emplace_back( _( "Stands a somewhat low chance of remaining intact once fired." ) ); - } else if( recover_chance <= 30 ) { - fx.emplace_back( _( "Stands a decent chance of remaining intact once fired." ) ); - } else { - fx.emplace_back( _( "Stands a good chance of remaining intact once fired." ) ); - } - break; - } + if( ammo.recovery_chance <= 5 ) { + fx.emplace_back( _( "Stands a very low chance of remaining intact once fired." ) ); + } else if( ammo.recovery_chance <= 10 ) { + fx.emplace_back( _( "Stands a low chance of remaining intact once fired." ) ); + } else if( ammo.recovery_chance <= 20 ) { + fx.emplace_back( _( "Stands a somewhat low chance of remaining intact once fired." ) ); + } else if( ammo.recovery_chance <= 30 ) { + fx.emplace_back( _( "Stands a decent chance of remaining intact once fired." ) ); + } else { + fx.emplace_back( _( "Stands a good chance of remaining intact once fired." ) ); } } - if( ( ammo.ammo_effects.count( "INCENDIARY" ) || ammo.ammo_effects.count( "IGNITE" ) ) && + if( ( ammo.ammo_effects.count( ammo_effect_INCENDIARY ) || + ammo.ammo_effects.count( ammo_effect_IGNITE ) ) && parts->test( iteminfo_parts::AMMO_FX_INCENDIARY ) ) { fx.emplace_back( _( "This ammo may start fires." ) ); } @@ -11230,13 +11225,13 @@ itype_id item::common_ammo_default( bool conversion ) const return itype_id::NULL_ID(); } -std::set item::ammo_effects( bool with_ammo ) const +std::set item::ammo_effects( bool with_ammo ) const { if( !is_gun() ) { - return std::set(); + return std::set(); } - std::set res = type->gun->ammo_effects; + std::set res = type->gun->ammo_effects; if( with_ammo && ammo_data() ) { res.insert( ammo_data()->ammo->ammo_effects.begin(), ammo_data()->ammo->ammo_effects.end() ); } diff --git a/src/item.h b/src/item.h index 2e0f19f5d23d2..75f4d20d20875 100644 --- a/src/item.h +++ b/src/item.h @@ -2544,7 +2544,7 @@ class item : public visitable itype_id common_ammo_default( bool conversion = true ) const; /** Get ammo effects for item optionally inclusive of any resulting from the loaded ammo */ - std::set ammo_effects( bool with_ammo = true ) const; + std::set ammo_effects( bool with_ammo = true ) const; /* Get the name to be used when sorting this item by ammo type */ std::string ammo_sort_name() const; diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 4d7b3d360e676..26973b9e1b071 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -60,6 +60,10 @@ struct tripoint; template struct enum_traits; +static const ammo_effect_str_id ammo_effect_COOKOFF( "COOKOFF" ); +static const ammo_effect_str_id ammo_effect_INCENDIARY( "INCENDIARY" ); +static const ammo_effect_str_id ammo_effect_SPECIAL_COOKOFF( "SPECIAL_COOKOFF" ); + static const ammotype ammo_NULL( "NULL" ); static const damage_type_id damage_bash( "bash" ); @@ -404,16 +408,9 @@ void Item_factory::finalize_pre( itype &obj ) if( mats.find( material_hydrocarbons ) == mats.end() && mats.find( material_oil ) == mats.end() ) { const auto &ammo_effects = obj.ammo->ammo_effects; - obj.ammo->cookoff = ammo_effects.count( "INCENDIARY" ) > 0 || - ammo_effects.count( "COOKOFF" ) > 0; - static const std::set special_cookoff_tags = {{ - "SPECIAL_COOKOFF" - } - }; - obj.ammo->special_cookoff = std::any_of( ammo_effects.begin(), ammo_effects.end(), - []( const std::string & s ) { - return special_cookoff_tags.count( s ) > 0; - } ); + obj.ammo->cookoff = ammo_effects.count( ammo_effect_INCENDIARY ) > 0 || + ammo_effects.count( ammo_effect_COOKOFF ) > 0; + obj.ammo->special_cookoff = ammo_effects.count( ammo_effect_SPECIAL_COOKOFF ) > 0; } else { obj.ammo->cookoff = false; obj.ammo->special_cookoff = false; @@ -2696,6 +2693,7 @@ void islot_ammo::load( const JsonObject &jo ) assign( jo, "dispersion", dispersion, strict, 0 ); optional( jo, was_loaded, "dispersion_modifier", disp_mod_by_barrels, {} ); assign( jo, "recoil", recoil, strict, 0 ); + optional( jo, was_loaded, "recovery_chance", recovery_chance, 0 ); assign( jo, "count", def_charges, strict, 1 ); assign( jo, "loudness", loudness, strict, 0 ); assign( jo, "effects", ammo_effects, strict ); diff --git a/src/itype.h b/src/itype.h index 813c934447b6f..5a8dcff50cfee 100644 --- a/src/itype.h +++ b/src/itype.h @@ -739,7 +739,7 @@ struct islot_gun : common_ranged_data { /** * Effects that are applied to the ammo when fired. */ - std::set ammo_effects; + std::set ammo_effects; /** * Location for gun mods. * Key is the location (untranslated!), value is the number of mods @@ -1008,7 +1008,7 @@ struct islot_ammo : common_ranged_data { /** * TODO: document me. */ - std::set ammo_effects; + std::set ammo_effects; /** * Base loudness of ammo (possibly modified by gun/gunmods). If unspecified an * appropriate value is calculated based upon the other properties of the ammo @@ -1018,6 +1018,9 @@ struct islot_ammo : common_ranged_data { /** Recoil (per shot), roughly equivalent to kinetic energy (in Joules) */ int recoil = 0; + /** Percentage of chance to recover the ammo after a shot*/ + int recovery_chance = 0; + /** * Should this ammo explode in fire? * This value is cached by item_factory based on ammo_effects and item material. diff --git a/src/magic.cpp b/src/magic.cpp index 1d571de084c49..9a09eca396613 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -53,6 +53,8 @@ #include "ui.h" #include "units.h" +static const ammo_effect_str_id ammo_effect_MAGIC( "MAGIC" ); + static const json_character_flag json_flag_NO_PSIONICS( "NO_PSIONICS" ); static const json_character_flag json_flag_NO_SPELLCASTING( "NO_SPELLCASTING" ); static const json_character_flag json_flag_SILENT_SPELL( "SILENT_SPELL" ); @@ -1808,7 +1810,7 @@ dealt_projectile_attack spell::get_projectile_attack( const tripoint &target, projectile bolt; bolt.speed = 10000; bolt.impact = get_damage_instance( caster ); - bolt.proj_effects.emplace( "MAGIC" ); + bolt.proj_effects.emplace( ammo_effect_MAGIC ); dealt_projectile_attack atk; atk.end_point = target; diff --git a/src/map.cpp b/src/map.cpp index 550f3608ead3f..b30c41f7f4bf4 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -109,6 +109,12 @@ #include "sdltiles.h" #endif +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_LASER( "LASER" ); +static const ammo_effect_str_id ammo_effect_LIGHTNING( "LIGHTNING" ); +static const ammo_effect_str_id ammo_effect_PLASMA( "PLASMA" ); + static const ammotype ammo_battery( "battery" ); static const damage_type_id damage_bash( "bash" ); @@ -4544,9 +4550,9 @@ void map::shoot( const tripoint &p, projectile &proj, const bool hit_items ) float dam = initial_damage; const auto &ammo_effects = proj.proj_effects; - const bool incendiary = ammo_effects.count( "INCENDIARY" ); - const bool ignite = ammo_effects.count( "IGNITE" ); - const bool laser = ammo_effects.count( "LASER" ); + const bool incendiary = ammo_effects.count( ammo_effect_INCENDIARY ); + const bool ignite = ammo_effects.count( ammo_effect_IGNITE ); + const bool laser = ammo_effects.count( ammo_effect_LASER ); if( const optional_vpart_position vp = veh_at( p ) ) { dam = vp->vehicle().damage( *this, vp->part_index(), dam, main_damage_type, hit_items ); @@ -4616,7 +4622,7 @@ void map::shoot( const tripoint &p, projectile &proj, const bool hit_items ) dam = std::max( 0.0f, dam ); for( const ammo_effect &ae : ammo_effects::get_all() ) { - if( ammo_effects.count( ae.id.str() ) > 0 ) { + if( ammo_effects.count( ae.id ) > 0 ) { if( x_in_y( ae.trail_chance, 100 ) ) { add_field( p, ae.trail_field_type, rng( ae.trail_intensity_min, ae.trail_intensity_max ) ); } @@ -4656,11 +4662,11 @@ void map::shoot( const tripoint &p, projectile &proj, const bool hit_items ) // Make sure the message is sensible for the ammo effects. Lasers aren't projectiles. std::string damage_message; - if( ammo_effects.count( "LASER" ) ) { + if( ammo_effects.count( ammo_effect_LASER ) ) { damage_message = _( "laser beam" ); - } else if( ammo_effects.count( "LIGHTNING" ) ) { + } else if( ammo_effects.count( ammo_effect_LIGHTNING ) ) { damage_message = _( "bolt of electricity" ); - } else if( ammo_effects.count( "PLASMA" ) ) { + } else if( ammo_effects.count( ammo_effect_PLASMA ) ) { damage_message = _( "bolt of plasma" ); } else { damage_message = _( "flying projectile" ); diff --git a/src/monattack.cpp b/src/monattack.cpp index 07ef4aeb17344..203e510a81717 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -93,6 +93,14 @@ static const activity_id ACT_RELOAD( "ACT_RELOAD" ); +static const ammo_effect_str_id ammo_effect_APPLY_SAP( "APPLY_SAP" ); +static const ammo_effect_str_id ammo_effect_BLINDS_EYES( "BLINDS_EYES" ); +static const ammo_effect_str_id ammo_effect_DRAW_AS_LINE( "DRAW_AS_LINE" ); +static const ammo_effect_str_id ammo_effect_JET( "JET" ); +static const ammo_effect_str_id ammo_effect_NO_DAMAGE_SCALING( "NO_DAMAGE_SCALING" ); +static const ammo_effect_str_id ammo_effect_NO_ITEM_DAMAGE( "NO_ITEM_DAMAGE" ); +static const ammo_effect_str_id ammo_effect_NO_OVERSHOOT( "NO_OVERSHOOT" ); + static const bionic_id bio_uncanny_dodge( "bio_uncanny_dodge" ); static const damage_type_id damage_acid( "acid" ); @@ -286,7 +294,7 @@ static bool sting_shoot( monster *z, Creature *target, damage_instance &dam, flo proj.speed = 10; proj.range = range; proj.impact.add( dam ); - proj.proj_effects.insert( "NO_OVERSHOOT" ); + proj.proj_effects.insert( ammo_effect_NO_OVERSHOOT ); dealt_projectile_attack atk = projectile_attack( proj, z->pos(), target->pos(), dispersion_sources{ 500 }, z ); @@ -792,7 +800,7 @@ bool mattack::acid( monster *z ) // Mostly just for momentum proj.impact.add_damage( damage_acid, 5 ); proj.range = 10; - proj.proj_effects.insert( "NO_OVERSHOOT" ); + proj.proj_effects.insert( ammo_effect_NO_OVERSHOOT ); dealt_projectile_attack dealt = projectile_attack( proj, z->pos(), target->pos(), dispersion_sources{ 5400 }, z ); const tripoint &hitp = dealt.end_point; @@ -904,8 +912,8 @@ bool mattack::acid_accurate( monster *z ) projectile proj; proj.speed = 10; proj.range = 10; - proj.proj_effects.insert( "BLINDS_EYES" ); - proj.proj_effects.insert( "NO_DAMAGE_SCALING" ); + proj.proj_effects.insert( ammo_effect_BLINDS_EYES ); + proj.proj_effects.insert( ammo_effect_NO_DAMAGE_SCALING ); proj.impact.add_damage( damage_acid, rng( 3, 5 ) ); // Make it arbitrarily less accurate at close ranges projectile_attack( proj, z->pos(), target->pos(), dispersion_sources{ 8000.0 * range }, z ); @@ -1046,7 +1054,7 @@ bool mattack::pull_metal_weapon( monster *z ) proj.impact = damage_instance( damage_bash, pulled_weapon.weight() / 250_gram ); // make the projectile stop one tile short to prevent hitting the monster proj.range = rl_dist( foe->pos(), z->pos() ) - 1; - proj.proj_effects = { { "NO_ITEM_DAMAGE", "DRAW_AS_LINE", "NO_DAMAGE_SCALING", "JET" } }; + proj.proj_effects = { { ammo_effect_NO_ITEM_DAMAGE, ammo_effect_DRAW_AS_LINE, ammo_effect_NO_DAMAGE_SCALING, ammo_effect_JET } }; dealt_projectile_attack dealt = projectile_attack( proj, foe->pos(), z->pos(), dispersion_sources{ 0 }, z ); @@ -1826,7 +1834,7 @@ bool mattack::spit_sap( monster *z ) projectile proj; proj.speed = 10; proj.range = 12; - proj.proj_effects.insert( "APPLY_SAP" ); + proj.proj_effects.insert( ammo_effect_APPLY_SAP ); proj.impact.add_damage( damage_acid, rng( 5, 10 ) ); projectile_attack( proj, z->pos(), target->pos(), dispersion_sources{ 150 }, z ); diff --git a/src/mondefense.cpp b/src/mondefense.cpp index c3e2d35967a5f..922c5556a08fe 100644 --- a/src/mondefense.cpp +++ b/src/mondefense.cpp @@ -32,6 +32,9 @@ #include "type_id.h" #include "viewer.h" +static const ammo_effect_str_id ammo_effect_DRAW_AS_LINE( "DRAW_AS_LINE" ); +static const ammo_effect_str_id ammo_effect_NO_DAMAGE_SCALING( "NO_DAMAGE_SCALING" ); + static const damage_type_id damage_acid( "acid" ); static const damage_type_id damage_electric( "electric" ); @@ -128,8 +131,8 @@ void mdefense::acidsplash( monster &m, Creature *const source, projectile prj; prj.speed = 10; prj.range = 4; - prj.proj_effects.insert( "DRAW_AS_LINE" ); - prj.proj_effects.insert( "NO_DAMAGE_SCALING" ); + prj.proj_effects.insert( ammo_effect_DRAW_AS_LINE ); + prj.proj_effects.insert( ammo_effect_NO_DAMAGE_SCALING ); prj.impact.add_damage( damage_acid, rng( 1, 3 ) ); for( size_t i = 0; i < num_drops; i++ ) { const tripoint &target = random_entry( pts ); diff --git a/src/monster.cpp b/src/monster.cpp index 4e89e6ffc0c31..2112989db4da5 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -71,6 +71,8 @@ #include "weakpoint.h" #include "weather.h" +static const ammo_effect_str_id ammo_effect_WHIP( "WHIP" ); + static const anatomy_id anatomy_default_anatomy( "default_anatomy" ); static const damage_type_id damage_bash( "bash" ); @@ -2105,7 +2107,7 @@ void monster::deal_projectile_attack( Creature *source, dealt_projectile_attack const auto &effects = proj.proj_effects; // Whip has a chance to scare wildlife even if it misses - if( effects.count( "WHIP" ) && type->in_category( "WILDLIFE" ) && one_in( 3 ) ) { + if( effects.count( ammo_effect_WHIP ) && type->in_category( "WILDLIFE" ) && one_in( 3 ) ) { add_effect( effect_run, rng( 3_turns, 5_turns ) ); } diff --git a/src/projectile.cpp b/src/projectile.cpp index 44c3af538ab4e..c65a6dc7aefa8 100644 --- a/src/projectile.cpp +++ b/src/projectile.cpp @@ -142,7 +142,7 @@ static void foamcrete_build( const tripoint &p ) } void apply_ammo_effects( const Creature *source, const tripoint &p, - const std::set &effects ) + const std::set &effects ) { map &here = get_map(); Character &player_character = get_player_character(); @@ -151,7 +151,7 @@ void apply_ammo_effects( const Creature *source, const tripoint &p, if( !one_in( ae.trigger_chance ) ) { continue; } - if( effects.count( ae.id.str() ) > 0 ) { + if( effects.count( ae.id ) > 0 ) { for( const tripoint &pt : here.points_in_radius( p, ae.aoe_radius, ae.aoe_radius_z ) ) { if( x_in_y( ae.aoe_chance, 100 ) ) { const bool check_sees = !ae.aoe_check_sees || here.sees( p, pt, ae.aoe_check_sees_radius ); @@ -196,12 +196,11 @@ void apply_ammo_effects( const Creature *source, const tripoint &p, } } - -int max_aoe_size( const std::set &tags ) +int max_aoe_size( const std::set &tags ) { int aoe_size = 0; for( const ammo_effect &aed : ammo_effects::get_all() ) { - if( tags.count( aed.id.str() ) > 0 ) { + if( tags.count( aed.id ) > 0 ) { aoe_size = std::max( aoe_size, aed.aoe_size ) ; } } diff --git a/src/projectile.h b/src/projectile.h index fd03d07ea80d3..454b850612bc0 100644 --- a/src/projectile.h +++ b/src/projectile.h @@ -30,7 +30,7 @@ struct projectile { damage_instance shot_impact; float critical_multiplier = 0.0f; - std::set proj_effects; + std::set proj_effects; /** * Returns an item that should be dropped or an item for which is_null() is true @@ -75,8 +75,8 @@ struct dealt_projectile_attack { }; void apply_ammo_effects( const Creature *source, const tripoint &p, - const std::set &effects ); -int max_aoe_size( const std::set &tags ); + const std::set &effects ); +int max_aoe_size( const std::set &tags ); void multi_projectile_hit_message( Creature *critter, int hit_count, int damage_taken, const std::string &projectile_name ); diff --git a/src/ranged.cpp b/src/ranged.cpp index b9b73698a2bea..4e61fc8532e8e 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -75,6 +75,30 @@ #include "vpart_position.h" #include "weakpoint.h" +static const ammo_effect_str_id ammo_effect_ACT_ON_RANGED_HIT( "ACT_ON_RANGED_HIT" ); +static const ammo_effect_str_id ammo_effect_BLACKPOWDER( "BLACKPOWDER" ); +static const ammo_effect_str_id ammo_effect_BOUNCE( "BOUNCE" ); +static const ammo_effect_str_id ammo_effect_BURST( "BURST" ); +static const ammo_effect_str_id ammo_effect_CUSTOM_EXPLOSION( "CUSTOM_EXPLOSION" ); +static const ammo_effect_str_id ammo_effect_EMP( "EMP" ); +static const ammo_effect_str_id ammo_effect_EXPLOSIVE( "EXPLOSIVE" ); +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_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" ); +static const ammo_effect_str_id ammo_effect_NO_EMBED( "NO_EMBED" ); +static const ammo_effect_str_id ammo_effect_NO_ITEM_DAMAGE( "NO_ITEM_DAMAGE" ); +static const ammo_effect_str_id ammo_effect_PLASMA( "PLASMA" ); +static const ammo_effect_str_id ammo_effect_RECYCLED( "RECYCLED" ); +static const ammo_effect_str_id ammo_effect_SHATTER_SELF( "SHATTER_SELF" ); +static const ammo_effect_str_id ammo_effect_SHOT( "SHOT" ); +static const ammo_effect_str_id ammo_effect_TANGLE( "TANGLE" ); +static const ammo_effect_str_id ammo_effect_WHIP( "WHIP" ); +static const ammo_effect_str_id ammo_effect_WIDE( "WIDE" ); + static const ammotype ammo_120mm( "120mm" ); static const ammotype ammo_12mm( "12mm" ); static const ammotype ammo_40x46mm( "40x46mm" ); @@ -675,7 +699,7 @@ bool Character::handle_gun_damage( item &it ) // Here we check for a chance for the weapon to suffer a misfire due to // using player-made 'RECYCLED' bullets. Note that not all forms of // player-made ammunition have this effect. - } else if( curammo_effects.count( "RECYCLED" ) && one_in( 256 ) ) { + } else if( curammo_effects.count( ammo_effect_RECYCLED ) && one_in( 256 ) ) { add_msg_player_or_npc( _( "Your %s misfires with a muffled click!" ), _( "'s %s misfires with a muffled click!" ), it.tname() ); @@ -685,8 +709,8 @@ bool Character::handle_gun_damage( item &it ) // Default chance is 1/10000 unless set via json, damage is proportional to caliber(see below). // Can be toned down with 'consume_divisor.' - } else if( it.has_flag( flag_CONSUMABLE ) && !curammo_effects.count( "LASER" ) && - !curammo_effects.count( "PLASMA" ) && !curammo_effects.count( "EMP" ) ) { + } else if( it.has_flag( flag_CONSUMABLE ) && !curammo_effects.count( ammo_effect_LASER ) && + !curammo_effects.count( ammo_effect_PLASMA ) && !curammo_effects.count( ammo_effect_EMP ) ) { int uncork = ( ( 10 * it.ammo_data()->ammo->loudness ) + ( it.ammo_data()->ammo->recoil / 2 ) ) / 100; uncork = std::pow( uncork, 3 ) * 6.5; @@ -736,16 +760,17 @@ bool Character::handle_gun_damage( item &it ) // Don't return false in this case; this shot happens, follow-up ones won't. } // These are the dirtying/fouling mechanics - if( !curammo_effects.count( "NON_FOULING" ) && !it.has_flag( flag_NON_FOULING ) ) { + if( !curammo_effects.count( ammo_effect_NON_FOULING ) && !it.has_flag( flag_NON_FOULING ) ) { if( dirt < static_cast( dirt_max_dbl ) ) { - dirtadder = curammo_effects.count( "BLACKPOWDER" ) * ( 200 - firing.blackpowder_tolerance * + dirtadder = curammo_effects.count( ammo_effect_BLACKPOWDER ) * ( 200 - + firing.blackpowder_tolerance * 2 ); // dirtadder is the dirt-increasing number for shots fired with gunpowder-based ammo. Usually dirt level increases by 1, unless it's blackpowder, in which case it increases by a higher number, but there is a reduction for blackpowder resistance of a weapon. if( dirtadder < 0 ) { dirtadder = 0; } // in addition to increasing dirt level faster, regular gunpowder fouling is also capped at 7,150, not 10,000. So firing with regular gunpowder can never make the gun quite as bad as firing it with black gunpowder. At 7,150 the chance to jam is significantly lower (though still significant) than it is at 10,000, the absolute cap. - if( curammo_effects.count( "BLACKPOWDER" ) || + if( curammo_effects.count( ammo_effect_BLACKPOWDER ) || dirt < 7150 ) { it.set_var( "dirt", std::min( static_cast( dirt_max_dbl ), dirt + dirtadder + 1 ) ); } @@ -755,7 +780,7 @@ bool Character::handle_gun_damage( item &it ) if( dirt > 0 && !it.has_fault_flag( "NO_DIRTYING" ) ) { it.faults.insert( fault_gun_dirt ); } - if( dirt > 0 && curammo_effects.count( "BLACKPOWDER" ) ) { + if( dirt > 0 && curammo_effects.count( ammo_effect_BLACKPOWDER ) ) { it.faults.erase( fault_gun_dirt ); it.faults.insert( fault_gun_blackpowder ); } @@ -765,7 +790,7 @@ bool Character::handle_gun_damage( item &it ) // chance to damage gun due to high levels of dirt. Very unlikely, especially at lower levels and impossible below 5,000. Lower than the chance of a jam at the same levels. 555555... is an arbitrary number that I came up with after playing with the formula in excel. It makes sense at low, medium, and high levels of dirt. // if you have a bullet loaded with match head powder this can happen randomly at any time. The chances are about the same as a recycled ammo jamming the gun if( ( dirt_dbl > 5000 && x_in_y( dirt_dbl * dirt_dbl * dirt_dbl, 5555555555555 ) ) || - ( curammo_effects.count( "MATCHHEAD" ) && one_in( 256 ) ) ) { + ( curammo_effects.count( ammo_effect_MATCHHEAD ) && one_in( 256 ) ) ) { add_msg_player_or_npc( m_bad, _( "Your %s is damaged by the high pressure!" ), _( "'s %s is damaged by the high pressure!" ), it.tname() ); @@ -877,7 +902,7 @@ int Character::fire_gun( const tripoint &target, int shots, item &gun, item_loca debugmsg( "%s tried to fire non-gun (%s).", get_name(), gun.tname() ); return 0; } - if( gun.has_flag( flag_CHOKE ) && !gun.ammo_effects().count( "SHOT" ) ) { + if( gun.has_flag( flag_CHOKE ) && !gun.ammo_effects().count( ammo_effect_SHOT ) ) { add_msg_if_player( _( "A shotgun equipped with choke cannot fire slugs." ) ); return 0; } @@ -1350,7 +1375,7 @@ dealt_projectile_attack Character::throw_item( const tripoint &target, const ite add_msg_debug( debugmode::DF_RANGED, "Adjusted throw skill %g", skill_level ); projectile proj = thrown_item_projectile( thrown ); damage_instance &impact = proj.impact; - std::set &proj_effects = proj.proj_effects; + std::set &proj_effects = proj.proj_effects; const bool do_railgun = has_active_bionic( bio_railgun ) && thrown.made_of_any( ferric ) && !throw_assist; @@ -1359,7 +1384,7 @@ dealt_projectile_attack Character::throw_item( const tripoint &target, const ite static_cast( thrown_item_adjusted_damage( thrown ) ) ) ); if( thrown.has_flag( flag_ACT_ON_RANGED_HIT ) ) { - proj_effects.insert( "ACT_ON_RANGED_HIT" ); + proj_effects.insert( ammo_effect_ACT_ON_RANGED_HIT ); thrown.active = true; } @@ -1379,37 +1404,37 @@ dealt_projectile_attack Character::throw_item( const tripoint &target, const ite // Add some flags to the projectile if( weight > 500_gram ) { - proj_effects.insert( "HEAVY_HIT" ); + proj_effects.insert( ammo_effect_HEAVY_HIT ); } - proj_effects.insert( "NO_ITEM_DAMAGE" ); + proj_effects.insert( ammo_effect_NO_ITEM_DAMAGE ); if( thrown.active ) { // Can't have Molotovs embed into monsters // Monsters don't have inventory processing - proj_effects.insert( "NO_EMBED" ); + proj_effects.insert( ammo_effect_NO_EMBED ); } if( do_railgun ) { - proj_effects.insert( "LIGHTNING" ); + proj_effects.insert( ammo_effect_LIGHTNING ); const units::energy trigger_cost = bio_railgun->power_trigger; mod_power_level( -trigger_cost ); } if( volume > 500_ml ) { - proj_effects.insert( "WIDE" ); + proj_effects.insert( ammo_effect_WIDE ); } // Deal extra cut damage if the item breaks if( shatter ) { impact.add_damage( damage_cut, units::to_milliliter( volume ) / 500.0f ); - proj_effects.insert( "SHATTER_SELF" ); + proj_effects.insert( ammo_effect_SHATTER_SELF ); } // TODO: Add wet effect if other things care about that if( burst ) { - proj_effects.insert( "BURST" ); + proj_effects.insert( ammo_effect_BURST ); } // Some minor (skill/2) armor piercing for skillful throws @@ -1419,7 +1444,7 @@ dealt_projectile_attack Character::throw_item( const tripoint &target, const ite } // handling for tangling thrown items if( thrown.has_flag( flag_TANGLE ) ) { - proj_effects.insert( "TANGLE" ); + proj_effects.insert( ammo_effect_TANGLE ); } Creature *critter = get_creature_tracker().creature_at( target, true ); @@ -2085,36 +2110,27 @@ static projectile make_gun_projectile( const item &gun ) auto &fx = proj.proj_effects; if( ( gun.ammo_data() && gun.ammo_data()->phase == phase_id::LIQUID ) || - fx.count( "SHOT" ) || fx.count( "BOUNCE" ) ) { - fx.insert( "WIDE" ); + fx.count( ammo_effect_SHOT ) || fx.count( ammo_effect_BOUNCE ) ) { + fx.insert( ammo_effect_WIDE ); } if( gun.ammo_data() ) { + const auto &ammo = gun.ammo_data()->ammo; + // Some projectiles have a chance of being recoverable - bool recover = std::any_of( fx.begin(), fx.end(), []( const std::string_view e ) { - if( !string_starts_with( e, "RECOVER_" ) ) { - return false; - } - ret_val n = try_parse_integer( e.substr( 8 ), false ); - if( !n.success() ) { - debugmsg( "Error parsing ammo RECOVER_ denominator: %s", n.str() ); - return false; - } - return !one_in( n.value() ); - } ); + bool recover = x_in_y( ammo->recovery_chance, 100 ); - if( recover && !fx.count( "IGNITE" ) && !fx.count( "EXPLOSIVE" ) ) { + if( recover && !fx.count( ammo_effect_IGNITE ) && !fx.count( ammo_effect_EXPLOSIVE ) ) { item drop( gun.ammo_current(), calendar::turn, 1 ); - drop.active = fx.count( "ACT_ON_RANGED_HIT" ); + drop.active = fx.count( ammo_effect_ACT_ON_RANGED_HIT ); drop.set_favorite( gun.get_contents().first_ammo().is_favorite ); proj.set_drop( drop ); } - const auto &ammo = gun.ammo_data()->ammo; proj.critical_multiplier = ammo->critical_multiplier; proj.count = ammo->count; proj.multi_projectile_effects = ammo->multi_projectile_effects; - if( fx.count( "MULTI_EFFECTS" ) ) { + if( fx.count( ammo_effect_MULTI_EFFECTS ) ) { proj.multi_projectile_effects = true; } proj.shot_spread = ammo->shot_spread * gun.gun_shot_spread_multiplier(); @@ -2126,7 +2142,7 @@ static projectile make_gun_projectile( const item &gun ) proj.set_drop( drop ); } - if( fx.count( "CUSTOM_EXPLOSION" ) > 0 ) { + if( fx.count( ammo_effect_CUSTOM_EXPLOSION ) > 0 ) { proj.set_custom_explosion( gun.ammo_data()->explosion ); } } @@ -2177,7 +2193,7 @@ static void cycle_action( item &weap, const itype_id &ammo, const tripoint &pos if( !!ammo->ammo->casing ) { item casing = item( *ammo->ammo->casing ); // blackpowder can gum up casings too - if( ( *ammo->ammo ).ammo_effects.count( "BLACKPOWDER" ) ) { + if( ( *ammo->ammo ).ammo_effects.count( ammo_effect_BLACKPOWDER ) ) { casing.set_flag( json_flag_FILTHY ); } if( weap.has_flag( flag_RELOAD_EJECT ) ) { @@ -2268,7 +2284,7 @@ item::sound_data item::gun_noise( const bool burst ) const auto fx = ammo_effects(); - if( fx.count( "LASER" ) || fx.count( "PLASMA" ) ) { + if( fx.count( ammo_effect_LASER ) || fx.count( ammo_effect_PLASMA ) ) { if( noise < 20 ) { return { noise, _( "Fzzt!" ) }; } else if( noise < 40 ) { @@ -2279,7 +2295,7 @@ item::sound_data item::gun_noise( const bool burst ) const return { noise, _( "Kra-kow!" ) }; } - } else if( fx.count( "LIGHTNING" ) ) { + } else if( fx.count( ammo_effect_LIGHTNING ) ) { if( noise < 20 ) { return { noise, _( "Bzzt!" ) }; } else if( noise < 40 ) { @@ -2290,7 +2306,7 @@ item::sound_data item::gun_noise( const bool burst ) const return { noise, _( "Kra-koom!" ) }; } - } else if( fx.count( "WHIP" ) ) { + } else if( fx.count( ammo_effect_WHIP ) ) { return { noise, _( "Crack!" ) }; } else if( noise > 0 ) { diff --git a/src/turret.cpp b/src/turret.cpp index eca8ffdeb5b3e..dc626a0aa83e4 100644 --- a/src/turret.cpp +++ b/src/turret.cpp @@ -175,10 +175,10 @@ bool turret_data::ammo_select( const itype_id &ammo ) return true; } -std::set turret_data::ammo_effects() const +std::set turret_data::ammo_effects() const { if( !veh || !part ) { - return std::set(); + return std::set(); } auto res = part->base.ammo_effects(); if( uses_vehicle_tanks_or_batteries() && ammo_data() ) { diff --git a/src/vehicle.h b/src/vehicle.h index af6a30f03bcf7..4f17cd4c782af 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -624,7 +624,7 @@ class turret_data bool ammo_select( const itype_id &ammo ); /** Effects inclusive of any from ammo loaded from tanks */ - std::set ammo_effects() const; + std::set ammo_effects() const; /** Maximum range considering current ammo (if any) */ int range() const; diff --git a/tests/coverage_test.cpp b/tests/coverage_test.cpp index 16f921b4b47b6..5b719427f5dea 100644 --- a/tests/coverage_test.cpp +++ b/tests/coverage_test.cpp @@ -128,7 +128,7 @@ static float get_avg_bullet_dmg( const std::string &clothing_id ) proj.speed = 1000; proj.impact = damage_instance( damage_bullet, 20 ); proj.range = 30; - proj.proj_effects = std::set(); + proj.proj_effects = std::set(); proj.critical_multiplier = 1; int dam_acc = 0; diff --git a/tests/explosion_balance_test.cpp b/tests/explosion_balance_test.cpp index f9b78c5f62112..10d66f1e7bdb0 100644 --- a/tests/explosion_balance_test.cpp +++ b/tests/explosion_balance_test.cpp @@ -28,6 +28,8 @@ #include "vpart_position.h" #include "vpart_range.h" +static const ammo_effect_str_id ammo_effect_NULL_SOURCE( "NULL_SOURCE" ); + static const damage_type_id damage_bullet( "bullet" ); enum class outcome_type { @@ -40,7 +42,7 @@ static float get_damage_vs_target( const std::string &target_id ) proj.speed = 1000; // Arbitrary damage, we only care about scaling. proj.impact = damage_instance( damage_bullet, 10 ); - proj.proj_effects.insert( "NULL_SOURCE" ); + proj.proj_effects.insert( ammo_effect_NULL_SOURCE ); dealt_projectile_attack frag; frag.proj = proj; diff --git a/tests/materials_test.cpp b/tests/materials_test.cpp index 3fc1b767f9733..ab9bbb2b558c7 100644 --- a/tests/materials_test.cpp +++ b/tests/materials_test.cpp @@ -7,6 +7,8 @@ #include "npc.h" #include "projectile.h" +static const ammo_effect_str_id ammo_effect_SHATTER_SELF( "SHATTER_SELF" ); + static const damage_type_id damage_acid( "acid" ); static const damage_type_id damage_bash( "bash" ); static const damage_type_id damage_bullet( "bullet" ); @@ -86,7 +88,7 @@ TEST_CASE( "Glass_portion_breakability", "[material] [slow]" ) int shatter_count = 0; for( int i = 0; i < num_iters; i++ ) { dealt_projectile_attack atk = dude.throw_item( target_pos, *dude.get_wielded_item() ); - if( atk.proj.proj_effects.find( "SHATTER_SELF" ) != atk.proj.proj_effects.end() ) { + if( atk.proj.proj_effects.find( ammo_effect_SHATTER_SELF ) != atk.proj.proj_effects.end() ) { shatter_count++; } } @@ -99,7 +101,7 @@ TEST_CASE( "Glass_portion_breakability", "[material] [slow]" ) int shatter_count = 0; for( int i = 0; i < num_iters; i++ ) { dealt_projectile_attack atk = dude.throw_item( target_pos, *dude.get_wielded_item() ); - if( atk.proj.proj_effects.find( "SHATTER_SELF" ) != atk.proj.proj_effects.end() ) { + if( atk.proj.proj_effects.find( ammo_effect_SHATTER_SELF ) != atk.proj.proj_effects.end() ) { shatter_count++; } } diff --git a/tests/vehicle_turrets_test.cpp b/tests/vehicle_turrets_test.cpp index 6f0b9abff8664..2a121bd90d239 100644 --- a/tests/vehicle_turrets_test.cpp +++ b/tests/vehicle_turrets_test.cpp @@ -22,6 +22,8 @@ #include "veh_type.h" #include "vehicle.h" +static const ammo_effect_str_id ammo_effect_RECYCLED( "RECYCLED" ); + static std::vector all_turret_types() { std::vector res; @@ -83,7 +85,8 @@ TEST_CASE( "vehicle_turret", "[vehicle][gun][magazine]" ) } else { CHECK( vp.ammo_set( ammo_itype ) > 0 ); } - const bool default_ammo_is_RECYCLED = vp.get_base().ammo_effects().count( "RECYCLED" ) > 0; + const bool default_ammo_is_RECYCLED = vp.get_base().ammo_effects().count( + ammo_effect_RECYCLED ) > 0; CAPTURE( default_ammo_is_RECYCLED ); INFO( "RECYCLED ammo can sometimes misfire and very rarely fail this test" );