From 7714fcfaba441e0384b9785fd9b0a4ddf9950557 Mon Sep 17 00:00:00 2001
From: timothyteakettle <59849408+timothyteakettle@users.noreply.github.com>
Date: Sat, 18 Jul 2020 01:26:38 +0100
Subject: [PATCH] more changes
---
code/__DEFINES/mobs.dm | 1 +
code/datums/wounds/_wounds.dm | 320 ++++++++++++
code/datums/wounds/cuts.dm | 9 +-
code/game/objects/objs.dm | 5 +
.../mob/living/carbon/carbon_defense.dm | 10 +-
.../mob/living/carbon/carbon_defines.dm | 5 +
.../modules/mob/living/carbon/damage_procs.dm | 21 +-
code/modules/mob/living/carbon/examine.dm | 40 ++
.../mob/living/carbon/human/damage_procs.dm | 6 +-
.../mob/living/carbon/human/examine.dm | 69 ++-
code/modules/mob/living/carbon/human/human.dm | 11 +
.../mob/living/carbon/human/human_defense.dm | 57 ++-
.../mob/living/carbon/human/human_defines.dm | 1 -
.../mob/living/carbon/human/human_helpers.dm | 19 +
.../mob/living/carbon/human/species.dm | 16 +-
.../carbon/human/species_types/humans.dm | 2 +-
.../human/species_types/lizardpeople.dm | 2 +-
.../carbon/human/species_types/mushpeople.dm | 2 +-
.../carbon/human/species_types/podpeople.dm | 2 +-
.../human/species_types/shadowpeople.dm | 2 +-
.../carbon/human/species_types/zombies.dm | 8 +-
code/modules/mob/living/carbon/life.dm | 6 +
.../mob/living/carbon/monkey/punpun.dm | 2 +
code/modules/mob/living/damage_procs.dm | 4 +-
code/modules/mob/living/life.dm | 5 +
code/modules/mob/living/living.dm | 2 +-
code/modules/mob/living/living_defense.dm | 4 +-
.../mob/living/silicon/damage_procs.dm | 2 +-
.../mob/living/silicon/robot/robot_modules.dm | 2 +
.../mob/living/simple_animal/hostile/bear.dm | 15 +-
.../hostile/megafauna/hierophant.dm | 2 +-
.../simple_animal/hostile/megafauna/legion.dm | 2 +
.../hostile/megafauna/wendigo.dm | 2 +-
.../living/simple_animal/hostile/syndicate.dm | 3 +
.../mob/living/simple_animal/simple_animal.dm | 7 +
.../ammunition/ballistic/shotgun.dm | 12 +
.../projectiles/ammunition/energy/laser.dm | 5 +
code/modules/projectiles/gun.dm | 5 +
code/modules/projectiles/guns/energy/laser.dm | 6 +
code/modules/projectiles/projectile.dm | 4 +
code/modules/projectiles/projectile/beams.dm | 18 +
.../modules/projectiles/projectile/bullets.dm | 1 +
.../projectile/bullets/revolver.dm | 3 +
.../projectiles/projectile/bullets/shotgun.dm | 15 +-
.../chemistry/reagents/medicine_reagents.dm | 29 +-
.../chemistry/reagents/other_reagents.dm | 33 ++
.../chemistry/reagents/toxin_reagents.dm | 16 +-
.../reagents/reagent_containers/hypospray.dm | 13 +-
...utolathe_designs_medical_and_dinnerware.dm | 18 +
.../research/techweb/nodes/medical_nodes.dm | 2 +-
code/modules/spells/spell_types/bloodcrawl.dm | 5 +
code/modules/spells/spell_types/shapeshift.dm | 4 +-
.../bodyparts/{bodyparts.dm => _bodyparts.dm} | 484 +++++++-----------
.../surgery/bodyparts/dismemberment.dm | 23 +-
code/modules/surgery/bodyparts/head.dm | 4 +
code/modules/surgery/bodyparts/helpers.dm | 10 +
code/modules/surgery/bodyparts/parts.dm | 307 +++++++++++
code/modules/surgery/bone_mending.dm | 142 +++++
code/modules/surgery/burn_dressing.dm | 108 ++++
code/modules/surgery/coronary_bypass.dm | 9 +-
.../surgery/experimental_dissection.dm | 4 +-
code/modules/surgery/healing.dm | 2 +-
code/modules/surgery/organic_steps.dm | 14 +-
code/modules/surgery/organs/vocal_cords.dm | 5 +-
code/modules/surgery/surgery.dm | 7 +
tgstation.dme | 6 +-
66 files changed, 1587 insertions(+), 393 deletions(-)
create mode 100644 code/datums/wounds/_wounds.dm
rename code/modules/surgery/bodyparts/{bodyparts.dm => _bodyparts.dm} (72%)
create mode 100644 code/modules/surgery/bodyparts/parts.dm
create mode 100644 code/modules/surgery/bone_mending.dm
create mode 100644 code/modules/surgery/burn_dressing.dm
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index b1f7cc84f8..ddb901f24c 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -308,6 +308,7 @@
#define GRAB_PIXEL_SHIFT_NECK 16
#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return;
+#define INTERACTING_WITH(X, Y) (Y in X.do_afters)
/// Field of vision defines.
#define FOV_90_DEGREES 90
diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm
new file mode 100644
index 0000000000..d70a4ebdb2
--- /dev/null
+++ b/code/datums/wounds/_wounds.dm
@@ -0,0 +1,320 @@
+/*
+ Wounds are specific medical complications that can arise and be applied to (currently) carbons, with a focus on humans. All of the code for and related to this is heavily WIP,
+ and the documentation will be slanted towards explaining what each part/piece is leading up to, until such a time as I finish the core implementations. The original design doc
+ can be found at https://hackmd.io/@Ryll/r1lb4SOwU
+
+ Wounds are datums that operate like a mix of diseases, brain traumas, and components, and are applied to a /obj/item/bodypart (preferably attached to a carbon) when they take large spikes of damage
+ or under other certain conditions (thrown hard against a wall, sustained exposure to plasma fire, etc). Wounds are categorized by the three following criteria:
+ 1. Severity: Either MODERATE, SEVERE, or CRITICAL. See the hackmd for more details
+ 2. Viable zones: What body parts the wound is applicable to. Generic wounds like broken bones and severe burns can apply to every zone, but you may want to add special wounds for certain limbs
+ like a twisted ankle for legs only, or open air exposure of the organs for particularly gruesome chest wounds. Wounds should be able to function for every zone they are marked viable for.
+ 3. Damage type: Currently either BRUTE or BURN. Again, see the hackmd for a breakdown of my plans for each type.
+
+ When a body part suffers enough damage to get a wound, the severity (determined by a roll or something, worse damage leading to worse wounds), affected limb, and damage type sustained are factored into
+ deciding what specific wound will be applied. I'd like to have a few different types of wounds for at least some of the choices, but I'm just doing rough generals for now. Expect polishing
+*/
+
+/datum/wound
+ /// What it's named
+ var/name = "ouchie"
+ /// The description shown on the scanners
+ var/desc = ""
+ /// The basic treatment suggested by health analyzers
+ var/treat_text = ""
+ /// What the limb looks like on a cursory examine
+ var/examine_desc = "is badly hurt"
+
+ /// needed for "your arm has a compound fracture" vs "your arm has some third degree burns"
+ var/a_or_from = "a"
+ /// The visible message when this happens
+ var/occur_text = ""
+ /// This sound will be played upon the wound being applied
+ var/sound_effect
+
+ /// Either WOUND_SEVERITY_TRIVIAL (meme wounds like stubbed toe), WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, or WOUND_SEVERITY_CRITICAL (or maybe WOUND_SEVERITY_LOSS)
+ var/severity = WOUND_SEVERITY_MODERATE
+ /// The list of wounds it belongs in, WOUND_LIST_BONE, WOUND_LIST_CUT, or WOUND_LIST_BURN
+ var/wound_type
+
+ /// What body zones can we affect
+ var/list/viable_zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
+ /// Who owns the body part that we're wounding
+ var/mob/living/carbon/victim = null
+ /// If we only work on organics (everything right now)
+ var/organic_only = TRUE
+ /// The bodypart we're parented to
+ var/obj/item/bodypart/limb = null
+
+ /// Specific items such as bandages or sutures that can try directly treating this wound
+ var/list/treatable_by
+ /// Specific items such as bandages or sutures that can try directly treating this wound only if the user has the victim in an aggressive grab or higher
+ var/list/treatable_by_grabbed
+ /// Tools with the specified tool flag will also be able to try directly treating this wound
+ var/treatable_tool
+ /// Set to TRUE if we don't give a shit about the patient's comfort and are allowed to just use any random sharp thing on this wound. Will require an aggressive grab or more to perform
+ var/treatable_sharp
+ /// How long it will take to treat this wound with a standard effective tool, assuming it doesn't need surgery
+ var/base_treat_time = 5 SECONDS
+
+ /// Using this limb in a do_after interaction will multiply the length by this duration (arms)
+ var/interaction_efficiency_penalty = 1
+ /// Incoming damage on this limb will be multiplied by this, to simulate tenderness and vulnerability (mostly burns).
+ var/damage_mulitplier_penalty = 1
+ /// If set and this wound is applied to a leg, we take this many deciseconds extra per step on this leg
+ var/limp_slowdown
+ /// How much we're contributing to this limb's bleed_rate
+ var/blood_flow
+
+ /// The minimum we need to roll on [/obj/item/bodypart/proc/check_wounding()] to begin suffering this wound, see check_wounding_mods() for more
+ var/threshold_minimum
+ /// How much having this wound will add to all future check_wounding() rolls on this limb, to allow progression to worse injuries with repeated damage
+ var/threshold_penalty
+ /// If we need to process each life tick
+ var/processes = FALSE
+
+ /// If TRUE and an item that can treat multiple different types of coexisting wounds (gauze can be used to splint broken bones, staunch bleeding, and cover burns), we get first dibs if we come up first for it, then become nonpriority.
+ /// Otherwise, if no untreated wound claims the item, we cycle through the non priority wounds and pick a random one who can use that item.
+ var/treat_priority = FALSE
+
+ /// If having this wound makes currently makes the parent bodypart unusable
+ var/disabling
+
+ /// What status effect we assign on application
+ var/status_effect_type
+ /// The status effect we're linked to
+ var/datum/status_effect/linked_status_effect
+ /// If we're operating on this wound and it gets healed, we'll nix the surgery too
+ var/datum/surgery/attached_surgery
+ /// if you're a lazy git and just throw them in cryo, the wound will go away after accumulating severity * 25 power
+ var/cryo_progress
+
+ /// What kind of scars this wound will create description wise once healed
+ var/list/scarring_descriptions = list("general disfigurement")
+ /// If we've already tried scarring while removing (since remove_wound calls qdel, and qdel calls remove wound, .....) TODO: make this cleaner
+ var/already_scarred = FALSE
+ /// If we forced this wound through badmin smite, we won't count it towards the round totals
+ var/from_smite
+
+/datum/wound/Destroy()
+ if(attached_surgery)
+ QDEL_NULL(attached_surgery)
+ if(limb?.wounds && (src in limb.wounds)) // destroy can call remove_wound() and remove_wound() calls qdel, so we check to make sure there's anything to remove first
+ remove_wound()
+ limb = null
+ victim = null
+ return ..()
+
+/**
+ * apply_wound() is used once a wound type is instantiated to assign it to a bodypart, and actually come into play.
+ *
+ *
+ * Arguments:
+ * * L: The bodypart we're wounding, we don't care about the person, we can get them through the limb
+ * * silent: Not actually necessary I don't think, was originally used for demoting wounds so they wouldn't make new messages, but I believe old_wound took over that, I may remove this shortly
+ * * old_wound: If our new wound is a replacement for one of the same time (promotion or demotion), we can reference the old one just before it's removed to copy over necessary vars
+ * * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented)
+ */
+/datum/wound/proc/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited = FALSE)
+ if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner))
+ qdel(src)
+ return
+
+ if(ishuman(L.owner))
+ var/mob/living/carbon/human/H = L.owner
+ if(organic_only && ((NOBLOOD in H.dna.species.species_traits) || !L.is_organic_limb()))
+ qdel(src)
+ return
+
+ // we accept promotions and demotions, but no point in redundancy. This should have already been checked wherever the wound was rolled and applied for (see: bodypart damage code), but we do an extra check
+ // in case we ever directly add wounds
+ for(var/i in L.wounds)
+ var/datum/wound/preexisting_wound = i
+ if((preexisting_wound.type == type) && (preexisting_wound != old_wound))
+ qdel(src)
+ return
+
+ victim = L.owner
+ limb = L
+ LAZYADD(victim.all_wounds, src)
+ LAZYADD(limb.wounds, src)
+ limb.update_wounds()
+ if(status_effect_type)
+ linked_status_effect = victim.apply_status_effect(status_effect_type, src)
+ SEND_SIGNAL(victim, COMSIG_CARBON_GAIN_WOUND, src, limb)
+ if(!victim.alerts["wound"]) // only one alert is shared between all of the wounds
+ victim.throw_alert("wound", /obj/screen/alert/status_effect/wound)
+
+ var/demoted
+ if(old_wound)
+ demoted = (severity <= old_wound.severity)
+
+ if(severity == WOUND_SEVERITY_TRIVIAL)
+ return
+
+ if(!(silent || demoted))
+ var/msg = "[victim]'s [limb.name] [occur_text]!"
+ var/vis_dist = COMBAT_MESSAGE_RANGE
+
+ if(severity != WOUND_SEVERITY_MODERATE)
+ msg = "[msg]"
+ vis_dist = DEFAULT_MESSAGE_RANGE
+
+ victim.visible_message(msg, "Your [limb.name] [occur_text]!", vision_distance = vis_dist)
+ if(sound_effect)
+ playsound(L.owner, sound_effect, 60 + 20 * severity, TRUE)
+
+ if(!demoted)
+ wound_injury(old_wound)
+ second_wind()
+
+/// Remove the wound from whatever it's afflicting, and cleans up whateverstatus effects it had or modifiers it had on interaction times. ignore_limb is used for detachments where we only want to forget the victim
+/datum/wound/proc/remove_wound(ignore_limb, replaced = FALSE)
+ //TODO: have better way to tell if we're getting removed without replacement (full heal) scar stuff
+ if(limb && !already_scarred && !replaced)
+ already_scarred = TRUE
+ var/datum/scar/new_scar = new
+ new_scar.generate(limb, src)
+ if(victim)
+ LAZYREMOVE(victim.all_wounds, src)
+ if(!victim.all_wounds)
+ victim.clear_alert("wound")
+ SEND_SIGNAL(victim, COMSIG_CARBON_LOSE_WOUND, src, limb)
+ if(limb && !ignore_limb)
+ LAZYREMOVE(limb.wounds, src)
+ limb.update_wounds()
+
+/**
+ * replace_wound() is used when you want to replace the current wound with a new wound, presumably of the same category, just of a different severity (either up or down counts)
+ *
+ * This proc actually instantiates the new wound based off the specific type path passed, then returns the new instantiated wound datum.
+ *
+ * Arguments:
+ * * new_type- The TYPE PATH of the wound you want to replace this, like /datum/wound/brute/cut/severe
+ * * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented)
+ */
+/datum/wound/proc/replace_wound(new_type, smited = FALSE)
+ var/datum/wound/new_wound = new new_type
+ already_scarred = TRUE
+ remove_wound(replaced=TRUE)
+ new_wound.apply_wound(limb, old_wound = src, smited = smited)
+ qdel(src)
+ return new_wound
+
+/// The immediate negative effects faced as a result of the wound
+/datum/wound/proc/wound_injury(datum/wound/old_wound = null)
+ return
+
+/// Additional beneficial effects when the wound is gained, in case you want to give a temporary boost to allow the victim to try an escape or last stand
+/datum/wound/proc/second_wind()
+ if(HAS_TRAIT(victim, TRAIT_NOMETABOLISM))
+ return
+
+ switch(severity)
+ if(WOUND_SEVERITY_MODERATE)
+ victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_MODERATE)
+ if(WOUND_SEVERITY_SEVERE)
+ victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_SEVERE)
+ if(WOUND_SEVERITY_CRITICAL)
+ victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_CRITICAL)
+
+/**
+ * try_treating() is an intercept run from [/mob/living/carbon/attackby()] right after surgeries but before anything else. Return TRUE here if the item is something that is relevant to treatment to take over the interaction.
+ *
+ * This proc leads into [/datum/wound/proc/treat()] and probably shouldn't be added onto in children types. You can specify what items or tools you want to be intercepted
+ * with var/list/treatable_by and var/treatable_tool, then if an item fulfills one of those requirements and our wound claims it first, it goes over to treat() and treat_self().
+ *
+ * Arguments:
+ * * I: The item we're trying to use
+ * * user: The mob trying to use it on us
+ */
+/datum/wound/proc/try_treating(obj/item/I, mob/user)
+ // first we weed out if we're not dealing with our wound's bodypart, or if it might be an attack
+ if(!I || limb.body_zone != user.zone_selected || (I.force && user.a_intent != INTENT_HELP))
+ return FALSE
+
+ var/allowed = FALSE
+
+ // check if we have a valid treatable tool (or, if cauteries are allowed, if we have something hot)
+ if((I.tool_behaviour == treatable_tool) || (treatable_tool == TOOL_CAUTERY && I.get_temperature()))
+ allowed = TRUE
+ // failing that, see if we're aggro grabbing them and if we have an item that works for aggro grabs only
+ else if(user.pulling == victim && user.grab_state >= GRAB_AGGRESSIVE && check_grab_treatments(I, user))
+ allowed = TRUE
+ // failing THAT, we check if we have a generally allowed item
+ else
+ for(var/allowed_type in treatable_by)
+ if(istype(I, allowed_type))
+ allowed = TRUE
+ break
+
+ // if none of those apply, we return false to avoid interrupting
+ if(!allowed)
+ return FALSE
+
+ // now that we've determined we have a valid attempt at treating, we can stomp on their dreams if we're already interacting with the patient
+ if(INTERACTING_WITH(user, victim))
+ to_chat(user, "You're already interacting with [victim]!")
+ return TRUE
+
+ // lastly, treat them
+ treat(I, user)
+ return TRUE
+
+/// Return TRUE if we have an item that can only be used while aggro grabbed (unhanded aggro grab treatments go in [/datum/wound/proc/try_handling()]). Treatment is still is handled in [/datum/wound/proc/treat()]
+/datum/wound/proc/check_grab_treatments(obj/item/I, mob/user)
+ return FALSE
+
+/// Like try_treating() but for unhanded interactions from humans, used by joint dislocations for manual bodypart chiropractice for example.
+/datum/wound/proc/try_handling(mob/living/carbon/human/user)
+ return FALSE
+
+/// Someone is using something that might be used for treating the wound on this limb
+/datum/wound/proc/treat(obj/item/I, mob/user)
+ return
+
+/// If var/processing is TRUE, this is run on each life tick
+/datum/wound/proc/handle_process()
+ return
+
+/// For use in do_after callback checks
+/datum/wound/proc/still_exists()
+ return (!QDELETED(src) && limb)
+
+/// When our parent bodypart is hurt
+/datum/wound/proc/receive_damage(wounding_type, wounding_dmg, wound_bonus)
+ return
+
+/// Called from cryoxadone and pyroxadone when they're proc'ing. Wounds will slowly be fixed separately from other methods when these are in effect. crappy name but eh
+/datum/wound/proc/on_xadone(power)
+ cryo_progress += power
+ if(cryo_progress > 33 * severity)
+ qdel(src)
+
+/// Called when we're crushed in an airlock or firedoor, for one of the improvised joint dislocation fixes
+/datum/wound/proc/crush()
+ return
+
+/**
+ * get_examine_description() is used in carbon/examine and human/examine to show the status of this wound. Useful if you need to show some status like the wound being splinted or bandaged.
+ *
+ * Return the full string line you want to show, note that we're already dealing with the 'warning' span at this point, and that \n is already appended for you in the place this is called from
+ *
+ * Arguments:
+ * * mob/user: The user examining the wound's owner, if that matters
+ */
+/datum/wound/proc/get_examine_description(mob/user)
+ return "[victim.p_their(TRUE)] [limb.name] [examine_desc]!"
+
+/datum/wound/proc/get_scanner_description(mob/user)
+ return "Type: [name]\nSeverity: [severity_text()]\nDescription: [desc]\nRecommended Treatment: [treat_text]"
+
+/datum/wound/proc/severity_text()
+ switch(severity)
+ if(WOUND_SEVERITY_TRIVIAL)
+ return "Trivial"
+ if(WOUND_SEVERITY_MODERATE)
+ return "Moderate"
+ if(WOUND_SEVERITY_SEVERE)
+ return "Severe"
+ if(WOUND_SEVERITY_CRITICAL)
+ return "Critical"
diff --git a/code/datums/wounds/cuts.dm b/code/datums/wounds/cuts.dm
index 61a4b8a1ea..5f9d754238 100644
--- a/code/datums/wounds/cuts.dm
+++ b/code/datums/wounds/cuts.dm
@@ -179,8 +179,7 @@
/datum/wound/brute/cut/proc/tool_cauterize(obj/item/I, mob/user)
var/self_penalty_mult = (user == victim ? 1.5 : 1)
user.visible_message("[user] begins cauterizing [victim]'s [limb.name] with [I]...", "You begin cauterizing [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...")
- var/time_mod = user.mind?.get_skill_modifier(/datum/skill/healing, SKILL_SPEED_MODIFIER) || 1
- if(!do_after(user, base_treat_time * time_mod * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
+ if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
return
user.visible_message("[user] cauterizes some of the bleeding on [victim].", "You cauterize some of the bleeding on [victim].")
@@ -200,8 +199,7 @@
/datum/wound/brute/cut/proc/suture(obj/item/stack/medical/suture/I, mob/user)
var/self_penalty_mult = (user == victim ? 1.4 : 1)
user.visible_message("[user] begins stitching [victim]'s [limb.name] with [I]...", "You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...")
- var/time_mod = user.mind?.get_skill_modifier(/datum/skill/healing, SKILL_SPEED_MODIFIER) || 1
- if(!do_after(user, base_treat_time * time_mod * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
+ if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
return
user.visible_message("[user] stitches up some of the bleeding on [victim].", "You stitch up some of the bleeding on [user == victim ? "yourself" : "[victim]"].")
var/blood_sutured = I.stop_bleeding / self_penalty_mult
@@ -224,8 +222,7 @@
user.visible_message("[user] begins rewrapping the cuts on [victim]'s [limb.name] with [I]...", "You begin rewrapping the cuts on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...")
else
user.visible_message("[user] begins wrapping the cuts on [victim]'s [limb.name] with [I]...", "You begin wrapping the cuts on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...")
- var/time_mod = user.mind?.get_skill_modifier(/datum/skill/healing, SKILL_SPEED_MODIFIER) || 1
- if(!do_after(user, base_treat_time * time_mod, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
+ if(!do_after(user, base_treat_time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists)))
return
user.visible_message("[user] applies [I] to [victim]'s [limb.name].", "You bandage some of the bleeding on [user == victim ? "yourself" : "[victim]"].")
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index ba3eba9bd3..c54c49b7e5 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -10,6 +10,11 @@
var/damtype = BRUTE
var/force = 0
+ /// How good a given object is at causing wounds on carbons. Higher values equal better shots at creating serious wounds.
+ var/wound_bonus = 0
+ /// If this attacks a human with no wound armor on the affected body part, add this to the wound mod. Some attacks may be significantly worse at wounding if there's even a slight layer of armor to absorb some of it vs bare flesh
+ var/bare_wound_bonus = 0
+
var/datum/armor/armor
var/obj_integrity //defaults to max_integrity
var/max_integrity = 500
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index bfa9c40a7c..ea42c4df2f 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -79,7 +79,7 @@
send_item_attack_message(I, user, affecting.name, totitemdamage)
I.do_stagger_action(src, user, totitemdamage)
if(I.force)
- apply_damage(totitemdamage, I.damtype, affecting) //CIT CHANGE - replaces I.force with totitemdamage
+ apply_damage(totitemdamage, I.damtype, affecting, wound_bonus = I.wound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.get_sharpness()) //CIT CHANGE - replaces I.force with totitemdamage
if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC)
var/basebloodychance = affecting.brute_dam + totitemdamage
if(prob(basebloodychance))
@@ -132,6 +132,9 @@
if(S.next_step(user, user.a_intent))
return TRUE
+ for(var/datum/wound/W in all_wounds)
+ if(W.try_handling(user))
+ return 1
/mob/living/carbon/attack_paw(mob/living/carbon/monkey/M)
@@ -467,3 +470,8 @@
if (BP.status < 2)
amount += BP.burn_dam
return amount
+
+/mob/living/carbon/proc/get_interaction_efficiency(zone)
+ var/obj/item/bodypart/limb = get_bodypart(zone)
+ if(!limb)
+ return
diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm
index 15413f76d4..cce3fb6a9b 100644
--- a/code/modules/mob/living/carbon/carbon_defines.dm
+++ b/code/modules/mob/living/carbon/carbon_defines.dm
@@ -64,3 +64,8 @@
var/drunkenness = 0 //Overall drunkenness - check handle_alcohol() in life.dm for effects
var/tackling = FALSE //Whether or not we are tackling, this will prevent the knock into effects for carbons
+
+ /// All of the wounds a carbon has afflicted throughout their limbs
+ var/list/all_wounds
+ /// All of the scars a carbon has afflicted throughout their limbs
+ var/list/all_scars
diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm
index becff250b9..61b14b2cce 100644
--- a/code/modules/mob/living/carbon/damage_procs.dm
+++ b/code/modules/mob/living/carbon/damage_procs.dm
@@ -1,6 +1,6 @@
-/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE)
+/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE)
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone)
var/hit_percent = (100-blocked)/100
if(!forced && hit_percent <= 0)
@@ -21,13 +21,13 @@
switch(damagetype)
if(BRUTE)
if(BP)
- if(damage > 0 ? BP.receive_damage(damage_amount) : BP.heal_damage(abs(damage_amount), 0))
+ if(BP.receive_damage(damage_amount, 0, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness))
update_damage_overlays()
else //no bodypart, we deal damage with a more general method.
adjustBruteLoss(damage_amount, forced = forced)
if(BURN)
if(BP)
- if(damage > 0 ? BP.receive_damage(0, damage_amount) : BP.heal_damage(0, abs(damage_amount)))
+ if(BP.receive_damage(0, damage_amount, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness))
update_damage_overlays()
else
adjustFireLoss(damage_amount, forced = forced)
@@ -202,12 +202,12 @@
//Damages ONE bodypart randomly selected from damagable ones.
//It automatically updates damage overlays if necessary
//It automatically updates health status
-/mob/living/carbon/take_bodypart_damage(brute = 0, burn = 0, stamina = 0)
+/mob/living/carbon/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, required_status, check_armor = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE)
var/list/obj/item/bodypart/parts = get_damageable_bodyparts()
if(!parts.len)
return
var/obj/item/bodypart/picked = pick(parts)
- if(picked.receive_damage(brute, burn, stamina))
+ if(picked.receive_damage(brute, burn, stamina,check_armor ? run_armor_check(picked, (brute ? "melee" : burn ? "fire" : stamina ? "bullet" : null)) : FALSE, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness))
update_damage_overlays()
//Heal MANY bodyparts, in random order
@@ -253,7 +253,7 @@
var/stamina_was = picked.stamina_dam
- update |= picked.receive_damage(brute_per_part, burn_per_part, stamina_per_part, FALSE)
+ update |= picked.receive_damage(brute_per_part, burn_per_part, stamina_per_part, FALSE, required_status, wound_bonus = CANT_WOUND) // disabling wounds from these for now cuz your entire body snapping cause your heart stopped would suck
brute = round(brute - (picked.brute_dam - brute_was), DAMAGE_PRECISION)
burn = round(burn - (picked.burn_dam - burn_was), DAMAGE_PRECISION)
@@ -265,3 +265,12 @@
if(update)
update_damage_overlays()
update_stamina()
+
+///Returns a list of bodyparts with wounds (in case someone has a wound on an otherwise fully healed limb)
+/mob/living/carbon/proc/get_wounded_bodyparts(brute = FALSE, burn = FALSE, stamina = FALSE, status)
+ var/list/obj/item/bodypart/parts = list()
+ for(var/X in bodyparts)
+ var/obj/item/bodypart/BP = X
+ if(LAZYLEN(BP.wounds))
+ parts += BP
+ return parts
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index b07aab30d1..be046a3713 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -44,6 +44,8 @@
msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!\n"
else
msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!\n"
+ for(var/datum/wound/W in BP.wounds)
+ msg += "[W.get_examine_description(user)]\n"
for(var/X in disabled)
var/obj/item/bodypart/BP = X
@@ -99,6 +101,22 @@
if(pulledby && pulledby.grab_state)
msg += "[t_He] [t_is] restrained by [pulledby]'s grip.\n"
+ var/scar_severity = 0
+ for(var/i in all_scars)
+ var/datum/scar/S = i
+ if(S.is_visible(user))
+ scar_severity += S.severity
+
+ switch(scar_severity)
+ if(1 to 2)
+ msg += "[t_He] [t_has] visible scarring, you can look again to take a closer look...\n"
+ if(3 to 4)
+ msg += "[t_He] [t_has] several bad scars, you can look again to take a closer look...\n"
+ if(5 to 6)
+ msg += "[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...\n"
+ if(7 to INFINITY)
+ msg += "[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...\n"
+
if(msg.len)
. += "[msg.Join("")]"
@@ -135,3 +153,25 @@
. += "[t_He] look[p_s()] ecstatic."
SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .)
. += "*---------*"
+
+/mob/living/carbon/examine_more(mob/user)
+ if(!all_scars)
+ return ..()
+
+ var/list/visible_scars
+ for(var/i in all_scars)
+ var/datum/scar/S = i
+ if(S.is_visible(user))
+ LAZYADD(visible_scars, S)
+
+ if(!visible_scars)
+ return ..()
+
+ var/msg = list("You examine [src] closer, and note the following...")
+ for(var/i in visible_scars)
+ var/datum/scar/S = i
+ var/scar_text = S.get_examine_description(user)
+ if(scar_text)
+ msg += "[scar_text]"
+
+ return msg
diff --git a/code/modules/mob/living/carbon/human/damage_procs.dm b/code/modules/mob/living/carbon/human/damage_procs.dm
index 651fec8415..5cd00b7e6a 100644
--- a/code/modules/mob/living/carbon/human/damage_procs.dm
+++ b/code/modules/mob/living/carbon/human/damage_procs.dm
@@ -1,5 +1,5 @@
+// depending on the species, it will run the corresponding apply_damage code there
+/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE)
+ return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, forced, spread_damage, wound_bonus, bare_wound_bonus, sharpness)
-/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage)
- // depending on the species, it will run the corresponding apply_damage code there
- return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, forced, spread_damage)
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index d32184edb5..77137c7239 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -163,16 +163,18 @@
msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!\n"
else
msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!\n"
+ for(var/datum/wound/W in BP.wounds)
+ msg += "[W.get_examine_description(user)]\n"
for(var/X in disabled)
var/obj/item/bodypart/BP = X
var/damage_text
- if(!(BP.get_damage(include_stamina = FALSE) >= BP.max_damage)) //Stamina is disabling the limb
- damage_text = "limp and lifeless"
- else
- damage_text = (BP.brute_dam >= BP.burn_dam) ? BP.heavy_brute_msg : BP.heavy_burn_msg
- msg += "[capitalize(t_his)] [BP.name] is [damage_text]!\n"
-
+ if(BP.is_disabled() != BODYPART_DISABLED_WOUND) // skip if it's disabled by a wound (cuz we'll be able to see the bone sticking out!)
+ if(!(BP.get_damage(include_stamina = FALSE) >= BP.max_damage)) //we don't care if it's stamcritted
+ damage_text = "limp and lifeless"
+ else
+ damage_text = (BP.brute_dam >= BP.burn_dam) ? BP.heavy_brute_msg : BP.heavy_burn_msg
+ msg += "[capitalize(t_his)] [BP.name] is [damage_text]!\n"
//stores missing limbs
var/l_limbs_missing = 0
var/r_limbs_missing = 0
@@ -246,16 +248,40 @@
if(DISGUST_LEVEL_DISGUSTED to INFINITY)
msg += "[t_He] look[p_s()] extremely disgusted.\n"
- if(ShowAsPaleExamine())
- msg += "[t_He] [t_has] pale skin.\n"
+ var/apparent_blood_volume = blood_volume
+ if(skin_tone == "albino")
+ apparent_blood_volume -= 150 // enough to knock you down one tier
+ switch(apparent_blood_volume)
+ if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
+ msg += "[t_He] [t_has] pale skin.\n"
+ if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
+ msg += "[t_He] look[p_s()] like pale death.\n"
+ if(-INFINITY to BLOOD_VOLUME_BAD)
+ msg += "[t_He] resemble[p_s()] a crushed, empty juice pouch.\n"
if(bleedsuppress)
- msg += "[t_He] [t_is] bandaged with something.\n"
- else if(bleed_rate)
- if(bleed_rate >= 8) //8 is the rate at which heparin causes you to bleed
- msg += "[t_He] [t_is] bleeding uncontrollably!\n"
- else
- msg += "[t_He] [t_is] bleeding!\n"
+ msg += "[t_He] [t_is] embued with a power that defies bleeding.\n" // only statues and highlander sword can cause this so whatever
+ else if(is_bleeding())
+ var/list/obj/item/bodypart/bleeding_limbs = list()
+
+ for(var/i in bodyparts)
+ var/obj/item/bodypart/BP = i
+ if(BP.get_bleed_rate())
+ bleeding_limbs += BP
+
+ var/num_bleeds = LAZYLEN(bleeding_limbs)
+ var/bleed_text = "[t_He] [t_is] bleeding from [t_his]"
+ switch(num_bleeds)
+ if(1 to 2)
+ bleed_text += " [bleeding_limbs[1].name][num_bleeds == 2 ? " and [bleeding_limbs[2].name]" : ""]"
+ if(3 to INFINITY)
+ for(var/i in 1 to (num_bleeds - 1))
+ var/obj/item/bodypart/BP = bleeding_limbs[i]
+ bleed_text += " [BP.name],"
+ bleed_text += " and [bleeding_limbs[num_bleeds].name]"
+
+ bleed_text += "!\n"
+ msg += bleed_text
if(reagents.has_reagent(/datum/reagent/teslium))
msg += "[t_He] [t_is] emitting a gentle blue glow!\n"
@@ -331,6 +357,21 @@
if(digitalcamo)
msg += "[t_He] [t_is] moving [t_his] body in an unnatural and blatantly inhuman manner.\n"
+ var/scar_severity = 0
+ for(var/i in all_scars)
+ var/datum/scar/S = i
+ if(S.is_visible(user))
+ scar_severity += S.severity
+
+ switch(scar_severity)
+ if(1 to 2)
+ msg += "[t_He] [t_has] visible scarring, you can look again to take a closer look...\n"
+ if(3 to 4)
+ msg += "[t_He] [t_has] several bad scars, you can look again to take a closer look...\n"
+ if(5 to 6)
+ msg += "[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...\n"
+ if(7 to INFINITY)
+ msg += "[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...\n"
if (length(msg))
. += "[msg.Join("")]"
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index a2d53f6f0a..033b3bf42e 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1055,10 +1055,21 @@
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
+
/mob/living/carbon/human/do_after_coefficent()
. = ..()
. *= physiology.do_after_speed
+/mob/living/carbon/human/is_bleeding()
+ if(NOBLOOD in dna.species.species_traits || bleedsuppress)
+ return FALSE
+ return ..()
+
+/mob/living/carbon/human/get_total_bleed_rate()
+ if(NOBLOOD in dna.species.species_traits)
+ return FALSE
+ return ..()
+
/mob/living/carbon/human/species
var/race = null
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 63296021ff..c592301b96 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -34,6 +34,19 @@
protection += physiology.armor.getRating(d_type)
return protection
+///Get all the clothing on a specific body part
+/mob/living/carbon/human/proc/clothingonpart(obj/item/bodypart/def_zone)
+ var/list/covering_part = list()
+ var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform, back, gloves, shoes, belt, s_store, glasses, ears, wear_id, wear_neck) //Everything but pockets. Pockets are l_store and r_store. (if pockets were allowed, putting something armored, gloves or hats for example, would double up on the armor)
+ for(var/bp in body_parts)
+ if(!bp)
+ continue
+ if(bp && istype(bp , /obj/item/clothing))
+ var/obj/item/clothing/C = bp
+ if(C.body_parts_covered & def_zone.body_part)
+ covering_part += C
+ return covering_part
+
/mob/living/carbon/human/on_hit(obj/item/projectile/P)
if(dna && dna.species)
dna.species.on_hit(P, src)
@@ -106,7 +119,7 @@
visible_message("[user] [hulk_verb_continous] [src]!", \
"[user] [hulk_verb_continous] you!", null, COMBAT_MESSAGE_RANGE, null, user,
"You [hulk_verb_simple] [src]!")
- adjustBruteLoss(15)
+ apply_damage(15, BRUTE, wound_bonus=10)
return 1
/mob/living/carbon/human/attack_hand(mob/user)
@@ -217,16 +230,17 @@
if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST)
var/armor = run_armor_check(affecting, "melee", armour_penetration = M.armour_penetration)
- apply_damage(damage, M.melee_damage_type, affecting, armor)
-
+ apply_damage(damage, M.melee_damage_type, affecting, armor, wound_bonus = M.wound_bonus, bare_wound_bonus = M.bare_wound_bonus, sharpness = M.sharpness)
/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M)
. = ..()
if(!.) //unsuccessful slime attack
return
var/damage = rand(5, 25)
+ var/wound_mod = -45 // 25^1.4=90, 90-45=45
if(M.is_adult)
damage = rand(10, 35)
+ wound_mod = -90 // 35^1.4=145, 145-90=55
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
if(!dam_zone) //Dismemberment successful
@@ -236,7 +250,7 @@
if(!affecting)
affecting = get_bodypart(BODY_ZONE_CHEST)
var/armor_block = run_armor_check(affecting, "melee")
- apply_damage(damage, BRUTE, affecting, armor_block)
+ apply_damage(damage, BRUTE, affecting, armor_block, wound_bonus=wound_mod)
/mob/living/carbon/human/mech_melee_attack(obj/mecha/M)
if(M.occupant.a_intent == INTENT_HARM)
@@ -614,6 +628,20 @@
no_damage = TRUE
to_send += "\t Your [LB.name] [HAS_TRAIT(src, TRAIT_SELF_AWARE) ? "has" : "is"] [status].\n"
+ for(var/thing in LB.wounds)
+ var/datum/wound/W = thing
+ var/msg
+ switch(W.severity)
+ if(WOUND_SEVERITY_TRIVIAL)
+ msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]."
+ if(WOUND_SEVERITY_MODERATE)
+ msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!"
+ if(WOUND_SEVERITY_SEVERE)
+ msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!"
+ if(WOUND_SEVERITY_CRITICAL)
+ msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!!"
+ to_chat(src, msg)
+
for(var/obj/item/I in LB.embedded_objects)
if(I.isEmbedHarmless())
to_chat(src, "\t There is \a [I] stuck to your [LB.name]!")
@@ -623,8 +651,25 @@
for(var/t in missing)
to_send += "Your [parse_zone(t)] is missing!\n"
- if(bleed_rate)
- to_send += "You are bleeding!\n"
+ if(is_bleeding())
+ var/list/obj/item/bodypart/bleeding_limbs = list()
+ for(var/i in bodyparts)
+ var/obj/item/bodypart/BP = i
+ if(BP.get_bleed_rate())
+ bleeding_limbs += BP
+
+ var/num_bleeds = LAZYLEN(bleeding_limbs)
+ var/bleed_text = "You are bleeding from your"
+ switch(num_bleeds)
+ if(1 to 2)
+ bleed_text += " [bleeding_limbs[1].name][num_bleeds == 2 ? " and [bleeding_limbs[2].name]" : ""]"
+ if(3 to INFINITY)
+ for(var/i in 1 to (num_bleeds - 1))
+ var/obj/item/bodypart/BP = bleeding_limbs[i]
+ bleed_text += " [BP.name],"
+ bleed_text += " and [bleeding_limbs[num_bleeds].name]"
+ bleed_text += "!"
+ to_chat(src, bleed_text)
if(getStaminaLoss())
if(getStaminaLoss() > 30)
to_send += "You're completely exhausted.\n"
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index e7be540eb9..71005334f8 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -49,7 +49,6 @@
var/special_voice = "" // For changing our voice. Used by a symptom.
- var/bleed_rate = 0 //how much are we bleeding
var/bleedsuppress = 0 //for stopping bloodloss, eventually this will be limb-based like bleeding
var/blood_state = BLOOD_STATE_NOT_BLOODY
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 7c256f5367..8672c0e83d 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -151,3 +151,22 @@
if(blood_dna.len)
last_bloodtype = blood_dna[blood_dna[blood_dna.len]]//trust me this works
last_blood_DNA = blood_dna[blood_dna.len]*/
+
+/// For use formatting all of the scars this human has for saving for persistent scarring
+/mob/living/carbon/human/proc/format_scars()
+ if(!all_scars)
+ return
+ var/scars = ""
+ for(var/i in all_scars)
+ var/datum/scar/S = i
+ scars += "[S.format()];"
+ return scars
+
+/// Takes a single scar from the persistent scar loader and recreates it from the saved data
+/mob/living/carbon/human/proc/load_scar(scar_line)
+ var/list/scar_data = splittext(scar_line, "|")
+ if(LAZYLEN(scar_data) != 4)
+ return // invalid, should delete
+ var/obj/item/bodypart/BP = get_bodypart("[scar_data[SCAR_SAVE_ZONE]]")
+ var/datum/scar/S = new
+ return S.load(BP, scar_data[SCAR_SAVE_DESC], scar_data[SCAR_SAVE_PRECISE_LOCATION], text2num(scar_data[SCAR_SAVE_SEVERITY]))
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index efff888c41..50f29a866f 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1728,9 +1728,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/armor_block = H.run_armor_check(affecting, "melee", "Your armor has protected your [hit_area].", "Your armor has softened a hit to your [hit_area].",I.armour_penetration)
armor_block = min(90,armor_block) //cap damage reduction at 90%
var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords)
+ var/Iwound_bonus = I.wound_bonus
+
+ // this way, you can't wound with a surgical tool on help intent if they have a surgery active and are laying down, so a misclick with a circular saw on the wrong limb doesn't bleed them dry (they still get hit tho)
+ if((I.item_flags & SURGICAL_TOOL) && user.a_intent == INTENT_HELP && (H.mobility_flags & ~MOBILITY_STAND) && (LAZYLEN(H.surgeries) > 0))
+ Iwound_bonus = CANT_WOUND
var/weakness = H.check_weakness(I, user)
- apply_damage(totitemdamage * weakness, I.damtype, def_zone, armor_block, H) //CIT CHANGE - replaces I.force with totitemdamage
+ apply_damage(totitemdamage * weakness, I.damtype, def_zone, armor_block, H, wound_bonus = Iwound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.get_sharpness())
+
H.send_item_attack_message(I, user, hit_area, totitemdamage)
@@ -1945,8 +1951,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
append_message += ", causing them to drop [target_held_item]"
log_combat(user, target, "shoved", append_message)
-/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE)
- SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone)
+/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE)
+ SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal
var/hit_percent = (100-(blocked+armor))/100
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
if(!forced && hit_percent <= 0)
@@ -1973,7 +1979,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.damageoverlaytemp = 20
var/damage_amount = forced ? damage : damage * hit_percent * brutemod * H.physiology.brute_mod
if(BP)
- if(damage > 0 ? BP.receive_damage(damage_amount, 0) : BP.heal_damage(abs(damage_amount), 0))
+ if(BP.receive_damage(damage_amount, 0, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness))
H.update_damage_overlays()
if(HAS_TRAIT(H, TRAIT_MASO) && prob(damage_amount))
H.mob_climax(forced_climax=TRUE)
@@ -1984,7 +1990,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.damageoverlaytemp = 20
var/damage_amount = forced ? damage : damage * hit_percent * burnmod * H.physiology.burn_mod
if(BP)
- if(damage > 0 ? BP.receive_damage(0, damage_amount) : BP.heal_damage(0, abs(damage_amount)))
+ if(BP.receive_damage(0, damage_amount, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness))
H.update_damage_overlays()
else
H.adjustFireLoss(damage_amount)
diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm
index 606b7a8bfd..1868bd22df 100644
--- a/code/modules/mob/living/carbon/human/species_types/humans.dm
+++ b/code/modules/mob/living/carbon/human/species_types/humans.dm
@@ -3,7 +3,7 @@
id = "human"
default_color = "FFFFFF"
- species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS_PARTSONLY,WINGCOLOR)
+ species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS_PARTSONLY,WINGCOLOR,CAN_SCAR)
mutant_bodyparts = list("mcolor" = "FFF", "mcolor2" = "FFF","mcolor3" = "FFF","tail_human" = "None", "ears" = "None", "taur" = "None", "deco_wings" = "None")
use_skintones = USE_SKINTONES_GRAYSCALE_CUSTOM
skinned_type = /obj/item/stack/sheet/animalhide/human
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index c42e0bf175..a424969175 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -4,7 +4,7 @@
id = "lizard"
say_mod = "hisses"
default_color = "00FF00"
- species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,LIPS,HORNCOLOR,WINGCOLOR)
+ species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,LIPS,HORNCOLOR,WINGCOLOR,CAN_SCAR)
mutant_bodyparts = list("tail_lizard", "snout", "spines", "horns", "frills", "body_markings", "legs", "taur", "deco_wings")
inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_REPTILE
mutanttongue = /obj/item/organ/tongue/lizard
diff --git a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm
index dcb6d868fc..8e8c65b15e 100644
--- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm
@@ -8,7 +8,7 @@
nojumpsuit = TRUE
say_mod = "poofs" //what does a mushroom sound like
- species_traits = list(MUTCOLORS, NOEYES, NO_UNDERWEAR,NOGENITALS,NOAROUSAL)
+ species_traits = list(MUTCOLORS, NOEYES, NO_UNDERWEAR,NOGENITALS,NOAROUSAL,CAN_SCAR)
inherent_traits = list(TRAIT_NOBREATH)
speedmod = 1.5 //faster than golems but not by much
diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
index e79160da06..36a1d52cba 100644
--- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
@@ -3,7 +3,7 @@
name = "Anthromorphic Plant"
id = "pod"
default_color = "59CE00"
- species_traits = list(MUTCOLORS,EYECOLOR)
+ species_traits = list(MUTCOLORS,EYECOLOR,CAN_SCAR)
attack_verb = "slash"
attack_sound = 'sound/weapons/slice.ogg'
miss_sound = 'sound/weapons/slashmiss.ogg'
diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
index fede67b47a..da03340ff9 100644
--- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm
@@ -10,7 +10,7 @@
ignored_by = list(/mob/living/simple_animal/hostile/faithless)
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/shadow
species_traits = list(NOBLOOD,NOEYES)
- inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_NOBREATH)
+ inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_NOBREATH,CAN_SCAR)
dangerous_existence = 1
mutanteyes = /obj/item/organ/eyes/night_vision
diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm
index 26a99dbc2b..c0369e7711 100644
--- a/code/modules/mob/living/carbon/human/species_types/zombies.dm
+++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm
@@ -8,7 +8,7 @@
sexes = 0
blacklisted = 1
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/zombie
- species_traits = list(NOBLOOD,NOZOMBIE,NOTRANSSTING)
+ species_traits = list(NOBLOOD,NOZOMBIE,NOTRANSSTING,CAN_SCAR)
inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NOBREATH,TRAIT_NODEATH,TRAIT_FAKEDEATH)
inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID
mutanttongue = /obj/item/organ/tongue/zombie
@@ -45,7 +45,7 @@
/datum/species/zombie/infectious/spec_stun(mob/living/carbon/human/H,amount)
. = min(20, amount)
-/datum/species/zombie/infectious/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE)
+/datum/species/zombie/infectious/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE)
. = ..()
if(.)
regen_cooldown = world.time + REGENERATION_DELAY
@@ -62,6 +62,10 @@
heal_amt *= 2
C.heal_overall_damage(heal_amt,heal_amt)
C.adjustToxLoss(-heal_amt)
+ for(var/i in C.all_wounds)
+ var/datum/wound/W = i
+ if(prob(4-W.severity))
+ W.remove_wound()
if(!C.InCritical() && prob(4))
playsound(C, pick(spooks), 50, TRUE, 10)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index b40d87a68a..725feb214c 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -404,6 +404,12 @@
if(stat != DEAD || D.process_dead)
D.stage_act()
+/mob/living/carbon/handle_wounds()
+ for(var/thing in all_wounds)
+ var/datum/wound/W = thing
+ if(W.processes) // meh
+ W.handle_process()
+
//todo generalize this and move hud out
/mob/living/carbon/proc/handle_changeling()
if(mind && hud_used && hud_used.lingchemdisplay)
diff --git a/code/modules/mob/living/carbon/monkey/punpun.dm b/code/modules/mob/living/carbon/monkey/punpun.dm
index c5cb4dc713..c59218a8a3 100644
--- a/code/modules/mob/living/carbon/monkey/punpun.dm
+++ b/code/modules/mob/living/carbon/monkey/punpun.dm
@@ -26,6 +26,8 @@
//These have to be after the parent new to ensure that the monkey
//bodyparts are actually created before we try to equip things to
//those slots
+ if(ancestor_chain > 1)
+ generate_fake_scars(rand(ancestor_chain, ancestor_chain * 4))
if(relic_hat)
equip_to_slot_or_del(new relic_hat, SLOT_HEAD)
if(relic_mask)
diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm
index 0c049ab5c1..012808647e 100644
--- a/code/modules/mob/living/damage_procs.dm
+++ b/code/modules/mob/living/damage_procs.dm
@@ -14,7 +14,7 @@
*
* Returns TRUE if damage applied
*/
-/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE)
+/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE)
var/hit_percent = (100-blocked)/100
if(!damage || (hit_percent <= 0))
return 0
@@ -245,7 +245,7 @@
update_stamina()
// damage ONE external organ, organ gets randomly selected from damaged ones.
-/mob/living/proc/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE)
+/mob/living/proc/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, required_status, check_armor = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE)
adjustBruteLoss(brute, FALSE) //zero as argument for no instant health update
adjustFireLoss(burn, FALSE)
adjustStaminaLoss(stamina, FALSE)
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index 1993d86b74..e20567121e 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -45,6 +45,8 @@
/mob/living/proc/BiologicalLife(seconds, times_fired)
handle_diseases()// DEAD check is in the proc itself; we want it to spread even if the mob is dead, but to handle its disease-y properties only if you're not.
+ handle_wounds()
+
// Everything after this shouldn't process while dead (as of the time of writing)
if(stat == DEAD)
return FALSE
@@ -109,6 +111,9 @@
/mob/living/proc/handle_diseases()
return
+/mob/living/proc/handle_wounds()
+ return
+
/mob/living/proc/handle_diginvis()
if(!digitaldisguise)
src.digitaldisguise = image(loc = src)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index c7ed6e743d..7fe96b0af7 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -636,7 +636,7 @@
TH.transfer_mob_blood_dna(src)
/mob/living/carbon/human/makeTrail(turf/T)
- if((NOBLOOD in dna.species.species_traits) || !bleed_rate || bleedsuppress)
+ if((NOBLOOD in dna.species.species_traits) || !is_bleeding() || bleedsuppress)
return
..()
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 07252b4c45..d3afad23aa 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -85,7 +85,7 @@
totaldamage = block_calculate_resultant_damage(totaldamage, returnlist)
var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null)
if(!P.nodamage)
- apply_damage(totaldamage, P.damage_type, def_zone, armor)
+ apply_damage(P.damage, P.damage_type, def_zone, armor, wound_bonus=P.wound_bonus, bare_wound_bonus=P.bare_wound_bonus, sharpness=P.sharpness)
if(P.dismemberment)
check_projectile_dismemberment(P, def_zone)
var/missing = 100 - final_percent
@@ -138,7 +138,7 @@
visible_message("[src] has been hit by [I].", \
"You have been hit by [I].")
var/armor = run_armor_check(impacting_zone, "melee", "Your armor has protected your [parse_zone(impacting_zone)].", "Your armor has softened hit to your [parse_zone(impacting_zone)].",I.armour_penetration)
- apply_damage(total_damage, dtype, impacting_zone, armor)
+ apply_damage(total_damage, dtype, impacting_zone, armor, sharpness=I.sharpness)
if(I.thrownby)
log_combat(I.thrownby, src, "threw and hit", I)
else
diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm
index 2a03afb61a..b89f249c80 100644
--- a/code/modules/mob/living/silicon/damage_procs.dm
+++ b/code/modules/mob/living/silicon/damage_procs.dm
@@ -1,5 +1,5 @@
-/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE)
+/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE)
var/hit_percent = (100-blocked)/100
if(!damage || (!forced && hit_percent <= 0))
return 0
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 8f103c496e..d50808c92a 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -334,9 +334,11 @@
/obj/item/surgicaldrill,
/obj/item/scalpel,
/obj/item/circular_saw,
+ /obj/item/bonesetter,
/obj/item/roller/robo,
/obj/item/borg/cyborghug/medical,
/obj/item/stack/medical/gauze/cyborg,
+ /obj/item/stack/medical/bone_gel/cyborg,
/obj/item/organ_storage,
/obj/item/borg/lollipop,
/obj/item/sensor_device,
diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm
index 5dbf3e8e3b..dc3c90cc64 100644
--- a/code/modules/mob/living/simple_animal/hostile/bear.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bear.dm
@@ -29,8 +29,11 @@
var/armored = FALSE
obj_damage = 60
- melee_damage_lower = 20
- melee_damage_upper = 30
+ melee_damage_lower = 15 // i know it's like half what it used to be, but bears cause bleeding like crazy now so it works out
+ melee_damage_upper = 15
+ wound_bonus = -5
+ bare_wound_bonus = 10 // BEAR wound bonus am i right
+ sharpness = TRUE
attack_verb_continuous = "claws"
attack_verb_simple = "claw"
attack_sound = 'sound/weapons/bladeslice.ogg'
@@ -69,8 +72,9 @@
icon_dead = "combatbear_dead"
faction = list("russian")
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/bear = 5, /obj/item/clothing/head/bearpelt = 1, /obj/item/bear_armor = 1)
- melee_damage_lower = 25
- melee_damage_upper = 35
+ melee_damage_lower = 18
+ melee_damage_upper = 20
+ wound_bonus = 0
armour_penetration = 20
health = 120
maxHealth = 120
@@ -99,8 +103,9 @@
A.maxHealth += 60
A.health += 60
A.armour_penetration += 20
- A.melee_damage_lower += 5
+ A.melee_damage_lower += 3
A.melee_damage_upper += 5
+ A.wound_bonus += 5
A.update_icons()
to_chat(user, "You strap the armor plating to [A] and sharpen [A.p_their()] claws with the nail filer. This was a great idea.")
qdel(src)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
index a66ee7b1de..c00d7c3130 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
@@ -643,7 +643,7 @@ Difficulty: Normal
to_chat(L, "You're struck by a [name]!")
var/limb_to_hit = L.get_bodypart(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
var/armor = L.run_armor_check(limb_to_hit, "melee", "Your armor absorbs [src]!", "Your armor blocks part of [src]!", 50, "Your armor was penetrated by [src]!")
- L.apply_damage(damage, BURN, limb_to_hit, armor)
+ L.apply_damage(damage, BURN, limb_to_hit, armor, wound_bonus=CANT_WOUND)
if(ishostile(L))
var/mob/living/simple_animal/hostile/H = L //mobs find and damage you...
if(H.stat == CONSCIOUS && !H.target && H.AIStatus != AI_OFF && !H.client)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
index 4da8a90b23..78c02fb7a7 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
@@ -52,6 +52,8 @@ Difficulty: Medium
elimination = 1
appearance_flags = 0
mouse_opacity = MOUSE_OPACITY_ICON
+ wound_bonus = -40
+ bare_wound_bonus = 20
/mob/living/simple_animal/hostile/megafauna/legion/Initialize()
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm
index ed189f052d..869f29951b 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm
@@ -129,7 +129,7 @@ Difficulty: Hard
to_chat(L, "[src]'s ground slam shockwave sends you flying!")
var/turf/thrownat = get_ranged_target_turf_direct(src, L, 8, rand(-10, 10))
L.throw_at(thrownat, 8, 2, src, TRUE) //, force = MOVE_FORCE_OVERPOWERING, gentle = TRUE)
- L.apply_damage(20, BRUTE)
+ L.apply_damage(20, BRUTE, wound_bonus=CANT_WOUND)
shake_camera(L, 2, 1)
all_turfs -= T
sleep(delay)
diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
index 9a362b680d..4ea8a3c5dc 100644
--- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
@@ -77,6 +77,9 @@
/mob/living/simple_animal/hostile/syndicate/melee
melee_damage_lower = 15
melee_damage_upper = 15
+ wound_bonus = -10
+ bare_wound_bonus = 20
+ sharpness = TRUE
icon_state = "syndicate_knife"
icon_living = "syndicate_knife"
loot = list(/obj/effect/gibspawner/human)
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 1a646ea73e..1102559aca 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -140,6 +140,13 @@
///What kind of footstep this mob should have. Null if it shouldn't have any.
var/footstep_type
+ //How much wounding power it has
+ var/wound_bonus = CANT_WOUND
+ //How much bare wounding power it has
+ var/bare_wound_bonus = 0
+ //If the attacks from this are sharp
+ var/sharpness = FALSE
+
/mob/living/simple_animal/Initialize()
. = ..()
GLOB.simple_animals[AIStatus] += src
diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
index ea84e23d01..db2914e620 100644
--- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm
+++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
@@ -8,6 +8,18 @@
projectile_type = /obj/item/projectile/bullet/shotgun_slug
custom_materials = list(/datum/material/iron=4000)
+obj/item/ammo_casing/shotgun/executioner
+ name = "executioner slug"
+ desc = "A 12 gauge lead slug purpose built to annihilate flesh on impact."
+ icon_state = "stunshell"
+ projectile_type = /obj/projectile/bullet/shotgun_slug/executioner
+
+/obj/item/ammo_casing/shotgun/pulverizer
+ name = "pulverizer slug"
+ desc = "A 12 gauge lead slug purpose built to annihilate bones on impact."
+ icon_state = "stunshell"
+ projectile_type = /obj/projectile/bullet/shotgun_slug/pulverizer
+
/obj/item/ammo_casing/shotgun/beanbag
name = "beanbag slug"
desc = "A weak beanbag slug for riot control."
diff --git a/code/modules/projectiles/ammunition/energy/laser.dm b/code/modules/projectiles/ammunition/energy/laser.dm
index 174645dd11..5c19edc012 100644
--- a/code/modules/projectiles/ammunition/energy/laser.dm
+++ b/code/modules/projectiles/ammunition/energy/laser.dm
@@ -37,6 +37,11 @@
select_name = "anti-vehicle"
fire_sound = 'sound/weapons/lasercannonfire.ogg'
+/obj/item/ammo_casing/energy/laser/hellfire
+ projectile_type = /obj/projectile/beam/laser/hellfire
+ e_cost = 130
+ select_name = "maim"
+
/obj/item/ammo_casing/energy/laser/pulse
projectile_type = /obj/item/projectile/beam/pulse
e_cost = 200
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index b90f0aee0d..f34343debd 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -184,6 +184,11 @@
return
if(target == user && user.zone_selected != BODY_ZONE_PRECISE_MOUTH) //so we can't shoot ourselves (unless mouth selected)
return
+ if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ for(var/datum/wound/W in C.all_wounds)
+ if(W.try_treating(src, user))
+ return // another coward cured!
if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can.
var/mob/living/L = user
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 72ac9620b5..8e61221cc8 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -44,6 +44,12 @@
ammo_type = list(/obj/item/ammo_casing/energy/lasergun/old)
ammo_x_offset = 3
+/obj/item/gun/energy/laser/hellgun
+ name ="hellfire laser gun"
+ desc = "A relic of a weapon, built before NT began installing regulators on its laser weaponry. This pattern of laser gun became infamous for the gruesome burn wounds it caused, and was quietly discontinued once it began to affect NT's reputation."
+ icon_state = "hellgun"
+ ammo_type = list(/obj/item/ammo_casing/energy/laser/hellfire)
+
/obj/item/gun/energy/laser/captain
name = "antique laser gun"
icon_state = "caplaser"
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 7c988ca730..69e3991e39 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -154,6 +154,10 @@
///If TRUE, hit mobs even if they're on the floor and not our target
var/hit_stunned_targets = FALSE
+ wound_bonus = CANT_WOUND
+ /// For telling whether we want to roll for bone breaking or lacerations if we're bothering with wounds
+ var/sharpness = FALSE
+
/obj/item/projectile/Initialize()
. = ..()
permutated = list()
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index 2f12f0f69b..7147754211 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -14,11 +14,26 @@
ricochets_max = 50 //Honk!
ricochet_chance = 80
is_reflectable = TRUE
+ wound_bonus = -20
+ bare_wound_bonus = 10
/obj/item/projectile/beam/laser
tracer_type = /obj/effect/projectile/tracer/laser
muzzle_type = /obj/effect/projectile/muzzle/laser
impact_type = /obj/effect/projectile/impact/laser
+ wound_bonus = -30
+ bare_wound_bonus = 40
+
+//overclocked laser, does a bit more damage but has much higher wound power (-0 vs -20)
+/obj/projectile/beam/laser/hellfire
+ name = "hellfire laser"
+ wound_bonus = 0
+ damage = 25
+ speed = 0.6 // higher power = faster, that's how light works right
+
+/obj/projectile/beam/laser/hellfire/Initialize()
+ . = ..()
+ transform *= 2
/obj/item/projectile/beam/laser/heavylaser
name = "heavy laser"
@@ -90,6 +105,7 @@
tracer_type = /obj/effect/projectile/tracer/pulse
muzzle_type = /obj/effect/projectile/muzzle/pulse
impact_type = /obj/effect/projectile/impact/pulse
+ wound_bonus = 10
/obj/item/projectile/beam/pulse/on_hit(atom/target, blocked = FALSE)
. = ..()
@@ -116,6 +132,8 @@
damage = 30
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
light_color = LIGHT_COLOR_GREEN
+ wound_bonus = -40
+ bare_wound_bonus = 70
/obj/item/projectile/beam/emitter/singularity_pull()
return
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index 6d03012315..6a081f9b03 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -8,3 +8,4 @@
flag = "bullet"
hitsound_wall = "ricochet"
impact_effect_type = /obj/effect/temp_visual/impact_effect
+ sharpness = TRUE
\ No newline at end of file
diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm
index 5643804ac1..c793e9f95e 100644
--- a/code/modules/projectiles/projectile/bullets/revolver.dm
+++ b/code/modules/projectiles/projectile/bullets/revolver.dm
@@ -19,6 +19,8 @@
ricochet_chance = 50
ricochet_auto_aim_angle = 10
ricochet_auto_aim_range = 3
+ wound_bonus = -35
+ sharpness = TRUE
/obj/item/projectile/bullet/c38/match
name = ".38 Match bullet"
@@ -29,6 +31,7 @@
ricochet_incidence_leeway = 50
ricochet_decay_chance = 1
ricochet_decay_damage = 1
+ wound_bonus = 0
/obj/item/projectile/bullet/c38/match/bouncy
name = ".38 Rubber bullet"
diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm
index 264df22c76..b268877451 100644
--- a/code/modules/projectiles/projectile/bullets/shotgun.dm
+++ b/code/modules/projectiles/projectile/bullets/shotgun.dm
@@ -2,10 +2,22 @@
name = "12g shotgun slug"
damage = 60
+/obj/projectile/bullet/shotgun_slug/executioner
+ name = "executioner slug" // admin only, can dismember limbs
+ sharpness = TRUE
+ wound_bonus = 0
+
+/obj/projectile/bullet/shotgun_slug/pulverizer
+ name = "pulverizer slug" // admin only, can crush bones
+ sharpness = FALSE
+ wound_bonus = 0
+
/obj/item/projectile/bullet/shotgun_beanbag
name = "beanbag slug"
- damage = 5
+ damage = 10
stamina = 70
+ wound_bonus = 20
+ sharpness = FALSE
/obj/item/projectile/bullet/incendiary/shotgun
name = "incendiary slug"
@@ -77,6 +89,7 @@
/obj/item/projectile/bullet/pellet/shotgun_buckshot
name = "buckshot pellet"
damage = 12.5
+ wound_bonus = -10
/obj/item/projectile/bullet/pellet/shotgun_rubbershot
name = "rubbershot pellet"
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index b9da8271ab..050b1f29b5 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -144,6 +144,9 @@
M.adjustFireLoss(-power, 0)
M.adjustToxLoss(-power, 0, TRUE) //heals TOXINLOVERs
M.adjustCloneLoss(-power, 0)
+ for(var/i in M.all_wounds)
+ var/datum/wound/W = i
+ W.on_xadone(power)
REMOVE_TRAIT(M, TRAIT_DISFIGURED, TRAIT_GENERIC) //fixes common causes for disfiguration
. = 1
metabolization_rate = REAGENTS_METABOLISM * (0.00001 * (M.bodytemperature ** 2) + 0.5)
@@ -192,6 +195,9 @@
M.adjustFireLoss(-1.5 * power, 0)
M.adjustToxLoss(-power, 0, TRUE)
M.adjustCloneLoss(-power, 0)
+ for(var/i in M.all_wounds)
+ var/datum/wound/W = i
+ W.on_xadone(power)
REMOVE_TRAIT(M, TRAIT_DISFIGURED, TRAIT_GENERIC)
. = 1
..()
@@ -231,7 +237,7 @@
/datum/reagent/medicine/spaceacillin
name = "Spaceacillin"
- description = "Spaceacillin will prevent a patient from conventionally spreading any diseases they are currently infected with."
+ description = "Spaceacillin will prevent a patient from conventionally spreading any diseases they are currently infected with. Also reduces infection in serious burns."
color = "#f2f2f2"
metabolization_rate = 0.1 * REAGENTS_METABOLISM
pH = 8.1
@@ -403,7 +409,7 @@
/datum/reagent/medicine/mine_salve
name = "Miner's Salve"
- description = "A powerful painkiller. Restores bruising and burns in addition to making the patient believe they are fully healed."
+ description = "A powerful painkiller. Restores bruising and burns in addition to making the patient believe they are fully healed. Also great for treating severe burn wounds in a pinch."
reagent_state = LIQUID
color = "#6D6374"
metabolization_rate = 0.4 * REAGENTS_METABOLISM
@@ -432,7 +438,7 @@
// +10% success propability on each step, useful while operating in less-than-perfect conditions
if(show_message)
- to_chat(M, "You feel your wounds fade away to nothing!" )
+ to_chat(M, "You feel your injuries fade away to nothing!" )
..()
/datum/reagent/medicine/mine_salve/on_mob_end_metabolize(mob/living/M)
@@ -453,10 +459,10 @@
/datum/reagent/medicine/synthflesh/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message = 1)
if(iscarbon(M))
- if (M.stat == DEAD)
+ var/mob/living/carbon/C = M
+ if(M.stat == DEAD)
show_message = 0
if(method in list(INGEST, VAPOR))
- var/mob/living/carbon/C = M
C.losebreath++
C.emote("cough")
to_chat(M, "You feel your throat closing up!")
@@ -465,6 +471,8 @@
else if(method in list(PATCH, TOUCH))
M.adjustBruteLoss(-1 * reac_volume)
M.adjustFireLoss(-1 * reac_volume)
+ for(var/datum/wound/burn/burn_wound in C.all_wounds)
+ burn_wound.regenerate_flesh(reac_volume)
if(show_message)
to_chat(M, "You feel your burns and bruises healing! It stings like hell!")
SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "painful_medicine", /datum/mood_event/painful_medicine)
@@ -1552,10 +1560,6 @@
/datum/reagent/medicine/polypyr/on_mob_life(mob/living/carbon/M) //I wanted a collection of small positive effects, this is as hard to obtain as coniine after all.
M.adjustOrganLoss(ORGAN_SLOT_LUNGS, -0.25)
M.adjustBruteLoss(-0.35, 0)
- if(prob(50))
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- H.bleed_rate = max(H.bleed_rate - 1, 0)
..()
. = 1
@@ -1588,3 +1592,10 @@
to_chat(C, "[pick(GLOB.wisdoms)]") //give them a random wisdom
..()
+// handled in cut wounds process
+/datum/reagent/medicine/coagulant
+ name = "Sanguirite"
+ description = "A coagulant used to help open cuts clot faster."
+ reagent_state = LIQUID
+ color = "#bb2424"
+ metabolization_rate = 0.25 * REAGENTS_METABOLISM
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 988441fa2b..9dc091b2eb 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -2296,3 +2296,36 @@
if(data["blood_DNA"])
S.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"]))
+/datum/reagent/determination
+ name = "Determination"
+ description = "For when you need to push on a little more. Do NOT allow near plants."
+ reagent_state = LIQUID
+ color = "#D2FFFA"
+ metabolization_rate = 0.75 * REAGENTS_METABOLISM // 5u (WOUND_DETERMINATION_CRITICAL) will last for ~17 ticks
+ /// Whether we've had at least WOUND_DETERMINATION_SEVERE (2.5u) of determination at any given time. No damage slowdown immunity or indication we're having a second wind if it's just a single moderate wound
+ var/significant = FALSE
+
+/datum/reagent/determination/on_mob_end_metabolize(mob/living/carbon/M)
+ if(significant)
+ var/stam_crash = 0
+ for(var/thing in M.all_wounds)
+ var/datum/wound/W = thing
+ stam_crash += (W.severity + 1) * 3 // spike of 3 stam damage per wound severity (moderate = 6, severe = 9, critical = 12) when the determination wears off if it was a combat rush
+ M.adjustStaminaLoss(stam_crash)
+ M.remove_status_effect(STATUS_EFFECT_DETERMINED)
+ ..()
+
+/datum/reagent/determination/on_mob_life(mob/living/carbon/M)
+ if(!significant && volume >= WOUND_DETERMINATION_SEVERE)
+ significant = TRUE
+ M.apply_status_effect(STATUS_EFFECT_DETERMINED) // in addition to the slight healing, limping cooldowns are divided by 4 during the combat high
+
+ volume = min(volume, WOUND_DETERMINATION_MAX)
+
+ for(var/thing in M.all_wounds)
+ var/datum/wound/W = thing
+ var/obj/item/bodypart/wounded_part = W.limb
+ if(wounded_part)
+ wounded_part.heal_damage(0.25, 0.25)
+ M.adjustStaminaLoss(-0.25*REM) // the more wounds, the more stamina regen
+ ..()
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index 02e4a89e1d..a763376caa 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -154,6 +154,11 @@
pH = 12
value = REAGENT_VALUE_RARE
+/datum/reagent/toxin/carpotoxin/on_mob_life(mob/living/carbon/M)
+ . = ..()
+ for(var/i in M.all_scars)
+ qdel(i)
+
/datum/reagent/toxin/zombiepowder
name = "Zombie Powder"
description = "A strong neurotoxin that puts the subject into a death-like state."
@@ -736,22 +741,13 @@
/datum/reagent/toxin/heparin //Based on a real-life anticoagulant. I'm not a doctor, so this won't be realistic.
name = "Heparin"
- description = "A powerful anticoagulant. Victims will bleed uncontrollably and suffer scaling bruising."
+ description = "A powerful anticoagulant. All open cut wounds on the victim will open up and bleed much faster"
reagent_state = LIQUID
color = "#C8C8C8" //RGB: 200, 200, 200
metabolization_rate = 0.2 * REAGENTS_METABOLISM
toxpwr = 0
value = REAGENT_VALUE_VERY_RARE
-/datum/reagent/toxin/heparin/on_mob_life(mob/living/carbon/M)
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- H.bleed_rate = min(H.bleed_rate + 2, 8)
- H.adjustBruteLoss(1, 0) //Brute damage increases with the amount they're bleeding
- . = 1
- return ..() || .
-
-
/datum/reagent/toxin/rotatium //Rotatium. Fucks up your rotation and is hilarious
name = "Rotatium"
description = "A constantly swirling, oddly colourful fluid. Causes the consumer's sense of direction and hand-eye coordination to become wild."
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 799a6db9f5..35f5ebd46e 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -90,12 +90,12 @@
item_state = "medipen"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
- amount_per_transfer_from_this = 13
- volume = 13
+ amount_per_transfer_from_this = 15
+ volume = 15
ignore_flags = 1 //so you can medipen through hardsuits
reagent_flags = DRAWABLE
flags_1 = null
- list_reagents = list(/datum/reagent/medicine/epinephrine = 10, /datum/reagent/preservahyde = 3)
+ list_reagents = list(/datum/reagent/medicine/epinephrine = 10, /datum/reagent/preservahyde = 3, /datum/reagent/medicine/coagulant = 2)
custom_premium_price = PRICE_ALMOST_EXPENSIVE
/obj/item/reagent_containers/hypospray/medipen/suicide_act(mob/living/carbon/user)
@@ -133,6 +133,13 @@
else
. += "It is spent."
+/obj/item/reagent_containers/hypospray/medipen/ekit
+ name = "emergency first-aid autoinjector"
+ desc = "An epinephrine medipen with trace amounts of coagulants and antibiotics to help stabilize bad cuts and burns."
+ volume = 15
+ amount_per_transfer_from_this = 15
+ list_reagents = list(/datum/reagent/medicine/epinephrine = 12, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/medicine/spaceacillin = 0.5)
+
/obj/item/reagent_containers/hypospray/medipen/stimulants
name = "illegal stimpack medipen"
desc = "A highly illegal medipen due to its load and small injections, allow for five uses before being drained"
diff --git a/code/modules/research/designs/autolathe_desings/autolathe_designs_medical_and_dinnerware.dm b/code/modules/research/designs/autolathe_desings/autolathe_designs_medical_and_dinnerware.dm
index 76ec6224b8..574c7c9282 100644
--- a/code/modules/research/designs/autolathe_desings/autolathe_designs_medical_and_dinnerware.dm
+++ b/code/modules/research/designs/autolathe_desings/autolathe_designs_medical_and_dinnerware.dm
@@ -119,6 +119,24 @@
category = list("initial", "Medical","Tool Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+/datum/design/bonesetter
+ name = "Bonesetter"
+ id = "bonesetter"
+ build_type = AUTOLATHE | PROTOLATHE
+ materials = list(/datum/material/iron = 1000)
+ build_path = /obj/item/bonesetter
+ category = list("initial", "Medical", "Tool Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE
+
+/datum/design/sticky_tape/surgical
+ name = "Surgical Tape"
+ id = "surgical_tape"
+ build_type = PROTOLATHE
+ materials = list(/datum/material/plastic = 500)
+ build_path = /obj/item/stack/sticky_tape/surgical
+ category = list("initial", "Medical")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+
/datum/design/beaker
name = "Beaker"
id = "beaker"
diff --git a/code/modules/research/techweb/nodes/medical_nodes.dm b/code/modules/research/techweb/nodes/medical_nodes.dm
index 71dd7c943c..3a9e654b81 100644
--- a/code/modules/research/techweb/nodes/medical_nodes.dm
+++ b/code/modules/research/techweb/nodes/medical_nodes.dm
@@ -104,7 +104,7 @@
display_name = "Advanced Surgery Tools"
description = "Refined and improved redesigns for the run-of-the-mill medical utensils."
prereq_ids = list("adv_biotech", "adv_surgery")
- design_ids = list("drapes", "retractor_adv", "surgicaldrill_adv", "scalpel_adv")
+ design_ids = list("drapes", "retractor_adv", "surgicaldrill_adv", "scalpel_adv", "bonesetter", "surgical_tape")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/adv_surgery
diff --git a/code/modules/spells/spell_types/bloodcrawl.dm b/code/modules/spells/spell_types/bloodcrawl.dm
index e59fc2049d..39b0f1709e 100644
--- a/code/modules/spells/spell_types/bloodcrawl.dm
+++ b/code/modules/spells/spell_types/bloodcrawl.dm
@@ -25,6 +25,11 @@
/obj/effect/proc_holder/spell/bloodcrawl/perform(obj/effect/decal/cleanable/target, recharge = 1, mob/living/user = usr)
if(istype(user))
+ if(istype(user, /mob/living/simple_animal/slaughter))
+ var/mob/living/simple_animal/slaughter/slaught = user
+ slaught.current_hitstreak = 0
+ slaught.wound_bonus = initial(slaught.wound_bonus)
+ slaught.bare_wound_bonus = initial(slaught.bare_wound_bonus)
if(phased)
if(user.phasein(target))
phased = 0
diff --git a/code/modules/spells/spell_types/shapeshift.dm b/code/modules/spells/spell_types/shapeshift.dm
index a8f9c8bce5..e513865246 100644
--- a/code/modules/spells/spell_types/shapeshift.dm
+++ b/code/modules/spells/spell_types/shapeshift.dm
@@ -105,7 +105,7 @@
var/damage_percent = (stored.maxHealth - stored.health)/stored.maxHealth;
var/damapply = damage_percent * shape.maxHealth;
- shape.apply_damage(damapply, source.convert_damage_type, forced = TRUE);
+ shape.apply_damage(damapply, source.convert_damage_type, forced = TRUE, wound_bonus=CANT_WOUND);
slink = soullink(/datum/soullink/shapeshift, stored , shape)
slink.source = src
@@ -158,7 +158,7 @@
var/damage_percent = (shape.maxHealth - shape.health)/shape.maxHealth;
var/damapply = stored.maxHealth * damage_percent
- stored.apply_damage(damapply, source.convert_damage_type, forced = TRUE)
+ stored.apply_damage(damapply, source.convert_damage_type, forced = TRUE, wound_bonus=CANT_WOUND)
qdel(shape)
qdel(src)
diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
similarity index 72%
rename from code/modules/surgery/bodyparts/bodyparts.dm
rename to code/modules/surgery/bodyparts/_bodyparts.dm
index fcdb07ca17..711f90a629 100644
--- a/code/modules/surgery/bodyparts/bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -71,6 +71,28 @@
var/medium_burn_msg = "blistered"
var/heavy_burn_msg = "peeling away"
+ /// The wounds currently afflicting this body part
+ var/list/wounds
+
+ /// The scars currently afflicting this body part
+ var/list/scars
+ /// Our current stored wound damage multiplier
+ var/wound_damage_multiplier = 1
+
+ /// This number is subtracted from all wound rolls on this bodypart, higher numbers mean more defense, negative means easier to wound
+ var/wound_resistance = 0
+ /// When this bodypart hits max damage, this number is added to all wound rolls. Obviously only relevant for bodyparts that have damage caps.
+ var/disabled_wound_penalty = 15
+
+ /// A hat won't cover your face, but a shirt covering your chest will cover your... you know, chest
+ var/scars_covered_by_clothes = TRUE
+ /// Descriptions for the locations on the limb for scars to be assigned, just cosmetic
+ var/list/specific_locations = list("general area")
+ /// So we know if we need to scream if this limb hits max damage
+ var/last_maxed
+ /// How much generic bleedstacks we have on this bodypart
+ var/generic_bleedstacks
+
/obj/item/bodypart/examine(mob/user)
. = ..()
if(brute_dam > DAMAGE_PRECISION)
@@ -149,7 +171,7 @@
//Applies brute and burn damage to the organ. Returns 1 if the damage-icon states changed at all.
//Damage will not exceed max_damage using this proc
//Cannot apply negative damage
-/obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE)
+/obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, stamina = 0, blocked = 0, updating_health = TRUE, required_status = null, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) // maybe separate BRUTE_SHARP and BRUTE_OTHER eventually somehow hmm
if(owner && (owner.status_flags & GODMODE))
return FALSE //godmode
var/dmg_mlt = CONFIG_GET(number/damage_multiplier)
@@ -163,20 +185,38 @@
if(!brute && !burn && !stamina)
return FALSE
+ brute *= wound_damage_multiplier
+ burn *= wound_damage_multiplier
+
switch(animal_origin)
if(ALIEN_BODYPART,LARVA_BODYPART) //aliens take some additional burn //nothing can burn with so much snowflake code around
burn *= 1.2
+ var/wounding_type = (brute > burn ? WOUND_BRUTE : WOUND_BURN)
+ var/wounding_dmg = max(brute, burn)
+ if(wounding_type == WOUND_BRUTE && sharpness)
+ wounding_type = WOUND_SHARP
+ // i know this is effectively the same check as above but i don't know if those can null the damage by rounding and want to be safe
+ if(owner && wounding_dmg > 4 && wound_bonus != CANT_WOUND)
+ // if you want to make tox wounds or some other type, this will need to be expanded and made more modular
+ // handle all our wounding stuff
+ check_wounding(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus)
+
var/can_inflict = max_damage - get_damage()
- if(can_inflict <= 0)
- return FALSE
var/total_damage = brute + burn
- if(total_damage > can_inflict)
+ if(total_damage > can_inflict && total_damage > 0) // TODO: the second part of this check should be removed once disabling is all done
brute = round(brute * (max_damage / total_damage),DAMAGE_PRECISION)
burn = round(burn * (max_damage / total_damage),DAMAGE_PRECISION)
+ if(can_inflict <= 0)
+ return FALSE
+
+ for(var/i in wounds)
+ var/datum/wound/W = i
+ W.receive_damage(wounding_type, wounding_dmg, wound_bonus)
+
brute_dam += brute
burn_dam += burn
@@ -196,6 +236,107 @@
update_disabled()
return update_bodypart_damage_state()
+/**
+ * check_wounding() is where we handle rolling for, selecting, and applying a wound if we meet the criteria
+ *
+ * We generate a "score" for how woundable the attack was based on the damage and other factors discussed in [check_wounding_mods()], then go down the list from most severe to least severe wounds in that category.
+ * We can promote a wound from a lesser to a higher severity this way, but we give up if we have a wound of the given type and fail to roll a higher severity, so no sidegrades/downgrades
+ *
+ * Arguments:
+ * * woundtype- Either WOUND_SHARP, WOUND_BRUTE, or WOUND_BURN based on the attack type.
+ * * damage- How much damage is tied to this attack, since wounding potential scales with damage in an attack (see: WOUND_DAMAGE_EXPONENT)
+ * * wound_bonus- The wound_bonus of an attack
+ * * bare_wound_bonus- The bare_wound_bonus of an attack
+ */
+/obj/item/bodypart/proc/check_wounding(woundtype, damage, wound_bonus, bare_wound_bonus)
+ // actually roll wounds if applicable
+ if(HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE))
+ damage *= 1.5
+
+ var/base_roll = rand(1, round(damage ** WOUND_DAMAGE_EXPONENT))
+ var/injury_roll = base_roll
+ injury_roll += check_woundings_mods(woundtype, damage, wound_bonus, bare_wound_bonus)
+ var/list/wounds_checking
+
+ switch(woundtype)
+ if(WOUND_SHARP)
+ wounds_checking = WOUND_LIST_CUT
+ if(WOUND_BRUTE)
+ wounds_checking = WOUND_LIST_BONE
+ if(WOUND_BURN)
+ wounds_checking = WOUND_LIST_BURN
+
+ //cycle through the wounds of the relevant category from the most severe down
+ for(var/PW in wounds_checking)
+ var/datum/wound/possible_wound = PW
+ var/datum/wound/replaced_wound
+ for(var/i in wounds)
+ var/datum/wound/existing_wound = i
+ if(existing_wound.type in wounds_checking)
+ if(existing_wound.severity >= initial(possible_wound.severity))
+ return
+ else
+ replaced_wound = existing_wound
+
+ if(initial(possible_wound.threshold_minimum) < injury_roll)
+ if(replaced_wound)
+ var/datum/wound/new_wound = replaced_wound.replace_wound(possible_wound)
+ log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll)
+ else
+ var/datum/wound/new_wound = new possible_wound
+ new_wound.apply_wound(src)
+ log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll)
+ return
+
+// try forcing a specific wound, but only if there isn't already a wound of that severity or greater for that type on this bodypart
+/obj/item/bodypart/proc/force_wound_upwards(specific_woundtype, smited = FALSE)
+ var/datum/wound/potential_wound = specific_woundtype
+ for(var/i in wounds)
+ var/datum/wound/existing_wound = i
+ if(existing_wound.type in (initial(potential_wound.wound_type)))
+ if(existing_wound.severity < initial(potential_wound.severity)) // we only try if the existing one is inferior to the one we're trying to force
+ existing_wound.replace_wound(potential_wound, smited)
+ return
+
+ var/datum/wound/new_wound = new potential_wound
+ new_wound.apply_wound(src, smited = smited)
+
+/obj/item/bodypart/proc/check_woundings_mods(wounding_type, damage, wound_bonus, bare_wound_bonus)
+ var/armor_ablation = 0
+ var/injury_mod = 0
+
+ //var/bwb = 0
+
+ if(owner && ishuman(owner))
+ var/mob/living/carbon/human/H = owner
+ var/list/clothing = H.clothingonpart(src)
+ for(var/c in clothing)
+ var/obj/item/clothing/C = c
+ // unlike normal armor checks, we tabluate these piece-by-piece manually so we can also pass on appropriate damage the clothing's limbs if necessary
+ armor_ablation += C.armor.getRating("wound")
+ if(wounding_type == WOUND_SHARP)
+ C.take_damage_zone(body_zone, damage, BRUTE, armour_penetration)
+ else if(wounding_type == WOUND_BURN && damage >= 10) // lazy way to block freezing from shredding clothes without adding another var onto apply_damage()
+ C.take_damage_zone(body_zone, damage, BURN, armour_penetration)
+
+ if(!armor_ablation)
+ injury_mod += bare_wound_bonus
+
+ injury_mod -= armor_ablation
+ injury_mod += wound_bonus
+
+ for(var/thing in wounds)
+ var/datum/wound/W = thing
+ injury_mod += W.threshold_penalty
+
+ var/part_mod = -wound_resistance
+ if(is_disabled())
+ part_mod += disabled_wound_penalty
+
+ injury_mod += part_mod
+
+ return injury_mod
+
//Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all.
//Damage cannot go below zero.
//Cannot remove negative damage (i.e. apply damage)
@@ -227,16 +368,29 @@
//Checks disabled status thresholds
/obj/item/bodypart/proc/update_disabled()
+ if(!owner)
+ return
set_disabled(is_disabled())
/obj/item/bodypart/proc/is_disabled()
+ if(!owner)
+ return
if(HAS_TRAIT(owner, TRAIT_PARALYSIS))
return BODYPART_DISABLED_PARALYSIS
+ for(var/i in wounds)
+ var/datum/wound/W = i
+ if(W.disabling)
+ return BODYPART_DISABLED_WOUND
if(can_dismember() && !HAS_TRAIT(owner, TRAIT_NODISMEMBER))
. = disabled //inertia, to avoid limbs healing 0.1 damage and being re-enabled
if((get_damage(TRUE) >= max_damage) || (HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE) && (get_damage(TRUE) >= (max_damage * 0.6)))) //Easy limb disable disables the limb at 40% health instead of 0%
- return BODYPART_DISABLED_DAMAGE
- if(disabled && (get_damage(TRUE) <= (max_damage * 0.5)))
+ if(!last_maxed)
+ owner.emote("scream")
+ last_maxed = TRUE
+ if(!is_organic_limb())
+ return BODYPART_DISABLED_DAMAGE
+ else if(disabled && (get_damage(TRUE) <= (max_damage * 0.8))) // reenabled at 80% now instead of 50% as of wounds update
+ last_maxed = FALSE
return BODYPART_NOT_DISABLED
else
return BODYPART_NOT_DISABLED
@@ -251,9 +405,11 @@
/obj/item/bodypart/proc/set_disabled(new_disabled)
- if(disabled == new_disabled)
+ if(disabled == new_disabled || !owner)
return FALSE
disabled = new_disabled
+ if(disabled && owner.get_item_for_held_index(held_index))
+ owner.dropItemToGround(owner.get_item_for_held_index(held_index))
owner.update_health_hud() //update the healthdoll
owner.update_body()
owner.update_mobility()
@@ -581,293 +737,41 @@
drop_organs()
qdel(src)
-/obj/item/bodypart/chest
- name = BODY_ZONE_CHEST
- desc = "It's impolite to stare at a person's chest."
- icon_state = "default_human_chest"
- max_damage = 200
- body_zone = BODY_ZONE_CHEST
- body_part = CHEST
- px_x = 0
- px_y = 0
- stam_damage_coeff = 1
- max_stamina_damage = 200
- var/obj/item/cavity_item
-
-/obj/item/bodypart/chest/can_dismember(obj/item/I)
- if(!((owner.stat == DEAD) || owner.InFullCritical()))
- return FALSE
- return ..()
-
-/obj/item/bodypart/chest/Destroy()
- if(cavity_item)
- qdel(cavity_item)
- return ..()
-
-/obj/item/bodypart/chest/drop_organs(mob/user)
- if(cavity_item)
- cavity_item.forceMove(user.loc)
- cavity_item = null
- ..()
-
-/obj/item/bodypart/chest/monkey
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "default_monkey_chest"
- animal_origin = MONKEY_BODYPART
-
-/obj/item/bodypart/chest/alien
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "alien_chest"
- dismemberable = 0
- max_damage = 500
- animal_origin = ALIEN_BODYPART
-
-/obj/item/bodypart/chest/devil
- dismemberable = 0
- max_damage = 5000
- animal_origin = DEVIL_BODYPART
-
-/obj/item/bodypart/chest/larva
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "larva_chest"
- dismemberable = 0
- max_damage = 50
- animal_origin = LARVA_BODYPART
-
-/obj/item/bodypart/l_arm
- name = "left arm"
- desc = "Did you know that the word 'sinister' stems originally from the \
- Latin 'sinestra' (left hand), because the left hand was supposed to \
- be possessed by the devil? This arm appears to be possessed by no \
- one though."
- icon_state = "default_human_l_arm"
- attack_verb = list("slapped", "punched")
- max_damage = 50
- max_stamina_damage = 50
- body_zone = BODY_ZONE_L_ARM
- body_part = ARM_LEFT
- aux_icons = list(BODY_ZONE_PRECISE_L_HAND = HANDS_PART_LAYER, "l_hand_behind" = BODY_BEHIND_LAYER)
- body_damage_coeff = 0.75
- held_index = 1
- px_x = -6
- px_y = 0
- stam_heal_tick = STAM_RECOVERY_LIMB
-
-/obj/item/bodypart/l_arm/is_disabled()
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM))
- return BODYPART_DISABLED_PARALYSIS
- return ..()
-
-/obj/item/bodypart/l_arm/set_disabled(new_disabled)
- . = ..()
- if(!.)
+/// Get whatever wound of the given type is currently attached to this limb, if any
+/obj/item/bodypart/proc/get_wound_type(checking_type)
+ if(isnull(wounds))
return
- if(owner.stat < UNCONSCIOUS)
- switch(disabled)
- if(BODYPART_DISABLED_DAMAGE)
- owner.emote("scream")
- to_chat(owner, "Your [name] is too damaged to function!")
- if(BODYPART_DISABLED_PARALYSIS)
- to_chat(owner, "You can't feel your [name]!")
- if(held_index)
- owner.dropItemToGround(owner.get_item_for_held_index(held_index))
- if(owner.hud_used)
- var/obj/screen/inventory/hand/L = owner.hud_used.hand_slots["[held_index]"]
- if(L)
- L.update_icon()
+ for(var/thing in wounds)
+ var/datum/wound/W = thing
+ if(istype(W, checking_type))
+ return W
-/obj/item/bodypart/l_arm/monkey
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "default_monkey_l_arm"
- animal_origin = MONKEY_BODYPART
- px_x = -5
- px_y = -3
+/// very rough start for updating efficiency and other stats on a body part whenever a wound is gained/lost
+/obj/item/bodypart/proc/update_wounds()
+ var/dam_mul = 1 //initial(wound_damage_multiplier)
+ // we can only have one wound per type, but remember there's multiple types
+ for(var/datum/wound/W in wounds)
+ dam_mul *= W.damage_mulitplier_penalty
+ wound_damage_multiplier = dam_mul
+ update_disabled()
-/obj/item/bodypart/l_arm/alien
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "alien_l_arm"
- px_x = 0
- px_y = 0
- dismemberable = 0
- max_damage = 100
- animal_origin = ALIEN_BODYPART
-
-/obj/item/bodypart/l_arm/devil
- dismemberable = 0
- max_damage = 5000
- animal_origin = DEVIL_BODYPART
-
-/obj/item/bodypart/r_arm
- name = "right arm"
- desc = "Over 87% of humans are right handed. That figure is much lower \
- among humans missing their right arm."
- icon_state = "default_human_r_arm"
- attack_verb = list("slapped", "punched")
- max_damage = 50
- body_zone = BODY_ZONE_R_ARM
- body_part = ARM_RIGHT
- aux_icons = list(BODY_ZONE_PRECISE_R_HAND = HANDS_PART_LAYER, "r_hand_behind" = BODY_BEHIND_LAYER)
- body_damage_coeff = 0.75
- held_index = 2
- px_x = 6
- px_y = 0
- stam_heal_tick = STAM_RECOVERY_LIMB
- max_stamina_damage = 50
-
-/obj/item/bodypart/r_arm/is_disabled()
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM))
- return BODYPART_DISABLED_PARALYSIS
- return ..()
-
-/obj/item/bodypart/r_arm/set_disabled(new_disabled)
- . = ..()
- if(!.)
+/obj/item/bodypart/proc/get_bleed_rate()
+ if(status != BODYPART_ORGANIC) // maybe in the future we can bleed oil from aug parts, but not now
return
- if(owner.stat < UNCONSCIOUS)
- switch(disabled)
- if(BODYPART_DISABLED_DAMAGE)
- owner.emote("scream")
- to_chat(owner, "Your [name] is too damaged to function!")
- if(BODYPART_DISABLED_PARALYSIS)
- to_chat(owner, "You can't feel your [name]!")
- if(held_index)
- owner.dropItemToGround(owner.get_item_for_held_index(held_index))
- if(owner.hud_used)
- var/obj/screen/inventory/hand/R = owner.hud_used.hand_slots["[held_index]"]
- if(R)
- R.update_icon()
+ var/bleed_rate = 0
+ if(generic_bleedstacks > 0)
+ bleed_rate++
+ if(brute_dam >= 40)
+ bleed_rate += (brute_dam * 0.008)
+ //We want an accurate reading of .len
+ listclearnulls(embedded_objects)
+ for(var/obj/item/embeddies in embedded_objects)
+ if(!embeddies.isEmbedHarmless())
+ bleed_rate += 0.5
-/obj/item/bodypart/r_arm/monkey
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "default_monkey_r_arm"
- animal_origin = MONKEY_BODYPART
- px_x = 5
- px_y = -3
+ for(var/thing in wounds)
+ var/datum/wound/W = thing
+ bleed_rate += W.blood_flow
-/obj/item/bodypart/r_arm/alien
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "alien_r_arm"
- px_x = 0
- px_y = 0
- dismemberable = 0
- max_damage = 100
- animal_origin = ALIEN_BODYPART
-
-/obj/item/bodypart/r_arm/devil
- dismemberable = 0
- max_damage = 5000
- animal_origin = DEVIL_BODYPART
-
-/obj/item/bodypart/l_leg
- name = "left leg"
- desc = "Some athletes prefer to tie their left shoelaces first for good \
- luck. In this instance, it probably would not have helped."
- icon_state = "default_human_l_leg"
- attack_verb = list("kicked", "stomped")
- max_damage = 50
- body_zone = BODY_ZONE_L_LEG
- body_part = LEG_LEFT
- body_damage_coeff = 0.75
- px_x = -2
- px_y = 12
- stam_heal_tick = STAM_RECOVERY_LIMB
- max_stamina_damage = 50
-
-/obj/item/bodypart/l_leg/is_disabled()
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG))
- return BODYPART_DISABLED_PARALYSIS
- return ..()
-
-/obj/item/bodypart/l_leg/set_disabled(new_disabled)
- . = ..()
- if(!. || owner.stat >= UNCONSCIOUS)
- return
- switch(disabled)
- if(BODYPART_DISABLED_DAMAGE)
- owner.emote("scream")
- to_chat(owner, "Your [name] is too damaged to function!")
- if(BODYPART_DISABLED_PARALYSIS)
- to_chat(owner, "You can't feel your [name]!")
-
-
-/obj/item/bodypart/l_leg/digitigrade
- name = "left digitigrade leg"
- use_digitigrade = FULL_DIGITIGRADE
-
-/obj/item/bodypart/l_leg/monkey
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "default_monkey_l_leg"
- animal_origin = MONKEY_BODYPART
- px_y = 4
-
-/obj/item/bodypart/l_leg/alien
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "alien_l_leg"
- px_x = 0
- px_y = 0
- dismemberable = 0
- max_damage = 100
- animal_origin = ALIEN_BODYPART
-
-/obj/item/bodypart/l_leg/devil
- dismemberable = 0
- max_damage = 5000
- animal_origin = DEVIL_BODYPART
-
-/obj/item/bodypart/r_leg
- name = "right leg"
- desc = "You put your right leg in, your right leg out. In, out, in, out, \
- shake it all about. And apparently then it detaches.\n\
- The hokey pokey has certainly changed a lot since space colonisation."
- // alternative spellings of 'pokey' are availible
- icon_state = "default_human_r_leg"
- attack_verb = list("kicked", "stomped")
- max_damage = 50
- body_zone = BODY_ZONE_R_LEG
- body_part = LEG_RIGHT
- body_damage_coeff = 0.75
- px_x = 2
- px_y = 12
- max_stamina_damage = 50
- stam_heal_tick = STAM_RECOVERY_LIMB
-
-/obj/item/bodypart/r_leg/is_disabled()
- if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG))
- return BODYPART_DISABLED_PARALYSIS
- return ..()
-
-/obj/item/bodypart/r_leg/set_disabled(new_disabled)
- . = ..()
- if(!. || owner.stat >= UNCONSCIOUS)
- return
- switch(disabled)
- if(BODYPART_DISABLED_DAMAGE)
- owner.emote("scream")
- to_chat(owner, "Your [name] is too damaged to function!")
- if(BODYPART_DISABLED_PARALYSIS)
- to_chat(owner, "You can't feel your [name]!")
-
-/obj/item/bodypart/r_leg/digitigrade
- name = "right digitigrade leg"
- use_digitigrade = FULL_DIGITIGRADE
-
-/obj/item/bodypart/r_leg/monkey
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "default_monkey_r_leg"
- animal_origin = MONKEY_BODYPART
- px_y = 4
-
-/obj/item/bodypart/r_leg/alien
- icon = 'icons/mob/animal_parts.dmi'
- icon_state = "alien_r_leg"
- px_x = 0
- px_y = 0
- dismemberable = 0
- max_damage = 100
- animal_origin = ALIEN_BODYPART
-
-/obj/item/bodypart/r_leg/devil
- dismemberable = 0
- max_damage = 5000
- animal_origin = DEVIL_BODYPART
+ return bleed_rate
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index f654270df7..2ce6ca8862 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -15,7 +15,7 @@
if(HAS_TRAIT(C, TRAIT_NODISMEMBER))
return FALSE
var/obj/item/bodypart/affecting = C.get_bodypart(BODY_ZONE_CHEST)
- affecting.receive_damage(clamp(brute_dam/2 * affecting.body_damage_coeff, 15, 50), clamp(burn_dam/2 * affecting.body_damage_coeff, 0, 50)) //Damage the chest based on limb's existing damage
+ affecting.receive_damage(clamp(brute_dam/2 * affecting.body_damage_coeff, 15, 50), clamp(burn_dam/2 * affecting.body_damage_coeff, 0, 50), wound_bonus=CANT_WOUND) //Damage the chest based on limb's existing damage
C.visible_message("[C]'s [src.name] has been violently dismembered!")
C.emote("scream")
SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "dismembered", /datum/mood_event/dismembered)
@@ -83,11 +83,12 @@
//limb removal. The "special" argument is used for swapping a limb with a new one without the effects of losing a limb kicking in.
-/obj/item/bodypart/proc/drop_limb(special)
+/obj/item/bodypart/proc/drop_limb(special, dismembered)
if(!owner)
return
var/atom/Tsec = owner.drop_location()
var/mob/living/carbon/C = owner
+ SEND_SIGNAL(C, COMSIG_CARBON_REMOVE_LIMB, src, dismembered)
update_limb(1)
C.bodyparts -= src
@@ -95,6 +96,15 @@
C.dropItemToGround(owner.get_item_for_held_index(held_index), 1)
C.hand_bodyparts[held_index] = null
+ for(var/thing in scars)
+ var/datum/scar/S = thing
+ S.victim = null
+ LAZYREMOVE(owner.all_scars, S)
+
+ for(var/thing in wounds)
+ var/datum/wound/W = thing
+ W.remove_wound(TRUE)
+
owner = null
for(var/X in C.surgeries) //if we had an ongoing surgery on that limb, we stop it.
@@ -298,6 +308,15 @@
for(var/obj/item/organ/O in contents)
O.Insert(C)
+ for(var/thing in scars)
+ var/datum/scar/S = thing
+ S.victim = C
+ LAZYADD(C.all_scars, thing)
+
+ for(var/i in wounds)
+ var/datum/wound/W = i
+ W.apply_wound(src, TRUE)
+
update_bodypart_damage_state()
update_disabled()
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index a74b1dad28..a67f16b934 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -35,6 +35,10 @@
//If the head is a special sprite
var/custom_head
+ wound_resistance = 10
+ specific_locations = list("left eyebrow", "cheekbone", "neck", "throat", "jawline", "entire face")
+ scars_covered_by_clothes = FALSE
+
/obj/item/bodypart/head/can_dismember(obj/item/I)
if(!((owner.stat == DEAD) || owner.InFullCritical()))
return FALSE
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index 29aca7166f..3161419449 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -10,6 +10,16 @@
if(L.body_zone == zone)
return L
+///Get the bodypart for whatever hand we have active, Only relevant for carbons
+/mob/proc/get_active_hand()
+ return FALSE
+
+/mob/living/carbon/get_active_hand()
+ var/which_hand = BODY_ZONE_PRECISE_L_HAND
+ if(!(active_hand_index % 2))
+ which_hand = BODY_ZONE_PRECISE_R_HAND
+ return get_bodypart(check_zone(which_hand))
+
/mob/living/carbon/has_hand_for_held_index(i)
if(i)
var/obj/item/bodypart/L = hand_bodyparts[i]
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
new file mode 100644
index 0000000000..bd4abaa765
--- /dev/null
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -0,0 +1,307 @@
+
+/obj/item/bodypart/chest
+ name = BODY_ZONE_CHEST
+ desc = "It's impolite to stare at a person's chest."
+ icon_state = "default_human_chest"
+ max_damage = 200
+ body_zone = BODY_ZONE_CHEST
+ body_part = CHEST
+ px_x = 0
+ px_y = 0
+ stam_damage_coeff = 1
+ max_stamina_damage = 120
+ var/obj/item/cavity_item
+ specific_locations = list("upper chest", "lower abdomen", "midsection", "collarbone", "lower back")
+ wound_resistance = 10
+
+/obj/item/bodypart/chest/can_dismember(obj/item/I)
+ if(!((owner.stat == DEAD) || owner.InFullCritical()))
+ return FALSE
+ return ..()
+
+/obj/item/bodypart/chest/Destroy()
+ QDEL_NULL(cavity_item)
+ return ..()
+
+/obj/item/bodypart/chest/drop_organs(mob/user, violent_removal)
+ if(cavity_item)
+ cavity_item.forceMove(drop_location())
+ cavity_item = null
+ ..()
+
+/obj/item/bodypart/chest/monkey
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "default_monkey_chest"
+ animal_origin = MONKEY_BODYPART
+ wound_resistance = -10
+
+/obj/item/bodypart/chest/alien
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "alien_chest"
+ dismemberable = 0
+ max_damage = 500
+ animal_origin = ALIEN_BODYPART
+
+/obj/item/bodypart/chest/devil
+ dismemberable = 0
+ max_damage = 5000
+ animal_origin = DEVIL_BODYPART
+
+/obj/item/bodypart/chest/larva
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "larva_chest"
+ dismemberable = 0
+ max_damage = 50
+ animal_origin = LARVA_BODYPART
+
+/obj/item/bodypart/l_arm
+ name = "left arm"
+ desc = "Did you know that the word 'sinister' stems originally from the \
+ Latin 'sinestra' (left hand), because the left hand was supposed to \
+ be possessed by the devil? This arm appears to be possessed by no \
+ one though."
+ icon_state = "default_human_l_arm"
+ attack_verb = list("slapped", "punched")
+ max_damage = 50
+ max_stamina_damage = 50
+ body_zone = BODY_ZONE_L_ARM
+ body_part = ARM_LEFT
+ aux_zone = BODY_ZONE_PRECISE_L_HAND
+ aux_layer = HANDS_PART_LAYER
+ body_damage_coeff = 0.75
+ held_index = 1
+ px_x = -6
+ px_y = 0
+ specific_locations = list("outer left forearm", "inner left wrist", "left elbow", "left bicep", "left shoulder")
+
+/obj/item/bodypart/l_arm/is_disabled()
+ if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM))
+ return BODYPART_DISABLED_PARALYSIS
+ return ..()
+
+/obj/item/bodypart/l_arm/set_disabled(new_disabled)
+ . = ..()
+ if(!.)
+ return
+ if(disabled == BODYPART_DISABLED_DAMAGE)
+ if(owner.stat < UNCONSCIOUS)
+ owner.emote("scream")
+ if(owner.stat < DEAD)
+ to_chat(owner, "Your [name] is too damaged to function!")
+ if(held_index)
+ owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+ else if(disabled == BODYPART_DISABLED_PARALYSIS)
+ if(owner.stat < DEAD)
+ to_chat(owner, "You can't feel your [name]!")
+ if(held_index)
+ owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+ if(owner.hud_used)
+ var/obj/screen/inventory/hand/L = owner.hud_used.hand_slots["[held_index]"]
+ if(L)
+ L.update_icon()
+
+/obj/item/bodypart/l_arm/monkey
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "default_monkey_l_arm"
+ animal_origin = MONKEY_BODYPART
+ wound_resistance = -10
+ px_x = -5
+ px_y = -3
+
+/obj/item/bodypart/l_arm/alien
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "alien_l_arm"
+ px_x = 0
+ px_y = 0
+ dismemberable = 0
+ max_damage = 100
+ animal_origin = ALIEN_BODYPART
+
+/obj/item/bodypart/l_arm/devil
+ dismemberable = 0
+ max_damage = 5000
+ animal_origin = DEVIL_BODYPART
+
+/obj/item/bodypart/r_arm
+ name = "right arm"
+ desc = "Over 87% of humans are right handed. That figure is much lower \
+ among humans missing their right arm."
+ icon_state = "default_human_r_arm"
+ attack_verb = list("slapped", "punched")
+ max_damage = 50
+ body_zone = BODY_ZONE_R_ARM
+ body_part = ARM_RIGHT
+ aux_zone = BODY_ZONE_PRECISE_R_HAND
+ aux_layer = HANDS_PART_LAYER
+ body_damage_coeff = 0.75
+ held_index = 2
+ px_x = 6
+ px_y = 0
+ max_stamina_damage = 50
+ specific_locations = list("outer right forearm", "inner right wrist", "right elbow", "right bicep", "right shoulder")
+
+/obj/item/bodypart/r_arm/is_disabled()
+ if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM))
+ return BODYPART_DISABLED_PARALYSIS
+ return ..()
+
+/obj/item/bodypart/r_arm/set_disabled(new_disabled)
+ . = ..()
+ if(!.)
+ return
+ if(disabled == BODYPART_DISABLED_DAMAGE)
+ if(owner.stat < UNCONSCIOUS)
+ owner.emote("scream")
+ if(owner.stat < DEAD)
+ to_chat(owner, "Your [name] is too damaged to function!")
+ if(held_index)
+ owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+ else if(disabled == BODYPART_DISABLED_PARALYSIS)
+ if(owner.stat < DEAD)
+ to_chat(owner, "You can't feel your [name]!")
+ if(held_index)
+ owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+ if(owner.hud_used)
+ var/obj/screen/inventory/hand/R = owner.hud_used.hand_slots["[held_index]"]
+ if(R)
+ R.update_icon()
+
+/obj/item/bodypart/r_arm/monkey
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "default_monkey_r_arm"
+ animal_origin = MONKEY_BODYPART
+ wound_resistance = -10
+ px_x = 5
+ px_y = -3
+
+/obj/item/bodypart/r_arm/alien
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "alien_r_arm"
+ px_x = 0
+ px_y = 0
+ dismemberable = 0
+ max_damage = 100
+ animal_origin = ALIEN_BODYPART
+
+/obj/item/bodypart/r_arm/devil
+ dismemberable = 0
+ max_damage = 5000
+ animal_origin = DEVIL_BODYPART
+
+/obj/item/bodypart/l_leg
+ name = "left leg"
+ desc = "Some athletes prefer to tie their left shoelaces first for good \
+ luck. In this instance, it probably would not have helped."
+ icon_state = "default_human_l_leg"
+ attack_verb = list("kicked", "stomped")
+ max_damage = 50
+ body_zone = BODY_ZONE_L_LEG
+ body_part = LEG_LEFT
+ body_damage_coeff = 0.75
+ px_x = -2
+ px_y = 12
+ max_stamina_damage = 50
+ specific_locations = list("inner left thigh", "outer left calf", "outer left hip", " left kneecap", "lower left shin")
+
+/obj/item/bodypart/l_leg/is_disabled()
+ if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG))
+ return BODYPART_DISABLED_PARALYSIS
+ return ..()
+
+/obj/item/bodypart/l_leg/set_disabled(new_disabled)
+ . = ..()
+ if(!.)
+ return
+ if(disabled == BODYPART_DISABLED_DAMAGE)
+ if(owner.stat < UNCONSCIOUS)
+ owner.emote("scream")
+ if(owner.stat < DEAD)
+ to_chat(owner, "Your [name] is too damaged to function!")
+ else if(disabled == BODYPART_DISABLED_PARALYSIS)
+ if(owner.stat < DEAD)
+ to_chat(owner, "You can't feel your [name]!")
+
+/obj/item/bodypart/l_leg/digitigrade
+ name = "left digitigrade leg"
+ use_digitigrade = FULL_DIGITIGRADE
+
+/obj/item/bodypart/l_leg/monkey
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "default_monkey_l_leg"
+ animal_origin = MONKEY_BODYPART
+ wound_resistance = -10
+ px_y = 4
+
+/obj/item/bodypart/l_leg/alien
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "alien_l_leg"
+ px_x = 0
+ px_y = 0
+ dismemberable = 0
+ max_damage = 100
+ animal_origin = ALIEN_BODYPART
+
+/obj/item/bodypart/l_leg/devil
+ dismemberable = 0
+ max_damage = 5000
+ animal_origin = DEVIL_BODYPART
+
+/obj/item/bodypart/r_leg
+ name = "right leg"
+ desc = "You put your right leg in, your right leg out. In, out, in, out, \
+ shake it all about. And apparently then it detaches.\n\
+ The hokey pokey has certainly changed a lot since space colonisation."
+ // alternative spellings of 'pokey' are availible
+ icon_state = "default_human_r_leg"
+ attack_verb = list("kicked", "stomped")
+ max_damage = 50
+ body_zone = BODY_ZONE_R_LEG
+ body_part = LEG_RIGHT
+ body_damage_coeff = 0.75
+ px_x = 2
+ px_y = 12
+ max_stamina_damage = 50
+ specific_locations = list("inner right thigh", "outer right calf", "outer right hip", "right kneecap", "lower right shin")
+
+/obj/item/bodypart/r_leg/is_disabled()
+ if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG))
+ return BODYPART_DISABLED_PARALYSIS
+ return ..()
+
+/obj/item/bodypart/r_leg/set_disabled(new_disabled)
+ . = ..()
+ if(!.)
+ return
+ if(disabled == BODYPART_DISABLED_DAMAGE)
+ if(owner.stat < UNCONSCIOUS)
+ owner.emote("scream")
+ if(owner.stat < DEAD)
+ to_chat(owner, "Your [name] is too damaged to function!")
+ else if(disabled == BODYPART_DISABLED_PARALYSIS)
+ if(owner.stat < DEAD)
+ to_chat(owner, "You can't feel your [name]!")
+
+/obj/item/bodypart/r_leg/digitigrade
+ name = "right digitigrade leg"
+ use_digitigrade = FULL_DIGITIGRADE
+
+/obj/item/bodypart/r_leg/monkey
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "default_monkey_r_leg"
+ animal_origin = MONKEY_BODYPART
+ wound_resistance = -10
+ px_y = 4
+
+/obj/item/bodypart/r_leg/alien
+ icon = 'icons/mob/animal_parts.dmi'
+ icon_state = "alien_r_leg"
+ px_x = 0
+ px_y = 0
+ dismemberable = 0
+ max_damage = 100
+ animal_origin = ALIEN_BODYPART
+
+/obj/item/bodypart/r_leg/devil
+ dismemberable = 0
+ max_damage = 5000
+ animal_origin = DEVIL_BODYPART
diff --git a/code/modules/surgery/bone_mending.dm b/code/modules/surgery/bone_mending.dm
new file mode 100644
index 0000000000..6afe494105
--- /dev/null
+++ b/code/modules/surgery/bone_mending.dm
@@ -0,0 +1,142 @@
+
+/////BONE FIXING SURGERIES//////
+
+///// Repair Hairline Fracture (Severe)
+/datum/surgery/repair_bone_hairline
+ name = "Repair bone fracture (hairline)"
+ steps = list(/datum/surgery_step/incise, /datum/surgery_step/repair_bone_hairline, /datum/surgery_step/close)
+ target_mobtypes = list(/mob/living/carbon/human)
+ possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD)
+ requires_real_bodypart = TRUE
+ targetable_wound = /datum/wound/brute/bone/severe
+
+/datum/surgery/repair_bone_hairline/can_start(mob/living/user, mob/living/carbon/target)
+ if(..())
+ var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected)
+ return(targeted_bodypart.get_wound_type(targetable_wound))
+
+
+///// Repair Compound Fracture (Critical)
+/datum/surgery/repair_bone_compound
+ name = "Repair Compound Fracture"
+ steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/reset_compound_fracture, /datum/surgery_step/repair_bone_compound, /datum/surgery_step/close)
+ target_mobtypes = list(/mob/living/carbon/human)
+ possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD)
+ requires_real_bodypart = TRUE
+ targetable_wound = /datum/wound/brute/bone/critical
+
+/datum/surgery/repair_bone_compound/can_start(mob/living/user, mob/living/carbon/target)
+ if(..())
+ var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected)
+ return(targeted_bodypart.get_wound_type(targetable_wound))
+
+
+
+//SURGERY STEPS
+
+///// Repair Hairline Fracture (Severe)
+/datum/surgery_step/repair_bone_hairline
+ name = "repair hairline fracture (bonesetter/bone gel/tape)"
+ implements = list(/obj/item/bonesetter = 100, /obj/item/stack/medical/bone_gel = 100, /obj/item/stack/sticky_tape/surgical = 100, /obj/item/stack/sticky_tape/super = 50, /obj/item/stack/sticky_tape = 30)
+ time = 40
+ experience_given = MEDICAL_SKILL_MEDIUM
+
+/datum/surgery_step/repair_bone_hairline/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(surgery.operated_wound)
+ display_results(user, target, "You begin to repair the fracture in [target]'s [parse_zone(user.zone_selected)]...",
+ "[user] begins to repair the fracture in [target]'s [parse_zone(user.zone_selected)] with [tool].",
+ "[user] begins to repair the fracture in [target]'s [parse_zone(user.zone_selected)].")
+ else
+ user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...")
+
+/datum/surgery_step/repair_bone_hairline/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE)
+ if(surgery.operated_wound)
+ if(istype(tool, /obj/item/stack))
+ var/obj/item/stack/used_stack = tool
+ used_stack.use(1)
+ display_results(user, target, "You successfully repair the fracture in [target]'s [parse_zone(target_zone)].",
+ "[user] successfully repairs the fracture in [target]'s [parse_zone(target_zone)] with [tool]!",
+ "[user] successfully repairs the fracture in [target]'s [parse_zone(target_zone)]!")
+ log_combat(user, target, "repaired a hairline fracture in", addition="INTENT: [uppertext(user.a_intent)]")
+ qdel(surgery.operated_wound)
+ else
+ to_chat(user, "[target] has no hairline fracture there!")
+ return ..()
+
+/datum/surgery_step/repair_bone_hairline/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0)
+ ..()
+ if(istype(tool, /obj/item/stack))
+ var/obj/item/stack/used_stack = tool
+ used_stack.use(1)
+
+
+
+///// Reset Compound Fracture (Crticial)
+/datum/surgery_step/reset_compound_fracture
+ name = "reset bone"
+ implements = list(/obj/item/bonesetter = 100, /obj/item/stack/sticky_tape/surgical = 60, /obj/item/stack/sticky_tape/super = 40, /obj/item/stack/sticky_tape = 20)
+ time = 40
+ experience_given = MEDICAL_SKILL_MEDIUM
+
+/datum/surgery_step/reset_compound_fracture/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(surgery.operated_wound)
+ display_results(user, target, "You begin to reset the bone in [target]'s [parse_zone(user.zone_selected)]...",
+ "[user] begins to reset the bone in [target]'s [parse_zone(user.zone_selected)] with [tool].",
+ "[user] begins to reset the bone in [target]'s [parse_zone(user.zone_selected)].")
+ else
+ user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...")
+
+/datum/surgery_step/reset_compound_fracture/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE)
+ if(surgery.operated_wound)
+ if(istype(tool, /obj/item/stack))
+ var/obj/item/stack/used_stack = tool
+ used_stack.use(1)
+ display_results(user, target, "You successfully reset the bone in [target]'s [parse_zone(target_zone)].",
+ "[user] successfully resets the bone in [target]'s [parse_zone(target_zone)] with [tool]!",
+ "[user] successfully resets the bone in [target]'s [parse_zone(target_zone)]!")
+ log_combat(user, target, "reset a compound fracture in", addition="INTENT: [uppertext(user.a_intent)]")
+ else
+ to_chat(user, "[target] has no compound fracture there!")
+ return ..()
+
+/datum/surgery_step/reset_compound_fracture/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0)
+ ..()
+ if(istype(tool, /obj/item/stack))
+ var/obj/item/stack/used_stack = tool
+ used_stack.use(1)
+
+
+///// Repair Compound Fracture (Crticial)
+/datum/surgery_step/repair_bone_compound
+ name = "repair compound fracture (bone gel/tape)"
+ implements = list(/obj/item/stack/medical/bone_gel = 100, /obj/item/stack/sticky_tape/surgical = 100, /obj/item/stack/sticky_tape/super = 50, /obj/item/stack/sticky_tape = 30)
+ time = 40
+ experience_given = MEDICAL_SKILL_MEDIUM
+
+/datum/surgery_step/repair_bone_compound/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(surgery.operated_wound)
+ display_results(user, target, "You begin to repair the fracture in [target]'s [parse_zone(user.zone_selected)]...",
+ "[user] begins to repair the fracture in [target]'s [parse_zone(user.zone_selected)] with [tool].",
+ "[user] begins to repair the fracture in [target]'s [parse_zone(user.zone_selected)].")
+ else
+ user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...")
+
+/datum/surgery_step/repair_bone_compound/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE)
+ if(surgery.operated_wound)
+ if(istype(tool, /obj/item/stack))
+ var/obj/item/stack/used_stack = tool
+ used_stack.use(1)
+ display_results(user, target, "You successfully repair the fracture in [target]'s [parse_zone(target_zone)].",
+ "[user] successfully repairs the fracture in [target]'s [parse_zone(target_zone)] with [tool]!",
+ "[user] successfully repairs the fracture in [target]'s [parse_zone(target_zone)]!")
+ log_combat(user, target, "repaired a compound fracture in", addition="INTENT: [uppertext(user.a_intent)]")
+ qdel(surgery.operated_wound)
+ else
+ to_chat(user, "[target] has no compound fracture there!")
+ return ..()
+
+/datum/surgery_step/repair_bone_compound/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0)
+ ..()
+ if(istype(tool, /obj/item/stack))
+ var/obj/item/stack/used_stack = tool
+ used_stack.use(1)
diff --git a/code/modules/surgery/burn_dressing.dm b/code/modules/surgery/burn_dressing.dm
new file mode 100644
index 0000000000..1d64d29ff6
--- /dev/null
+++ b/code/modules/surgery/burn_dressing.dm
@@ -0,0 +1,108 @@
+
+/////BURN FIXING SURGERIES//////
+
+///// Debride burnt flesh
+/datum/surgery/debride
+ name = "Debride infected flesh"
+ steps = list(/datum/surgery_step/debride, /datum/surgery_step/dress)
+ target_mobtypes = list(/mob/living/carbon/human)
+ possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD)
+ requires_real_bodypart = TRUE
+ targetable_wound = /datum/wound/burn
+
+/datum/surgery/debride/can_start(mob/living/user, mob/living/carbon/target)
+ if(..())
+ var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected)
+ var/datum/wound/burn/burn_wound = targeted_bodypart.get_wound_type(targetable_wound)
+ return(burn_wound && burn_wound.infestation > 0)
+
+//SURGERY STEPS
+
+///// Debride
+/datum/surgery_step/debride
+ name = "excise infection"
+ implements = list(TOOL_HEMOSTAT = 100, TOOL_SCALPEL = 85, TOOL_SAW = 60, TOOL_WIRECUTTER = 40)
+ time = 30
+ repeatable = TRUE
+ experience_given = MEDICAL_SKILL_MEDIUM
+
+/datum/surgery_step/debride/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(surgery.operated_wound)
+ var/datum/wound/burn/burn_wound = surgery.operated_wound
+ if(burn_wound.infestation <= 0)
+ to_chat(user, "[target]'s [parse_zone(user.zone_selected)] has no infected flesh to remove!")
+ surgery.status++
+ repeatable = FALSE
+ return
+ display_results(user, target, "You begin to excise infected flesh from [target]'s [parse_zone(user.zone_selected)]...",
+ "[user] begins to excise infected flesh from [target]'s [parse_zone(user.zone_selected)] with [tool].",
+ "[user] begins to excise infected flesh from [target]'s [parse_zone(user.zone_selected)].")
+ else
+ user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...")
+
+/datum/surgery_step/debride/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE)
+ var/datum/wound/burn/burn_wound = surgery.operated_wound
+ if(burn_wound)
+ display_results(user, target, "You successfully excise some of the infected flesh from [target]'s [parse_zone(target_zone)].",
+ "[user] successfully excises some of the infected flesh from [target]'s [parse_zone(target_zone)] with [tool]!",
+ "[user] successfully excises some of the infected flesh from [target]'s [parse_zone(target_zone)]!")
+ log_combat(user, target, "excised infected flesh in", addition="INTENT: [uppertext(user.a_intent)]")
+ surgery.operated_bodypart.receive_damage(brute=3, wound_bonus=CANT_WOUND)
+ burn_wound.infestation -= 0.5
+ burn_wound.sanitization += 0.5
+ if(burn_wound.infestation <= 0)
+ repeatable = FALSE
+ else
+ to_chat(user, "[target] has no infected flesh there!")
+ return ..()
+
+/datum/surgery_step/debride/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0)
+ ..()
+ display_results(user, target, "You carve away some of the healthy flesh from [target]'s [parse_zone(target_zone)].",
+ "[user] carves away some of the healthy flesh from [target]'s [parse_zone(target_zone)] with [tool]!",
+ "[user] carves away some of the healthy flesh from [target]'s [parse_zone(target_zone)]!")
+ surgery.operated_bodypart.receive_damage(brute=rand(4,8), sharpness=TRUE)
+
+/datum/surgery_step/debride/initiate(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, try_to_fail = FALSE)
+ if(!..())
+ return
+ var/datum/wound/burn/burn_wound = surgery.operated_wound
+ while(burn_wound && burn_wound.infestation > 0.25)
+ if(!..())
+ break
+
+///// Dressing burns
+/datum/surgery_step/dress
+ name = "bandage burns"
+ implements = list(/obj/item/stack/medical/gauze = 100, /obj/item/stack/sticky_tape/surgical = 100)
+ time = 40
+ experience_given = MEDICAL_SKILL_MEDIUM
+
+/datum/surgery_step/dress/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/datum/wound/burn/burn_wound = surgery.operated_wound
+ if(burn_wound)
+ display_results(user, target, "You begin to dress the burns on [target]'s [parse_zone(user.zone_selected)]...",
+ "[user] begins to dress the burns on [target]'s [parse_zone(user.zone_selected)] with [tool].",
+ "[user] begins to dress the burns on [target]'s [parse_zone(user.zone_selected)].")
+ else
+ user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...")
+
+/datum/surgery_step/dress/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE)
+ var/datum/wound/burn/burn_wound = surgery.operated_wound
+ if(burn_wound)
+ display_results(user, target, "You successfully wrap [target]'s [parse_zone(target_zone)] with [tool].",
+ "[user] successfully wraps [target]'s [parse_zone(target_zone)] with [tool]!",
+ "[user] successfully wraps [target]'s [parse_zone(target_zone)]!")
+ log_combat(user, target, "dressed burns in", addition="INTENT: [uppertext(user.a_intent)]")
+ burn_wound.sanitization += 3
+ burn_wound.flesh_healing += 5
+ burn_wound.force_bandage(tool)
+ else
+ to_chat(user, "[target] has no burns there!")
+ return ..()
+
+/datum/surgery_step/dress/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0)
+ ..()
+ if(istype(tool, /obj/item/stack))
+ var/obj/item/stack/used_stack = tool
+ used_stack.use(1)
diff --git a/code/modules/surgery/coronary_bypass.dm b/code/modules/surgery/coronary_bypass.dm
index af736e393c..d1d478ddfe 100644
--- a/code/modules/surgery/coronary_bypass.dm
+++ b/code/modules/surgery/coronary_bypass.dm
@@ -31,7 +31,8 @@
display_results(user, target, "Blood pools around the incision in [H]'s heart.",
"Blood pools around the incision in [H]'s heart.",
"")
- H.bleed_rate += 10
+ var/obj/item/bodypart/BP = H.get_bodypart(target_zone)
+ BP.generic_bleedstacks += 10
H.adjustBruteLoss(10)
return TRUE
@@ -41,7 +42,8 @@
display_results(user, target, "You screw up, cutting too deeply into the heart!",
"[user] screws up, causing blood to spurt out of [H]'s chest!",
"[user] screws up, causing blood to spurt out of [H]'s chest!")
- H.bleed_rate += 20
+ var/obj/item/bodypart/BP = H.get_bodypart(target_zone)
+ BP.generic_bleedstacks += 10
H.adjustOrganLoss(ORGAN_SLOT_HEART, 10)
H.adjustBruteLoss(10)
@@ -73,5 +75,6 @@
"[user] screws up, causing blood to spurt out of [H]'s chest profusely!",
"[user] screws up, causing blood to spurt out of [H]'s chest profusely!")
H.adjustOrganLoss(ORGAN_SLOT_HEART, 20)
- H.bleed_rate += 30
+ var/obj/item/bodypart/BP = H.get_bodypart(target_zone)
+ BP.generic_bleedstacks += 30
return FALSE
diff --git a/code/modules/surgery/experimental_dissection.dm b/code/modules/surgery/experimental_dissection.dm
index 6110bb6202..b9d877d0c1 100644
--- a/code/modules/surgery/experimental_dissection.dm
+++ b/code/modules/surgery/experimental_dissection.dm
@@ -78,7 +78,7 @@
"[user] dissects [target]!")
SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = points_earned))
var/obj/item/bodypart/L = target.get_bodypart(BODY_ZONE_CHEST)
- target.apply_damage(80, BRUTE, L)
+ target.apply_damage(80, BRUTE, L, wound_bonus=CANT_WOUND)
ADD_TRAIT(target, TRAIT_DISSECTED, "[surgery.name]")
repeatable = FALSE
return TRUE
@@ -89,7 +89,7 @@
"[user] dissects [target], but looks a little dissapointed.")
SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = (round(check_value(target, surgery) * 0.01))))
var/obj/item/bodypart/L = target.get_bodypart(BODY_ZONE_CHEST)
- target.apply_damage(80, BRUTE, L)
+ target.apply_damage(80, BRUTE, L, wound_bonus=CANT_WOUND)
return TRUE
/datum/surgery/advanced/experimental_dissection/adv
diff --git a/code/modules/surgery/healing.dm b/code/modules/surgery/healing.dm
index 4069199864..0680dda404 100644
--- a/code/modules/surgery/healing.dm
+++ b/code/modules/surgery/healing.dm
@@ -87,7 +87,7 @@
urdamageamt_brute += round((target.getBruteLoss()/ (missinghpbonus*2)),0.1)
urdamageamt_burn += round((target.getFireLoss()/ (missinghpbonus*2)),0.1)
- target.take_bodypart_damage(urdamageamt_brute, urdamageamt_burn)
+ target.take_bodypart_damage(urdamageamt_brute, urdamageamt_burn, wound_bonus=CANT_WOUND)
return FALSE
/***************************BRUTE***************************/
diff --git a/code/modules/surgery/organic_steps.dm b/code/modules/surgery/organic_steps.dm
index 0b38ecc2fe..8e893875a9 100644
--- a/code/modules/surgery/organic_steps.dm
+++ b/code/modules/surgery/organic_steps.dm
@@ -21,7 +21,9 @@
display_results(user, target, "Blood pools around the incision in [H]'s [parse_zone(target_zone)].",
"Blood pools around the incision in [H]'s [parse_zone(target_zone)].",
"")
- H.bleed_rate += 3
+ var/obj/item/bodypart/BP = target.get_bodypart(target_zone)
+ if(BP)
+ BP.generic_bleedstacks += 10
return TRUE
/datum/surgery_step/incise/nobleed //silly friendly!
@@ -50,7 +52,9 @@
target.heal_bodypart_damage(20,0)
if (ishuman(target))
var/mob/living/carbon/human/H = target
- H.bleed_rate = max( (H.bleed_rate - 3), 0)
+ var/obj/item/bodypart/BP = H.get_bodypart(target_zone)
+ if(BP)
+ BP.generic_bleedstacks -= 3
return ..()
//retract skin
/datum/surgery_step/retract_skin
@@ -86,7 +90,9 @@
target.heal_bodypart_damage(45,0)
if (ishuman(target))
var/mob/living/carbon/human/H = target
- H.bleed_rate = max( (H.bleed_rate - 3), 0)
+ var/obj/item/bodypart/BP = H.get_bodypart(target_zone)
+ if(BP)
+ BP.generic_bleedstacks -= 3
return ..()
//saw bone
/datum/surgery_step/saw
@@ -100,7 +106,7 @@
"[user] begins to saw through the bone in [target]'s [parse_zone(target_zone)].")
/datum/surgery_step/saw/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- target.apply_damage(50, BRUTE, "[target_zone]")
+ target.apply_damage(50, BRUTE, "[target_zone]", wound_bonus=CANT_WOUND)
display_results(user, target, "You saw [target]'s [parse_zone(target_zone)] open.",
"[user] saws [target]'s [parse_zone(target_zone)] open!",
"[user] saws [target]'s [parse_zone(target_zone)] open!")
diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm
index fa49e3eff5..b6b74efe32 100644
--- a/code/modules/surgery/organs/vocal_cords.dm
+++ b/code/modules/surgery/organs/vocal_cords.dm
@@ -318,13 +318,14 @@
cooldown = COOLDOWN_DAMAGE
for(var/V in listeners)
var/mob/living/L = V
- L.apply_damage(15 * power_multiplier, def_zone = BODY_ZONE_CHEST)
+ L.apply_damage(15 * power_multiplier, def_zone = BODY_ZONE_CHEST, wound_bonus=CANT_WOUND)
//BLEED
else if((findtext(message, bleed_words)))
cooldown = COOLDOWN_DAMAGE
for(var/mob/living/carbon/human/H in listeners)
- H.bleed_rate += (5 * power_multiplier)
+ var/obj/item/bodypart/BP = pick(H.bodyparts)
+ BP.generic_bleedstacks += 5
//FIRE
else if((findtext(message, burn_words)))
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index 51ce2726b0..4d42cb6c23 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -18,6 +18,8 @@
var/lying_required = TRUE //Does the vicitm needs to be lying down.
var/requires_tech = FALSE
var/replaced_by
+ var/datum/wound/operated_wound //The actual wound datum instance we're targeting
+ var/datum/wound/targetable_wound //The wound type this surgery targets
/datum/surgery/New(surgery_target, surgery_location, surgery_bodypart)
..()
@@ -28,8 +30,13 @@
location = surgery_location
if(surgery_bodypart)
operated_bodypart = surgery_bodypart
+ if(targetable_wound)
+ operated_wound = operated_bodypart.get_wound_type(targetable_wound)
+ operated_wound.attached_surgery = src
/datum/surgery/Destroy()
+ if(operated_wound)
+ operated_wound.attached_surgery = null
if(target)
target.surgeries -= src
target = null
diff --git a/tgstation.dme b/tgstation.dme
index 9c1f8a2378..dd57ad97a2 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -654,6 +654,7 @@
#include "code\datums\wires\tesla_coil.dm"
#include "code\datums\wires\vending.dm"
#include "code\datums\wounds\_scars.dm"
+#include "code\datums\wounds\_wounds.dm"
#include "code\datums\wounds\cuts.dm"
#include "code\game\alternate_appearance.dm"
#include "code\game\atoms.dm"
@@ -3256,7 +3257,9 @@
#include "code\modules\station_goals\shield.dm"
#include "code\modules\station_goals\station_goal.dm"
#include "code\modules\surgery\amputation.dm"
+#include "code\modules\surgery\bone_mending.dm"
#include "code\modules\surgery\brain_surgery.dm"
+#include "code\modules\surgery\burn_dressing.dm"
#include "code\modules\surgery\cavity_implant.dm"
#include "code\modules\surgery\core_removal.dm"
#include "code\modules\surgery\coronary_bypass.dm"
@@ -3297,10 +3300,11 @@
#include "code\modules\surgery\advanced\bioware\nerve_grounding.dm"
#include "code\modules\surgery\advanced\bioware\nerve_splicing.dm"
#include "code\modules\surgery\advanced\bioware\vein_threading.dm"
-#include "code\modules\surgery\bodyparts\bodyparts.dm"
+#include "code\modules\surgery\bodyparts\_bodyparts.dm"
#include "code\modules\surgery\bodyparts\dismemberment.dm"
#include "code\modules\surgery\bodyparts\head.dm"
#include "code\modules\surgery\bodyparts\helpers.dm"
+#include "code\modules\surgery\bodyparts\parts.dm"
#include "code\modules\surgery\bodyparts\robot_bodyparts.dm"
#include "code\modules\surgery\organs\appendix.dm"
#include "code\modules\surgery\organs\augments_arms.dm"