From 7c8fdbd10235ad66f3a0ca47e99ce7d4bffdf99f Mon Sep 17 00:00:00 2001 From: MorvarchPrincess <63691647+MorvarchPrincess@users.noreply.github.com> Date: Thu, 12 Oct 2023 19:50:47 -0500 Subject: [PATCH] Better Gas masks - Model concentration better, and make gas mask cartiges last longer (#68570) * Some initial work on better modelling of gas mask usage, doesn't work as I can't get the intensity level * add absorbtion calculation * Modify calculation to make gas mask last 20 minutes from full charge in thick toxic gas * Add low gas charges warning * Change gas_absorption _factor to be a timescale * Fix loading of concentration * Add documentation * linting C++ code * Apply suggestions from code review Add linting suggestions Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestions from code review * Better documentation clarity * Update src/iuse.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Zhilkin Serg --- data/json/field_type.json | 111 ++++++++++++++++++++++++-------------- doc/JSON_INFO.md | 3 +- src/field_type.cpp | 4 +- src/field_type.h | 3 +- src/iuse.cpp | 26 ++++++--- 5 files changed, 99 insertions(+), 48 deletions(-) diff --git a/data/json/field_type.json b/data/json/field_type.json index 0e44cef107d93..e1b7ec983c7a5 100644 --- a/data/json/field_type.json +++ b/data/json/field_type.json @@ -336,6 +336,7 @@ "color": "white", "dangerous": true, "translucency": 0.1, + "concentration": 1, "convection_temperature_mod": -10, "effects": [ { @@ -355,6 +356,7 @@ "color": "white", "convection_temperature_mod": -20, "translucency": 0.4, + "concentration": 2, "effects": [ { "effect_id": "poison", @@ -373,6 +375,7 @@ "color": "white", "convection_temperature_mod": -30, "translucency": 0.7, + "concentration": 4, "effects": [ { "effect_id": "poison", @@ -407,7 +410,7 @@ "display_items": false, "display_field": true, "looks_like": "fd_fog", - "gas_absorption_factor": 15, + "gas_absorption_factor": "20h", "has_fume": true, "percent_spread": 90, "dirty_transparency_cache": true, @@ -452,6 +455,7 @@ "sym": "8", "dangerous": true, "translucency": 1, + "concentration": 1, "effects": [ { "effect_id": "smoke_eyes", @@ -478,6 +482,7 @@ "color": "light_gray", "transparent": false, "translucency": 10, + "concentration": 2, "effects": [ { "effect_id": "smoke_eyes", @@ -504,6 +509,7 @@ "name": "thick smoke", "color": "dark_gray", "translucency": 0, + "concentration": 4, "effects": [ { "effect_id": "smoke_eyes", @@ -528,7 +534,7 @@ } ], "decay_amount_factor": 5, - "gas_absorption_factor": 12, + "gas_absorption_factor": "100m", "dirty_transparency_cache": true, "percent_spread": 10, "outdoor_age_speedup": "0 turns", @@ -550,6 +556,7 @@ "sym": "8", "dangerous": true, "translucency": 1, + "concentration": 1, "effects": [ { "effect_id": "poison", @@ -568,6 +575,7 @@ "color": "light_green", "transparent": false, "translucency": 10, + "concentration": 2, "effects": [ { "effect_id": "poison", @@ -585,6 +593,7 @@ "name": "thick toxic gas", "color": "green", "translucency": 0, + "concentration": 4, "effects": [ { "effect_id": "poison", @@ -612,7 +621,7 @@ } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 30, "outdoor_age_speedup": "3 minutes", "dirty_transparency_cache": true, @@ -635,6 +644,7 @@ "sym": "8", "dangerous": true, "translucency": 1, + "concentration": 1, "effects": [ { "effect_id": "tpollen", @@ -653,6 +663,7 @@ "color": "light_green", "transparent": false, "translucency": 10, + "concentration": 2, "effects": [ { "effect_id": "tpollen", @@ -668,7 +679,7 @@ } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 30, "outdoor_age_speedup": "3 minutes", "dirty_transparency_cache": true, @@ -686,18 +697,19 @@ "type": "field_type", "legacy_enum_id": 14, "intensity_levels": [ - { "name": "hazy cloud", "sym": "8", "dangerous": true, "translucency": 1 }, + { "name": "hazy cloud", "sym": "8", "dangerous": true, "translucency": 1, "concentration": 1 }, { "name": "tear gas", "color": "light_green", "transparent": false, "translucency": 10, + "concentration": 2, "scent_neutralization": 1 }, - { "name": "thick tear gas", "color": "green", "translucency": 0, "scent_neutralization": 5 } + { "name": "thick tear gas", "color": "green", "translucency": 0, "concentration": 4, "scent_neutralization": 5 } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 10, "outdoor_age_speedup": "0 turns", "dirty_transparency_cache": true, @@ -715,12 +727,20 @@ "type": "field_type", "legacy_enum_id": 15, "intensity_levels": [ - { "name": "hazy cloud", "sym": "8", "dangerous": true, "translucency": 10, "extra_radiation_max": 1 }, - { "name": "radioactive gas", "color": "light_green", "extra_radiation_max": 2 }, + { + "name": "hazy cloud", + "sym": "8", + "dangerous": true, + "translucency": 10, + "concentration": 1, + "extra_radiation_max": 1 + }, + { "name": "radioactive gas", "color": "light_green", "extra_radiation_max": 2, "concentration": 2 }, { "name": "thick radioactive gas", "color": "green", "transparent": false, + "concentration": 4, "extra_radiation_max": 3, "radiation_hurt_damage_min": 1, "radiation_hurt_damage_max": 3, @@ -743,10 +763,10 @@ "id": "fd_gas_vent", "type": "field_type", "legacy_enum_id": 16, - "intensity_levels": [ { "name": "gas vent" }, { "//": "repeat last entry" }, { "//": "repeat last entry" } ], + "intensity_levels": [ { "name": "gas vent", "concentration": 4 }, { "//": "repeat last entry" }, { "//": "repeat last entry" } ], "description_affix": "under", "wandering_field": "fd_toxic_gas", - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "dirty_transparency_cache": true, "immunity_data": { "body_part_env_resistance": [ [ "mouth", 15 ] ] }, "phase": "gas", @@ -758,12 +778,13 @@ "id": "fd_tindalos_rift", "type": "field_type", "intensity_levels": [ - { "name": "angular ripple", "color": "light_gray", "sym": "*", "light_emitted": 160 }, - { "name": "angular rift", "color": "dark_gray" }, + { "name": "angular ripple", "color": "light_gray", "sym": "*", "light_emitted": 160, "concentration": 1 }, + { "name": "angular rift", "color": "dark_gray", "concentration": 1 }, { "name": "fractal fissure", "color": "magenta", "transparent": false, + "concentration": 1, "monster_spawn_chance": 15, "monster_spawn_count": 1, "monster_spawn_radius": 1, @@ -775,7 +796,7 @@ "half_life": "10 seconds", "accelerated_decay": true, "wandering_field": "fd_tindalos_gas", - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "dirty_transparency_cache": true, "phase": "gas", "display_items": false, @@ -1240,6 +1261,7 @@ "sym": ".", "dangerous": true, "translucency": 5, + "concentration": 1, "effects": [ { "effect_id": "relax_gas", @@ -1255,6 +1277,7 @@ { "name": "sedative gas", "color": "pink", + "concentration": 2, "effects": [ { "effect_id": "relax_gas", @@ -1269,6 +1292,7 @@ { "name": "relaxation gas", "color": "cyan", + "concentration": 4, "effects": [ { "effect_id": "relax_gas", @@ -1282,7 +1306,7 @@ } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 15, "outdoor_age_speedup": "5 minutes", "dirty_transparency_cache": true, @@ -1299,12 +1323,12 @@ "id": "fd_swamp_gas", "type": "field_type", "intensity_levels": [ - { "name": "swamp gas", "sym": ".", "dangerous": true, "translucency": 5 }, + { "name": "swamp gas", "sym": ".", "dangerous": true, "translucency": 5, "concentration": 1 }, { "//": "repeat last entry" }, { "//": "repeat last entry" } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 25, "outdoor_age_speedup": "1 minutes", "dirty_transparency_cache": true, @@ -1321,12 +1345,12 @@ "id": "fd_fog", "type": "field_type", "intensity_levels": [ - { "name": "mist", "sym": "~", "dangerous": false, "transparent": false, "translucency": 5 }, + { "name": "mist", "sym": "~", "dangerous": false, "transparent": false, "translucency": 5, "concentration": 0.5 }, { "name": "fog", "translucency": 3 }, { "name": "dense fog", "translucency": 1 } ], "decay_amount_factor": 15, - "gas_absorption_factor": 5, + "gas_absorption_factor": "80m", "percent_spread": 30, "outdoor_age_speedup": "1 minutes", "dirty_transparency_cache": true, @@ -1343,12 +1367,12 @@ "type": "field_type", "legacy_enum_id": 40, "intensity_levels": [ - { "name": "hazy cloud", "sym": ".", "dangerous": true }, - { "name": "fungal haze", "color": "cyan" }, - { "name": "thick fungal haze", "transparent": false } + { "name": "hazy cloud", "sym": ".", "dangerous": true, "concentration": 1 }, + { "name": "fungal haze", "color": "cyan", "concentration": 2 }, + { "name": "thick fungal haze", "transparent": false, "concentration": 4 } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 13, "outdoor_age_speedup": "5 turns", "dirty_transparency_cache": true, @@ -1513,6 +1537,7 @@ "name": "foul-smelling air", "sym": "8", "dangerous": true, + "concentration": 1, "effects": [ { "effect_id": "migo_atmosphere", @@ -1527,6 +1552,7 @@ { "name": "foul-smelling air", "translucency": 1, + "concentration": 2, "effects": [ { "effect_id": "migo_atmosphere", @@ -1539,6 +1565,7 @@ }, { "name": "foul-smelling air", + "concentration": 4, "effects": [ { "effect_id": "migo_atmosphere", @@ -1551,7 +1578,7 @@ } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 30, "outdoor_age_speedup": "3 minutes", "dirty_transparency_cache": true, @@ -1566,12 +1593,12 @@ "type": "field_type", "legacy_enum_id": 49, "intensity_levels": [ - { "name": "fungicidal mist", "sym": "8", "dangerous": true }, - { "name": "fungicidal haze", "color": "light_gray" }, - { "name": "thick fungicidal haze", "color": "dark_gray", "transparent": false } + { "name": "fungicidal mist", "sym": "8", "dangerous": true, "concentration": 1 }, + { "name": "fungicidal haze", "color": "light_gray", "concentration": 2 }, + { "name": "thick fungicidal haze", "color": "dark_gray", "transparent": false, "concentration": 4 } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 100, "outdoor_age_speedup": "1 minutes", "dirty_transparency_cache": true, @@ -1588,12 +1615,12 @@ "id": "fd_insecticidal_gas", "type": "field_type", "intensity_levels": [ - { "name": "insecticidal mist", "sym": "8", "dangerous": true }, - { "name": "insecticidal haze", "color": "light_gray" }, - { "name": "thick insecticidal haze", "color": "dark_gray", "transparent": false } + { "name": "insecticidal mist", "sym": "8", "dangerous": true, "concentration": 1 }, + { "name": "insecticidal haze", "color": "light_gray", "concentration": 2 }, + { "name": "thick insecticidal haze", "color": "dark_gray", "transparent": false, "concentration": 4 } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 100, "outdoor_age_speedup": "1 minutes", "dirty_transparency_cache": true, @@ -1610,10 +1637,14 @@ "id": "fd_smoke_vent", "type": "field_type", "legacy_enum_id": 50, - "intensity_levels": [ { "name": "smoke vent", "dangerous": true }, { "//": "repeat last entry" }, { "//": "repeat last entry" } ], + "intensity_levels": [ + { "name": "smoke vent", "dangerous": true, "concentration": 4 }, + { "//": "repeat last entry" }, + { "//": "repeat last entry" } + ], "description_affix": "under", "wandering_field": "fd_smoke", - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "dirty_transparency_cache": true, "has_fume": true, "phase": "gas", @@ -1675,9 +1706,9 @@ { "id": "fd_clairvoyant", "type": "field_type", - "intensity_levels": [ { "name": "clairvoyance", "sym": "8", "dangerous": false } ], + "intensity_levels": [ { "name": "clairvoyance", "sym": "8", "dangerous": false, "concentration": 1 } ], "decay_amount_factor": 5, - "gas_absorption_factor": 12, + "gas_absorption_factor": "100m", "dirty_transparency_cache": true, "outdoor_age_speedup": "0 turns", "priority": 8, @@ -1694,7 +1725,7 @@ { "name": "a cloud of bees", "color": "green", "translucency": 0 } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 25, "outdoor_age_speedup": "10 minutes", "dirty_transparency_cache": true, @@ -1733,6 +1764,7 @@ "dangerous": true, "light_emitted": 10, "translucency": 5, + "concentration": 1, "effects": [ { "effect_id": "glowing_gas_cover", @@ -1747,7 +1779,7 @@ { "//": "repeat last entry" } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 25, "outdoor_age_speedup": "1 minutes", "dirty_transparency_cache": true, @@ -1770,6 +1802,7 @@ "dangerous": true, "light_emitted": 10, "translucency": 5, + "concentration": 1, "effects": [ { "effect_id": "glowing_gas", @@ -1784,7 +1817,7 @@ { "//": "repeat last entry" } ], "decay_amount_factor": 5, - "gas_absorption_factor": 15, + "gas_absorption_factor": "80m", "percent_spread": 25, "outdoor_age_speedup": "1 minutes", "dirty_transparency_cache": true, diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 821c2293adce7..c1282e0d2870c 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -5931,6 +5931,7 @@ Fields can exist on top of terrain/furniture, and support different intensity le "light_emitted": 2.5, // light level emitted by this intensity "light_override": 3.7 }, //light level on the tile occupied by this field will be set at 3.7 no matter the ambient light. "translucency": 2.0, // How much light the field blocks (higher numbers mean less light can penetrate through) + "concentration": 1, // How concentrated this intensity of gas is. Generally the thin/hazy cloud intensity will be 1, the standard gas will be 2, and thick gas will be 4. The amount of time a gas mask filter will last will be divided by this value. "convection_temperature_mod": 12, // Heat given off by this level of intensity "effects": // List of effects applied to any creatures within the field as long as they aren't immune to the effect or the field itself [ @@ -5965,7 +5966,7 @@ Fields can exist on top of terrain/furniture, and support different intensity le "percent_spread": 90, // The field must succeed on a `rng( 1, 100 - local windpower ) > percent_spread` roll to spread. The field must have a non-zero spread percent and the GAS phase to be eligible to spread in the first place "phase": "gas", // Phase of the field. Gases can spread after spawning and can be spawned out of emitters through impassable terrain with the PERMEABLE flag "apply_slime_factor": 10, // Intensity of slime to apply to creatures standing in this field (Why not use an effect? No idea.) - "gas_absorption_factor": 15, // Amount of gasmask charges the field uses up per tick + "gas_absorption_factor": "80m", // Length a full 100 charge gas mask filter will last in this gas. Will be divided by the concentration of the gas, and should be 80m for concentration 1 toxic gas or similar. The worst gas should still be kept out for 20 minutes in a concentration 4 thick gas. "is_splattering": true, // If splatters of this field should bloody vehicle parts "dirty_transparency_cache": true, // Should the transparency cache be recalculated when the field is modified (used for nontransparent, spreading fields) "has_fire": false, // Is this field a kind of fire (for immunity, monster avoidance and similar checks) diff --git a/src/field_type.cpp b/src/field_type.cpp index 390b2e2f15dff..383fbcf033124 100644 --- a/src/field_type.cpp +++ b/src/field_type.cpp @@ -222,6 +222,8 @@ void field_type::load( const JsonObject &jo, const std::string_view ) fallback_intensity_level.local_light_override ); optional( jao, was_loaded, "translucency", intensity_level.translucency, fallback_intensity_level.translucency ); + optional( jao, was_loaded, "concentration", intensity_level.concentration, + 1 ); optional( jao, was_loaded, "convection_temperature_mod", intensity_level.convection_temperature_mod, fallback_intensity_level.convection_temperature_mod ); if( jao.has_array( "effects" ) ) { @@ -282,7 +284,7 @@ void field_type::load( const JsonObject &jo, const std::string_view ) optional( jo, was_loaded, "decay_amount_factor", decay_amount_factor, 0 ); optional( jo, was_loaded, "percent_spread", percent_spread, 0 ); optional( jo, was_loaded, "apply_slime_factor", apply_slime_factor, 0 ); - optional( jo, was_loaded, "gas_absorption_factor", gas_absorption_factor, 0 ); + optional( jo, was_loaded, "gas_absorption_factor", gas_absorption_factor, 0_turns ); optional( jo, was_loaded, "is_splattering", is_splattering, false ); optional( jo, was_loaded, "dirty_transparency_cache", dirty_transparency_cache, false ); optional( jo, was_loaded, "has_fire", has_fire, false ); diff --git a/src/field_type.h b/src/field_type.h index 926a2748e7718..c3468eac862f7 100644 --- a/src/field_type.h +++ b/src/field_type.h @@ -118,6 +118,7 @@ struct field_intensity_level { float light_emitted = 0.0f; float local_light_override = -1.0f; float translucency = 0.0f; + int concentration = 0; int convection_temperature_mod = 0; int scent_neutralization = 0; std::vector field_effects; @@ -197,7 +198,7 @@ struct field_type { int decay_amount_factor = 0; int percent_spread = 0; int apply_slime_factor = 0; - int gas_absorption_factor = 0; + time_duration gas_absorption_factor = 0_turns; bool is_splattering = false; bool dirty_transparency_cache = false; bool has_fire = false; diff --git a/src/iuse.cpp b/src/iuse.cpp index e56183e8f74f3..3fd7f7f227cdb 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -120,6 +120,7 @@ #include "vitamin.h" #include "vpart_position.h" #include "vpart_range.h" +#include "units.h" #include "weather.h" #include "weather_gen.h" #include "weather_type.h" @@ -4183,14 +4184,27 @@ std::optional iuse::gasmask( Character *p, item *it, const tripoint &pos ) const field &gasfield = get_map().field_at( pos ); for( const auto &dfield : gasfield ) { const field_entry &entry = dfield.second; - const int gas_abs_factor = entry.get_field_type()->gas_absorption_factor; - if( gas_abs_factor > 0 ) { - it->set_var( "gas_absorbed", it->get_var( "gas_absorbed", 0 ) + gas_abs_factor ); - } - } - if( it->get_var( "gas_absorbed", 0 ) >= 100 ) { + int gas_abs_factor = to_turns( entry.get_field_type()->gas_absorption_factor ); + const field_intensity_level &int_level = entry.get_intensity_level(); + // 6000 is the amount of "gas absorbed" charges in a full 100 capacity gas mask cartridge. + // factor/concentration gives an amount of seconds the cartidge is expected to last in current conditions. + /// 6000/that is the amount of "gas absorbed" charges to tick up every second in order to reach that number. + float gas_absorbed = 6000 / ( static_cast( gas_abs_factor ) / static_cast + ( int_level.concentration ) ); + if( gas_absorbed > 0 ) { + it->set_var( "gas_absorbed", it->get_var( "gas_absorbed", 0 ) + gas_absorbed ); + } + } + if( it->get_var( "gas_absorbed", 0 ) >= 60 ) { it->ammo_consume( 1, pos, p ); it->set_var( "gas_absorbed", 0 ); + if( it->ammo_remaining() < 10 ) { + p->add_msg_player_or_npc( + m_bad, + _( "Your %s is getting hard to breathe in!" ), + _( "'s gas mask is getting hard to breathe in!" ) + , it->tname() ); + } } if( it->ammo_remaining() == 0 ) { p->add_msg_player_or_npc(