mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-21 21:10:30 +01:00
4379dd2c1b
Skills being based on "Hard Requirements" absolutely suck, and I wasn't happy with the surgery code being essentially hardcoded around them. So this PR refactors surgery to use soft requirement checks, based on modifiers to the success rate probability. Surgeries still state what skills (if any) they require and at what level, but rather than blocking the surgery, it instead imposes a penalty(or bonus) on the success chance based on how much the user's surgical skill differs from the required level. This actually stacks with the penalties for "non ideal tools", so if you're completely unskilled in surgery and you attempt to make an incision with a shard of glass, you're going to have an extremely hard time. Individual surgeries also can define how much the success rate is modified by "skill diff", with the more advanced "surgeon exclusive" surgeries having extremely large potential modifiers. I have also added a signal based check for modifiers, which the Morale component now hooks into. This can also allow for other components, implants, drugs, etc to modify the surgery chances in the future. I have manually tested this PR and verified by examining debug breakpoints in VSCode that the system correctly applies its modifiers.
348 lines
16 KiB
Plaintext
348 lines
16 KiB
Plaintext
//Procedures in this file: Inernal wound patching, Implant removal.
|
|
//////////////////////////////////////////////////////////////////
|
|
// INTERNAL WOUND PATCHING //
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/singleton/surgery_step/fix_vein
|
|
name = "Repair Arterial Bleeding"
|
|
priority = 3
|
|
allowed_tools = list(
|
|
/obj/item/surgery/fix_o_vein = 100, \
|
|
TOOL_CABLECOIL = 75
|
|
)
|
|
can_infect = TRUE
|
|
blood_level = 1
|
|
|
|
min_duration = 40
|
|
max_duration = 60
|
|
skill_requirements = alist(SURGERY_SKILL_COMPONENT = SKILL_LEVEL_FAMILIAR)
|
|
skill_diff_fail_modifier = SURGERY_DIFFICULTY_HARD
|
|
|
|
/singleton/surgery_step/fix_vein/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
if(!..())
|
|
return FALSE
|
|
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
if(!affected)
|
|
return
|
|
|
|
return affected.open >= ORGAN_OPEN_RETRACTED && (affected.status & ORGAN_ARTERY_CUT)
|
|
|
|
/singleton/surgery_step/fix_vein/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message("[user] starts patching the damaged vein in [target]'s [affected.name] with \the [tool]." , \
|
|
"You start patching the damaged [affected.artery_name] in [target]'s [affected.name] with \the [tool].")
|
|
target.custom_pain("The pain in your [affected.name] is unbearable!", 100)
|
|
..()
|
|
|
|
/singleton/surgery_step/fix_vein/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message(SPAN_NOTICE("[user] has patched the damaged [affected.artery_name] in [target]'s [affected.name] with \the [tool]."), \
|
|
SPAN_NOTICE("You have patched the damaged [affected.artery_name] in [target]'s [affected.name] with \the [tool]."))
|
|
|
|
affected.status &= ~ORGAN_ARTERY_CUT
|
|
affected.update_damages()
|
|
if(ishuman(user) && prob(40))
|
|
var/mob/living/carbon/human/H = user
|
|
H.bloody_hands(target, 0)
|
|
|
|
/singleton/surgery_step/fix_vein/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message(SPAN_WARNING("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") , \
|
|
SPAN_WARNING("Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!"))
|
|
affected.take_damage(5, 0)
|
|
|
|
/singleton/surgery_step/internal/fix_dead_tissue //Debridement
|
|
name = "Debride Damaged Tissue"
|
|
priority = 4
|
|
allowed_tools = list(
|
|
TOOL_SCALPEL = 100,
|
|
/obj/item/material/knife = 75,
|
|
/obj/item/material/shard = 50
|
|
)
|
|
|
|
can_infect = TRUE
|
|
blood_level = 1
|
|
|
|
min_duration = 80
|
|
max_duration = 130
|
|
skill_requirements = alist(SURGERY_SKILL_COMPONENT = SKILL_LEVEL_PROFESSIONAL)
|
|
skill_diff_fail_modifier = SURGERY_DIFFICULTY_EXTREME
|
|
|
|
/singleton/surgery_step/internal/fix_dead_tissue/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
if(!..())
|
|
return FALSE
|
|
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
var/obj/item/organ/internal/organ
|
|
for(var/obj/item/organ/internal/I in affected.internal_organs)
|
|
if((I.status & ORGAN_DEAD) && !BP_IS_ROBOTIC(I))
|
|
organ = I
|
|
break
|
|
if(!organ)
|
|
return
|
|
if(organ.damage > organ.max_damage)
|
|
to_chat(user, SPAN_WARNING("\The [organ] is too damaged. Repair it first."))
|
|
return 0
|
|
|
|
return organ.status & ORGAN_DEAD
|
|
|
|
/singleton/surgery_step/internal/fix_dead_tissue/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
var/obj/item/organ/internal/organ
|
|
for(var/obj/item/organ/internal/I in affected.internal_organs)
|
|
if((I.status & ORGAN_DEAD) && !BP_IS_ROBOTIC(I))
|
|
organ = I
|
|
break
|
|
user.visible_message("[user] starts cutting away necrotic tissue from [target]'s [organ.name] with \the [tool]." , \
|
|
"You start cutting away necrotic tissue from [target]'s [organ.name] with \the [tool].[organ.max_damage > 15 ? " Some of it has to be cut away permanently." : ""]")
|
|
target.custom_pain("The pain in your [affected.name] is unbearable!", 75)
|
|
..()
|
|
|
|
/singleton/surgery_step/internal/fix_dead_tissue/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/list/obj/item/organ/internal/dead_organs = list()
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
for(var/obj/item/organ/internal/I in affected.internal_organs)
|
|
if(I && !(I.status & ORGAN_CUT_AWAY) && (I.status & ORGAN_DEAD) && !BP_IS_ROBOTIC(I))
|
|
dead_organs |= I
|
|
var/obj/item/organ/internal/organ_to_fix = dead_organs[1]
|
|
user.visible_message(SPAN_NOTICE("[user] has cut away necrotic tissue from [target]'s [organ_to_fix.name] with \the [tool]."), \
|
|
SPAN_NOTICE("You have cut away necrotic tissue in [target]'s [organ_to_fix.name] with \the [tool]."))
|
|
organ_to_fix.status &= ~ORGAN_DEAD
|
|
organ_to_fix.heal_damage(10) //so that they don't insta-die again
|
|
|
|
/singleton/surgery_step/internal/fix_dead_tissue/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message(SPAN_WARNING("[user]'s hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!"), \
|
|
SPAN_WARNING("Your hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!"))
|
|
affected.sever_artery()
|
|
|
|
/singleton/surgery_step/treat_necrosis
|
|
name = "Treat Necrosis"
|
|
priority = 4
|
|
allowed_tools = list(
|
|
/obj/item/reagent_containers/dropper = 100,
|
|
/obj/item/reagent_containers/glass/bottle = 75,
|
|
/obj/item/reagent_containers/glass/beaker = 75,
|
|
/obj/item/reagent_containers/spray = 50,
|
|
/obj/item/reagent_containers/glass/bucket = 50
|
|
)
|
|
|
|
can_infect = FALSE
|
|
blood_level = 0
|
|
|
|
min_duration = 80
|
|
max_duration = 90
|
|
skill_requirements = alist(SURGERY_SKILL_COMPONENT = SKILL_LEVEL_PROFESSIONAL)
|
|
skill_diff_fail_modifier = SURGERY_DIFFICULTY_EXTREME
|
|
|
|
/singleton/surgery_step/treat_necrosis/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
if(!..())
|
|
return FALSE
|
|
|
|
if(!istype(tool, /obj/item/reagent_containers))
|
|
return FALSE
|
|
|
|
var/obj/item/reagent_containers/container = tool
|
|
if(!container.reagents.has_reagent(/singleton/reagent/peridaxon))
|
|
return FALSE
|
|
|
|
if (target_zone == BP_MOUTH)
|
|
return FALSE
|
|
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
return affected && IS_ORGAN_FULLY_OPEN && (affected.status & ORGAN_DEAD)
|
|
|
|
/singleton/surgery_step/treat_necrosis/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message("[user] starts applying medication to the affected tissue in [target]'s [affected.name] with \the [tool]." , \
|
|
"You start applying medication to the affected tissue in [target]'s [affected.name] with \the [tool].")
|
|
target.custom_pain("Something in your [affected.name] is causing you a lot of pain!", 75)
|
|
..()
|
|
|
|
/singleton/surgery_step/treat_necrosis/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
if (!istype(tool, /obj/item/reagent_containers))
|
|
return
|
|
|
|
var/obj/item/reagent_containers/container = tool
|
|
|
|
var/trans = container.reagents.trans_to_mob(target, container.amount_per_transfer_from_this, CHEM_BLOOD) //technically it's contact, but the reagents are being applied to internal tissue
|
|
if (trans > 0)
|
|
if(container.reagents.has_reagent(/singleton/reagent/peridaxon))
|
|
affected.status &= ~ORGAN_DEAD
|
|
affected.owner.update_body(1)
|
|
|
|
user.visible_message(SPAN_NOTICE("[user] applies [trans] units of the solution to affected tissue in [target]'s [affected.name]"), \
|
|
SPAN_NOTICE("You apply [trans] units of the solution to affected tissue in [target]'s [affected.name] with \the [tool]."))
|
|
|
|
/singleton/surgery_step/treat_necrosis/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
if (!istype(tool, /obj/item/reagent_containers))
|
|
return
|
|
|
|
var/obj/item/reagent_containers/container = tool
|
|
var/trans = container.reagents.trans_to_mob(target, container.amount_per_transfer_from_this, CHEM_BLOOD)
|
|
|
|
user.visible_message(SPAN_WARNING("[user]'s hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!") , \
|
|
SPAN_WARNING("Your hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!"))
|
|
|
|
//no damage or anything, just wastes medicine
|
|
|
|
/singleton/surgery_step/fix_tendon
|
|
name = "Repair Tendons"
|
|
priority = 2
|
|
allowed_tools = list(
|
|
/obj/item/surgery/fix_o_vein = 100, \
|
|
TOOL_CABLECOIL = 75
|
|
)
|
|
can_infect = TRUE
|
|
blood_level = 1
|
|
|
|
min_duration = 50
|
|
max_duration = 70
|
|
skill_requirements = alist(SURGERY_SKILL_COMPONENT = SKILL_LEVEL_TRAINED)
|
|
skill_diff_fail_modifier = SURGERY_DIFFICULTY_MEDIUM
|
|
|
|
/singleton/surgery_step/fix_tendon/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
if(!..())
|
|
return FALSE
|
|
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
return affected && (affected.tendon_status() & TENDON_CUT) && affected.open >= ORGAN_OPEN_RETRACTED
|
|
|
|
/singleton/surgery_step/fix_tendon/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message("[user] starts reattaching the damaged [affected.tendon_name] in [target]'s [affected.name] with \the [tool]." , \
|
|
"You start reattaching the damaged [affected.tendon_name] in [target]'s [affected.name] with \the [tool].")
|
|
target.custom_pain("The pain in your [affected.name] is unbearable!", 100)
|
|
..()
|
|
|
|
/singleton/surgery_step/fix_tendon/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message(SPAN_NOTICE("[user] has reattached the [affected.tendon_name] in [target]'s [affected.name] with \the [tool]."), \
|
|
SPAN_NOTICE("You have reattached the [affected.tendon_name] in [target]'s [affected.name] with \the [tool]."))
|
|
affected.tendon.rejuvenate()
|
|
affected.update_damages()
|
|
|
|
/singleton/surgery_step/fix_tendon/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message(SPAN_WARNING("[user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!") , \
|
|
SPAN_WARNING("Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!"))
|
|
target.apply_damage(15, DAMAGE_PAIN)
|
|
|
|
/singleton/surgery_step/hardsuit
|
|
name = "Remove Hardsuit"
|
|
allowed_tools = list(
|
|
TOOL_WELDER = 80,
|
|
TOOL_SAW = 60,
|
|
/obj/item/gun/energy/plasmacutter = 100
|
|
)
|
|
|
|
can_infect = FALSE
|
|
blood_level = 0
|
|
|
|
min_duration = 100
|
|
max_duration = 160
|
|
skill_requirements = alist(ROBOTICS_SKILL_COMPONENT = SKILL_LEVEL_TRAINED)
|
|
skill_diff_fail_modifier = SURGERY_DIFFICULTY_TRIVIAL
|
|
|
|
/singleton/surgery_step/hardsuit/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
if(!..())
|
|
return FALSE
|
|
|
|
if(!istype(target))
|
|
return FALSE
|
|
if(tool.tool_behaviour == TOOL_WELDER)
|
|
var/obj/item/weldingtool/welder = tool
|
|
if(!welder.isOn() || !welder.use(1,user))
|
|
return FALSE
|
|
return (target_zone == BP_CHEST) && istype(target.back, /obj/item/rig) && !(target.back.canremove)
|
|
|
|
/singleton/surgery_step/hardsuit/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
user.visible_message("[user] starts cutting through the support systems of [target]'s [target.back] with \the [tool]." , \
|
|
"You start cutting through the support systems of [target]'s [target.back] with \the [tool].")
|
|
..()
|
|
|
|
/singleton/surgery_step/hardsuit/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/rig/rig = target.back
|
|
if(!istype(rig))
|
|
return
|
|
rig.reset()
|
|
user.visible_message(SPAN_NOTICE("[user] has cut through the support systems of [target]'s [rig] with \the [tool]."), \
|
|
SPAN_NOTICE("You have cut through the support systems of [target]'s [rig] with \the [tool]."))
|
|
|
|
/singleton/surgery_step/amputate
|
|
name = "Amputate Limb"
|
|
allowed_tools = list(
|
|
TOOL_SAW = 100,
|
|
/obj/item/melee/energy = 100,
|
|
/obj/item/melee/chainsword = 100,
|
|
/obj/item/material/hatchet = 55
|
|
)
|
|
|
|
min_duration = 90
|
|
max_duration = 140
|
|
skill_requirements = alist(SURGERY_SKILL_COMPONENT = SKILL_LEVEL_FAMILIAR)
|
|
|
|
/singleton/surgery_step/amputate/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
if(!..())
|
|
return FALSE
|
|
|
|
if(target_zone == BP_EYES) //there are specific steps for eye surgery
|
|
return FALSE
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
if(affected == null)
|
|
return FALSE
|
|
|
|
if(istype(tool, /obj/item/melee/energy))
|
|
var/obj/item/melee/energy/E = tool
|
|
if(!E.active)
|
|
to_chat(user, SPAN_WARNING("The energy blade is not turned on!"))
|
|
return FALSE
|
|
|
|
if(istype(tool, /obj/item/melee/chainsword))
|
|
var/obj/item/melee/chainsword/E = tool
|
|
if(!E.active)
|
|
to_chat(user, SPAN_WARNING("The blades aren't spinning, you can't cut anything!"))
|
|
return FALSE
|
|
|
|
if(affected.limb_flags & ORGAN_CAN_AMPUTATE)
|
|
var/confirmation = alert("You are about to amputate [target]'s [affected.name]! Are you sure you want to do that?", "Amputation confirmation", "Yes", "No")
|
|
return confirmation == "Yes"
|
|
|
|
/singleton/surgery_step/amputate/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message(SPAN_DANGER("[user] is beginning to amputate [target]'s [affected.name] with \the [tool].") , \
|
|
SPAN_DANGER("You begin to cut through [target]'s [affected.amputation_point] with \the [tool]."))
|
|
target.custom_pain("Your [affected.amputation_point] is being ripped apart!", 75)
|
|
..()
|
|
|
|
/singleton/surgery_step/amputate/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message(SPAN_DANGER("[user] amputates [target]'s [affected.name] at the [affected.amputation_point] with \the [tool]."), \
|
|
SPAN_DANGER("You amputate [target]'s [affected.name] with \the [tool]."))
|
|
|
|
var/clean = TRUE
|
|
if(istype(tool, /obj/item/melee/chainsword))//Chainswords rip and tear, so the limb removal is not clean
|
|
clean = FALSE
|
|
|
|
var/obj/item/organ/external/parent = affected.parent//Cache the parent organ of the limb before we sever it
|
|
affected.droplimb(clean,DROPLIMB_EDGE)
|
|
|
|
if(istype(tool, /obj/item/melee/energy))//Code for energy weapons cauterising the cut
|
|
affected = parent
|
|
affected.open = ORGAN_CLOSED//Close open wounds
|
|
for(var/datum/wound/lost_limb/W in affected.wounds)
|
|
W.disinfected = TRUE//Cleanse the wound of any germs
|
|
W.autoheal_cutoff = INFINITY//Allow the wound to auto-heal, regardless of damage
|
|
W.max_bleeding_stage = 0//Stop bleeding
|
|
|
|
/singleton/surgery_step/amputate/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
|
|
var/obj/item/organ/external/affected = target.get_organ(target_zone)
|
|
user.visible_message(SPAN_WARNING("[user]'s hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!"), \
|
|
SPAN_WARNING("Your hand slips, sawwing through the bone in [target]'s [affected.name] with \the [tool]!"))
|
|
target.apply_damage(30, DAMAGE_BRUTE, target_zone, 0, tool, damage_flags = tool.damage_flags())
|
|
affected.fracture()
|