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"