Makes wound treatment with medical stack items less jank (#88257)

This commit is contained in:
MrMelbert
2024-12-12 13:03:49 -06:00
committed by GitHub
parent 2aba23180b
commit 0c3316983f
8 changed files with 339 additions and 257 deletions

View File

@@ -303,8 +303,8 @@ GLOBAL_LIST_INIT(biotypes_to_scar_file, list(
// ~random wound balance defines
/// how quickly sanitization removes infestation and decays per second
#define WOUND_BURN_SANITIZATION_RATE 0.075
/// how much blood you can lose per tick per slash max.
#define WOUND_SLASH_MAX_BLOODFLOW 4.5
/// how much blood you can lose per tick per wound max.
#define WOUND_MAX_BLOODFLOW 4.5
/// further slash attacks on a bodypart with a slash wound have their blood_flow further increased by damage * this (10 damage slash adds .25 flow)
#define WOUND_SLASH_DAMAGE_FLOW_COEFF 0.025
/// if we suffer a bone wound to the head that creates brain traumas, the timer for the trauma cycle is +/- by this percent (0-100)

View File

@@ -159,7 +159,7 @@
if(4 to INFINITY)
bandage_condition = "clean"
condition += " underneath a dressing of [bandage_condition] [limb.current_gauze.name]"
condition += " underneath a dressing of [bandage_condition] [limb.current_gauze.name]."
else
switch(infestation)
if(WOUND_INFECTION_MODERATE to WOUND_INFECTION_SEVERE)
@@ -223,25 +223,13 @@
new burn common procs
*/
/// if someone is using ointment or mesh on our burns
/datum/wound/burn/flesh/proc/ointmentmesh(obj/item/stack/medical/I, mob/user)
user.visible_message(span_notice("[user] begins applying [I] to [victim]'s [limb.plaintext_zone]..."), span_notice("You begin applying [I] to [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone]..."))
if (I.amount <= 0)
/// Checks if the wound is in a state that ointment or flesh will help
/datum/wound/burn/flesh/proc/can_be_ointmented_or_meshed()
if(infestation > 0 || sanitization < infestation)
return TRUE
if(!do_after(user, (user == victim ? I.self_delay : I.other_delay), target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
if(flesh_damage > 0 || flesh_healing <= flesh_damage)
return TRUE
limb.heal_damage(I.heal_brute, I.heal_burn)
user.visible_message(span_green("[user] applies [I] to [victim]."), span_green("You apply [I] to [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone]."))
I.use(1)
sanitization += I.sanitization
flesh_healing += I.flesh_regeneration
if((infestation <= 0 || sanitization >= infestation) && (flesh_damage <= 0 || flesh_healing > flesh_damage))
to_chat(user, span_notice("You've done all you can with [I], now you must wait for the flesh on [victim]'s [limb.plaintext_zone] to recover."))
return TRUE
else
return try_treating(I, user)
return FALSE
/// Paramedic UV penlights
/datum/wound/burn/flesh/proc/uv(obj/item/flashlight/pen/paramedic/I, mob/user)
@@ -258,15 +246,7 @@
return TRUE
/datum/wound/burn/flesh/treat(obj/item/I, mob/user)
if(istype(I, /obj/item/stack/medical/ointment))
return ointmentmesh(I, user)
else if(istype(I, /obj/item/stack/medical/mesh))
var/obj/item/stack/medical/mesh/mesh_check = I
if(!mesh_check.is_open)
to_chat(user, span_warning("You need to open [mesh_check] first."))
return
return ointmentmesh(mesh_check, user)
else if(istype(I, /obj/item/flashlight/pen/paramedic))
if(istype(I, /obj/item/flashlight/pen/paramedic))
return uv(I, user)
// people complained about burns not healing on stasis beds, so in addition to checking if it's cured, they also get the special ability to very slowly heal on stasis beds if they have the healing effects stored

View File

@@ -80,11 +80,12 @@
if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return
set_blood_flow(min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW))
if(limb.can_bleed())
if(victim.bodytemperature < (BODYTEMP_NORMAL - 10))
adjust_blood_flow(-0.1 * seconds_per_tick)
if(QDELETED(src))
return
if(SPT_PROB(2.5, seconds_per_tick))
to_chat(victim, span_notice("You feel the [LOWER_TEXT(name)] in your [limb.plaintext_zone] firming up from the cold!"))
@@ -92,15 +93,16 @@
adjust_blood_flow(0.25 * seconds_per_tick) // old heparin used to just add +2 bleed stacks per tick, this adds 0.5 bleed flow to all open cuts which is probably even stronger as long as you can cut them first
if(limb.current_gauze)
adjust_blood_flow(-limb.current_gauze.absorption_rate * gauzed_clot_rate * seconds_per_tick)
limb.current_gauze.absorption_capacity -= limb.current_gauze.absorption_rate * seconds_per_tick
var/gauze_power = limb.current_gauze.absorption_rate
limb.seep_gauze(gauze_power * seconds_per_tick)
adjust_blood_flow(-gauze_power * gauzed_clot_rate * seconds_per_tick)
if(blood_flow <= 0)
qdel(src)
/datum/wound/pierce/bleed/on_stasis(seconds_per_tick, times_fired)
/datum/wound/pierce/bleed/adjust_blood_flow(adjust_by, minimum)
. = ..()
if(blood_flow <= 0)
if(blood_flow > WOUND_MAX_BLOODFLOW)
blood_flow = WOUND_MAX_BLOODFLOW
if(blood_flow <= 0 && !QDELETED(src))
to_chat(victim, span_green("The holes on your [limb.plaintext_zone] have [!limb.can_bleed() ? "healed up" : "stopped bleeding"]!"))
qdel(src)
/datum/wound/pierce/bleed/check_grab_treatments(obj/item/I, mob/user)
@@ -108,9 +110,7 @@
return TRUE
/datum/wound/pierce/bleed/treat(obj/item/I, mob/user)
if(istype(I, /obj/item/stack/medical/suture))
return suture(I, user)
else if(I.tool_behaviour == TOOL_CAUTERY || I.get_temperature())
if(I.tool_behaviour == TOOL_CAUTERY || I.get_temperature())
return tool_cauterize(I, user)
/datum/wound/pierce/bleed/on_xadone(power)
@@ -123,32 +123,6 @@
. = ..()
adjust_blood_flow(-0.025 * reac_volume) // 20u * 0.05 = -1 blood flow, less than with slashes but still good considering smaller bleed rates
/// If someone is using a suture to close this puncture
/datum/wound/pierce/bleed/proc/suture(obj/item/stack/medical/suture/I, mob/user)
var/self_penalty_mult = (user == victim ? 1.4 : 1)
var/treatment_delay = base_treat_time * self_penalty_mult
if(HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
treatment_delay *= 0.5
user.visible_message(span_notice("[user] begins expertly stitching [victim]'s [limb.plaintext_zone] with [I]..."), span_notice("You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone] with [I], keeping the holo-image information in mind..."))
else
user.visible_message(span_notice("[user] begins stitching [victim]'s [limb.plaintext_zone] with [I]..."), span_notice("You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone] with [I]..."))
if(!do_after(user, treatment_delay, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
return TRUE
var/bleeding_wording = (!limb.can_bleed() ? "holes" : "bleeding")
user.visible_message(span_green("[user] stitches up some of the [bleeding_wording] on [victim]."), span_green("You stitch up some of the [bleeding_wording] on [user == victim ? "yourself" : "[victim]"]."))
var/blood_sutured = I.stop_bleeding / self_penalty_mult
adjust_blood_flow(-blood_sutured)
limb.heal_damage(I.heal_brute, I.heal_burn)
I.use(1)
if(blood_flow > 0)
return try_treating(I, user)
else
to_chat(user, span_green("You successfully close the hole in [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone]."))
return TRUE
/// If someone is using either a cautery tool or something with heat to cauterize this pierce
/datum/wound/pierce/bleed/proc/tool_cauterize(obj/item/I, mob/user)
@@ -163,9 +137,13 @@
else
user.visible_message(span_danger("[user] begins cauterizing [victim]'s [limb.plaintext_zone] with [I]..."), span_warning("You begin cauterizing [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone] with [I]..."))
playsound(user, 'sound/items/handling/surgery/cautery1.ogg', 75, TRUE)
if(!do_after(user, treatment_delay, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
return TRUE
playsound(user, 'sound/items/handling/surgery/cautery2.ogg', 75, TRUE)
var/bleeding_wording = (!limb.can_bleed() ? "holes" : "bleeding")
user.visible_message(span_green("[user] cauterizes some of the [bleeding_wording] on [victim]."), span_green("You cauterize some of the [bleeding_wording] on [victim]."))
victim.apply_damage(2 + severity, BURN, limb, wound_bonus = CANT_WOUND)

View File

@@ -36,9 +36,6 @@
/// Once the blood flow drops below minimum_flow, we demote it to this type of wound. If there's none, we're all better
var/demotes_to
/// The maximum flow we've had so far
var/highest_flow
/// A bad system I'm using to track the worst scar we earned (since we can demote, we want the biggest our wound has been, not what it was when it was cured (probably moderate))
var/datum/scar/highest_scar
@@ -133,44 +130,23 @@
return BLOOD_FLOW_INCREASING
/datum/wound/slash/flesh/handle_process(seconds_per_tick, times_fired)
if (!victim || HAS_TRAIT(victim, TRAIT_STASIS))
return
// in case the victim has the NOBLOOD trait, the wound will simply not clot on its own
if(limb.can_bleed())
set_blood_flow(min(blood_flow, WOUND_SLASH_MAX_BLOODFLOW))
if(clot_rate > 0)
adjust_blood_flow(-clot_rate * seconds_per_tick)
if(QDELETED(src))
return
if(HAS_TRAIT(victim, TRAIT_BLOODY_MESS))
adjust_blood_flow(0.25) // old heparin used to just add +2 bleed stacks per tick, this adds 0.5 bleed flow to all open cuts which is probably even stronger as long as you can cut them first
//gauze always reduces blood flow, even for non bleeders
if(limb.current_gauze)
if(clot_rate > 0)
adjust_blood_flow(-clot_rate * seconds_per_tick)
adjust_blood_flow(-limb.current_gauze.absorption_rate * seconds_per_tick)
limb.seep_gauze(limb.current_gauze.absorption_rate * seconds_per_tick)
//otherwise, only clot if it's a bleeder
else if(limb.can_bleed())
adjust_blood_flow(-clot_rate * seconds_per_tick)
if(blood_flow > highest_flow)
highest_flow = blood_flow
if(blood_flow < minimum_flow)
if(demotes_to)
replace_wound(new demotes_to)
else
to_chat(victim, span_green("The cut on your [limb.plaintext_zone] has [!limb.can_bleed() ? "healed up" : "stopped bleeding"]!"))
qdel(src)
/datum/wound/slash/flesh/on_stasis(seconds_per_tick, times_fired)
if(blood_flow >= minimum_flow)
return
if(demotes_to)
replace_wound(new demotes_to)
return
qdel(src)
var/gauze_power = limb.current_gauze.absorption_rate
limb.seep_gauze(gauze_power * seconds_per_tick)
adjust_blood_flow(-gauze_power * seconds_per_tick)
/* BEWARE, THE BELOW NONSENSE IS MADNESS. bones.dm looks more like what I have in mind and is sufficiently clean, don't pay attention to this messiness */
@@ -185,8 +161,6 @@
return las_cauterize(I, user)
else if(I.tool_behaviour == TOOL_CAUTERY || I.get_temperature())
return tool_cauterize(I, user)
else if(istype(I, /obj/item/stack/medical/suture))
return suture(I, user)
/datum/wound/slash/flesh/try_handling(mob/living/user)
if(user.pulling != victim || !HAS_TRAIT(user, TRAIT_WOUND_LICKER) || !victim.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
@@ -212,8 +186,7 @@
/// if a felinid is licking this cut to reduce bleeding
/datum/wound/slash/flesh/proc/lick_wounds(mob/living/carbon/human/user)
// transmission is one way patient -> felinid since google said cat saliva is antiseptic or whatever, and also because felinids are already risking getting beaten for this even without people suspecting they're spreading a deathvirus
for(var/i in victim.diseases)
var/datum/disease/iter_disease = i
for(var/datum/disease/iter_disease as anything in victim.diseases)
if(iter_disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS))
continue
user.ForceContractDisease(iter_disease)
@@ -225,18 +198,28 @@
user.visible_message(span_notice("[user] licks the wounds on [victim]'s [limb.plaintext_zone]."), span_notice("You lick some of the wounds on [victim]'s [limb.plaintext_zone]"), ignored_mobs=victim)
to_chat(victim, span_green("[user] licks the wounds on your [limb.plaintext_zone]!"))
var/mob/victim_stored = victim
adjust_blood_flow(-0.5)
if(blood_flow > minimum_flow)
try_handling(user)
else if(demotes_to)
to_chat(user, span_green("You successfully lower the severity of [victim]'s cuts."))
to_chat(user, span_green("You successfully lower the severity of [user == victim_stored ? "your" : "[victim_stored]'s"] cuts."))
/datum/wound/slash/flesh/adjust_blood_flow(adjust_by, minimum)
. = ..()
if(blood_flow > WOUND_MAX_BLOODFLOW)
blood_flow = WOUND_MAX_BLOODFLOW
if(blood_flow < minimum_flow && !QDELETED(src))
if(demotes_to)
replace_wound(new demotes_to)
else
to_chat(victim, span_green("The cut on your [limb.plaintext_zone] has [!limb.can_bleed() ? "healed up" : "stopped bleeding"]!"))
qdel(src)
/datum/wound/slash/flesh/on_xadone(power)
. = ..()
if (limb) // parent can cause us to be removed, so its reasonable to check if we're still applied
adjust_blood_flow(-0.03 * power) // i think it's like a minimum of 3 power, so .09 blood_flow reduction per tick is pretty good for 0 effort
adjust_blood_flow(-0.03 * power) // i think it's like a minimum of 3 power, so .09 blood_flow reduction per tick is pretty good for 0 effort
/datum/wound/slash/flesh/on_synthflesh(reac_volume)
. = ..()
@@ -271,50 +254,29 @@
else
user.visible_message(span_danger("[user] begins cauterizing [victim]'s [limb.plaintext_zone] with [I]..."), span_warning("You begin cauterizing [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone] with [I]..."))
playsound(user, 'sound/items/handling/surgery/cautery1.ogg', 75, TRUE)
if(!do_after(user, treatment_delay, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
return
playsound(user, 'sound/items/handling/surgery/cautery2.ogg', 75, TRUE)
var/bleeding_wording = (!limb.can_bleed() ? "cuts" : "bleeding")
user.visible_message(span_green("[user] cauterizes some of the [bleeding_wording] on [victim]."), span_green("You cauterize some of the [bleeding_wording] on [victim]."))
victim.apply_damage(2 + severity, BURN, limb, wound_bonus = CANT_WOUND)
if(prob(30))
victim.emote("scream")
var/blood_cauterized = (0.6 / (self_penalty_mult * improv_penalty_mult))
var/mob/victim_stored = victim
adjust_blood_flow(-blood_cauterized)
if(blood_flow > minimum_flow)
return try_treating(I, user)
else if(demotes_to)
to_chat(user, span_green("You successfully lower the severity of [user == victim ? "your" : "[victim]'s"] cuts."))
to_chat(user, span_green("You successfully lower the severity of [user == victim_stored ? "your" : "[victim_stored]'s"] cuts."))
return TRUE
return FALSE
/// If someone is using a suture to close this cut
/datum/wound/slash/flesh/proc/suture(obj/item/stack/medical/suture/I, mob/user)
var/self_penalty_mult = (user == victim ? 1.4 : 1)
var/treatment_delay = base_treat_time * self_penalty_mult
if(HAS_TRAIT(src, TRAIT_WOUND_SCANNED))
treatment_delay *= 0.5
user.visible_message(span_notice("[user] begins expertly stitching [victim]'s [limb.plaintext_zone] with [I]..."), span_notice("You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone] with [I], keeping the holo-image information in mind..."))
else
user.visible_message(span_notice("[user] begins stitching [victim]'s [limb.plaintext_zone] with [I]..."), span_notice("You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.plaintext_zone] with [I]..."))
if(!do_after(user, treatment_delay, target = victim, extra_checks = CALLBACK(src, PROC_REF(still_exists))))
return TRUE
var/bleeding_wording = (!limb.can_bleed() ? "cuts" : "bleeding")
user.visible_message(span_green("[user] stitches up some of the [bleeding_wording] on [victim]."), span_green("You stitch up some of the [bleeding_wording] on [user == victim ? "yourself" : "[victim]"]."))
var/blood_sutured = I.stop_bleeding / self_penalty_mult
adjust_blood_flow(-blood_sutured)
limb.heal_damage(I.heal_brute, I.heal_burn)
I.use(1)
if(blood_flow > minimum_flow)
return try_treating(I, user)
else if(demotes_to)
to_chat(user, span_green("You successfully lower the severity of [user == victim ? "your" : "[victim]'s"] cuts."))
return TRUE
return TRUE
/datum/wound/slash/get_limb_examine_description()
return span_warning("The flesh on this limb appears badly lacerated.")

View File

@@ -32,16 +32,35 @@
var/sanitization
/// How much we add to flesh_healing for burn wounds on application
var/flesh_regeneration
/// Time it takes to assess injuries when looping healing
var/assessing_injury_delay = 1 SECONDS
/obj/item/stack/medical/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!isliving(interacting_with))
return NONE
if(!begin_heal_loop(interacting_with, user))
if(!begin_heal_loop(interacting_with, user, auto_change_zone = TRUE))
return NONE // [ITEM_INTERACT_BLOCKING] would be redundant as we are nobludgeon
return ITEM_INTERACT_SUCCESS
/obj/item/stack/medical/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
if(!isliving(interacting_with))
return NONE
if(!begin_heal_loop(interacting_with, user, auto_change_zone = FALSE))
return NONE // see above
return ITEM_INTERACT_SUCCESS
/obj/item/stack/medical/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
. = ..()
register_item_context()
/obj/item/stack/medical/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
if(!isliving(target))
return NONE
if(iscarbon(target))
context[SCREENTIP_CONTEXT_LMB] = "Auto Heal"
context[SCREENTIP_CONTEXT_RMB] = "Manual Heal"
else
context[SCREENTIP_CONTEXT_LMB] = "Heal"
return CONTEXTUAL_SCREENTIP_SET
/obj/item/stack/medical/apply_fantasy_bonuses(bonus)
. = ..()
if(heal_brute)
@@ -65,134 +84,251 @@
/// Used to begin the recursive healing loop.
/// Returns TRUE if we entered the loop, FALSE if we didn't
/obj/item/stack/medical/proc/begin_heal_loop(mob/living/patient, mob/user)
if(!can_heal(patient, user))
/obj/item/stack/medical/proc/begin_heal_loop(mob/living/patient, mob/living/user, auto_change_zone = TRUE)
if(DOING_INTERACTION_WITH_TARGET(user, patient))
return FALSE
INVOKE_ASYNC(src, PROC_REF(try_heal), patient, user)
var/heal_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, heal_zone))
return FALSE
SSblackbox.record_feedback("nested tally", "medical_item_used", 1, list(type, auto_change_zone ? "auto" : "manual"))
patient.balloon_alert(user, "treating [parse_zone(heal_zone)]...")
INVOKE_ASYNC(src, PROC_REF(try_heal), patient, user, heal_zone, FALSE, iscarbon(patient) && auto_change_zone) // auto change is useless for non-carbons
return TRUE
/// Checks if the passed patient can be healed by the passed user
/obj/item/stack/medical/proc/can_heal(mob/living/patient, mob/user)
return patient.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE)
/// In which we print the message that we're starting to heal someone, then we try healing them. Does the do_after whether or not it can actually succeed on a targeted mob
/obj/item/stack/medical/proc/try_heal(mob/living/patient, mob/user, silent = FALSE, looping = FALSE)
if(!try_heal_checks(patient, user, heal_brute, heal_burn, looping))
return
var/new_self_delay = self_delay
var/new_other_delay = other_delay
if(iscarbon(patient))
new_self_delay = looping ? clamp((self_delay - assessing_injury_delay), 0, self_delay) : self_delay
new_other_delay = looping ? clamp((other_delay - assessing_injury_delay), 0, other_delay) : other_delay
/**
* What actually handles printing the message that we're starting to heal someone, and trying to heal them
*
* This proc is recursively called until we run out of charges OR until the patient is fully healed
* OR until the target zone is fully healed (if auto_change_zone is FALSE)
*
* * patient - The mob we're trying to heal
* * user - The mob that's trying to heal the patient
* * healed_zone - The zone we're trying to heal on the patient
* Disregarded if auto_change_zone is TRUE
* * silent - If we should not print the message that we're starting to heal the patient
* Used so looping the proc doesn't spam messages
* * auto_change_zone - Handles the behavior when we finish healing a zone
* If auto_change_zone is set to TRUE, it picks the next most damaged zone to heal
* If auto_change_zone is set to FALSE, it'll give the user a chance to pick a new zone to heal
*/
/obj/item/stack/medical/proc/try_heal(mob/living/patient, mob/living/user, healed_zone, silent = FALSE, auto_change_zone = TRUE)
if(patient == user)
if(!silent)
user.balloon_alert(user, "treating [parse_zone(healed_zone)]...")
user.visible_message(
span_notice("[user] starts to apply [src] on [user.p_them()]self..."),
span_notice("You begin applying [src] on yourself..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
if(!do_after(
user,
new_self_delay,
self_delay * (auto_change_zone ? 1 : 0.9),
patient,
extra_checks = CALLBACK(src, PROC_REF(can_heal), patient, user),
extra_checks = CALLBACK(src, PROC_REF(can_heal), patient, user, healed_zone),
))
return
if(!auto_change_zone)
healed_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, healed_zone))
return
else if(other_delay)
if(!silent)
patient.balloon_alert(user, "treating [parse_zone(healed_zone)]...")
user.visible_message(
span_notice("[user] starts to apply [src] on [patient]."),
span_notice("You begin applying [src] on [patient]..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
if(!do_after(
user,
new_other_delay,
other_delay * (auto_change_zone ? 1 : 0.9),
patient,
extra_checks = CALLBACK(src, PROC_REF(can_heal), patient, user),
extra_checks = CALLBACK(src, PROC_REF(can_heal), patient, user, healed_zone),
))
return
if(!auto_change_zone)
healed_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, healed_zone))
return
if(!heal(patient, user))
return
log_combat(user, patient, "healed", name)
if(!use(1) || !repeating || amount <= 0)
return
if(!can_heal(patient, user))
return
try_heal(patient, user, silent = TRUE, looping = TRUE)
else
if(!silent)
user.visible_message(
span_notice("[user] applies [src] on [patient]."),
span_notice("You apply [src] on [patient]."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
/// Apply the actual effects of the healing if it's a simple animal, goes to [/obj/item/stack/medical/proc/heal_carbon] if it's a carbon, returns TRUE if it works, FALSE if it doesn't
/obj/item/stack/medical/proc/heal(mob/living/patient, mob/user)
if(patient.stat == DEAD)
patient.balloon_alert(user, "they're dead!")
return FALSE
if(iscarbon(patient))
return heal_carbon(patient, user, heal_brute, heal_burn)
if(!heal_carbon(patient, user, healed_zone))
return
else if(isanimal_or_basicmob(patient))
if(!try_heal_checks(patient, user, heal_brute, heal_burn))
return FALSE
if(patient.heal_bodypart_damage((heal_brute * patient.maxHealth/100)))
user.visible_message(span_infoplain(span_green("[user] applies [src] on [patient].")), span_infoplain(span_green("You apply [src] on [patient].")))
return TRUE
patient.balloon_alert(user, "can't heal [patient]!")
return FALSE
if(!heal_simplemob(patient, user))
return
else
CRASH("Stack medical item healing a non-carbon, non-animal mob [patient] ([patient.type])")
log_combat(user, patient, "healed", src)
if(!use(1) || !repeating || amount <= 0)
var/atom/alert_loc = QDELETED(src) ? user : src
alert_loc.balloon_alert(user, repeating ? "all used up!" : "treated [parse_zone(healed_zone)]")
return
// first, just try looping
// 1. we can keep healing the current target
// 2. the user's changed their target (and thus we should heal that limb instead)
var/preferred_target = check_zone(user.zone_selected)
if(try_heal_checks(patient, user, preferred_target, silent = TRUE))
if(preferred_target != healed_zone)
patient.balloon_alert(user, "treating [parse_zone(preferred_target)]...")
try_heal(patient, user, preferred_target, TRUE, auto_change_zone)
return
// second, handle what happens otherwise
if(!iscarbon(patient))
// behavior 0: non-carbons have no limbs so we can assume they are fully healed
patient.balloon_alert(user, "fully treated")
else if(auto_change_zone)
// behavior 1: automatically pick another zone to heal
try_heal_auto_change_zone(patient, user, preferred_target, healed_zone)
else
// behavior 2: assess injury, giving the user time to manually pick another zone
try_heal_manual_target(patient, user)
/obj/item/stack/medical/proc/try_heal_auto_change_zone(mob/living/carbon/patient, mob/living/user, preferred_target, last_zone)
PRIVATE_PROC(TRUE)
var/list/other_affected_limbs = list()
for(var/obj/item/bodypart/limb as anything in patient.bodyparts)
if(!try_heal_checks(patient, user, limb.body_zone, silent = TRUE))
continue
other_affected_limbs += limb.body_zone
if(!length(other_affected_limbs))
patient.balloon_alert(user, "fully treated")
return
var/next_picked = (preferred_target in other_affected_limbs) ? preferred_target : other_affected_limbs[1]
if(next_picked != last_zone)
user.balloon_alert(user, "treating [parse_zone(next_picked)]...")
try_heal(patient, user, next_picked, silent = TRUE, auto_change_zone = TRUE)
/obj/item/stack/medical/proc/try_heal_manual_target(mob/living/carbon/patient, mob/living/user)
PRIVATE_PROC(TRUE)
patient.balloon_alert(user, "assessing injury...")
if(!do_after(user, 1 SECONDS, patient))
return
var/new_zone = check_zone(user.zone_selected)
if(!try_heal_checks(patient, user, new_zone))
return
patient.balloon_alert(user, "treating [parse_zone(new_zone)]...")
try_heal(patient, user, new_zone, silent = TRUE, auto_change_zone = FALSE)
/// Checks if the passed patient can be healed by the passed user
/obj/item/stack/medical/proc/can_heal(mob/living/patient, mob/living/user, healed_zone, silent = FALSE)
return patient.try_inject(user, healed_zone, injection_flags = silent ? NONE : INJECT_TRY_SHOW_ERROR_MESSAGE)
/// Checks a bunch of stuff to see if we can heal the patient, including can_heal
/// Gives a feedback if we can't ultimatly heal the patient (unless silent is TRUE)
/obj/item/stack/medical/proc/try_heal_checks(mob/living/patient, mob/living/user, healed_zone, silent = FALSE)
if(!can_heal(patient, user, healed_zone, silent))
// has its own feedback
return FALSE
if(patient.stat == DEAD)
if(!silent)
patient.balloon_alert(user, "[patient.p_theyre()] dead!")
return FALSE
/obj/item/stack/medical/proc/try_heal_checks(mob/living/patient, mob/user, brute, burn, looping = FALSE)
if(iscarbon(patient))
if(looping)
balloon_alert(user, "assessing injuries...")
if(!do_after(user, assessing_injury_delay, patient))
return FALSE
var/mob/living/carbon/carbon_patient = patient
var/obj/item/bodypart/affecting = carbon_patient.get_bodypart(check_zone(user.zone_selected))
var/obj/item/bodypart/affecting = carbon_patient.get_bodypart(healed_zone)
if(!affecting) //Missing limb?
carbon_patient.balloon_alert(user, "no [parse_zone(user.zone_selected)]!")
if(!silent)
carbon_patient.balloon_alert(user, "no [parse_zone(healed_zone)]!")
return FALSE
if(!IS_ORGANIC_LIMB(affecting)) //Limb must be organic to be healed - RR
carbon_patient.balloon_alert(user, "[affecting.plaintext_zone] is not organic!")
if(!silent)
carbon_patient.balloon_alert(user, "[affecting.plaintext_zone] is not organic!")
return FALSE
if(!(affecting.brute_dam && brute) && !(affecting.burn_dam && burn))
if(!affecting.brute_dam && !affecting.burn_dam)
if(patient != user || !looping)
var/datum/wound/burn/flesh/any_burn_wound = locate() in affecting.wounds
var/can_heal_burn_wounds = (flesh_regeneration || sanitization) && any_burn_wound?.can_be_ointmented_or_meshed()
var/can_suture_bleeding = stop_bleeding && affecting.get_modified_bleed_rate() > 0
var/brute_to_heal = heal_brute && affecting.brute_dam > 0
var/burn_to_heal = heal_burn && affecting.burn_dam > 0
if(!brute_to_heal && !burn_to_heal && !can_heal_burn_wounds && !can_suture_bleeding)
if(!silent)
if(!brute_to_heal && stop_bleeding) // no brute, no bleeding
carbon_patient.balloon_alert(user, "[affecting.plaintext_zone] is not bleeding or bruised!")
else if(!burn_to_heal && (flesh_regeneration || sanitization) && any_burn_wound) // no burns, existing burn wounds are treated
carbon_patient.balloon_alert(user, "[affecting.plaintext_zone] has been fully treated!")
else if(!affecting.brute_dam && !affecting.burn_dam) // not hurt at all
carbon_patient.balloon_alert(user, "[affecting.plaintext_zone] is not hurt!")
else
carbon_patient.balloon_alert(user, "can't heal [affecting.plaintext_zone] with [name]!")
else // probably hurt in some way but we are not the right item for this
carbon_patient.balloon_alert(user, "can't heal [affecting.plaintext_zone] with [name]!")
return FALSE
return TRUE
if(isanimal_or_basicmob(patient))
if(patient.stat == DEAD)
patient.balloon_alert(user, "they're dead!")
return FALSE
if(!heal_brute) // only brute can heal
patient.balloon_alert(user, "can't heal with [name]!")
if(!silent)
patient.balloon_alert(user, "can't heal with [name]!")
return FALSE
if(!(patient.mob_biotypes & MOB_ORGANIC))
patient.balloon_alert(user, "no organic tissue!")
if(!silent)
patient.balloon_alert(user, "no organic tissue!")
return FALSE
if(patient.health == patient.maxHealth)
patient.balloon_alert(user, "not hurt!")
if(!silent)
patient.balloon_alert(user, "not hurt!")
return FALSE
return TRUE
return FALSE
/// The healing effects on a carbon patient. Since we have extra details for dealing with bodyparts, we get our own fancy proc. Still returns TRUE on success and FALSE on fail
/obj/item/stack/medical/proc/heal_carbon(mob/living/carbon/patient, mob/user, brute, burn)
var/obj/item/bodypart/affecting = patient.get_bodypart(check_zone(user.zone_selected))
if(!try_heal_checks(patient, user, brute, burn))
return FALSE
/// The healing effects on a carbon patient.
/// Since we have extra details for dealing with bodyparts, we get our own fancy proc.
/// Still returns TRUE on success and FALSE on fail
/obj/item/stack/medical/proc/heal_carbon(mob/living/carbon/patient, mob/living/user, healed_zone)
var/obj/item/bodypart/affecting = patient.get_bodypart(healed_zone)
user.visible_message(
span_infoplain(span_green("[user] applies [src] on [patient]'s [affecting.plaintext_zone].")),
span_infoplain(span_green("You apply [src] on [patient]'s [affecting.plaintext_zone]."))
span_green("[user] applies [src] on [patient]'s [affecting.plaintext_zone]."),
span_green("You apply [src] on [patient]'s [affecting.plaintext_zone]."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
var/previous_damage = affecting.get_damage()
if(affecting.heal_damage(brute, burn))
if(affecting.heal_damage(heal_brute, heal_burn))
patient.update_damage_overlays()
if(stop_bleeding)
for(var/datum/wound/wound as anything in affecting.wounds)
if(wound.blood_flow)
wound.adjust_blood_flow(-1 * stop_bleeding * (user == patient ? 0.7 : 1))
break // one at a time
affecting.adjustBleedStacks(-1 * stop_bleeding, 0)
if(flesh_regeneration || sanitization)
for(var/datum/wound/burn/flesh/wound as anything in affecting.wounds)
if(wound.can_be_ointmented_or_meshed())
wound.flesh_healing += flesh_regeneration
wound.sanitization += sanitization
break // one at a time
post_heal_effects(max(previous_damage - affecting.get_damage(), 0), patient, user)
return TRUE
///Override this proc for special post heal effects.
/obj/item/stack/medical/proc/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/user)
/// Healing a simple mob, just an adjustbruteloss call
/obj/item/stack/medical/proc/heal_simplemob(mob/living/patient, mob/living/user)
patient.adjustBruteLoss(-1 * (heal_brute * patient.maxHealth / 100))
user.visible_message(
span_green("[user] applies [src] on [patient]."),
span_green("You apply [src] on [patient]."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
return TRUE
///Override this proc for special post heal effects. Only called for carbon patients.
/obj/item/stack/medical/proc/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/living/user)
return
/obj/item/stack/medical/bruise_pack
@@ -239,47 +375,79 @@
SEND_SIGNAL(gauzed_bodypart, COMSIG_BODYPART_UNGAUZED, src)
gauzed_bodypart = null
// gauze is only relevant for wounds, which are handled in the wounds themselves
/obj/item/stack/medical/gauze/try_heal(mob/living/patient, mob/user, silent, looping)
/obj/item/stack/medical/gauze/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
if(iscarbon(target))
context[SCREENTIP_CONTEXT_LMB] = "Apply Gauze"
return CONTEXTUAL_SCREENTIP_SET
return NONE
var/treatment_delay = (user == patient ? self_delay : other_delay)
var/obj/item/bodypart/limb = patient.get_bodypart(check_zone(user.zone_selected))
if(!limb)
patient.balloon_alert(user, "missing limb!")
return
/obj/item/stack/medical/gauze/try_heal_checks(mob/living/patient, mob/living/user, healed_zone, silent = FALSE)
var/obj/item/bodypart/limb = patient.get_bodypart(healed_zone)
if(isnull(limb))
if(!silent)
patient.balloon_alert(user, "no [parse_zone(healed_zone)]!")
return FALSE
if(!LAZYLEN(limb.wounds))
patient.balloon_alert(user, "no wounds!") // good problem to have imo
return
var/gauzeable_wound = FALSE
var/datum/wound/woundies
for(var/i in limb.wounds)
woundies = i
if(woundies.wound_flags & ACCEPTS_GAUZE)
gauzeable_wound = TRUE
break
if(!gauzeable_wound)
patient.balloon_alert(user, "can't heal those!")
return
if(!silent)
patient.balloon_alert(user, "no wounds!") // good problem to have imo
return FALSE
if(limb.current_gauze && (limb.current_gauze.absorption_capacity * 1.2 > absorption_capacity)) // ignore if our new wrap is < 20% better than the current one, so someone doesn't bandage it 5 times in a row
patient.balloon_alert(user, pick("already bandaged!", "bandage is clean!")) // good enough
return
if(!silent)
patient.balloon_alert(user, pick("already bandaged!", "bandage is clean!")) // good enough
return FALSE
for(var/datum/wound/woundies as anything in limb.wounds)
if(woundies.wound_flags & ACCEPTS_GAUZE)
return TRUE
if(!silent)
patient.balloon_alert(user, "can't gauze!")
return FALSE
if(HAS_TRAIT(woundies, TRAIT_WOUND_SCANNED))
// gauze is only relevant for wounds, which are handled in the wounds themselves
/obj/item/stack/medical/gauze/try_heal(mob/living/patient, mob/living/user, silent, healed_zone, auto_change_zone)
var/obj/item/bodypart/limb = patient.get_bodypart(healed_zone)
var/treatment_delay = (user == patient ? self_delay : other_delay)
var/any_scanned = FALSE
for(var/datum/wound/woundies as anything in limb.wounds)
if(HAS_TRAIT(woundies, TRAIT_WOUND_SCANNED))
any_scanned = TRUE
break
if(any_scanned)
treatment_delay *= 0.5
if(user == patient)
to_chat(user, span_notice("You keep in mind the indications from the holo-image about your injury, and expertly begin wrapping your wounds with [src]."))
if(!silent)
user.visible_message(
span_warning("[user] begins expertly wrapping the wounds on [p_their()]'s [limb.plaintext_zone] with [src]..."),
span_warning("You begin quickly wrapping the wounds on your [limb.plaintext_zone] with [src], keeping the holo-image indications in mind..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
else
user.visible_message(span_warning("[user] begins expertly wrapping the wounds on [patient]'s [limb.plaintext_zone] with [src]..."), span_warning("You begin quickly wrapping the wounds on [patient]'s [limb.plaintext_zone] with [src], keeping the holo-image indications in mind..."))
if(!silent)
user.visible_message(
span_warning("[user] begins expertly wrapping the wounds on [patient]'s [limb.plaintext_zone] with [src]..."),
span_warning("You begin quickly wrapping the wounds on [patient]'s [limb.plaintext_zone] with [src], keeping the holo-image indications in mind..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
else
user.visible_message(span_warning("[user] begins wrapping the wounds on [patient]'s [limb.plaintext_zone] with [src]..."), span_warning("You begin wrapping the wounds on [user == patient ? "your" : "[patient]'s"] [limb.plaintext_zone] with [src]..."))
if(!silent)
user.visible_message(
span_warning("[user] begins wrapping the wounds on [patient]'s [limb.plaintext_zone] with [src]..."),
span_warning("You begin wrapping the wounds on [user == patient ? "your" : "[patient]'s"] [limb.plaintext_zone] with [src]..."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
patient.balloon_alert(user, "wrapping [parse_zone(healed_zone)]...")
if(!do_after(user, treatment_delay, target = patient))
return
user.visible_message(span_infoplain(span_green("[user] applies [src] to [patient]'s [limb.plaintext_zone].")), span_infoplain(span_green("You bandage the wounds on [user == patient ? "your" : "[patient]'s"] [limb.plaintext_zone].")))
if(!silent)
patient.balloon_alert(user, "wrapped [parse_zone(healed_zone)]")
user.visible_message(
span_green("[user] applies [src] to [patient]'s [limb.plaintext_zone]."),
span_green("You bandage the wounds on [user == patient ? "your" : "[patient]'s"] [limb.plaintext_zone]."),
visible_message_flags = ALWAYS_SHOW_SELF_MESSAGE,
)
limb.apply_gauze(src)
/obj/item/stack/medical/gauze/twelve
@@ -413,10 +581,11 @@
return ..()
icon_state = "regen_mesh_closed"
/obj/item/stack/medical/mesh/try_heal(mob/living/patient, mob/user, silent = FALSE, looping)
/obj/item/stack/medical/mesh/try_heal_checks(mob/living/patient, mob/living/user, silent = FALSE)
if(!is_open)
balloon_alert(user, "open it first!")
return
if(!silent)
balloon_alert(user, "open it first!")
return FALSE
return ..()
/obj/item/stack/medical/mesh/click_alt(mob/living/user)
@@ -529,7 +698,11 @@
/obj/item/stack/medical/poultice
name = "mourning poultices"
singular_name = "mourning poultice"
desc = "A type of primitive herbal poultice.\nWhile traditionally used to prepare corpses for the mourning feast, it can also treat scrapes and burns on the living, however, it is liable to cause shortness of breath when employed in this manner.\nIt is imbued with ancient wisdom."
desc = "A type of primitive herbal poultice.\n\
While traditionally used to prepare corpses for the mourning feast, \
it can also treat scrapes and burns on the living, however, \
it is liable to cause shortness of breath when employed in this manner.\n\
It is imbued with ancient wisdom."
icon_state = "poultice"
amount = 15
max_amount = 15
@@ -543,14 +716,9 @@
hitsound = 'sound/misc/moist_impact.ogg'
merge_type = /obj/item/stack/medical/poultice
/obj/item/stack/medical/poultice/heal(mob/living/patient, mob/user)
if(iscarbon(patient))
playsound(src, 'sound/misc/soggy.ogg', 30, TRUE)
return heal_carbon(patient, user, heal_brute, heal_burn)
return ..()
/obj/item/stack/medical/poultice/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/user)
/obj/item/stack/medical/poultice/post_heal_effects(amount_healed, mob/living/carbon/healed_mob, mob/living/user)
. = ..()
playsound(src, 'sound/misc/soggy.ogg', 30, TRUE)
healed_mob.adjustOxyLoss(amount_healed)
/obj/item/stack/medical/bandage

View File

@@ -391,10 +391,7 @@
/mob/living/carbon/human/try_inject(mob/user, target_zone, injection_flags)
. = ..()
if(!. && (injection_flags & INJECT_TRY_SHOW_ERROR_MESSAGE) && user)
if(!target_zone)
target_zone = get_bodypart(check_zone(user.zone_selected))
var/obj/item/bodypart/the_part = isbodypart(target_zone) ? target_zone : get_bodypart(check_zone(target_zone)) //keep these synced
to_chat(user, span_alert("There is no exposed flesh or thin material on [p_their()] [the_part.name]."))
balloon_alert(user, "no exposed skin on [target_zone || check_zone(user.zone_selected)]!")
/mob/living/carbon/human/get_butt_sprite()
var/obj/item/bodypart/chest/chest = get_bodypart(BODY_ZONE_CHEST)

View File

@@ -1264,14 +1264,11 @@
* * gauze- Just the gauze stack we're taking a sheet from to apply here
*/
/obj/item/bodypart/proc/apply_gauze(obj/item/stack/medical/gauze/new_gauze)
if(!istype(new_gauze) || !new_gauze.absorption_capacity)
if(!istype(new_gauze) || !new_gauze.absorption_capacity || !new_gauze.use(1))
return
var/newly_gauzed = FALSE
if(!current_gauze)
newly_gauzed = TRUE
var/newly_gauzed = !current_gauze
QDEL_NULL(current_gauze)
current_gauze = new new_gauze.type(src, 1)
new_gauze.use(1)
current_gauze.gauzed_bodypart = src
if(newly_gauzed)
SEND_SIGNAL(src, COMSIG_BODYPART_GAUZED, current_gauze, new_gauze)

View File

@@ -144,7 +144,7 @@
)
log_combat(user, target, "dressed burns in", addition="COMBAT MODE: [uppertext(user.combat_mode)]")
pierce_wound.adjust_blood_flow(-0.5)
if(pierce_wound.blood_flow > 0)
if(!QDELETED(pierce_wound) && pierce_wound.blood_flow > 0)
surgery.status = REALIGN_INNARDS
to_chat(user, span_notice("<i>There still seems to be misaligned blood vessels to finish...</i>"))
else