From 6c14e428f96da64428d38be35241d0bfbf6e2245 Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 28 May 2026 17:31:18 -0500 Subject: [PATCH] Hearts now regenerate blood (#96129) --- code/modules/mob/living/blood.dm | 6 +++--- code/modules/mob/living/carbon/life.dm | 15 +++++++++++++ .../surgery/organs/internal/heart/_heart.dm | 21 +++++++++++-------- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index ae4eb155480..6ed417d57eb 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -161,16 +161,16 @@ if((sigreturn & HANDLE_BLOOD_HANDLED) || !CAN_HAVE_BLOOD(src)) return + var/heart_blood_multiplier = get_heart_blood_regeneration_multiplier() //Blood regeneration if there is some space - if(!(sigreturn & HANDLE_BLOOD_NO_NUTRITION_DRAIN) && get_blood_volume() < BLOOD_VOLUME_NORMAL && !HAS_TRAIT(src, TRAIT_NOHUNGER)) + if(heart_blood_multiplier && !(sigreturn & HANDLE_BLOOD_NO_NUTRITION_DRAIN) && get_blood_volume() < BLOOD_VOLUME_NORMAL && !HAS_TRAIT(src, TRAIT_NOHUNGER)) var/nutrition_ratio = round(nutrition / NUTRITION_LEVEL_WELL_FED, 0.2) if(satiety > 80) nutrition_ratio *= 1.25 - var/blood_to_restore = BLOOD_REGEN_FACTOR * physiology.blood_regen_mod * nutrition_ratio * seconds_per_tick + var/blood_to_restore = BLOOD_REGEN_FACTOR * physiology.blood_regen_mod * heart_blood_multiplier * nutrition_ratio * seconds_per_tick var/blood_restored = adjust_blood_volume(blood_to_restore, maximum = BLOOD_VOLUME_NORMAL) - if (blood_restored > 0) adjust_nutrition(-nutrition_ratio * HUNGER_FACTOR * seconds_per_tick * (blood_restored / blood_to_restore)) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 5fa890e834c..5087244f22b 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -569,6 +569,21 @@ LAZYREMOVE(dna.temporary_mutations, mut) continue +/** + * Returns a multiplier representing how effectively this mob can regenerate blood + * + * A return value of 0 means the mob cannot regenerate blood at all. (missing heart or the heart has stopped or is failing) + * Mobs that do not require a heart always return 1, as their blood regeneration is unaffected by heart status. + */ +/mob/living/carbon/proc/get_heart_blood_regeneration_multiplier() + if(!needs_heart()) + return 1 + var/obj/item/organ/heart/heart = get_organ_slot(ORGAN_SLOT_HEART) + if(isnull(heart)) + return 0 + + return heart.get_blood_regeneration_multiplier() + /** * Handles calling metabolization for dead people. * Due to how reagent metabolization code works this couldn't be done anywhere else. diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm index 9102e581491..c7b0bcce0f6 100644 --- a/code/modules/surgery/organs/internal/heart/_heart.dm +++ b/code/modules/surgery/organs/internal/heart/_heart.dm @@ -36,7 +36,10 @@ var/beat = BEAT_NONE /// whether the heart's been operated on to fix some of its damages var/operated = FALSE + /// The message that is displayed when listening to a heart via a stethoscope var/beat_noise = "a rhythmic thumping" + /// The rate at which blood is pumped by the heart is multiplied by this (value of 0 disables blood regeneration entirely) + var/blood_regeneration_multiplier = 1 /obj/item/organ/heart/update_icon_state() . = ..() @@ -93,6 +96,14 @@ . = ..() Stop() +/// Returns how effectively this heart regenerates the owner's blood based on organ health +/obj/item/organ/heart/proc/get_blood_regeneration_multiplier() + if(!is_beating() || (organ_flags & (ORGAN_FAILING|ORGAN_EMP))) + return 0 + + var/health_percent = clamp((maxHealth - damage) / maxHealth, 0, 1) + return blood_regeneration_multiplier * health_percent + /// Checks if the heart is beating. /// Can be overridden to add more conditions for more complex hearts. /obj/item/organ/heart/proc/is_beating() @@ -204,16 +215,12 @@ beat_noise = "a steady fsssh of hydraulics" /// Whether or not we have a stabilization available. This prevents our owner from entering softcrit for an amount of time. var/stabilization_available = FALSE - /// How long our stabilization lasts for. var/stabilization_duration = 10 SECONDS - /// Whether our heart suppresses bleeders and restores blood automatically. var/bleed_prevention = FALSE - /// The probability that our blood replication causes toxin damage. var/toxification_probability = 20 - /// Chance of permanent effects if emp-ed. var/emp_vulnerability = 80 @@ -252,8 +259,6 @@ if(bleed_prevention && ishuman(owner) && owner.get_blood_volume() < BLOOD_VOLUME_NORMAL) var/mob/living/carbon/human/wounded_owner = owner - wounded_owner.adjust_blood_volume(2 * seconds_per_tick) - if(toxification_probability && prob(toxification_probability)) wounded_owner.adjust_tox_loss(1 * seconds_per_tick, updating_health = FALSE) @@ -282,6 +287,7 @@ maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD bleed_prevention = TRUE emp_vulnerability = 40 + blood_regeneration_multiplier = 9 // regenerates 2.25u of blood per tick (default is 0.25u) /obj/item/organ/heart/cybernetic/tier3 name = "upgraded cybernetic heart" @@ -343,9 +349,7 @@ desc = "It beats ever strong." icon_state = "heart-evolved-on" base_icon_state = "heart-evolved" - maxHealth = STANDARD_ORGAN_THRESHOLD * 1.2 - /// Chance to heal per on_life var/healing_probability = 10 /// Base healing we receive per tick at 0 damage and for standard versions @@ -371,7 +375,6 @@ healing_probability = 5 base_healing = 0.5 - // How much damage each magic block deals to us var/damage_per_block = 50