From e934c818c15d265afb867d4c06d635099d087eea Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Mon, 3 Nov 2025 03:13:03 -0500 Subject: [PATCH] The Medical Stasis Refactoring (#21311) This PR implements a variety pack of new interactions for Medical Stasis (stasis beds, stasis bags, and cryogenics), which should broadly extend their utility. Not all of it is necessarily a good thing to have around, such as for instance stasis slowing down the rate at which the liver removes toxins. A few more organs have by extension been time differentiated to allow for this change to take place. As a general rule of thumb, most organ effects that are handled "per second" are proportionally slowed down by the stasis rating of a given source of medical stasis. This can be a good or a bad thing depending on the context. **Good effects of Stasis:** - Slowing down appendicitis. - Reducing the damage organs take per second from most per-second sources. - Making kidneys and livers deal less dangerous effects to their owner when severely damaged. - (Not in this PR, but was done in a previous PR): Brains lose BA slower. **Bad effects of Stasis:** - Organs also heal slower from all per-second sources, this includes chems. So while they take less damage, they're also taking less healing. - The liver's rate at which it filters the blood is reduced. You get to stay black-out drunk for 10x longer if placed in stasis. - (Not in this PR, but was done in a previous PR): Brains heal their BA slower. --------- Signed-off-by: VMSolidus --- code/modules/organs/internal/_internal.dm | 18 +++++- code/modules/organs/internal/appendix.dm | 47 ++++++++++---- code/modules/organs/internal/heart.dm | 6 +- code/modules/organs/internal/kidneys.dm | 61 +++++++++++++----- code/modules/organs/internal/liver.dm | 4 ++ code/modules/organs/internal/stomach.dm | 50 ++++++++------- .../Chemistry-Reagents-Medicine.dm | 4 +- .../hellfirejag-organ-stasis-rework.yml | 62 +++++++++++++++++++ 8 files changed, 192 insertions(+), 60 deletions(-) create mode 100644 html/changelogs/hellfirejag-organ-stasis-rework.yml diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index 024c9e86615..4318d875160 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -15,6 +15,9 @@ var/active_emissive = FALSE var/list/possible_modifications = list("Normal","Assisted","Mechanical") //this is used in the character setup + /// The amount all organs heal themselves by per second when not being affected by chems. + var/organ_self_heal_per_second = 0.2 + min_broken_damage = 10 //Internal organs are frail, man. /obj/item/organ/internal/Destroy() @@ -145,8 +148,17 @@ /obj/item/organ/internal/process(seconds_per_tick) ..() - if(istype(owner) && (toxin_type in owner.chem_effects)) + if(!owner) + return + + if(owner.stasis_value > 0) + // Putting a body in stasis will simultaneously slow down the rate at which organs die + // while also slowing down the rate at which they are healed. + seconds_per_tick /= owner.stasis_value + + if(toxin_type in owner.chem_effects) take_damage(owner.chem_effects[toxin_type] * seconds_per_tick) + handle_regeneration(seconds_per_tick) tick_surge_damage() //Yes, this is intentional. @@ -155,7 +167,7 @@ if(!damage || BP_IS_ROBOTIC(src) || !istype(owner) || owner.is_asystole() || (owner.chem_effects[CE_TOXIN] || (toxin_type in owner.chem_effects))) return FALSE - var/repair_modifier = owner.chem_effects[CE_ORGANREPAIR] || 0.1 + var/repair_modifier = owner.chem_effects[CE_ORGANREPAIR] || organ_self_heal_per_second if(damage < repair_modifier*max_damage) - heal_damage(repair_modifier) + heal_damage(repair_modifier * seconds_per_tick) return TRUE // regeneration is allowed diff --git a/code/modules/organs/internal/appendix.dm b/code/modules/organs/internal/appendix.dm index 0846b8ed391..62999b8f36e 100644 --- a/code/modules/organs/internal/appendix.dm +++ b/code/modules/organs/internal/appendix.dm @@ -4,39 +4,62 @@ parent_organ = BP_GROIN organ_tag = BP_APPENDIX possible_modifications = list ("Normal","Removed") + + /** + * The amount of seconds that have passed since starting an appendicitis event. + * This number is set initially to 1 by the event, and increments by 1 each real life second. + * While its at 0, no appendix calculations are performed. + */ var/inflamed = 0 + /// The amount of pain to take per second while in stage 1 of appendicitis. + var/stage_one_pain_per_second = 1 + + /// The amount of pain to take per second while in stage 2 of appendicitis. + var/stage_two_pain_per_second = 1.5 + + /// The amount of damage an appendix takes per second while in stage 2 of appendicitis. + var/stage_two_organ_damage_per_second = 0.006 + /obj/item/organ/internal/appendix/update_icon() ..() if(inflamed) icon_state = "[icon_state]inflamed" name = "inflamed [name]" -/obj/item/organ/internal/appendix/process() +/obj/item/organ/internal/appendix/process(seconds_per_tick) ..() if(!owner || !inflamed || !BP_IS_ROBOTIC(src)) return + if(owner.stasis_value > 0) + // Decrease the effective tickrate when in stasis. + // Basically, you die of appendicitis slower while in stasis. + seconds_per_tick /= owner.stasis_value + var/can_feel_pain = ORGAN_CAN_FEEL_PAIN(src) - inflamed++ - if(can_feel_pain && prob(5)) - owner.custom_pain("You feel a stinging pain in your abdomen!") - owner.visible_message("\The [owner] winces slightly.") + inflamed += seconds_per_tick + + if(can_feel_pain) + if(prob(5)) + owner.custom_pain("You feel a stinging pain in your abdomen!") + owner.visible_message("\The [owner] winces slightly.") var/obj/item/organ/external/O = owner.get_organ(parent_organ) if(O) - O.add_pain(10) - if(inflamed > 200 && prob(3)) - take_internal_damage(0.1) + O.add_pain(stage_one_pain_per_second * seconds_per_tick) + if(inflamed > 400) + take_internal_damage(stage_two_organ_damage_per_second * seconds_per_tick) if(can_feel_pain) - owner.visible_message("\The [owner] winces painfully.") + if(prob(3)) + owner.visible_message("\The [owner] winces painfully.") var/obj/item/organ/external/O = owner.get_organ(parent_organ) if(O) - O.add_pain(25) + O.add_pain(stage_two_pain_per_second * seconds_per_tick) owner.adjustToxLoss(1) - if(inflamed > 400 && prob(1)) + if(inflamed > 800 && prob(1)) germ_level += rand(2,6) owner.vomit() - if(inflamed > 600 && prob(1)) + if(inflamed > 1200 && prob(1)) if(can_feel_pain) owner.custom_pain("You feel a stinging pain in your abdomen!") owner.Weaken(10) diff --git a/code/modules/organs/internal/heart.dm b/code/modules/organs/internal/heart.dm index ff1be01e848..5cfb60622d8 100644 --- a/code/modules/organs/internal/heart.dm +++ b/code/modules/organs/internal/heart.dm @@ -120,6 +120,8 @@ /obj/item/organ/internal/heart/process(seconds_per_tick) if(!owner) return ..() + if(owner.stasis_value > 0) // Decrease the effective tickrate when in stasis. + seconds_per_tick /= owner.stasis_value handle_pulse() if(pulse) if(pulse == PULSE_2FAST) @@ -346,9 +348,7 @@ . = "[pulsesound] pulse" -// Example heart item that has significantly higher statistics. -// Also to be used for the Galatean Bio-augments PRs. -// TODO: After refactoring the organ selector, make it so that this is a selectable heart type(For Galateans) +// Galatean boosted heart /obj/item/organ/internal/heart/boosted_heart name = "boosted heart" desc = "Intended for athletes, some workers, and soldiers, this improved heart increases blood flow and circulation." \ diff --git a/code/modules/organs/internal/kidneys.dm b/code/modules/organs/internal/kidneys.dm index 9e849477628..d23e183e893 100644 --- a/code/modules/organs/internal/kidneys.dm +++ b/code/modules/organs/internal/kidneys.dm @@ -12,34 +12,63 @@ relative_size = 10 toxin_type = CE_NEPHROTOXIC -/obj/item/organ/internal/kidneys/process() + /// The amount of toxin damage to take per second from coffee while the kidneys are bruised. + var/bruised_tox_damage_from_coffee = 0.2 + /// The amount of toxin damage to take per second from coffee while the kidneys are broken. + var/broken_tox_damage_from_coffee = 0.6 + + /// Bruised kidneys will leak up to this amount of potassium into the bloodstream. + var/bruised_max_potassium = 5 + + /** + * Damaged kidneys leak potassium into the blood when processing any medical chems. + * This is the amount of reagents of potassium per second added to the bloodstream while the kidneys are bruised. + */ + var/bruised_potassium_per_second = 0.1 + + /// Broken kidneys will leak up to this amount of potassium into the bloodstream. This number should generally be higher than the bruised value. + var/broken_max_potassium = 15 + + /** + * Damaged kidneys leak potassium into the blood when processing any medical chems. + * This is the amount of reagents of potassium per second added to the bloodstream while the kidneys are broken. + */ + var/broken_potassium_per_second = 4 + + /// The amount of toxin damage per second taken by a body with broken kidneys. + var/broken_toxin_accumulation_per_second = 0.33 + + /// The amount of toxin damage per second taken by a body with dead kidneys. + var/dead_toxin_accumulation_per_second = 0.66 + +/obj/item/organ/internal/kidneys/process(seconds_per_tick) ..() - if(!owner) return + if(owner.stasis_value > 0) // Decrease the effective tickrate when in stasis. + seconds_per_tick /= owner.stasis_value + // Coffee is really bad for you with busted kidneys. // This should probably be expanded in some way, but fucked if I know // what else kidneys can process in our reagent list. - var/singleton/reagent/drink/coffee = REAGENT_VOLUME(owner.reagents, /singleton/reagent/drink/coffee) - if(coffee) + if(REAGENT_VOLUME(owner.reagents, /singleton/reagent/drink/coffee)) if(is_bruised()) - owner.adjustToxLoss(0.1) + owner.adjustToxLoss(bruised_tox_damage_from_coffee * seconds_per_tick) else if(is_broken()) - owner.adjustToxLoss(0.3) + owner.adjustToxLoss(broken_tox_damage_from_coffee * seconds_per_tick) if(is_bruised()) - if(prob(5) && REAGENT_VOLUME(reagents, /singleton/reagent/potassium) < 5) - reagents.add_reagent(/singleton/reagent/potassium, REM*5) + if(REAGENT_VOLUME(reagents, /singleton/reagent/potassium) < bruised_max_potassium) + reagents.add_reagent(/singleton/reagent/potassium, bruised_potassium_per_second * seconds_per_tick) if(is_broken()) - if(REAGENT_VOLUME(owner.reagents, /singleton/reagent/potassium) < 15) - owner.reagents.add_reagent(/singleton/reagent/potassium, REM*2) + if(REAGENT_VOLUME(owner.reagents, /singleton/reagent/potassium) < broken_max_potassium) + owner.reagents.add_reagent(/singleton/reagent/potassium, broken_potassium_per_second * seconds_per_tick) //If your kidneys aren't working, your body's going to have a hard time cleaning your blood. - if(!owner.chem_effects[CE_ANTITOXIN]) - if(prob(33)) - if(is_broken()) - owner.adjustToxLoss(0.5) - if(status & ORGAN_DEAD) - owner.adjustToxLoss(1) + if(!owner.chem_effects[CE_ANTITOXIN]) // Dylovene fully prevents the toxin accumulation from kidneys. + if(is_broken()) + owner.adjustToxLoss(broken_toxin_accumulation_per_second * seconds_per_tick) + if(status & ORGAN_DEAD) + owner.adjustToxLoss(dead_toxin_accumulation_per_second * seconds_per_tick) diff --git a/code/modules/organs/internal/liver.dm b/code/modules/organs/internal/liver.dm index fab93c1dc4c..5f4c89f2804 100644 --- a/code/modules/organs/internal/liver.dm +++ b/code/modules/organs/internal/liver.dm @@ -76,6 +76,9 @@ if(!owner) return + if(owner.stasis_value > 0) // Decrease the effective tickrate when in stasis. + seconds_per_tick /= owner.stasis_value + if(germ_level > INFECTION_LEVEL_ONE) owner.notify_message(SPAN_WARNING(infection_level_one_warning), 2 MINUTES) if(germ_level > INFECTION_LEVEL_TWO && prob(1)) @@ -160,6 +163,7 @@ return TRUE return FALSE +// Galatean bioaugmented liver. Dramatically enhanced over a base human liver. /obj/item/organ/internal/liver/boosted_liver name = "boosted liver" desc = "Designed primarily for diplomats or Galateans abroad, the boosted liver improves toxin filtering, giving a resistance to toxin damage. As a consequence, it makes it impossible for the user to get drunk." diff --git a/code/modules/organs/internal/stomach.dm b/code/modules/organs/internal/stomach.dm index 4df7884e64b..ad32c24333a 100644 --- a/code/modules/organs/internal/stomach.dm +++ b/code/modules/organs/internal/stomach.dm @@ -101,38 +101,40 @@ /obj/item/organ/internal/stomach/process() ..() - if(owner) - var/functioning = is_usable() - if(damage >= min_bruised_damage && prob((damage / max_damage) * 100)) - functioning = FALSE + if(!owner) + return - if(functioning) - for(var/mob/living/M in contents) - if(M.stat == DEAD) - addtimer(CALLBACK(src, PROC_REF(digest_mob), M), 5 MINUTES, TIMER_UNIQUE) + var/functioning = is_usable() + if(damage >= min_bruised_damage && prob((damage / max_damage) * 100)) + functioning = FALSE - M.adjustBruteLoss(2) - M.adjustFireLoss(2) + if(functioning) + for(var/mob/living/M in contents) + if(M.stat == DEAD) + addtimer(CALLBACK(src, PROC_REF(digest_mob), M), 5 MINUTES, TIMER_UNIQUE) - var/digestion_product = M.get_digestion_product() - if(digestion_product) - ingested.add_reagent(digestion_product, rand(1,3)) + M.adjustBruteLoss(2) + M.adjustFireLoss(2) - else if(world.time >= next_cramp) - next_cramp = world.time + rand(200,800) - owner.custom_pain("Your stomach cramps agonizingly!",1) + var/digestion_product = M.get_digestion_product() + if(digestion_product) + ingested.add_reagent(digestion_product, rand(1,3)) - if(should_process_alcohol) + else if(world.time >= next_cramp) + next_cramp = world.time + rand(200,800) + owner.custom_pain("Your stomach cramps agonizingly!",1) - var/alcohol_volume = REAGENT_VOLUME(ingested, /singleton/reagent/alcohol) + if(should_process_alcohol) - // Alcohol counts as double volume for the purposes of vomit probability - var/effective_volume = ingested.total_volume + alcohol_volume + var/alcohol_volume = REAGENT_VOLUME(ingested, /singleton/reagent/alcohol) - // Just over the limit, the probability will be low. It rises a lot such that at double ingested it's 64% chance. - var/vomit_probability = (effective_volume / stomach_volume) ** 6 - if(prob(vomit_probability)) - owner.vomit() + // Alcohol counts as double volume for the purposes of vomit probability + var/effective_volume = ingested.total_volume + alcohol_volume + + // Just over the limit, the probability will be low. It rises a lot such that at double ingested it's 64% chance. + var/vomit_probability = (effective_volume / stomach_volume) ** 6 + if(prob(vomit_probability)) + owner.vomit() /obj/item/organ/internal/stomach/proc/digest_mob(mob/M) if(!QDELETED(M)) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm index 6949a39c596..5ac4a26c974 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm @@ -1641,7 +1641,7 @@ /singleton/reagent/rezadone/affect_chem_effect(var/mob/living/carbon/M, var/alien, var/removed, var/datum/reagents/holder) . = ..() if(.) - M.add_chemical_effect(CE_ORGANREPAIR, 1) + M.add_chemical_effect(CE_ORGANREPAIR, 2) M.add_chemical_effect(CE_BLOODRESTORE, 15) /singleton/reagent/rezadone/affect_blood(var/mob/living/carbon/M, var/alien, var/removed, var/datum/reagents/holder) @@ -1674,7 +1674,7 @@ /singleton/reagent/sanasomnum/affect_chem_effect(var/mob/living/carbon/M, var/alien, var/removed, var/datum/reagents/holder) . = ..() if(.) - M.add_chemical_effect(CE_ORGANREPAIR, 20) + M.add_chemical_effect(CE_ORGANREPAIR, 40) M.add_chemical_effect(CE_BLOODRESTORE, 15) M.add_chemical_effect(CE_BLOODCLOT, 15) M.add_chemical_effect(CE_BRAIN_REGEN, 20) diff --git a/html/changelogs/hellfirejag-organ-stasis-rework.yml b/html/changelogs/hellfirejag-organ-stasis-rework.yml new file mode 100644 index 00000000000..073e5ceaab4 --- /dev/null +++ b/html/changelogs/hellfirejag-organ-stasis-rework.yml @@ -0,0 +1,62 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# - (fixes bugs) +# wip +# - (work in progress) +# qol +# - (quality of life) +# soundadd +# - (adds a sound) +# sounddel +# - (removes a sound) +# rscadd +# - (adds a feature) +# rscdel +# - (removes a feature) +# imageadd +# - (adds an image or sprite) +# imagedel +# - (removes an image or sprite) +# spellcheck +# - (fixes spelling or grammar) +# experiment +# - (experimental change) +# balance +# - (balance changes) +# code_imp +# - (misc internal code change) +# refactor +# - (refactors code) +# config +# - (makes a change to the config files) +# admin +# - (makes changes to administrator tools) +# server +# - (miscellaneous changes to server) +################################# + +# Your name. +author: Hellfirejag + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Organ passive healing and passive damage are both reduced by medical stasis." + - rscadd: "Appendicitis now progresses slower while in stasis, and is also now tick-invariant." + - rscadd: "Hearts now take less damage from tachycardia while in stasis." + - rscadd: "Bruised/Broken kidneys now have reduced effects while in stasis." + - rscadd: "Livers now filter toxins and booze slower while in stasis."