Files
Cody Brittain 6d6735aa7b Clarified extreme wounds may require amputation (#21756)
Also ensured critical limbs (the torso and head) cannot be ruined like
this.

<img width="876" height="448" alt="image"
src="https://github.com/user-attachments/assets/ce6cd6c6-7130-48ff-a107-1d8b8dd70b91"
/>
2026-01-29 01:53:03 +00:00

254 lines
8.0 KiB
Plaintext

/****************************************************
WOUNDS
****************************************************/
/datum/wound
/// number representing the current stage
var/current_stage = 0
/// description of the wound
var/desc = "wound" //default in case something borks
/// amount of damage this wound causes
var/damage = 0
/// how many times we've tried to lower the bleed timer, used to determine whether hemophilia should let bleed timer lower
var/bleed_timer_increment = 0
/// ticks of bleeding left.
var/bleed_timer = 0
/// Above this amount wounds you will need to treat the wound to stop bleeding, regardless of bleed_timer
var/bleed_threshold = 30
/// amount of damage the current wound type requires(less means we need to apply the next healing stage)
var/min_damage = 0
/// is the wound bandaged?
var/bandaged = FALSE
/// Similar to bandaged, but works differently
var/clamped = FALSE
/// is the wound salved?
var/salved = FALSE
/// is the wound disinfected?
var/disinfected = FALSE
var/created = 0
/// number of wounds of this type
var/amount = 1
/// amount of germs in the wound
var/germ_level = 0
/// the organ the wound is on, if on an organ
var/obj/item/organ/external/parent_organ
/* These are defined by the wound type and should not be changed */
/// stages such as "cut", "deep cut", etc.
var/list/stages
/// maximum stage at which bleeding should still happen. Beyond this stage bleeding is prevented.
var/max_bleeding_stage = 0
/// String (One of `DAMAGE_TYPE_*`). The wound's injury type.
var/damage_type = INJURY_TYPE_CUT
/// whether this wound needs a bandage/salve to heal at all
/// the maximum amount of damage that this wound can have and still autoheal
var/autoheal_cutoff = 15
// helper lists
var/list/embedded_objects
var/tmp/list/desc_list = list()
var/tmp/list/damage_list = list()
/datum/wound/New(damage, obj/item/organ/external/organ = null)
created = world.time
// reading from a list("stage" = damage) is pretty difficult, so build two separate
// lists from them instead
for(var/V in stages)
desc_list += V
damage_list += stages[V]
src.damage = damage
// initialize with the appropriate stage
src.init_stage(damage)
bleed_timer += damage
if(istype(organ))
parent_organ = organ
/datum/wound/Destroy()
if(parent_organ)
LAZYREMOVE(parent_organ.wounds, src)
parent_organ = null
LAZYCLEARLIST(embedded_objects)
. = ..()
///Returns TRUE if there's a next stage, FALSE otherwise
/datum/wound/proc/init_stage(initial_damage)
current_stage = length(stages)
while(src.current_stage > 1 && src.damage_list[current_stage-1] <= initial_damage / src.amount)
src.current_stage--
src.min_damage = damage_list[current_stage]
src.desc = desc_list[current_stage]
///The amount of damage per wound
/datum/wound/proc/wound_damage()
return src.damage / src.amount
/datum/wound/proc/can_autoheal()
if(LAZYLEN(embedded_objects))
return FALSE
return (wound_damage() <= autoheal_cutoff) ? TRUE : is_treated()
/// Checks whether the wound has been appropriately treated
/datum/wound/proc/is_treated()
if(!LAZYLEN(embedded_objects))
switch(damage_type)
if(INJURY_TYPE_BRUISE, INJURY_TYPE_CUT, INJURY_TYPE_PIERCE)
return bandaged
if(INJURY_TYPE_BURN)
return salved
/// Checks whether other other can be merged into src.
/datum/wound/proc/can_merge(datum/wound/other)
if (other.type != src.type) return FALSE
if (other.current_stage != src.current_stage) return FALSE
if (other.damage_type != src.damage_type) return FALSE
if (!(other.can_autoheal()) != !(src.can_autoheal())) return FALSE
if (!(other.bandaged) != !(src.bandaged)) return FALSE
if (!(other.clamped) != !(src.clamped)) return FALSE
if (!(other.salved) != !(src.salved)) return FALSE
if (!(other.disinfected) != !(src.disinfected)) return FALSE
if (other.parent_organ != parent_organ) return FALSE
return TRUE
/datum/wound/proc/merge_wound(datum/wound/other)
if(LAZYLEN(other.embedded_objects))
LAZYDISTINCTADD(src.embedded_objects, other.embedded_objects)
src.damage += other.damage
src.amount += other.amount
src.bleed_timer += other.bleed_timer
src.bleed_timer_increment += other.bleed_timer_increment
src.germ_level = max(src.germ_level, other.germ_level)
src.created = max(src.created, other.created) //take the newer created time
qdel(other)
/// checks if wound is considered open for external infections
/// untreated cuts (and bleeding bruises) and burns are possibly infectable, chance higher if wound is bigger
/datum/wound/proc/infection_check()
if (damage < 10) //small cuts, tiny bruises, and moderate burns shouldn't be infectable.
return FALSE
if (is_treated() && damage < 25) //anything less than a flesh wound (or equivalent) isn't infectable if treated properly
return FALSE
if (disinfected)
germ_level = 0 //reset this, just in case
return FALSE
if (damage_type == INJURY_TYPE_BRUISE && !bleeding()) //bruises only infectable if bleeding
return FALSE
var/dam_coef = round(damage/10)
switch (damage_type)
if (INJURY_TYPE_BRUISE)
return prob(dam_coef*5)
if (INJURY_TYPE_BURN)
return prob(dam_coef*10)
if (INJURY_TYPE_CUT)
return prob(dam_coef*20)
return FALSE
/datum/wound/proc/bandage()
bandaged = TRUE
/datum/wound/proc/salve()
salved = TRUE
/datum/wound/proc/disinfect()
disinfected = TRUE
/// Heal the given amount of damage, and if the given amount of damage was more than what esd needed to be healed, return how much heal was left
/datum/wound/proc/heal_damage(amount)
if(LAZYLEN(embedded_objects))
return amount // heal nothing
if (parent_organ \
&& ((damage_type == INJURY_TYPE_BURN && !(parent_organ.burn_ratio < 100 || (parent_organ.limb_flags & ORGAN_HEALS_OVERKILL)))\
|| !(parent_organ.brute_ratio < 100 || (parent_organ.limb_flags & ORGAN_HEALS_OVERKILL))))
return amount // We don't want to heal wounds on irreparable organs
var/healed_damage = min(src.damage, amount)
amount -= healed_damage
src.damage -= healed_damage
while(src.wound_damage() < damage_list[current_stage] && current_stage < length(src.desc_list))
current_stage++
desc = desc_list[current_stage]
src.min_damage = damage_list[current_stage]
// return amount of healing still leftover, can be used for other wounds
return amount
/// Opens the wound again
/datum/wound/proc/open_wound(damage)
src.damage += damage
bleed_timer += damage
while(src.current_stage > 1 && src.damage_list[current_stage-1] <= src.damage / src.amount)
src.current_stage--
src.desc = desc_list[current_stage]
src.min_damage = damage_list[current_stage]
src.bandaged = FALSE
///returns whether this wound can absorb the given amount of damage.
///this will prevent large amounts of damage being trapped in less severe wound types
/datum/wound/proc/can_worsen(damage_type, damage)
if (src.damage_type != damage_type)
return FALSE //incompatible damage types
if (src.amount > 1)
return FALSE //merged wounds cannot be worsened.
//with 1.5*, a shallow cut will be able to carry at most 30 damage,
//37.5 for a deep cut
//52.5 for a flesh wound, etc.
var/max_wound_damage = 1.5*src.damage_list[1]
if (src.damage + damage > max_wound_damage)
return FALSE
return TRUE
/datum/wound/proc/bleeding()
for(var/obj/item/thing in embedded_objects)
if(thing.w_class > WEIGHT_CLASS_SMALL)
return FALSE
if (bandaged||clamped)
return FALSE
return ((bleed_timer > 0 || wound_damage() > bleed_threshold) && current_stage <= max_bleeding_stage)
/// Called in organ_external.dm update_damages, this will update the limb's status to bleeding, and lowers the bleed_timer if applicable
/datum/wound/proc/handle_bleeding(var/mob/victim, var/obj/item/organ/external/limb)
limb.status |= ORGAN_BLEEDING
var/can_lower_bleed = TRUE
if(HAS_TRAIT(victim, TRAIT_DISABILITY_HEMOPHILIA_MAJOR)) // major will NEVER lower
can_lower_bleed = FALSE
else if(HAS_TRAIT(victim, TRAIT_DISABILITY_HEMOPHILIA) && bleed_timer_increment % 2 != 0) // basic will lower half as fast
can_lower_bleed = FALSE
if(can_lower_bleed)
bleed_timer--
bleed_timer_increment++
/datum/wound/proc/close()
return