From a1e27c07dad5bba13074090d6a7d5af724c94f26 Mon Sep 17 00:00:00 2001 From: DGamerL <108773801+DGamerL@users.noreply.github.com> Date: Fri, 26 Dec 2025 23:29:05 +0100 Subject: [PATCH] Turns some wounds into datumized versions (#31297) * Wound datumization * Rejuv additions * Fixes * Adds `get_wound()` proc * Ruptured lungs get fixed now * Apply suggestions from code review Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com> * Fix this * Potentially fix an issue, might remove the proc later * Many fixes * Fuck --------- Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com> --- code/game/machinery/adv_med.dm | 10 +-- .../objects/items/devices/bodyanalyzer.dm | 3 +- .../antagonists/zombie/zombie_spells.dm | 4 +- .../mob/living/carbon/human/human_mob.dm | 11 ++-- .../mob/living/carbon/human/human_organs.dm | 17 +++--- code/modules/surgery/cavity_implant.dm | 2 +- code/modules/surgery/organs/eyes.dm | 1 + code/modules/surgery/organs/lungs.dm | 11 ++++ code/modules/surgery/organs/organ.dm | 19 +++++- .../surgery/organs/organ_datums/lung_datum.dm | 11 +--- code/modules/surgery/organs/organ_external.dm | 22 +++++-- .../modules/surgery/organs/organ_extractor.dm | 3 +- code/modules/surgery/organs/organ_internal.dm | 1 + code/modules/surgery/organs/wounds.dm | 61 +++++++++++++++++++ code/modules/surgery/organs_internal.dm | 3 +- code/modules/surgery/robotics.dm | 16 ++--- paradise.dme | 1 + 17 files changed, 148 insertions(+), 48 deletions(-) create mode 100644 code/modules/surgery/organs/wounds.dm diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index bf0aff6319b..450234717df 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -347,10 +347,9 @@ var/organStatus[0] if(E.status & ORGAN_BROKEN) - if(!E.broken_description) - organStatus["broken"] = "Broken" - else - organStatus["broken"] = E.broken_description + var/datum/wound/fracture = E.get_wound(/datum/wound/fracture) + organStatus["broken"] = fracture.name + if(E.is_robotic()) organStatus["robotic"] = TRUE if(E.status & ORGAN_SPLINTED) @@ -510,7 +509,8 @@ if(e.status & ORGAN_SPLINTED) ailments |= "Splinted" if(e.status & ORGAN_BROKEN) - ailments |= "[e.broken_description]" + var/datum/wound/fracture = e.get_wound(/datum/wound/fracture) + ailments |= "[fracture.name]" if(e.status & ORGAN_SALVED) ailments |= "Salved" if(e.status & ORGAN_BURNT) diff --git a/code/game/objects/items/devices/bodyanalyzer.dm b/code/game/objects/items/devices/bodyanalyzer.dm index 8b7a44e044d..528caae8ccb 100644 --- a/code/game/objects/items/devices/bodyanalyzer.dm +++ b/code/game/objects/items/devices/bodyanalyzer.dm @@ -218,7 +218,8 @@ if(e.status & ORGAN_SPLINTED) splint = "Splinted:" if(e.status & ORGAN_BROKEN) - AN = "[e.broken_description]:" + var/datum/wound/fracture = e.get_wound(/datum/wound/fracture) + AN = "[fracture.name]:" if(e.is_robotic()) robot = "Robotic:" if(e.open) diff --git a/code/modules/antagonists/zombie/zombie_spells.dm b/code/modules/antagonists/zombie/zombie_spells.dm index 5e28dd2cd75..406561c6cbe 100644 --- a/code/modules/antagonists/zombie/zombie_spells.dm +++ b/code/modules/antagonists/zombie/zombie_spells.dm @@ -136,8 +136,8 @@ return FALSE playsound(user.loc, 'sound/misc/moist_impact.ogg', 50, TRUE) - brain_holder.fracture() - brain_holder.broken_description = "split open" + brain_holder.fracture(fracture_name_override = "split open") + brain_holder.open = ORGAN_ORGANIC_VIOLENT_OPEN to_chat(target, SPAN_USERDANGER("Your [brain_holder.name] is violently cracked open!")) user.visible_message(SPAN_DANGER("[user] violently splits apart [target]'s [brain_holder.name]!"), "We crack apart [target]'s [brain_holder.name]!") diff --git a/code/modules/mob/living/carbon/human/human_mob.dm b/code/modules/mob/living/carbon/human/human_mob.dm index 1f7010535cc..e6959e2c806 100644 --- a/code/modules/mob/living/carbon/human/human_mob.dm +++ b/code/modules/mob/living/carbon/human/human_mob.dm @@ -1095,8 +1095,9 @@ /mob/living/carbon/human/proc/is_lung_ruptured() var/datum/organ/lungs/L = get_int_organ_datum(ORGAN_DATUM_LUNGS) - - return L?.linked_organ.is_bruised() + if(!L) + return FALSE + return L.linked_organ.get_wound(/datum/wound/ruptured_lungs) /mob/living/carbon/human/proc/rupture_lung() var/datum/organ/lungs/L = get_int_organ_datum(ORGAN_DATUM_LUNGS) @@ -2252,10 +2253,8 @@ Eyes need to have significantly high darksight to shine unless the mob has the X if(E.status & ORGAN_BURNT) analysis += SPAN_INFO("You conclude [src]'s [E.name] has been critically burned.") if(E.status & ORGAN_BROKEN) - if(!E.broken_description) - analysis += SPAN_INFO("You conclude [src]'s [E.name] is broken.") - else - analysis += SPAN_INFO("You conclude [src]'s [E.name] has a [E.broken_description].") + var/datum/wound/fracture = E.get_wound(/datum/wound/fracture) + analysis += SPAN_INFO("You conclude [src]'s [E.name] has a [fracture.name].") if(!length(analysis)) analysis += SPAN_INFO("[src] appears to be in perfect health.") to_chat(user, chat_box_healthscan(analysis.Join("
"))) diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index bf02fe98097..4d86b4ef4f8 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -12,19 +12,18 @@ var/obj/item/organ/internal/I = X I.process() - for(var/Y in bodyparts) - var/obj/item/organ/external/E = Y - E.process() + for(var/obj/item/organ/external/limb in bodyparts) + limb.process() if(!IS_HORIZONTAL(src) && world.time - l_move_time < 15) //Moving around with fractured ribs won't do you any good - if(E.is_broken() && E.internal_organs && length(E.internal_organs) && prob(15)) - var/obj/item/organ/internal/I = pick(E.internal_organs) - E.custom_pain("You feel broken bones moving in your [E.name]!") + if(limb.is_broken() && limb.internal_organs && length(limb.internal_organs) && prob(15)) + var/obj/item/organ/internal/I = pick(limb.internal_organs) + limb.custom_pain("You feel broken bones moving in your [limb.name]!") I.receive_damage(rand(3, 5)) - if((E.status & ORGAN_BURNT) && !(E.status & ORGAN_SALVED)) - E.custom_pain("You feel the skin sloughing off the burn on your [E.name]!") - E.germ_level++ + if((limb.status & ORGAN_BURNT) && !(limb.status & ORGAN_SALVED)) + limb.custom_pain("You feel the skin sloughing off the burn on your [limb.name]!") + limb.germ_level++ //handle_stance() diff --git a/code/modules/surgery/cavity_implant.dm b/code/modules/surgery/cavity_implant.dm index d95b32e6cf9..8ac1f0fc4c7 100644 --- a/code/modules/surgery/cavity_implant.dm +++ b/code/modules/surgery/cavity_implant.dm @@ -263,7 +263,7 @@ chat_message_type = MESSAGE_TYPE_COMBAT ) - affected.damage += rand(3, 5) + affected.receive_damage(rand(3, 5)) return SURGERY_STEP_INCOMPLETE diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index 70b42bc9fa2..33b7275e49b 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -267,6 +267,7 @@ flash_protect = FLASH_PROTECTION_SENSITIVE lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE emp_proof = TRUE //They are crystal artifacts, not metal + max_damage = 120 min_bruised_damage = 30 min_broken_damage = 60 actions_types = list(/datum/action/item_action/organ_action/use/eyesofgod) diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index b5833467810..f74d545b890 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -8,6 +8,17 @@ organ_datums = list(/datum/organ/lungs) +/obj/item/organ/internal/lungs/receive_damage(amount, silent) + ..() + if(damage >= min_bruised_damage && amount > 1 && !get_wound(/datum/wound/ruptured_lungs)) + wound_list += new /datum/wound/ruptured_lungs(src) + +/obj/item/organ/internal/lungs/heal_internal_damage(amount, robo_repair) + ..() + var/datum/wound/ruptured_lungs/wound = get_wound(/datum/wound/ruptured_lungs) + if(wound && damage < min_bruised_damage) + wound.cure_wound() + /obj/item/organ/internal/lungs/plasmaman name = "plasma filter" desc = "A spongy rib-shaped mass for filtering plasma from the air." diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm index 181afa95c30..06cfbeec16e 100644 --- a/code/modules/surgery/organs/organ.dm +++ b/code/modules/surgery/organs/organ.dm @@ -44,6 +44,9 @@ /// What level of upgrades are needed to detect this. Level 0 is default. 1 is hidden from health analysers. 2 is hidden from cyborg analysers, and the body scanner at level 1. 4 is the highest level the body scanner can reach. var/stealth_level = 0 + /// A list of all wounds currently on this organ + var/list/wound_list = list() + /obj/item/organ/Destroy() STOP_PROCESSING(SSobj, src) if(owner) @@ -193,6 +196,10 @@ status = ORGAN_ROBOT else status = 0 + + for(var/datum/wound/wound as anything in wound_list) + wound.cure_wound() + if(!owner) START_PROCESSING(SSobj, src) @@ -218,7 +225,7 @@ W.time_inflicted = world.time //Note: external organs have their own version of this proc -/obj/item/organ/proc/receive_damage(amount, silent = 0) +/obj/item/organ/proc/receive_damage(amount, silent = FALSE) if(tough) return damage = clamp(damage + amount, 0, max_damage) @@ -335,3 +342,13 @@ I use this so that this can be made better once the organ overhaul rolls out -- last_pain_message = msg to_chat(owner, msg) next_pain_time = world.time + 10 SECONDS + +/// Finds a wound datum. `wound_to_find` should be a typepath, and if `exact` is FALSE, it will grab subtypes aswell. +/obj/item/organ/proc/get_wound(wound_to_find, exact = FALSE) + if(!wound_to_find) + return + if(!exact) + return locate(wound_to_find) in wound_list + for(var/datum/wound/wound as anything in wound_list) + if(wound.type == wound_to_find) + return wound diff --git a/code/modules/surgery/organs/organ_datums/lung_datum.dm b/code/modules/surgery/organs/organ_datums/lung_datum.dm index 7cfd3e7ca79..82d70d39cd5 100644 --- a/code/modules/surgery/organs/organ_datums/lung_datum.dm +++ b/code/modules/surgery/organs/organ_datums/lung_datum.dm @@ -287,14 +287,9 @@ if(prob(5)) linked_organ.owner.emote("cough") //respitory tract infection - if(linked_organ.is_bruised()) - if(prob(2) && !(NO_BLOOD in linked_organ.owner.dna.species.species_traits)) - linked_organ.owner.custom_emote(EMOTE_VISIBLE, "coughs up blood!") - linked_organ.owner.bleed(1) - if(prob(4)) - linked_organ.owner.custom_emote(EMOTE_VISIBLE, "gasps for air!") - linked_organ.owner.AdjustLoseBreath(10 SECONDS) - + var/datum/wound/ruptured_lungs/wound = locate(/datum/wound/ruptured_lungs) in linked_organ.wound_list + if(wound) + wound.do_effect() /datum/organ/lungs/on_prepare_eat(obj/item/food/organ/snorgan) snorgan.reagents.add_reagent("salbutamol", 5) diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm index 98de484c068..b31876a9e1e 100644 --- a/code/modules/surgery/organs/organ_external.dm +++ b/code/modules/surgery/organs/organ_external.dm @@ -47,7 +47,6 @@ var/list/internal_organs = list() var/damage_msg = SPAN_WARNING("You feel an intense pain") - var/broken_description var/open = 0 // If the body part has an open incision from surgery. Can have values > 1. var/sabotaged = FALSE //If a prosthetic limb is emagged, it will detonate when it fails. @@ -266,7 +265,7 @@ if(status & ORGAN_BROKEN && prob(40) && brute && !owner.stat) owner.emote("scream") //getting hit on broken hand hurts - if(status & ORGAN_SPLINTED && prob((brute + burn)*4)) //taking damage to splinted limbs removes the splints + if(status & ORGAN_SPLINTED && prob((brute + burn) * 4)) //taking damage to splinted limbs removes the splints status &= ~ORGAN_SPLINTED owner.visible_message(SPAN_DANGER("The splint on [owner]'s left arm unravels from [owner.p_their()] [name]!"),SPAN_USERDANGER("The splint on your [name] unravels!")) owner.handle_splints() @@ -345,6 +344,8 @@ if(internal) status &= ~ORGAN_BROKEN + var/datum/wound/fracture = get_wound(/datum/wound/fracture) + fracture.cure_wound() perma_injury = 0 if(updating_health) @@ -414,6 +415,9 @@ This function completely restores a damaged organ to perfect condition. for(var/obj/item/organ/internal/current_organ in internal_organs) current_organ.rejuvenate() + for(var/datum/wound/wound as anything in wound_list) + wound.cure_wound() + for(var/obj/item/organ/external/EO in contents) EO.rejuvenate() @@ -782,7 +786,7 @@ Note that amputating the affected organ does in fact remove the infection from t "\The [holder.legcuffed.name] falls off you.") holder.drop_item_to_ground(holder.legcuffed) -/obj/item/organ/external/proc/fracture(silent = FALSE) +/obj/item/organ/external/proc/fracture(silent = FALSE, fracture_name_override) if(is_robotic()) return //ORGAN_BROKEN doesn't have the same meaning for robot limbs @@ -799,7 +803,11 @@ Note that amputating the affected organ does in fact remove the infection from t owner.emote("scream") status |= ORGAN_BROKEN - broken_description = pick("broken", "fracture", "hairline fracture") + var/picked_type = pick(typesof(/datum/wound/fracture)) + var/datum/wound/fracture = new picked_type(src) + if(fracture_name_override) + fracture.name = fracture_name_override + wound_list += fracture // Fractures have a chance of getting you out of restraints if(prob(25)) @@ -807,13 +815,17 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/proc/mend_fracture() if(is_robotic()) - return FALSE //ORGAN_BROKEN doesn't have the same meaning for robot limbs + return FALSE // ORGAN_BROKEN doesn't have the same meaning for robot limbs if(!(status & ORGAN_BROKEN)) return FALSE status &= ~ORGAN_BROKEN status &= ~ORGAN_SPLINTED + var/datum/wound/fracture = get_wound(/datum/wound/fracture) + if(fracture) // Not sure how we got here but it happens + fracture.cure_wound() + if(owner) owner.handle_splints() return TRUE diff --git a/code/modules/surgery/organs/organ_extractor.dm b/code/modules/surgery/organs/organ_extractor.dm index fd8397632f8..d71f3d68e5a 100644 --- a/code/modules/surgery/organs/organ_extractor.dm +++ b/code/modules/surgery/organs/organ_extractor.dm @@ -144,7 +144,8 @@ replaced.remove(C) replaced.forceMove(get_turf(src)) if(istype(storedorgan, /obj/item/organ/internal/heart) && ((/obj/item/organ/internal/cyberimp/brain/sensory_enhancer in C.internal_organs) || C.reagents.addiction_threshold_accumulated[/datum/reagent/mephedrone])) - storedorgan.damage = 40 // Damage the heart so you can't endlessly OD for cheap easily. + var/damage_to_deal = 40 - storedorgan.damage // Make it so the heart ends up with 40 damage + storedorgan.receive_damage(damage_to_deal) // Damage the heart so you can't endlessly OD for cheap easily. to_chat(user, SPAN_WARNING("CAUTION: Crystalized mephedrone has bounced off the drill into [storedorgan], causing internal damage!")) SSblackbox.record_feedback("tally", "o_implant_extract", 1, "[storedorgan.type]") storedorgan.insert(C) diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index b37b3653eb5..fb2ea36f40a 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -2,6 +2,7 @@ origin_tech = "biotech=3" force = 1 w_class = WEIGHT_CLASS_SMALL + max_damage = 60 var/slot // DO NOT add slots with matching names to different zones - it will break internal_organs_slot list! var/non_primary = 0 diff --git a/code/modules/surgery/organs/wounds.dm b/code/modules/surgery/organs/wounds.dm new file mode 100644 index 00000000000..e917f6a61bb --- /dev/null +++ b/code/modules/surgery/organs/wounds.dm @@ -0,0 +1,61 @@ +// Datumized wounds. Can be applied to either internal or external organs + +/datum/wound + /// What is this wound called + var/name = ABSTRACT_TYPE_DESC + /// Reference to the parent organ + var/obj/item/organ/parent + +/datum/wound/New(obj/item/organ/_parent) + . = ..() + if(!_parent) // So many things will runtime without a parent organ + INVOKE_ASYNC(src, GLOBAL_PROC_REF(qdel), src) + return + parent = _parent + RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(remove_and_destroy)) + +/datum/wound/Destroy(force, ...) + . = ..() + if(parent) // Someone directly deleted this + parent.wound_list -= src + parent = null + +/datum/wound/proc/remove_and_destroy() + parent.wound_list -= src + parent = null + qdel(src) + +/// A proc that will do an effect from the wound +/datum/wound/proc/do_effect() + return + +/// The cure wounds proc. Just deletes the wound by default, but can be used to apply additional effects upon cure +/datum/wound/proc/cure_wound() + SHOULD_CALL_PARENT(TRUE) + remove_and_destroy() + +// MARK: Fractures +/datum/wound/fracture + name = "hairline fracture" + +// One day these will be more than just renamed variants +/datum/wound/fracture/spiral + name = "spiral fracture" + +/datum/wound/fracture/transverse + name = "transverse fracture" + +/datum/wound/facture/linear + name = "linear fracture" + +// MARK: Ruptured lungs +/datum/wound/ruptured_lungs + name = "ruptured lungs" + +/datum/wound/ruptured_lungs/do_effect() + if(prob(2) && !(NO_BLOOD in parent.owner.dna.species.species_traits)) + parent.owner.custom_emote(EMOTE_VISIBLE, "coughs up blood!") + parent.owner.bleed(1) + if(prob(4)) + parent.owner.custom_emote(EMOTE_VISIBLE, "gasps for air!") + parent.owner.AdjustLoseBreath(10 SECONDS) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 7240d9a72d8..1704817b532 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -221,7 +221,8 @@ SPAN_NOTICE("You treat damage to [target]'s [I.name] with [tool_name]."), chat_message_type = MESSAGE_TYPE_COMBAT ) - I.damage = 0 + I.heal_internal_damage(I.max_damage) + else if(I.is_robotic() && istype (tool, /obj/item/stack/nanopaste)) user.visible_message( SPAN_NOTICE("[user] treats damage to [target]'s [I.name] with [tool_name]."), diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index e264cb27abf..8f4708d6238 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -414,17 +414,17 @@ return var/obj/item/organ/external/affected = target.get_organ(target_zone) for(var/obj/item/organ/internal/I in affected.internal_organs) - if(I && I.damage) - if(I.is_robotic()) - user.visible_message( + if(I.damage && I.is_robotic()) + user.visible_message( SPAN_NOTICE("[user] repairs [target]'s [I.name] with [tool]."), SPAN_NOTICE("You repair [target]'s [I.name] with [tool]."), - chat_message_type = MESSAGE_TYPE_COMBAT + chat_message_type = MESSAGE_TYPE_COMBAT ) - I.damage = 0 - I.surgeryize() - if(istype(tool, /obj/item/stack/nanopaste)) - I.rejuvenate() + I.heal_internal_damage(I.max_damage) + I.surgeryize() + if(istype(tool, /obj/item/stack/nanopaste)) + I.rejuvenate() + target.update_stat("internal organs repaired") return ..() diff --git a/paradise.dme b/paradise.dme index 032e3122dfe..1706170a025 100644 --- a/paradise.dme +++ b/paradise.dme @@ -3290,6 +3290,7 @@ #include "code\modules\surgery\organs\robolimbs.dm" #include "code\modules\surgery\organs\skeleton_organs.dm" #include "code\modules\surgery\organs\vocal_cords.dm" +#include "code\modules\surgery\organs\wounds.dm" #include "code\modules\surgery\organs\organ_datums\battery_datum.dm" #include "code\modules\surgery\organs\organ_datums\heart_datum.dm" #include "code\modules\surgery\organs\organ_datums\lung_datum.dm"