Improve, cleanup, and expand wound code (#20757)

This PR includes several changes to bring our wound code up to date with
baystation, nebula, and modern standards. Highlights include:
- The usage of integers between 0 and 100 for ratios (expressed as a
percentage), instead of 0.0 to 1.0. Small decimal values are prone to
floating point precision errors, but by moving to a percentage instead,
we can reduce the impact of this.
- The untangling of damage flags and injury flags, which were used
interchangably in the code. This could have caused issues should we
expand any of these.
- Larger embedded objects now halt bleeding in wounds, until they are
pulled out. In exchange however, embedded objects will halt healing in
wounds (it is difficult to close an open wound that currently has a rod
stuck in it)
- A lot of data, such as implanted objects and organs, are now stored in
amputated limbs.
This commit is contained in:
Cody Brittain
2025-12-19 15:22:29 -05:00
committed by GitHub
parent 034d0e4cb1
commit 96a2951608
23 changed files with 861 additions and 575 deletions
+14 -7
View File
@@ -422,7 +422,14 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
surge_damage = clamp(0, surge + surge_damage, MAXIMUM_SURGE_DAMAGE) //We want X seconds at most of hampered movement or what have you.
surge_time = world.time
/obj/item/organ/proc/removed(var/mob/living/carbon/human/target,var/mob/living/user)
/**
* Remove an organ
*
* drop_organ - if true, organ will be dropped at the loc of its former owner
* detach - if true, organ will be detached from parent. Keep false for organs
* removed together with parent, as with an amputation.
*/
/obj/item/organ/proc/removed(mob/living/carbon/human/target, mob/living/user, drop_organ = TRUE, detach = TRUE)
if(!istype(owner))
return
@@ -433,13 +440,13 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
owner.internal_organs_by_name -= null
owner.internal_organs -= src
var/obj/item/organ/external/affected = owner.get_organ(parent_organ)
if(affected) affected.internal_organs -= src
if(detach)
var/obj/item/organ/external/affected = owner.get_organ(parent_organ)
if(affected)
affected.internal_organs -= src
var/turf/T = get_turf(owner)
// to avoid brains and things like that getting put into nullspace and thus entering spatial grids
if(!isnull(T))
loc = T
if(drop_organ)
dropInto(owner.loc)
START_PROCESSING(SSprocessing, src)
rejecting = null
if (!reagents)
+159 -100
View File
@@ -84,7 +84,7 @@
var/hair_color
/// A `/list` of wounds
var/list/datum/wound/wounds = list()
var/list/wounds
/// A list of implants present in this organ
var/list/implants = list()
@@ -223,6 +223,9 @@
limb_flags &= ~ORGAN_HAS_TENDON
/obj/item/organ/external/Destroy()
. = ..()
if(parent?.children)
parent.children -= src
@@ -245,9 +248,11 @@
cached_markings = null
mob_icon = null
for(var/datum/wound/wound in wounds)
qdel(wound)
QDEL_LIST(children)
QDEL_LIST(internal_organs)
QDEL_LIST(wounds)
QDEL_LIST(implants)
infect_target_internal = null
@@ -259,13 +264,12 @@
QDEL_NULL(tendon)
. = ..()
/obj/item/organ/external/proc/invalidate_marking_cache()
cached_markings = null
/obj/item/organ/external/attack_self(var/mob/user)
if(!contents.len)
if(!length(contents))
return ..()
var/list/removable_objects = list()
for(var/obj/item/organ/external/E in (contents + src))
@@ -275,7 +279,7 @@
if(istype(I,/obj/item/organ))
continue
removable_objects |= I
if(removable_objects.len)
if(length(removable_objects))
var/obj/item/I = pick(removable_objects)
I.forceMove(get_turf(user)) //just in case something was embedded that is not an item
if(istype(I))
@@ -307,9 +311,18 @@
return
if(2)
if(istype(attacking_item, /obj/item/surgery/hemostat))
if(contents.len)
var/obj/item/removing = pick(contents)
removing.forceMove(get_turf(user.loc))
var/list/organs = get_contents_recursive()
if(length(organs))
var/obj/item/removing = pick(organs)
var/obj/item/organ/external/current_child = removing.loc
current_child.implants.Remove(removing)
current_child.internal_organs.Remove(removing)
status |= ORGAN_CUT_AWAY
removing.forceMove(get_turf(user))
if(!(user.l_hand && user.r_hand))
user.put_in_hands(removing)
user.visible_message(SPAN_DANGER("<b>[user]</b> extracts [removing] from [src] with [attacking_item]!"))
@@ -318,6 +331,20 @@
return
..()
/**
* Get a list of contents of this organ and all the child organs
*/
/obj/item/organ/external/proc/get_contents_recursive()
var/list/all_items = list()
all_items.Add(implants)
all_items.Add(internal_organs)
for(var/obj/item/organ/external/child in children)
all_items.Add(child.get_contents_recursive())
return all_items
/obj/item/organ/external/proc/dislocate(var/primary)
if(dislocated == -1)
return
@@ -358,9 +385,22 @@
owner.organs |= src
if(!species)
species = owner.species
for(var/obj/item/organ/organ in src)
for(var/obj/item/organ/organ in internal_organs)
organ.replaced(owner,src)
for(var/obj/implant in implants)
implant.forceMove(owner)
if(istype(implant, /obj/item/implant))
var/obj/item/implant/imp_device = implant
// we can't use implanted() here since it's often interactive
imp_device.imp_in = owner
imp_device.implanted = 1
for(var/obj/item/organ/external/organ in children)
organ.replaced(owner)
if(!parent && parent_organ)
parent = owner.organs_by_name[src.parent_organ]
if(parent)
@@ -369,7 +409,6 @@
parent.children.Add(src)
//Remove all stump wounds since limb is not missing anymore
for(var/datum/wound/lost_limb/W in parent.wounds)
parent.wounds -= W
qdel(W)
break
parent.update_damages()
@@ -386,12 +425,11 @@
return (BP_IS_ROBOTIC(src) || brute_dam + burn_dam + additional_damage < max_damage * 4)
/obj/item/organ/external/take_damage(brute, burn, damage_flags, used_weapon = null, list/forbidden_limbs = list(), var/silent)
brute = round(brute * brute_mod, 0.1)
burn = round(burn * burn_mod, 0.1)
if((brute <= 0) && (burn <= 0))
return 0
brute *= brute_mod
burn *= burn_mod
var/laser = (damage_flags & DAMAGE_FLAG_LASER)
var/sharp = (damage_flags & DAMAGE_FLAG_SHARP)
var/edge = (damage_flags & DAMAGE_FLAG_EDGE)
@@ -434,21 +472,22 @@
brute /= 2
burn /= 2
var/datum/wound/created_wound
var/can_cut = !BP_IS_ROBOTIC(src) && (sharp || prob(brute))
if(brute)
var/to_create = BRUISE
var/to_create = INJURY_TYPE_BRUISE
if(can_cut)
to_create = CUT
to_create = INJURY_TYPE_CUT
//need to check sharp again here so that blunt damage that was strong enough to break skin doesn't give puncture wounds
if(sharp && !edge)
to_create = PIERCE
createwound(to_create, brute)
to_create = INJURY_TYPE_PIERCE
created_wound = createwound(to_create, brute)
if(burn)
if(laser)
createwound(LASER, burn)
created_wound = createwound(INJURY_TYPE_LASER, burn)
else
createwound(DAMAGE_BURN, burn)
created_wound = createwound(INJURY_TYPE_BURN, burn)
add_pain(0.6 * burn + 0.4 * brute)
@@ -460,8 +499,9 @@
update_damages()
if(owner)
owner.updatehealth() //droplimb will call updatehealth() again if it does end up being called
update_icon()
return update_icon()
return created_wound
/obj/item/organ/external/proc/damage_internal_organs(brute, burn, damage_flags)
if(!length(internal_organs))
@@ -553,7 +593,7 @@
break
// heal brute damage
if(W.damage_type == DAMAGE_BURN)
if(W.damage_type == INJURY_TYPE_BURN)
burn = W.heal_damage(burn)
else
brute = W.heal_damage(brute)
@@ -581,7 +621,7 @@ This function completely restores a damaged organ to perfect condition.
brute_dam = 0
burn_dam = 0
germ_level = 0
wounds.Cut()
QDEL_LIST(wounds)
number_wounds = 0
// handle internal organs
@@ -600,7 +640,7 @@ This function completely restores a damaged organ to perfect condition.
owner.updatehealth()
/obj/item/organ/external/proc/createwound(var/type = CUT, var/damage)
/obj/item/organ/external/proc/createwound(type = INJURY_TYPE_CUT, damage)
if(damage <= 0 || !owner)
return
@@ -608,41 +648,41 @@ This function completely restores a damaged organ to perfect condition.
//Possibly trigger an internal wound, too.
var/local_damage = brute_dam + burn_dam + damage
var/is_burn_type_damage = (type in list(DAMAGE_BURN, LASER))
var/is_burn_type_damage = (type in list(INJURY_TYPE_BURN, INJURY_TYPE_LASER))
var/is_brute_type_damage = (type in list(INJURY_TYPE_CUT, INJURY_TYPE_PIERCE, INJURY_TYPE_BRUISE))
if(damage > (min_broken_damage / 2) && local_damage > min_broken_damage && !(status & ORGAN_ROBOT))
if(!is_burn_type_damage)
if(prob(damage) && sever_artery())
owner.custom_pain("You feel something rip in your [name]!", 25)
if(damage > (min_broken_damage / 2) && local_damage > min_broken_damage && !(status & ORGAN_ROBOT) && is_brute_type_damage)
if(prob(damage) && sever_artery())
owner.custom_pain("You feel something rip in your [name]!", 25)
if(istype(tendon) && type == CUT)
if(istype(tendon))
var/new_brute = damage
if(min_broken_damage - brute_dam > 0)
if(min_broken_damage - damage > 0)
// Only pass on damage that goes over the broken threshold
new_brute = brute_dam + damage - min_broken_damage
new_brute = damage - min_broken_damage
tendon.damage(new_brute)
//Burn damage can cause fluid loss due to blistering and cook-off
if(is_burn_type_damage && (damage > 5 || damage + burn_dam >= 15) && !BP_IS_ROBOTIC(src))
var/fluid_loss_severity
switch(type)
if(DAMAGE_BURN)
if(INJURY_TYPE_BURN)
fluid_loss_severity = FLUIDLOSS_WIDE_BURN
if(LASER)
if(INJURY_TYPE_LASER)
fluid_loss_severity = FLUIDLOSS_CONC_BURN
var/fluid_loss = (damage/(owner.maxHealth - GLOB.config.health_threshold_dead)) * DEFAULT_BLOOD_AMOUNT * fluid_loss_severity
owner.remove_blood_simple(fluid_loss)
// first check whether we can widen an existing wound
if(wounds.len > 0 && prob(max(50+(number_wounds-1)*10,90)))
if((type == CUT || type == BRUISE) && damage >= 5)
if(LAZYLEN(wounds) && prob(max(50+(number_wounds-1)*10,90)))
if((type == INJURY_TYPE_CUT || type == INJURY_TYPE_BRUISE) && damage >= 5)
//we need to make sure that the wound we are going to worsen is compatible with the type of damage...
var/list/compatible_wounds = list()
for (var/datum/wound/W in wounds)
if (W.can_worsen(type, damage))
compatible_wounds += W
if(compatible_wounds.len)
if(length(compatible_wounds))
var/datum/wound/W = pick(compatible_wounds)
W.open_wound(damage)
@@ -661,13 +701,13 @@ This function completely restores a damaged organ to perfect condition.
SPAN_WARNING("The wound on your [name] widens with a nasty ripping noise."),\
"You hear a nasty ripping noise, as if flesh is being torn apart.")
return
return W
//Creating wound
var/wound_type = get_wound_type(type, damage)
if(wound_type)
var/datum/wound/W = new wound_type(damage)
var/datum/wound/W = new wound_type(damage, src)
//Check whether we can add the wound to an existing wound
for(var/datum/wound/other in wounds)
@@ -675,13 +715,13 @@ This function completely restores a damaged organ to perfect condition.
other.merge_wound(W)
W = null // to signify that the wound was added
break
if(W)
wounds += W
LAZYADD(wounds, W)
if(bandage_level)
owner.visible_message(SPAN_WARNING("The bandages on [owner.name]'s [name] gets [is_burn_type_damage ? "burnt" : "ripped"] off!"), SPAN_WARNING("The bandages on your [name] gets [is_burn_type_damage ? "burnt" : "ripped"] off!"))
bandage_level = BANDAGE_LEVEL_NONE
owner.update_bandages()
return W
/****************************************************
PROCESSING & UPDATING
@@ -926,8 +966,8 @@ Note that amputating the affected organ does in fact remove the infection from t
var/updatehud
for(var/datum/wound/W in wounds)
// wounds can disappear after 10 minutes at the earliest
if(W.damage <= 0 && W.created + 6000 <= world.time)
wounds -= W
if(W.damage <= 0 && W.created + (10 MINUTES) <= world.time)
qdel(W)
continue
// let the GC handle the deletion of the wound
@@ -938,7 +978,7 @@ Note that amputating the affected organ does in fact remove the infection from t
var/heal_amt = 0
// if damage >= 50 AFTER treatment then it's probably too severe to heal within the timeframe of a round.
if (W.can_autoheal() && W.wound_damage() < 50)
if (W.can_autoheal() && W.wound_damage() && brute_ratio < 50 && burn_ratio < 50)
heal_amt += 0.5
//we only update wounds once in [wound_update_accuracy] ticks so have to emulate realtime
@@ -946,14 +986,19 @@ Note that amputating the affected organ does in fact remove the infection from t
//configurable regen speed woo, no-regen hardcore or instaheal hugbox, choose your destiny
heal_amt = heal_amt * GLOB.config.organ_regeneration_multiplier
// amount of healing is spread over all the wounds
heal_amt = heal_amt / (wounds.len + 1)
heal_amt = heal_amt / (LAZYLEN(wounds) + 1)
// making it look prettier on scanners
heal_amt = round(heal_amt,0.1)
W.heal_damage(heal_amt)
var/dam_type = DAMAGE_BRUTE
if (W.damage_type == INJURY_TYPE_BURN)
dam_type = DAMAGE_BURN
if(owner.can_autoheal(dam_type) && (heal_amt > 0))
W.heal_damage(heal_amt)
// Salving also helps against infection
if(W.germ_level > 0 && W.salved && prob(2))
W.disinfected = 1
W.disinfected = TRUE
W.germ_level = 0
// sync the organ's damage with its wounds
@@ -981,11 +1026,11 @@ Note that amputating the affected organ does in fact remove the infection from t
//update damage counts
for(var/datum/wound/W in wounds)
if(W.damage_type == DAMAGE_BURN)
if(W.damage_type == INJURY_TYPE_BURN)
burn_dam += W.damage
else
brute_dam += W.damage
if(W.damage_type == CUT)
if(W.damage_type == INJURY_TYPE_CUT)
cut_dam += W.damage
if(!(status & ORGAN_ROBOT) && W.bleeding() && (H && !(H.species.flags & NO_BLOOD)))
@@ -1005,9 +1050,9 @@ Note that amputating the affected organ does in fact remove the infection from t
update_damage_ratios()
/obj/item/organ/external/proc/update_damage_ratios()
var/limb_loss_threshold = max_damage
brute_ratio = brute_dam / (limb_loss_threshold * 2)
burn_ratio = burn_dam / (limb_loss_threshold * 2)
var/limb_loss_threshold = max_damage * 2
brute_ratio = Percent(brute_dam, limb_loss_threshold)
burn_ratio = Percent(burn_dam, limb_loss_threshold)
// new damage icon system
// returns just the brute/burn damage code
@@ -1046,7 +1091,7 @@ Note that amputating the affected organ does in fact remove the infection from t
dir = 2
//Handles dismemberment
/obj/item/organ/external/proc/droplimb(var/clean, var/disintegrate = DROPLIMB_EDGE, var/ignore_children = null)
/obj/item/organ/external/proc/droplimb(clean, disintegrate = DROPLIMB_EDGE, ignore_children = null)
if(!(limb_flags & ORGAN_CAN_AMPUTATE) || !owner)
return
@@ -1073,7 +1118,7 @@ Note that amputating the affected organ does in fact remove the infection from t
SPAN_DANGER("You hear the [gore_sound]."))
var/mob/living/carbon/human/victim = owner //Keep a reference for post-removed().
var/obj/item/organ/external/parent_organ = parent
var/obj/item/organ/external/original_parent = parent
if(!clean)
victim.shock_stage += min_broken_damage
@@ -1084,18 +1129,18 @@ Note that amputating the affected organ does in fact remove the infection from t
if(istype(last_owner) && !QDELETED(last_owner) && length(last_owner.organs) <= 1)
last_owner.drop_all_limbs(disintegrate) // drops the last remaining part, usually the torso, as an item
if(parent_organ)
if(original_parent)
var/datum/wound/lost_limb/W = new(src, disintegrate, clean)
if(clean)
parent_organ.wounds |= W
parent_organ.update_damages()
else
var/obj/item/organ/external/stump/stump = new(victim, 0, src)
if(status & ORGAN_ROBOT)
stump.robotize()
stump.wounds |= W
victim.organs |= stump
stump.update_damages()
var/obj/item/organ/external/damaged_organ = original_parent
if(!clean)
var/obj/item/organ/external/stump/stump = new (victim, 0, src)
stump.add_pain(max_damage)
damaged_organ = stump
if(disintegrate != DROPLIMB_BURN)
stump.sever_artery()
W.parent_organ = damaged_organ
LAZYADD(damaged_organ.wounds, W)
damaged_organ.update_damages()
post_droplimb(victim)
SEND_SIGNAL(victim, COMSIG_LIMB_LOSS)
@@ -1107,6 +1152,7 @@ Note that amputating the affected organ does in fact remove the infection from t
var/matrix/M = matrix()
M.Turn(rand(180))
src.transform = M
forceMove(get_turf(src))
if(!clean)
//Throw limb around.
if(src && isturf(loc))
@@ -1131,18 +1177,9 @@ Note that amputating the affected organ does in fact remove the infection from t
INVOKE_ASYNC(gore, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 4)
for(var/obj/item/organ/I in internal_organs)
I.removed()
victim.blood_squirt(2, loc, rand(2,5))
if(istype(loc,/turf))
INVOKE_ASYNC(I, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 4)
var/turf/Tloc = get_turf(src)
for(var/obj/item/I in src)
if(I.w_class <= 2)
qdel(I)
continue
I.forceMove(Tloc)
I.dropInto(Tloc)
INVOKE_ASYNC(I, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 4)
qdel(src)
@@ -1196,21 +1233,21 @@ Note that amputating the affected organ does in fact remove the infection from t
status &= ~ORGAN_BLEEDING
for(var/datum/wound/W in wounds)
rval |= !W.bandaged
W.bandaged = 1
W.bandage()
return rval
/obj/item/organ/external/proc/salve()
var/rval = 0
for(var/datum/wound/W in wounds)
rval |= !W.salved
W.salved = 1
W.salve()
return rval
/obj/item/organ/external/proc/disinfect()
var/rval = 0
for(var/datum/wound/W in wounds)
rval |= !W.disinfected
W.disinfected = 1
W.disinfect()
W.germ_level = 0
return rval
@@ -1353,14 +1390,14 @@ Note that amputating the affected organ does in fact remove the infection from t
return FALSE
if(get_pain() > pain_disability_threshold)
return FALSE
if(brute_ratio > 1)
if(brute_ratio >= 100)
return FALSE
if(burn_ratio > 1)
if(burn_ratio >= 100)
return FALSE
return ..()
/obj/item/organ/external/proc/is_malfunctioning()
if(BP_IS_ROBOTIC(src) && (brute_ratio + burn_ratio) >= 0.3 && prob(brute_dam + burn_dam) || (surge_damage > (MAXIMUM_SURGE_DAMAGE * 0.25)))
if(BP_IS_ROBOTIC(src) && (brute_ratio + burn_ratio) >= 10 && prob(brute_dam + burn_dam) || (surge_damage > (MAXIMUM_SURGE_DAMAGE * 0.25)))
return TRUE
if(robotize_type)
var/datum/robolimb/R = GLOB.all_robolimbs[robotize_type]
@@ -1369,7 +1406,7 @@ Note that amputating the affected organ does in fact remove the infection from t
else
return FALSE
/obj/item/organ/external/proc/embed(var/obj/item/W, var/silent = 0, var/supplied_message)
/obj/item/organ/external/proc/embed(obj/item/W, silent = FALSE, supplied_message, datum/wound/supplied_wound)
if(!owner || loc != owner)
return
if(!W.canremove || is_robot_module(W)) //Modules and augments cannot embed
@@ -1383,6 +1420,16 @@ Note that amputating the affected organ does in fact remove the infection from t
owner.visible_message(SPAN_DANGER("\The [W] sticks in [owner]'s wound!"),
SPAN_DANGER("\The [W] sticks in your wound!"))
if(supplied_wound)
for(var/datum/wound/wound in wounds)
if ((wound.damage_type == INJURY_TYPE_CUT || wound.damage_type == INJURY_TYPE_PIERCE) && wound.damage >= W.w_class * 5)
supplied_wound = wound
break
if(!supplied_wound || (W in supplied_wound.embedded_objects)) // Just in case.
return
LAZYADD(supplied_wound.embedded_objects, W)
implants += W
owner.embedded_flag = 1
add_verb(owner, /mob/proc/yank_out_object)
@@ -1390,14 +1437,13 @@ Note that amputating the affected organ does in fact remove the infection from t
if(ismob(W.loc))
var/mob/living/H = W.loc
H.drop_from_inventory(W,owner)
else
W.forceMove(owner)
W.forceMove(owner)
/obj/item/organ/external/removed(var/mob/living/user, var/ignore_children = 0)
if(!owner)
return
var/is_robotic = status & ORGAN_ROBOT
var/is_robotic = BP_IS_ROBOTIC(src)
var/mob/living/carbon/human/victim = owner
..(null, user)
@@ -1407,24 +1453,30 @@ Note that amputating the affected organ does in fact remove the infection from t
for(var/atom/movable/implant in implants)
//large items and non-item objs fall to the floor, everything else stays
var/obj/item/I = implant
if(istype(I) && I.w_class < 3)
implant.forceMove(get_turf(victim.loc))
else
if(istype(I) && I.w_class < WEIGHT_CLASS_NORMAL)
implant.forceMove(src)
implants.Cut()
// let actual implants still inside know they're no longer implanted
if(istype(I, /obj/item/implant))
var/obj/item/implant/imp_device = I
imp_device.removed()
else
implants.Remove(implant)
implant.forceMove(get_turf(src))
// Attached organs also fly off.
if(!ignore_children)
for(var/obj/item/organ/external/O in children)
O.removed()
if(O)
if(!QDELETED(O))
O.forceMove(src)
for(var/obj/item/I in O.contents)
I.forceMove(src)
// if we didn't lose the organ we still want it as a child
children += O
O.parent = src
// Grab all the internal giblets too.
for(var/obj/item/organ/organ in internal_organs)
organ.removed()
organ.removed(user, FALSE, FALSE) // Organ stays inside and connected
organ.forceMove(src)
// Remove parent references
@@ -1434,6 +1486,7 @@ Note that amputating the affected organ does in fact remove the infection from t
release_restraints(victim)
victim.organs -= src
victim.organs_by_name[limb_name] = null // Remove from owner's vars.
victim.organs_by_name -= organ_tag
//Robotic limbs explode if sabotaged.
if(is_robotic && sabotaged)
@@ -1497,14 +1550,20 @@ Note that amputating the affected organ does in fact remove the infection from t
wound_descriptors["an incision"] = 1
for(var/datum/wound/W in wounds)
var/this_wound_desc = W.desc
if(W.damage_type == DAMAGE_BURN && W.salved) this_wound_desc = "salved [this_wound_desc]"
if(W.bleeding()) this_wound_desc = "bleeding [this_wound_desc]"
if(W.bandaged == 1)
if(W.damage_type == DAMAGE_BURN && W.salved)
this_wound_desc = "salved [this_wound_desc]"
if(W.bleeding())
if(W.wound_damage() > W.bleed_threshold)
this_wound_desc = "<b>bleeding</b> [this_wound_desc]"
else
this_wound_desc = "bleeding [this_wound_desc]"
else if(W.bandaged == TRUE)
this_wound_desc = "bandaged [this_wound_desc]"
else if(W.bandaged != 0)
this_wound_desc = "[W.bandaged] [this_wound_desc]"
if(W.germ_level > 600) this_wound_desc = "badly infected [this_wound_desc]"
else if(W.germ_level > 330) this_wound_desc = "lightly infected [this_wound_desc]"
if(W.germ_level > 600)
this_wound_desc = "badly infected [this_wound_desc]"
else if(W.germ_level > 330)
this_wound_desc = "lightly infected [this_wound_desc]"
if(wound_descriptors[this_wound_desc])
wound_descriptors[this_wound_desc] += W.amount
else
@@ -1520,7 +1579,7 @@ Note that amputating the affected organ does in fact remove the infection from t
var/list/bits = list()
for(var/obj/item/organ/internal/organ in internal_organs)
bits += organ.get_visible_state()
if(bits.len)
if(length(bits))
wound_descriptors["[english_list(bits)] visible in the wounds"] = 1
if(wound_descriptors.len)
-1
View File
@@ -11,7 +11,6 @@
amputation_point = limb.amputation_point
joint = limb.joint
parent_organ = limb.parent_organ
wounds = limb.wounds
. = ..(mapload, internal)
if(istype(limb))
max_damage = limb.max_damage
@@ -108,7 +108,7 @@
var/list/brute_wounds = list()
for(var/wound in O.wounds)
var/datum/wound/W = wound
if(W.damage_type in list(CUT, BRUISE, PIERCE))
if(W.damage_type in list(INJURY_TYPE_CUT, INJURY_TYPE_BRUISE, INJURY_TYPE_PIERCE))
brute_wounds += W
for(var/wound in brute_wounds)
var/datum/wound/W = wound
-410
View File
@@ -1,410 +0,0 @@
/****************************************************
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 = 0
/// Similar to bandaged, but works differently
var/clamped = 0
/// is the wound salved?
var/salved = 0
/// is the wound disinfected?
var/disinfected = 0
var/created = 0
/// number of wounds of this type
var/amount = 1
/// amount of germs in the wound
var/germ_level = 0
/* 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
/// one of CUT, BRUISE, PIERCE, BURN
var/damage_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/tmp/list/desc_list = list()
var/tmp/list/damage_list = list()
/datum/wound/New(var/damage)
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
// returns 1 if there's a next stage, 0 otherwise
/datum/wound/proc/init_stage(var/initial_damage)
current_stage = stages.len
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(src.wound_damage() <= autoheal_cutoff)
return 1
return is_treated()
// checks whether the wound has been appropriately treated
/datum/wound/proc/is_treated()
switch(damage_type)
if(BRUISE, CUT, PIERCE)
return bandaged
if(DAMAGE_BURN)
return salved
// Checks whether other other can be merged into src.
/datum/wound/proc/can_merge(var/datum/wound/other)
if (other.type != src.type) return 0
if (other.current_stage != src.current_stage) return 0
if (other.damage_type != src.damage_type) return 0
if (!(other.can_autoheal()) != !(src.can_autoheal())) return 0
if (!(other.bandaged) != !(src.bandaged)) return 0
if (!(other.clamped) != !(src.clamped)) return 0
if (!(other.salved) != !(src.salved)) return 0
if (!(other.disinfected) != !(src.disinfected)) return 0
//if (other.germ_level != src.germ_level) return 0
return 1
/datum/wound/proc/merge_wound(var/datum/wound/other)
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 = (src.germ_level + other.germ_level)/2
src.created = max(src.created, other.created) //take the newer created time
// 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 0
if (is_treated() && damage < 25) //anything less than a flesh wound (or equivalent) isn't infectable if treated properly
return 0
if (disinfected)
germ_level = 0 //reset this, just in case
return 0
if (damage_type == BRUISE && !bleeding()) //bruises only infectable if bleeding
return 0
var/dam_coef = round(damage/10)
switch (damage_type)
if (BRUISE)
return prob(dam_coef*5)
if (DAMAGE_BURN)
return prob(dam_coef*10)
if (CUT)
return prob(dam_coef*20)
return 0
/datum/wound/proc/bandage(var/examine_desc)
if(!examine_desc)
bandaged = 1
else bandaged = examine_desc
/datum/wound/proc/salve()
salved = 1
/datum/wound/proc/disinfect()
disinfected = 1
// heal the given amount of damage, and if the given amount of damage was more
// than what needed to be healed, return how much heal was left
// set @heals_internal to also heal internal organ damage
/datum/wound/proc/heal_damage(amount, heals_internal = 0)
var/healed_damage = min(src.damage, amount)
amount -= healed_damage
src.damage -= healed_damage
while(src.wound_damage() < damage_list[current_stage] && current_stage < src.desc_list.len)
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 0 //incompatible damage types
if (src.amount > 1)
return 0
//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 0
return 1
/datum/wound/proc/bleeding()
if (current_stage > max_bleeding_stage)
return 0
if (bandaged||clamped)
return 0
if (bleed_timer <= 0 && wound_damage() <= bleed_threshold)
return 0 //Bleed timer has run out. Once a wound is big enough though, you'll need a bandage to stop it
return 1
/// 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++
/** WOUND DEFINITIONS **/
//Note that the MINIMUM damage before a wound can be applied should correspond to
//the damage amount for the stage with the same name as the wound.
//e.g. /datum/wound/cut/deep should only be applied for 15 damage and up,
//because in it's stages list, "deep cut" = 15.
/proc/get_wound_type(var/type = CUT, var/damage)
switch(type)
if(CUT)
switch(damage)
if(70 to INFINITY)
return /datum/wound/cut/massive
if(60 to 70)
return /datum/wound/cut/gaping_big
if(50 to 60)
return /datum/wound/cut/gaping
if(25 to 50)
return /datum/wound/cut/flesh
if(15 to 25)
return /datum/wound/cut/deep
if(0 to 15)
return /datum/wound/cut/small
if(PIERCE)
switch(damage)
if(60 to INFINITY)
return /datum/wound/puncture/massive
if(50 to 60)
return /datum/wound/puncture/gaping_big
if(30 to 50)
return /datum/wound/puncture/gaping
if(15 to 30)
return /datum/wound/puncture/flesh
if(0 to 15)
return /datum/wound/puncture/small
if(BRUISE)
return /datum/wound/bruise
if(DAMAGE_BURN, LASER)
switch(damage)
if(50 to INFINITY)
return /datum/wound/burn/carbonised
if(40 to 50)
return /datum/wound/burn/deep
if(30 to 40)
return /datum/wound/burn/severe
if(15 to 30)
return /datum/wound/burn/large
if(0 to 15)
return /datum/wound/burn/moderate
return null //no wound
/** CUTS **/
/datum/wound/cut
bleed_threshold = 5
damage_type = CUT
/datum/wound/cut/small
// link wound descriptions to amounts of damage
// Minor cuts have max_bleeding_stage set to the stage that bears the wound type's name.
// The major cut types have the max_bleeding_stage set to the clot stage (which is accordingly given the "blood soaked" descriptor).
max_bleeding_stage = 3
stages = list("ugly ripped cut" = 20, "ripped cut" = 10, "cut" = 5, "healing cut" = 2, "small scab" = 0)
/datum/wound/cut/deep
max_bleeding_stage = 3
stages = list("ugly deep ripped cut" = 25, "deep ripped cut" = 20, "deep cut" = 15, "clotted cut" = 8, "scab" = 2, "fresh skin" = 0)
/datum/wound/cut/flesh
max_bleeding_stage = 4
stages = list("ugly ripped flesh wound" = 35, "ugly flesh wound" = 30, "flesh wound" = 25, "blood soaked clot" = 15, "large scab" = 5, "fresh skin" = 0)
/datum/wound/cut/gaping
max_bleeding_stage = 3
stages = list("gaping wound" = 50, "large blood soaked clot" = 25, "blood soaked clot" = 15, "small angry scar" = 5, "small straight scar" = 0)
/datum/wound/cut/gaping_big
max_bleeding_stage = 3
stages = list("big gaping wound" = 60, "healing gaping wound" = 40, "large blood soaked clot" = 25, "large angry scar" = 10, "large straight scar" = 0)
/datum/wound/cut/massive
max_bleeding_stage = 3
stages = list("massive wound" = 70, "massive healing wound" = 50, "massive blood soaked clot" = 25, "massive angry scar" = 10, "massive jagged scar" = 0)
/** PUNCTURES **/
/datum/wound/puncture
bleed_threshold = 10
damage_type = PIERCE
/datum/wound/puncture/can_worsen(damage_type, damage)
return 0
/datum/wound/puncture/small
max_bleeding_stage = 2
stages = list("puncture" = 5, "healing puncture" = 2, "small scab" = 0)
/datum/wound/puncture/flesh
max_bleeding_stage = 2
stages = list("puncture wound" = 15, "blood soaked clot" = 5, "large scab" = 2, "small round scar" = 0)
/datum/wound/puncture/gaping
max_bleeding_stage = 3
stages = list("gaping hole" = 30, "large blood soaked clot" = 15, "blood soaked clot" = 10, "small angry scar" = 5, "small round scar" = 0)
/datum/wound/puncture/gaping_big
max_bleeding_stage = 3
stages = list("big gaping hole" = 50, "healing gaping hole" = 20, "large blood soaked clot" = 15, "large angry scar" = 10, "large round scar" = 0)
/datum/wound/puncture/massive
max_bleeding_stage = 3
stages = list("massive wound" = 60, "massive healing wound" = 30, "massive blood soaked clot" = 25, "massive angry scar" = 10, "massive jagged scar" = 0)
/** BRUISES **/
/datum/wound/bruise
stages = list("monumental bruise" = 80, "huge bruise" = 50, "large bruise" = 30,
"moderate bruise" = 20, "small bruise" = 10, "tiny bruise" = 5)
bleed_threshold = 20
max_bleeding_stage = 3 //only large bruise and above can bleed.
autoheal_cutoff = 30
damage_type = BRUISE
/** BURNS **/
/datum/wound/burn
damage_type = DAMAGE_BURN
max_bleeding_stage = 0
/datum/wound/burn/bleeding()
return 0
/datum/wound/burn/moderate
stages = list("ripped burn" = 10, "moderate burn" = 5, "healing moderate burn" = 2, "fresh skin" = 0)
/datum/wound/burn/large
stages = list("ripped large burn" = 20, "large burn" = 15, "healing large burn" = 5, "fresh skin" = 0)
/datum/wound/burn/severe
stages = list("ripped severe burn" = 35, "severe burn" = 30, "healing severe burn" = 10, "burn scar" = 0)
/datum/wound/burn/deep
stages = list("ripped deep burn" = 45, "deep burn" = 40, "healing deep burn" = 15, "large burn scar" = 0)
/datum/wound/burn/carbonised
stages = list("carbonised area" = 50, "healing carbonised area" = 20, "massive burn scar" = 0)
/** EXTERNAL ORGAN LOSS **/
/datum/wound/lost_limb
/datum/wound/lost_limb/New(var/obj/item/organ/external/lost_limb, var/losstype, var/clean)
var/damage_amt = lost_limb.max_damage
if(clean) damage_amt /= 2
switch(losstype)
if(DROPLIMB_EDGE, DROPLIMB_BLUNT)
damage_type = CUT
max_bleeding_stage = 3 //clotted stump and above can bleed.
stages = list(
"ripped stump" = damage_amt*1.3,
"bloody stump" = damage_amt,
"clotted stump" = damage_amt*0.5,
"scarred stump" = 0
)
if(DROPLIMB_BURN)
damage_type = DAMAGE_BURN
stages = list(
"ripped charred stump" = damage_amt*1.3,
"charred stump" = damage_amt,
"scarred stump" = damage_amt*0.5,
"scarred stump" = 0
)
..(damage_amt)
/datum/wound/lost_limb/can_merge(var/datum/wound/other)
return 0 //cannot be merged
+254
View File
@@ -0,0 +1,254 @@
/****************************************************
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)
if (damage_type == INJURY_TYPE_BURN && !(parent_organ.burn_ratio < 100))
return amount // We don't want to heal wounds on irreparable organs
else if(!(parent_organ.brute_ratio < 100))
return amount
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
+291
View File
@@ -0,0 +1,291 @@
/** WOUND DEFINITIONS **/
//Note that the MINIMUM damage before a wound can be applied should correspond to
//the damage amount for the stage with the same name as the wound.
//e.g. /datum/wound/cut/deep should only be applied for 15 damage and up,
//because in it's stages list, "deep cut" = 15.
/proc/get_wound_type(type = INJURY_TYPE_CUT, damage)
switch(type)
if(INJURY_TYPE_CUT)
switch(damage)
if(70 to INFINITY)
return /datum/wound/cut/massive
if(60 to 70)
return /datum/wound/cut/gaping_big
if(50 to 60)
return /datum/wound/cut/gaping
if(25 to 50)
return /datum/wound/cut/flesh
if(15 to 25)
return /datum/wound/cut/deep
if(0 to 15)
return /datum/wound/cut/small
if(INJURY_TYPE_PIERCE)
switch(damage)
if(60 to INFINITY)
return /datum/wound/puncture/massive
if(50 to 60)
return /datum/wound/puncture/gaping_big
if(30 to 50)
return /datum/wound/puncture/gaping
if(15 to 30)
return /datum/wound/puncture/flesh
if(0 to 15)
return /datum/wound/puncture/small
if(INJURY_TYPE_BRUISE)
return /datum/wound/bruise
if(INJURY_TYPE_BURN, INJURY_TYPE_LASER)
switch(damage)
if(50 to INFINITY)
return /datum/wound/burn/carbonised
if(40 to 50)
return /datum/wound/burn/deep
if(30 to 40)
return /datum/wound/burn/severe
if(15 to 30)
return /datum/wound/burn/large
if(0 to 15)
return /datum/wound/burn/moderate
return null //no wound
/** CUTS **/
/datum/wound/cut
bleed_threshold = 5
damage_type = INJURY_TYPE_CUT
/datum/wound/cut/bandage()
..()
if(!autoheal_cutoff)
autoheal_cutoff = initial(autoheal_cutoff)
/datum/wound/cut/close()
current_stage = max_bleeding_stage + 1
desc = desc_list[current_stage]
min_damage = damage_list[current_stage]
if(damage > min_damage)
heal_damage(damage-min_damage)
/datum/wound/cut/small
// link wound descriptions to amounts of damage
// Minor cuts have max_bleeding_stage set to the stage that bears the wound type's name.
// The major cut types have the max_bleeding_stage set to the clot stage (which is accordingly given the "blood soaked" descriptor).
max_bleeding_stage = 3
stages = list(
"ugly ripped cut" = 20,
"ripped cut" = 10,
"cut" = 5,
"healing cut" = 2,
"small scab" = 0
)
/datum/wound/cut/deep
max_bleeding_stage = 3
stages = list(
"ugly deep ripped cut" = 25,
"deep ripped cut" = 20,
"deep cut" = 15,
"clotted cut" = 8,
"scab" = 2,
"fresh skin" = 0
)
/datum/wound/cut/flesh
max_bleeding_stage = 4
stages = list(
"ugly ripped flesh wound" = 35,
"ugly flesh wound" = 30,
"flesh wound" = 25,
"blood soaked clot" = 15,
"large scab" = 5,
"fresh skin" = 0
)
/datum/wound/cut/gaping
max_bleeding_stage = 3
stages = list(
"gaping wound" = 50,
"large blood soaked clot" = 25,
"blood soaked clot" = 15,
"small angry scar" = 5,
"small straight scar" = 0
)
/datum/wound/cut/gaping_big
max_bleeding_stage = 3
stages = list(
"big gaping wound" = 60,
"healing gaping wound" = 40,
"large blood soaked clot" = 25,
"large angry scar" = 10,
"large straight scar" = 0
)
/datum/wound/cut/massive
max_bleeding_stage = 3
stages = list(
"massive wound" = 70,
"massive healing wound" = 50,
"massive blood soaked clot" = 25,
"massive angry scar" = 10,
"massive jagged scar" = 0
)
/** PUNCTURES **/
/datum/wound/puncture
bleed_threshold = 10
damage_type = INJURY_TYPE_PIERCE
/datum/wound/puncture/can_worsen(damage_type, damage)
return FALSE //puncture wounds cannot be enlargened
/datum/wound/puncture/small
max_bleeding_stage = 2
stages = list(
"puncture" = 5,
"healing puncture" = 2,
"small scab" = 0
)
/datum/wound/puncture/flesh
max_bleeding_stage = 2
stages = list(
"puncture wound" = 15,
"blood soaked clot" = 5,
"large scab" = 2,
"small round scar" = 0
)
/datum/wound/puncture/gaping
max_bleeding_stage = 3
stages = list(
"gaping hole" = 30,
"large blood soaked clot" = 15,
"blood soaked clot" = 10,
"small angry scar" = 5,
"small round scar" = 0
)
/datum/wound/puncture/gaping_big
max_bleeding_stage = 3
stages = list(
"big gaping hole" = 50,
"healing gaping hole" = 20,
"large blood soaked clot" = 15,
"large angry scar" = 10,
"large round scar" = 0
)
/datum/wound/puncture/massive
max_bleeding_stage = 3
stages = list(
"massive wound" = 60,
"massive healing wound" = 30,
"massive blood soaked clot" = 25,
"massive angry scar" = 10,
"massive jagged scar" = 0
)
/** BRUISES **/
/datum/wound/bruise
stages = list(
"monumental bruise" = 80,
"huge bruise" = 50,
"large bruise" = 30,
"moderate bruise" = 20,
"small bruise" = 10,
"tiny bruise" = 5
)
bleed_threshold = 20
max_bleeding_stage = 3 //only large bruise and above can bleed.
autoheal_cutoff = 30
damage_type = INJURY_TYPE_BRUISE
/** BURNS **/
/datum/wound/burn
damage_type = INJURY_TYPE_BURN
max_bleeding_stage = 0
/datum/wound/burn/bleeding()
return FALSE
/datum/wound/burn/moderate
stages = list(
"ripped burn" = 10,
"moderate burn" = 5,
"healing moderate burn" = 2,
"fresh skin" = 0
)
/datum/wound/burn/large
stages = list(
"ripped large burn" = 20,
"large burn" = 15,
"healing large burn" = 5,
"fresh skin" = 0
)
/datum/wound/burn/severe
stages = list(
"ripped severe burn" = 35,
"severe burn" = 30,
"healing severe burn" = 10,
"burn scar" = 0
)
/datum/wound/burn/deep
stages = list(
"ripped deep burn" = 45,
"deep burn" = 40,
"healing deep burn" = 15,
"large burn scar" = 0
)
/datum/wound/burn/carbonised
stages = list(
"carbonised area" = 50,
"healing carbonised area" = 20,
"massive burn scar" = 0
)
/** EXTERNAL ORGAN LOSS **/
/datum/wound/lost_limb
var/limb_tag
/datum/wound/lost_limb/New(obj/item/organ/external/lost_limb, losstype, clean)
var/damage_amt = lost_limb.max_damage
if(clean) damage_amt /= 2
limb_tag = lost_limb.organ_tag
switch(losstype)
if(DROPLIMB_EDGE, DROPLIMB_BLUNT)
damage_type = INJURY_TYPE_CUT
if(BP_IS_ROBOTIC(lost_limb))
max_bleeding_stage = -1 //Robotic stumps do not bleed
bleed_threshold = INFINITY
stages = list("mangled robotic socket" = 0)
else
max_bleeding_stage = 3 //clotted stump and above can bleed.
stages = list(
"ripped stump" = damage_amt*1.3,
"bloody stump" = damage_amt,
"clotted stump" = damage_amt*0.5,
"scarred stump" = 0
)
if(DROPLIMB_BURN)
damage_type = INJURY_TYPE_BURN
stages = list(
"ripped charred stump" = damage_amt*1.3,
"charred stump" = damage_amt,
"scarred stump" = damage_amt*0.5,
"scarred stump" = 0
)
..(damage_amt)
/datum/wound/lost_limb/can_merge(var/datum/wound/other)
return 0 //cannot be merged