Surgery Hotfixes: Part 1 (#19108)

* Fixes up a few notable bugs with surgery
- Synthetic cavity implants now have a close step (oops!)
- Fixes up logic for intermediate surgery steps

* nah I'll deal with this later

* oops this was unused
This commit is contained in:
Luc
2022-09-21 21:46:03 +01:00
committed by GitHub
parent 065ffff420
commit b060b4b6d2
5 changed files with 155 additions and 91 deletions
+55 -18
View File
@@ -45,7 +45,7 @@
/obj/item/scalpel/laser/manager // IMS
)
/// Whether or not we should add ourselves as a step after we run a branch
/// Whether or not we should add ourselves as a step after we run a branch. This doesn't apply to failures, those will always add ourselves after.
var/insert_self_after = TRUE
/datum/surgery_step/proxy/New()
@@ -133,7 +133,7 @@
if(istype(next_surgery_step, /datum/surgery_step/proxy))
// It might make sense to support this, and I think the flow could work (just treating them like a single step, sorta)
// but I think for simplicity's sake it's better to just say no
CRASH("[src] was followed by another proxy surgery step in [surgery].")
CRASH("[src] was followed by another proxy surgery step [next_surgery_step] in [surgery].")
if((SURGERY_TOOL_HAND in starting_tools) && next_surgery_step.accept_hand)
CRASH("[src] has a conflict with the next main step [next_surgery_step] in surgery [surgery]: both require an open hand.")
@@ -173,10 +173,8 @@
return FALSE
if(overridden_tool || next_surgery == surgery || !next_surgery)
// Continue along with the original surgery
surgery.step_number++
var/datum/surgery_step/next_step = surgery.get_surgery_step()
return next_step.try_op(user, target, target_zone, tool, surgery)
// Continue along with the original surgery.
return try_next_step(user, target, target_zone, tool, surgery, null, TRUE)
if(!target.can_run_surgery(next_surgery, user))
// Make sure the target can support the surgery.
@@ -188,20 +186,59 @@
// Let them try other tools if necessary.
return TRUE
// Insert the steps in our intermediate surgery into the current surgery.
// This is how we keep our surgeries still technically linear.
var/list/steps_to_insert = next_surgery.steps
if(insert_self_after)
// add ourselves afterwards as well so we can repeat this step
steps_to_insert.Add(type)
return try_next_step(user, target, target_zone, tool, surgery, next_surgery.steps)
// Also, bump the status so we skip past this abstract step.
surgery.steps.Insert(surgery.step_number + 1, next_surgery.steps)
surgery.step_number++
/**
* Test the next step, but don't fully commit to it unless it completes successfully.
* If the next step doesn't fully complete (such as being interrupted or failing), we'll insert ourselves again to bring us back
* to the "base" state.
* If it does, we'll add the subsequent steps to the surgery and continue down the expected branch. If you complete the surgery step, it
* means you've committed to what comes next.
* Part of the motivation behind this is that I don't want to mutate a surgery retroactively. We can insert, but we shouldn't be changing anything
* behind us.
*
* Arguments:
* * next_surgery_steps - the steps for the branching surgery to add to the current surgery. If there's no branching surgery (or this would continue the main surgery) ignore this.
* * override_adding_self - If true, then regardless of the value of insert_self_after, we won't add ourselves in as another step.
* (for other arguments, see try_op())
*/
/datum/surgery_step/proxy/proc/try_next_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/running_surgery, list/next_surgery_steps, override_adding_self)
var/list/following_steps = list()
if(length(next_surgery_steps))
// add the first step from the following surgery into the surgery list, to make it the next step.
running_surgery.steps.Insert(running_surgery.step_number + 1, next_surgery_steps[1])
// grab the remaining steps to possibly insert after this surgery, depending on what we're doing
// skip the current step though, since if our try_op works, we've completed it.
following_steps = next_surgery_steps.Copy()
following_steps.Cut(1, 2)
running_surgery.step_number++
var/datum/surgery_step/next_step = running_surgery.get_surgery_step()
var/step_status = next_step.try_op(user, target, target_zone, tool, running_surgery)
if(step_status != SURGERY_INITIATE_SUCCESS)
// always add ourselves after a failure so someone can make a different choice.
running_surgery.steps.Insert(running_surgery.step_number + 1, type)
running_surgery.step_number++
else
// Insert the steps in our intermediate surgery into the current surgery.
// This is how we keep our surgeries still technically linear.
if(insert_self_after && !override_adding_self)
// add ourselves afterwards as well so we can repeat this step
following_steps.Add(type)
// insert at the current step number since we're not trying to bump it up
running_surgery.steps.Insert(running_surgery.step_number, following_steps)
return step_status
// force the next surgery step so we don't have to click again.
var/datum/surgery_step/next_step = surgery.get_surgery_step()
return next_step.try_op(user, target, target_zone, tool, surgery)
// Some intermediate surgeries
/datum/surgery/intermediate/bleeding
+42 -38
View File
@@ -52,11 +52,52 @@
/datum/surgery_step/robotics/external/unscrew_hatch,
/datum/surgery_step/robotics/external/open_hatch,
/datum/surgery_step/proxy/cavity_manipulation/robotic,
/datum/surgery_step/cavity/close_space,
/datum/surgery_step/robotics/external/close_hatch
)
possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN)
requires_organic_bodypart = FALSE
/datum/surgery_step/proxy/cavity_manipulation
name = "Cavity Manipulation (proxy)"
branches = list(
/datum/surgery/intermediate/open_cavity/implant,
/datum/surgery/intermediate/open_cavity/extract,
/datum/surgery/intermediate/bleeding
)
insert_self_after = TRUE
/datum/surgery_step/proxy/cavity_manipulation/robotic
name = "Robotic Cavity Manipulation (proxy)"
branches = list(
/datum/surgery/intermediate/open_cavity/implant/robotic,
/datum/surgery/intermediate/open_cavity/extract/robotic
)
/datum/surgery/intermediate/open_cavity
possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD)
/datum/surgery/intermediate/open_cavity/implant
name = "implant object"
steps = list(
/datum/surgery_step/cavity/place_item
)
/datum/surgery/intermediate/open_cavity/extract
name = "extract object"
steps = list(
/datum/surgery_step/cavity/remove_item
)
/datum/surgery/intermediate/open_cavity/implant/robotic
requires_organic_bodypart = FALSE
/datum/surgery/intermediate/open_cavity/extract/robotic
requires_organic_bodypart = FALSE
/datum/surgery_step/cavity
/datum/surgery_step/cavity/proc/get_max_wclass(obj/item/organ/external/affected)
@@ -206,43 +247,6 @@
return SURGERY_STEP_INCOMPLETE
/datum/surgery_step/proxy/cavity_manipulation
name = "Cavity Manipulation (proxy)"
branches = list(
/datum/surgery/intermediate/open_cavity/implant,
/datum/surgery/intermediate/open_cavity/extract
)
insert_self_after = TRUE
/datum/surgery_step/proxy/cavity_manipulation/robotic
name = "Robotic Cavity Manipulation (proxy)"
branches = list(
/datum/surgery/intermediate/open_cavity/implant/robotic,
/datum/surgery/intermediate/open_cavity/extract/robotic
)
/datum/surgery/intermediate/open_cavity
possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD)
/datum/surgery/intermediate/open_cavity/implant
name = "implant object"
steps = list(
/datum/surgery_step/cavity/place_item
)
/datum/surgery/intermediate/open_cavity/extract
name = "extract object"
steps = list(
/datum/surgery_step/cavity/remove_item
)
/datum/surgery/intermediate/open_cavity/implant/robotic
requires_organic_bodypart = FALSE
/datum/surgery/intermediate/open_cavity/extract/robotic
requires_organic_bodypart = FALSE
/datum/surgery_step/cavity/place_item
name = "implant object"
accept_any_item = TRUE
@@ -281,7 +285,7 @@
var/can_fit = !affected.hidden && tool.w_class <= get_max_wclass(affected)
if(!can_fit)
to_chat(user, "<span class='warning'>\The [tool] won't fit in \the [affected]!</span>")
return SURGERY_BEGINSTEP_ABORT
return SURGERY_BEGINSTEP_SKIP
user.visible_message(
"[user] starts putting \the [tool] inside [target]'s [get_cavity(affected)] cavity.",
+39 -34
View File
@@ -227,10 +227,9 @@
if(is_valid_tool(user, tool))
if(target_zone == surgery.location)
if(get_location_accessible(target, target_zone) || surgery.ignore_clothes)
initiate(user, target, target_zone, tool, surgery)
else
to_chat(user, "<span class='warning'>You need to expose [target]'s [parse_zone(target_zone)] before you can perform surgery on it!")
return TRUE //returns TRUE so we don't stab the guy in the dick or wherever.
return initiate(user, target, target_zone, tool, surgery)
to_chat(user, "<span class='warning'>You need to expose [target]'s [parse_zone(target_zone)] before you can perform surgery on it!")
return SURGERY_INITIATE_FAILURE //returns TRUE so we don't stab the guy in the dick or wherever.
if(repeatable)
// you can continuously, manually, perform a step, so long as you continue to use the correct tool.
@@ -241,11 +240,11 @@
if(next_step)
surgery.step_number++
if(next_step.try_op(user, target, user.zone_selected, user.get_active_hand(), surgery))
return TRUE
return SURGERY_INITIATE_SUCCESS
else
surgery.step_number--
return FALSE
return SURGERY_INITIATE_CONTINUE_CHAIN
/**
* Determines whether or not this surgery step can repeat if its end/fail steps returned SURGERY_STEP_RETRY.
@@ -286,14 +285,15 @@
var/begin_step_result = begin_step(user, target, target_zone, tool, surgery)
if(begin_step_result == SURGERY_BEGINSTEP_ABORT)
surgery.step_in_progress = FALSE
return
return SURGERY_INITIATE_FAILURE
if(begin_step_result == SURGERY_BEGINSTEP_SKIP)
surgery.step_number++
if(surgery.step_number > length(surgery.steps))
surgery.complete(target)
surgery.step_in_progress = FALSE
return TRUE
return SURGERY_INITIATE_SUCCESS
if(tool)
speed_mod = tool.toolspeed
@@ -314,40 +314,45 @@
prob_success = allowed_tools[implement_type]
prob_success *= get_location_modifier(target)
if(do_after(user, modded_time, target = target))
if(!do_after(user, modded_time, target = target))
surgery.step_in_progress = FALSE
return SURGERY_INITIATE_INTERRUPTED
var/chem_check_result = chem_check(target)
var/pain_mod = deal_pain(user, target, target_zone, tool, surgery)
prob_success *= pain_mod
var/chem_check_result = chem_check(target)
var/pain_mod = deal_pain(user, target, target_zone, tool, surgery)
prob_success *= pain_mod
var/step_result
var/step_result
if((prob(prob_success) || isrobot(user) && !silicons_obey_prob) && chem_check_result && !try_to_fail)
step_result = end_step(user, target, target_zone, tool, surgery)
else
step_result = fail_step(user, target, target_zone, tool, surgery)
switch(step_result)
if(SURGERY_STEP_CONTINUE)
advance = TRUE
if(SURGERY_STEP_RETRY_ALWAYS)
if((prob(prob_success) || isrobot(user) && !silicons_obey_prob) && chem_check_result && !try_to_fail)
step_result = end_step(user, target, target_zone, tool, surgery)
else
step_result = fail_step(user, target, target_zone, tool, surgery)
switch(step_result)
if(SURGERY_STEP_CONTINUE)
advance = TRUE
if(SURGERY_STEP_RETRY_ALWAYS)
retry = TRUE
if(SURGERY_STEP_RETRY)
if(can_repeat(user, target, target_zone, tool, surgery))
retry = TRUE
if(SURGERY_STEP_RETRY)
if(can_repeat(user, target, target_zone, tool, surgery))
retry = TRUE
if(retry)
// if at first you don't succeed...
return .(user, target, target_zone, tool, surgery, try_to_fail)
if(retry)
// if at first you don't succeed...
return .(user, target, target_zone, tool, surgery, try_to_fail)
// Bump the surgery status
// if it's repeatable, don't let it truly "complete" though
if(advance && !repeatable)
surgery.step_number++
if(surgery.step_number > length(surgery.steps))
surgery.complete(target)
// Bump the surgery status
// if it's repeatable, don't let it truly "complete" though
if(advance && !repeatable)
surgery.step_number++
if(surgery.step_number > length(surgery.steps))
surgery.complete(target)
surgery.step_in_progress = FALSE
return advance
if(advance)
return SURGERY_INITIATE_SUCCESS
else
return SURGERY_INITIATE_FAILURE
/**
* Try to inflict pain during a surgery, a surgeon's dream come true.