From e8a61cf2f179533545fe03a3c67bc828683dee6b Mon Sep 17 00:00:00 2001 From: Luc <89928798+lewcc@users.noreply.github.com> Date: Wed, 5 Feb 2025 13:33:06 -0500 Subject: [PATCH] Adds Tend Wounds/Tend Burns surgeries (#28105) * Add wound tending surgeries * Reworks the surgery a bit more to work with the open organ framework * Minor tidiness * oop --- code/modules/surgery/other.dm | 2 +- code/modules/surgery/surgery.dm | 8 + code/modules/surgery/tending.dm | 292 ++++++++++++++++++++++++++++++++ paradise.dme | 1 + 4 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 code/modules/surgery/tending.dm diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm index 09ba772bae4..0261027e548 100644 --- a/code/modules/surgery/other.dm +++ b/code/modules/surgery/other.dm @@ -126,7 +126,7 @@ return SURGERY_STEP_RETRY /datum/surgery_step/treat_burns - name = "mend burns" + name = "treat severe burns" allowed_tools = list( /obj/item/stack/medical/ointment/advanced = 100, /obj/item/stack/medical/ointment = 90 diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index 2ee8ddfb8ea..a6e80322516 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -49,6 +49,14 @@ /// How likely it should be for the surgery to cause infection: 0-1 var/germ_prevention_quality = 0 +/** + * Create a new surgery. + * + * Arguments: + * * surgery_target - The atom the target is being performed on. + * * surgery_location - The body zone that the surgery is being performed on. + * * surgery_bodypart - The body part that the surgery is being performed on. + */ /datum/surgery/New(atom/surgery_target, surgery_location, surgery_bodypart) ..() if(!surgery_target) diff --git a/code/modules/surgery/tending.dm b/code/modules/surgery/tending.dm new file mode 100644 index 00000000000..2613d204f8a --- /dev/null +++ b/code/modules/surgery/tending.dm @@ -0,0 +1,292 @@ +/** + * Surgeries for tending brute and burn damage without needing to expend items. + */ + + +// Since these both share surgery steps, we can only realistically expose one +/datum/surgery_step/proxy/open_organ/extra + /// Other surgeries to fire off next. + var/list/other_surgeries + +/datum/surgery_step/proxy/open_organ/extra/New() + for(var/healing_step_type in other_surgeries) + if(!ispath(healing_step_type)) + CRASH("open_organ was given an additional option that was not a type path: [healing_step_type]") + branches |= healing_step_type + return ..() + +/datum/surgery_step/proxy/open_organ/extra/brute + other_surgeries = list(/datum/surgery/intermediate/heal/brute) + +/datum/surgery_step/proxy/open_organ/extra/burn + other_surgeries = list(/datum/surgery/intermediate/heal/burn) + +/datum/surgery/heal + abstract = TRUE // don't need this popping up + requires_organic_bodypart = TRUE + /// A subtype of /datum/surgery_step/heal that this will invoke. + var/healing_step_type + possible_locs = list(BODY_ZONE_CHEST) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/heal, + /datum/surgery_step/generic/cauterize + ) + +/datum/surgery/heal/New(atom/surgery_target, surgery_location, surgery_bodypart) + ..() + if(ispath(healing_step_type)) + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + healing_step_type, + /datum/surgery_step/generic/cauterize + ) + +/datum/surgery_step/heal + name = "tend something" + + allowed_tools = list( + TOOL_HEMOSTAT = 100, + TOOL_WIRECUTTER = 65, + /obj/item/pen = 55 + ) + + time = 2.5 SECONDS + repeatable = TRUE + + // no sounds, since it would quickly get annoying + + var/shown_starting_message_already = FALSE + + COOLDOWN_DECLARE(success_message_spam_cooldown) + + var/damage_name_pretty = "wounds" + /// Amount of brute damage to treat per op. + var/brute_damage_healed + /// Amount of burn damage to treat per op. + var/burn_damage_healed + + /// Multiplier based on the amount of brute damage that the patient has, increasing the efficiency at higher levels of damage. + var/brute_damage_healmod + /// Multiplier based on the amount of burn damage that the patient has. + var/burn_damage_healmod + +/// Get a message indicating how the surgery is going. +/datum/surgery_step/heal/proc/get_progress(mob/user, mob/living/carbon/target, brute_healed, burn_healed) + return + +/datum/surgery_step/heal/proc/can_be_healed(mob/living/user, mob/living/carbon/target, target_zone) + if(brute_damage_healed == 0 && burn_damage_healed == 0) + stack_trace("A healing surgery was given no healing values.") + return FALSE + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(affected.is_broken()) + return FALSE + + return (brute_damage_healed && target.get_damage_amount(BRUTE) || burn_damage_healed && target.get_damage_amount(BURN)) + +/datum/surgery_step/heal/can_repeat(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(!can_be_healed(user, target, target_zone)) + to_chat(user, "It doesn't look like [target] has any more [damage_name_pretty].") + return FALSE + return TRUE + +/datum/surgery_step/heal/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(!shown_starting_message_already) + shown_starting_message_already = TRUE + user.visible_message( + "[user] starts to patch some of the [damage_name_pretty] on [target]'s [affected.name] with [tool].", + "You start to patch some of the [damage_name_pretty] on [target]'s [affected.name] with [tool].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + affected.custom_pain("Something in your [affected.name] is causing you a lot of pain!") + + return ..() + +/datum/surgery_step/heal/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + + if(!can_be_healed(user, target, target_zone)) + to_chat(user, "It doesn't look like [target] has any more [damage_name_pretty].") + return SURGERY_BEGINSTEP_SKIP + + var/brute_healed = brute_damage_healed + var/burn_healed = burn_damage_healed + + var/outer_msg = "[user] succeeds in fixing some of [target]'s [damage_name_pretty]" + var/self_msg = "You successfully manage to patch up some of [target]'s [damage_name_pretty]" + + if(target.stat == DEAD) //dead patients get way less additional heal from the damage they have. + brute_healed += round((target.getBruteLoss() * (brute_damage_healmod * 0.2)), 0.1) + burn_healed += round((target.getFireLoss() * (burn_damage_healmod * 0.2)), 0.1) + else + brute_healed += round((target.getBruteLoss() * brute_damage_healmod), 0.1) + burn_healed += round((target.getFireLoss() * burn_damage_healmod), 0.1) + + if(!get_location_accessible(target, target_zone)) + brute_healed *= 0.55 + burn_healed *= 0.55 + self_msg += " as best as you can while [target.p_they()] [target.p_have()] clothing on" + outer_msg += " as best as [user.p_they()] can while [target.p_they()] [target.p_have()] clothing on" + + target.heal_overall_damage(brute_healed, burn_healed) + + self_msg += get_progress(user, target, brute_healed, burn_healed) + + if(COOLDOWN_FINISHED(src, success_message_spam_cooldown)) + user.visible_message( + "[outer_msg].", + "[self_msg].", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + + COOLDOWN_START(src, success_message_spam_cooldown, 10 SECONDS) + + // retry ad nauseum; can_repeat should handle anything else. + return SURGERY_STEP_RETRY + +/datum/surgery_step/heal/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message( + "[user] screws up, making things worse!", + "You screw up, making things worse!", + chat_message_type = MESSAGE_TYPE_COMBAT + ) + var/burn_dealt = burn_damage_healed * 0.8 + var/brute_dealt = brute_damage_healed * 0.8 + + brute_dealt += round((target.getBruteLoss() * (brute_damage_healmod * 0.5)), 0.1) + burn_dealt += round((target.getFireLoss() * (burn_damage_healmod * 0.5)), 0.1) + + target.take_overall_damage(brute_dealt, burn_dealt) + + return SURGERY_STEP_RETRY + +/datum/surgery/heal/wounds + name = "Tend Wounds" + desc = "Tend a patient's wounds, gradually treating their brute damage." + abstract = FALSE + healing_step_type = /datum/surgery_step/proxy/open_organ/extra/brute + +/datum/surgery/heal/burns + name = "Treat Burns" + desc = "Tend a patient's burns, gradually treating their burn damage." + abstract = FALSE + healing_step_type = /datum/surgery_step/proxy/open_organ/extra/burn + + +/********************BRUTE STEPS********************/ + + +/datum/surgery_step/heal/brute + name = "tend wounds" + damage_name_pretty = "wounds" + brute_damage_healed = 5 + brute_damage_healmod = 0.07 + +/datum/surgery_step/heal/brute/get_progress(mob/user, mob/living/carbon/target, brute_healed, burn_healed) + if(!brute_healed) + return + + var/estimated_remaining_steps = target.getBruteLoss() / brute_healed + var/progress_text + + if(locate(/obj/item/healthanalyzer) in list(user.l_hand, user.r_hand)) + progress_text = ". Remaining brute: [target.getBruteLoss()]" + else + switch(estimated_remaining_steps) + if(-INFINITY to 1) + return + if(1 to 3) + progress_text = ", stitching up the last few scrapes" + if(3 to 6) + progress_text = ", counting down the last few bruises left to treat" + if(6 to 9) + progress_text = ", continuing to plug away at [target.p_their()] extensive rupturing" + if(9 to 12) + progress_text = ", steadying yourself for the long surgery ahead" + if(12 to 15) + progress_text = ", though [target.p_they()] still look[target.p_s()] more like ground beef than a person" + if(15 to INFINITY) + progress_text = ", though you feel like you're barely making a dent in treating [target.p_their()] pulped body" + + return progress_text + +/datum/surgery_step/heal/burn + name = "treat burns" + damage_name_pretty = "burns" + burn_damage_healed = 5 + burn_damage_healmod = 0.07 + +/********************BURN STEPS********************/ +/datum/surgery_step/heal/burn/get_progress(mob/living/user, mob/living/carbon/target, brute_healed, burn_healed) + if(!burn_healed) + return + var/estimated_remaining_steps = target.getFireLoss() / burn_healed + var/progress_text + + if(locate(/obj/item/healthanalyzer) in list(user.l_hand, user.r_hand)) + progress_text = ". Remaining burn: [target.getFireLoss()]" + else + switch(estimated_remaining_steps) + if(-INFINITY to 1) + return + if(1 to 3) + progress_text = ", finishing up the last few singe marks" + if(3 to 6) + progress_text = ", counting down the last few blisters left to treat" + if(6 to 9) + progress_text = ", continuing to plug away at [target.p_their()] thorough roasting" + if(9 to 12) + progress_text = ", steadying yourself for the long surgery ahead" + if(12 to 15) + progress_text = ", though [target.p_they()] still look[target.p_s()] more like burnt steak than a [target.dna?.species.name || "person" ]" + if(15 to INFINITY) + progress_text = ", though you feel like you're barely making a dent in treating [target.p_their()] charred body" + + return progress_text + + +// Intermediate versions of the above surgeries + + +/datum/surgery/intermediate/heal + possible_locs = list(BODY_ZONE_CHEST) + +/datum/surgery/intermediate/heal/brute + name = "Tend Wounds (abstract)" + desc = "An intermediate surgery to tend to a patient's wounds while they're undergoing another procedure." + steps = list( + /datum/surgery_step/heal/brute + ) + + +/datum/surgery/intermediate/heal/brute/can_start(mob/user, mob/living/carbon/target) + . = ..() + if(!.) + return + if(target.getBruteLoss() == 0) + to_chat(user, "[target] doesn't even have a bruise on [target.p_them()], there's nothing to treat.") + return FALSE + return TRUE + // Normally, adding to_chat to can_start is poor practice since this gets called when listing surgery steps. + // It's alright for intermediate surgeries, though, since they never get listed out + +/datum/surgery/intermediate/heal/burn + name = "Treat Burns (abstract)" + desc = "An intermediate surgery to tend to a patient's burns while they're undergoing another procedure." + steps = list( + /datum/surgery_step/heal/burn + ) + +/datum/surgery/intermediate/heal/burn/can_start(mob/user, mob/living/carbon/target) + . = ..() + if(!.) + return + if(target.getFireLoss() == 0) + to_chat(user, "[target] doesn't even have a blister on [target.p_them()], there's nothing to treat.") + return FALSE + return TRUE diff --git a/paradise.dme b/paradise.dme index 760ff4f5c62..5b2179beca8 100644 --- a/paradise.dme +++ b/paradise.dme @@ -2936,6 +2936,7 @@ #include "code\modules\surgery\robotics.dm" #include "code\modules\surgery\surgery.dm" #include "code\modules\surgery\surgery_helpers.dm" +#include "code\modules\surgery\tending.dm" #include "code\modules\surgery\tools.dm" #include "code\modules\surgery\organs\augments_arms.dm" #include "code\modules\surgery\organs\augments_eyes.dm"