From 99cd00f5fea0402d49bf28fd356a4da5addd8368 Mon Sep 17 00:00:00 2001 From: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Date: Fri, 12 Mar 2021 17:49:50 -0800 Subject: [PATCH] Makes gas actually dissipate (#57634) * Raises the quantize threshold from 1E-7 to 1E-4. This makes gas dissipate as expected, and should help with the amount of useless gas floating around the station at highpop Adds a garbage_collect() call to the portion of pipeline code where all gasmixes are in one place, this should clean things up properly. Changes BREATH_VOLUME from 2 to 1.99. This is imperative Documents a FUCKING HELLBUG in quantize/breathcode that can lead to breaths just not working sometimes. I'm not sure how to fix this totally, so I'll document it and pray. See Adds a unit test to check for this sort of failure. Addendum for people tweaking this value in the future. Because o2 tank release values/human o2 requirements are very strictly set to the same pressure, small errors can cause breakage This comes from QUANTIZE being used in /datum/gas_mixture.remove(), forming a slight sawtooth pattern of the added/removed gas, centered on the actual pressure Changing BREATH_VOLUME can set us on the lower half of this sawtooth, making humans unable to breath at standard pressure. There's no good way I can come up with to hardcode a fix for this. So if you're going to change this variable graph the functions that describe how it is used/how it interacts with breath code, and pick something on the upper half of the sawtooth NOTE: I've made this change with a focus on o2 requirements. Changing this will effect other settings, but most all of them can be ignored, as none will notice. * Thank you moth man Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> * Docs the purpose of the breath unit test, and better explains partial pressure Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> --- code/__DEFINES/atmospherics.dm | 13 +++++-- code/_onclick/hud/alert.dm | 4 +++ .../atmospherics/gasmixtures/gas_mixture.dm | 15 ++++---- .../atmospherics/machinery/datum_pipeline.dm | 2 ++ code/modules/surgery/organs/lungs.dm | 2 +- code/modules/unit_tests/_unit_tests.dm | 1 + code/modules/unit_tests/breath.dm | 36 +++++++++++++++++++ 7 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 code/modules/unit_tests/breath.dm diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index 4400837ec78..da4c77fd79b 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -41,9 +41,16 @@ #define MOLES_N2STANDARD (MOLES_CELLSTANDARD*N2STANDARD) /// liters in a cell #define CELL_VOLUME 2500 - -/// liters in a normal breath. note that breaths are taken once every 4 life ticks, which is 8 seconds -#define BREATH_VOLUME 2 +/** liters in a normal breath. note that breaths are taken once every 4 life ticks, which is 8 seconds + * Addendum for people tweaking this value in the future. + * Because o2 tank release values/human o2 requirements are very strictly set to the same pressure, small errors can cause breakage + * This comes from QUANTIZE being used in /datum/gas_mixture.remove(), forming a slight sawtooth pattern of the added/removed gas, centered on the actual pressure + * Changing BREATH_VOLUME can set us on the lower half of this sawtooth, making humans unable to breath at standard pressure. + * There's no good way I can come up with to hardcode a fix for this. So if you're going to change this variable + * graph the functions that describe how it is used/how it interacts with breath code, and pick something on the upper half of the sawtooth + * +**/ +#define BREATH_VOLUME 1.99 /// Amount of air to take a from a tile #define BREATH_PERCENTAGE (BREATH_VOLUME/CELL_VOLUME) diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index fbef432f041..4a7dc3109fe 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -86,6 +86,10 @@ client.screen -= alert qdel(alert) +// Proc to check for an alert +/mob/proc/has_alert(category) + return !isnull(alerts[category]) + /atom/movable/screen/alert icon = 'icons/hud/screen_alert.dmi' icon_state = "default" diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index 3e2295c8c65..d0a94edc501 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -5,15 +5,16 @@ This prevents race conditions that arise based on the order of tile processing. */ #define MINIMUM_HEAT_CAPACITY 0.0003 #define MINIMUM_MOLE_COUNT 0.01 -#define MOLAR_ACCURACY 1E-7 +#define MOLAR_ACCURACY 1E-4 /** *I feel the need to document what happens here. Basically this is used - *catch most rounding errors, however its previous value made it so that - *once gases got hot enough, most procedures wouldn't occur due to the fact that the mole - *counts would get rounded away. Thus, we lowered it a few orders of magnitude - *Edit: As far as I know this might have a bug caused by round(). When it has a second arg it will round up. - *So for instance round(0.5, 1) == 1. Trouble is I haven't found any instances of it causing a bug, - *and any attempts to fix it just killed atmos. I leave this to a greater man then I + *catch rounding errors, and make gas go away in small portions. + *People have raised it to higher levels in the past, do not do this. Consider this number a soft limit + *If you're making gasmixtures that have unexpected behavior related to this value, you're doing something wrong. + * + *On an unrelated note this may cause a bug that creates negative gas, related to round(). When it has a second arg it will round up. + *So for instance round(0.5, 1) == 1. I've hardcoded a fix for this into share, by forcing the garbage collect. + *Any other attempts to fix it just killed atmos. I leave this to a greater man then I */ #define QUANTIZE(variable) (round((variable), (MOLAR_ACCURACY))) GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm index c7b90709fbf..29702d3d3da 100644 --- a/code/modules/atmospherics/machinery/datum_pipeline.dm +++ b/code/modules/atmospherics/machinery/datum_pipeline.dm @@ -292,6 +292,8 @@ total_gas_mixture.temperature = total_heat_capacity ? (total_thermal_energy / total_heat_capacity) : 0 + total_gas_mixture.garbage_collect() + if(total_gas_mixture.volume > 0) //Update individual gas_mixtures by volume ratio for(var/mixture in gas_mixture_list) diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index 41857269998..c1068e2cb1e 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -21,7 +21,7 @@ food_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/medicine/salbutamol = 5) //Breath damage - //These numbers are roughly equivilant to molar count at room temperature, they scale up and down with temp tho + //These thresholds are checked against what amounts to total_mix_pressure * (gas_type_mols/total_mols) var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa var/safe_oxygen_max = 0 diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 2ef83c14d27..040f9f0bceb 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -40,6 +40,7 @@ #include "anchored_mobs.dm" #include "bespoke_id.dm" #include "binary_insert.dm" +#include "breath.dm" #include "card_mismatch.dm" #include "chain_pull_through_space.dm" #include "combat.dm" diff --git a/code/modules/unit_tests/breath.dm b/code/modules/unit_tests/breath.dm new file mode 100644 index 00000000000..a36e5731408 --- /dev/null +++ b/code/modules/unit_tests/breath.dm @@ -0,0 +1,36 @@ +/// Tests to make sure humans can breath in normal situations +/// Built to prevent regression on an issue surrounding QUANTIZE() and BREATH_VOLUME +/// See the comment on BREATH_VOLUME for more details +/datum/unit_test/breath_sanity + +/datum/unit_test/breath_sanity/Run() + var/mob/living/carbon/human/lab_rat = allocate(/mob/living/carbon/human) + var/obj/item/clothing/mask/breath/tube = allocate(/obj/item/clothing/mask/breath) + var/obj/item/tank/internals/emergency_oxygen/source = allocate(/obj/item/tank/internals/emergency_oxygen) + + lab_rat.equip_to_slot_if_possible(tube, ITEM_SLOT_MASK) + lab_rat.equip_to_slot_if_possible(source, ITEM_SLOT_HANDS) + source.toggle_internals(lab_rat) + + lab_rat.breathe() + + TEST_ASSERT(!lab_rat.has_alert("not_enough_oxy"), "Humans can't get a full breath from standard o2 tanks") + lab_rat.clear_alert("not_enough_oxy") + + //Prep the mob + lab_rat.forceMove(run_loc_floor_bottom_left) + source.toggle_internals(lab_rat) + TEST_ASSERT(!lab_rat.internal, "toggle_internals() failed to toggle internals") + + var/turf/open/to_fill = run_loc_floor_bottom_left + //Prep the floor + to_fill.initial_gas_mix = OPENTURF_DEFAULT_ATMOS + to_fill.air = new + to_fill.air.copy_from_turf(to_fill) + + lab_rat.breathe() + + TEST_ASSERT(!lab_rat.has_alert("not_enough_oxy"), "Humans can't get a full breath from the standard initial_gas_mix on a turf") + + +