From 346a1b8142dd85028d17d349ab368a00027c412b Mon Sep 17 00:00:00 2001
From: Luc <89928798+lewcc@users.noreply.github.com>
Date: Fri, 16 Sep 2022 13:48:43 -0400
Subject: [PATCH] Yet Another Surgery Refactor (#18325)
* Initial commit
Fixes up surgery.dm
Adds some tool behavior
* More basic changes
* Checkpointing: this is a little gross right now
* Add signal COMPONENT_CANCEL_ATTACK_CHAIN
* Cleans up surgery initiator
* Mostly gets surgery (and canceling it) working
* Add abstract proxy surgery steps
Also adds them to organ manipulation
* Clean up most existing surgeries
* Rework organ openness, adds define for aborting a beginstep
* surgery works again, also implements retry defines
* fix surgery computer
* add limb repair to synth implant removal
* retry implant checks
* Clean up abductor surgeries as well as some other things
* A lot
- Reworks organ manipulation to use a series of surgery steps instead
- Fixes some runtimes with open hands
- Lets mito zero out the germ level while treating necrosis
- Adds a debug surgery tool
* add debug surgery tool, note some TODOs for later
* Add conditional check for surgeries repeating
* update surgery retry logic to make it more of a bonus
* Lets abductors automatically retry any failed surgery steps
* Rework robotic surgery to use abstract/proxy steps
* Bunch of bugfixes and more!
- Limb reattachment works properly now, you can just slap a limb onto a person
- If the limb isn't robotic, it'll be useless until the surgery is finished with a hemostat, though it might be enough to get the patient out of the OR.
* Remove more now-implicit checks
* Slight reorganization
* more fixes across the board
* Remove unused variable
* Trying not to lose my mind here
- Does away with can_run() entirely
- Cleans up visible messages in code
- begin steps should now all have ..() afterwards
- slime bone surgery should be fixed now
- more docs
* Robotic surgery is stoppable with a crowbar, all surgery can_start now checks parent
* Fix some broken robotic typepaths
* Typepath fixes, do away with some last TODOs
* Forgor
* Last cleanups before we go gold
* jk lol
* Make early surgery termination clearer
* More "last" cleanups
* Fixes tool flags, surgery initiation
- Fixes surgery not being startable by sharp objects
- Moves surgical tool flags to item traits
* Clean up surgery cancellation, especially for borgos
* I think this should GC better
* Apply suggestions from code review
Co-authored-by: Sirryan2002 <80364400+Sirryan2002@users.noreply.github.com>
* Status is now step number
* Add a 20% chance to find nothing during organ manipulation
* Improve documentation, make forced_surgery a normal arg
* Charlie's reviews
* Why are abductors like this
* Little more verification, ensuring limb augmentation and organ manip healing work properly
* Fix torso organ manip being unfinishable
* Fix cavity implants, open-hand/any item steps
* Make sharp objects not try to start an operation with help intent
* Comments, quick target fix
* Re-order list so advanced bruise pack is pulled first
* Make surgical gripper function like an open hand
* Make mito only use one unit per organ for now
* Check if user is on operable surface before trying to operate
* Reduce admin logging
* Fix some bugs that appeared during the testmerge
- you can no longer start robotic surgeries with a scalpel (lol)
- you can now cancel surgeries on the first step after I fixed some bugs that I introduced (woo hoo)
- Synthetic limb attachment is now combined into a single startable surgery step, though still retains the fun flavor of both
* Swats some more bugs
- (hopefully) fixes a huge source of runtimes where we tried to check if we could run surgeries before checking if the surgery used an organ
- In doing so, moves the logic for determining if a surgery can start to the mob-level
- Fixes robotic reattachment surgery not working
* multi-bug drifting???
- Fixes a bug where a branching surgery with an any tool option could possibly override a step with a matching tool
- Fixes some intermediate surgeries failing due to not having specified possible_locs
* A few more fixes
- Fixes any surgery tool steps again
- Fixes cavity surgery again
* Hopefully fixes getting stuck in robotic organ manip
* Remove extra parent call
* Steel review
* Steel review
* Fix spacing for possible locs
* Roundstart traits
* Advanced surgical traits and other hal fixes
Co-authored-by: Sirryan2002 <80364400+Sirryan2002@users.noreply.github.com>
---
code/__DEFINES/dcs/signals.dm | 10 +
code/__DEFINES/surgery.dm | 56 +
code/__DEFINES/tools.dm | 24 +
code/__HELPERS/tool_helpers.dm | 17 +
code/__HELPERS/traits.dm | 8 +
code/_onclick/click.dm | 3 +-
code/_onclick/item_attack.dm | 30 +-
code/_onclick/observer.dm | 4 +-
code/_onclick/other_mobs.dm | 3 +-
code/datums/components/surgery_initiator.dm | 308 +++++
.../miniantags/abduction/abduction_surgery.dm | 63 +-
code/game/machinery/computer/Operating.dm | 23 +-
code/game/objects/items/devices/autopsy.dm | 2 +-
code/game/objects/items/robot/robot_parts.dm | 2 +
code/game/objects/items/stacks/medical.dm | 4 +-
code/game/objects/items/tools/crowbar.dm | 5 +
code/game/objects/items/tools/screwdriver.dm | 8 +
.../objects/items/weapons/melee/energy.dm | 1 +
code/game/objects/items/weapons/twohanded.dm | 4 +-
code/game/objects/items/weapons/whetstone.dm | 2 +-
code/game/objects/objs.dm | 11 +
code/modules/hydroponics/hydroitemdefines.dm | 2 +-
.../carbon/alien/special/alien_embryo.dm | 2 +-
.../mob/living/carbon/carbon_defense.dm | 10 +-
.../mob/living/carbon/human/examine.dm | 3 +
.../mob/living/carbon/human/human_defense.dm | 2 +-
.../mob/living/carbon/human/human_organs.dm | 19 +-
.../living/carbon/human/species/abductor.dm | 2 +-
.../mob/living/carbon/human/species/slime.dm | 2 +-
.../living/silicon/robot/drone/drone_items.dm | 2 +-
code/modules/paperwork/pen.dm | 3 +-
code/modules/power/cable.dm | 2 +-
code/modules/surgery/abstract_steps.dm | 276 +++++
code/modules/surgery/bones.dm | 211 ++--
code/modules/surgery/cavity_implant.dm | 402 ++++---
code/modules/surgery/core_removal.dm | 49 +-
code/modules/surgery/dental_implant.dm | 17 +-
code/modules/surgery/encased.dm | 202 ++--
code/modules/surgery/generic.dm | 307 ++---
code/modules/surgery/helpers.dm | 177 +--
code/modules/surgery/implant_removal.dm | 98 +-
code/modules/surgery/limb_augmentation.dm | 41 +-
code/modules/surgery/limb_reattach.dm | 225 ++--
code/modules/surgery/organs/organ_external.dm | 21 +-
code/modules/surgery/organs/pain.dm | 42 +-
code/modules/surgery/organs_internal.dm | 1056 ++++++++++-------
code/modules/surgery/other.dm | 238 ++--
code/modules/surgery/plastic_surgery.dm | 37 +-
.../modules/surgery/remove_embedded_object.dm | 62 +-
code/modules/surgery/robotics.dm | 1009 ++++++++--------
code/modules/surgery/surgery.dm | 514 ++++++--
code/modules/surgery/tools.dm | 63 +
paradise.dme | 3 +
53 files changed, 3575 insertions(+), 2112 deletions(-)
create mode 100644 code/__DEFINES/surgery.dm
create mode 100644 code/datums/components/surgery_initiator.dm
create mode 100644 code/modules/surgery/abstract_steps.dm
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index d73198d466d..55f3d746e15 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -174,6 +174,16 @@
#define COMSIG_ATOM_ORBIT_STOP "atom_orbit_stop"
///from base of atom/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
#define COMSIG_ATOM_HITBY "atom_hitby"
+/// Called when an atom is sharpened or dulled.
+#define COMSIG_ATOM_UPDATE_SHARPNESS "atom_update_sharpness"
+
+// Attack signals. These should share the returned flags, to standardize the attack chain.
+// The chain currently works like:
+// tool_act -> pre_attack -> target.attackby (item.attack) -> afterattack
+// You can use these signal responses to cancel the attack chain at a certain point from most attack signal types.
+ /// This response cancels the attack chain entirely. If sent early, it might cause some later effects to be skipped.
+ #define COMPONENT_CANCEL_ATTACK_CHAIN (1<<0)
+
/// Called from atom/Initialize() of target: (atom/target)
#define COMSIG_ATOM_INITIALIZED_ON "atom_initialized_on"
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
new file mode 100644
index 00000000000..b08132fb219
--- /dev/null
+++ b/code/__DEFINES/surgery.dm
@@ -0,0 +1,56 @@
+/// Defines for surgery and other stuff.
+
+
+// Used in surgery step to determine how blood should be spread to the doc
+/// Don't splash any blood.
+#define SURGERY_BLOODSPREAD_NONE 0
+/// Cover the surgeon's hands in blood.
+#define SURGERY_BLOODSPREAD_HANDS 1
+/// Cover the surgeon's body in blood.
+#define SURGERY_BLOODSPREAD_FULLBODY 2
+
+// The type of surgeries that an initiator can start.
+// Note that this doesn't apply for surgeries applied on missing organs.
+/// An initiator with this can start surgeries on organic organs. Make sure that anything that can be sharp gets this as well.
+#define SURGERY_INITIATOR_ORGANIC 1
+/// An initiator with this can start surgeries on robotic organs.
+#define SURGERY_INITIATOR_ROBOTIC 2
+
+// How "open" an organ is.
+
+/// Closed up.
+#define ORGAN_CLOSED 0
+
+// Different defines for different organ types, though both can still reference ORGAN_CLOSED
+/// An organic limb that's been opened, at least once.
+#define ORGAN_ORGANIC_OPEN 1
+/// An organ that's encased, probably with bone, where that casing has been cut through.
+#define ORGAN_ORGANIC_ENCASED_OPEN 2
+
+/// Synthetic organ that's been unscrewed.
+#define ORGAN_SYNTHETIC_LOOSENED 3
+/// Synthetic organ that's had its panel opened.
+#define ORGAN_SYNTHETIC_OPEN 4
+
+// Return defines for surgery steps
+
+/// Return this from begin_step() to abort the step and not try the surgery.
+#define SURGERY_BEGINSTEP_ABORT (-1)
+
+/// Return this from begin_step() to skip the current step entirely and proceed to the next one.
+/// Use this if you would end up leaving someone in an invalid state.
+#define SURGERY_BEGINSTEP_SKIP (1)
+
+// Return these from end_step/fail_step to indicate the next move
+
+/// The surgery step was not completed for some reason, and the next action will again be on this step.
+#define SURGERY_STEP_INCOMPLETE 0
+/// The surgery step was completed, and the surgery should continue to the next step.
+#define SURGERY_STEP_CONTINUE 1
+/// This step will automatically be retried without question as long as this is returned.
+/// Be very cautious with this one! Make sure that any flow where this is used has an exit condition where something else will be returned.
+/// Otherwise, the user will be stuck in a loop!
+#define SURGERY_STEP_RETRY_ALWAYS 2
+/// This surgery step will be conditionally retried, so long as the surgery step's can_repeat() proc returns TRUE.
+/// Otherwise, it'll behave just like SURGERY_STEP_INCOMPLETE.
+#define SURGERY_STEP_RETRY 3
diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm
index e1dfa6b7aed..62422f4b4d8 100644
--- a/code/__DEFINES/tools.dm
+++ b/code/__DEFINES/tools.dm
@@ -1,3 +1,4 @@
+// Tool tools
#define TOOL_CROWBAR "crowbar"
#define TOOL_MULTITOOL "multitool"
#define TOOL_SCREWDRIVER "screwdriver"
@@ -5,6 +6,29 @@
#define TOOL_WRENCH "wrench"
#define TOOL_WELDER "welder"
+// Surgery tools
+#define TOOL_RETRACTOR "retractor"
+#define TOOL_HEMOSTAT "hemostat"
+#define TOOL_CAUTERY "cautery"
+#define TOOL_DRILL "drill"
+#define TOOL_SCALPEL "scalpel"
+#define TOOL_SAW "saw"
+#define TOOL_BONESET "bonesetter"
+#define TOOL_BONEGEL "bonegel"
+#define TOOL_FIXOVEIN "fixovein"
+
+GLOBAL_LIST_INIT(surgery_tool_behaviors, list(
+ TOOL_RETRACTOR,
+ TOOL_HEMOSTAT,
+ TOOL_CAUTERY,
+ TOOL_DRILL,
+ TOOL_SCALPEL,
+ TOOL_SAW,
+ TOOL_BONESET,
+ TOOL_BONEGEL,
+ TOOL_FIXOVEIN
+))
+
#define MIN_TOOL_SOUND_DELAY 20
//Crowbar messages
diff --git a/code/__HELPERS/tool_helpers.dm b/code/__HELPERS/tool_helpers.dm
index 15c8e60591e..fb3c3d4227a 100644
--- a/code/__HELPERS/tool_helpers.dm
+++ b/code/__HELPERS/tool_helpers.dm
@@ -49,3 +49,20 @@
istype(W, /obj/item/bonegel) || \
istype(W, /obj/item/bonesetter)
)
+
+/proc/is_surgery_tool_by_behavior(obj/item/W)
+ if(!istype(W))
+ return FALSE
+ var/tool_behavior = W.tool_behaviour
+ return tool_behavior in list(
+ TOOL_BONEGEL,
+ TOOL_BONESET,
+ TOOL_CAUTERY,
+ TOOL_DRILL,
+ TOOL_FIXOVEIN,
+ TOOL_HEMOSTAT,
+ TOOL_RETRACTOR,
+ TOOL_SAW,
+ TOOL_SCALPEL,
+ TOOL_SCREWDRIVER
+ )
diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm
index 02b517e74ce..2f5ab6b0eec 100644
--- a/code/__HELPERS/traits.dm
+++ b/code/__HELPERS/traits.dm
@@ -193,6 +193,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NOPAIN "no_pain"
#define TRAIT_FORCE_DOORS "force_doors"
#define TRAIT_AI_UNTRACKABLE "AI_untrackable"
+#define TRAIT_REPEATSURGERY "master_surgeon" // Lets you automatically repeat surgeries regardless of tool
#define TRAIT_ELITE_CHALLENGER "elite_challenger"
//***** ITEM TRAITS *****//
@@ -201,6 +202,13 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_BUTCHERS_HUMANS "butchers_humans"
#define TRAIT_CMAGGED "cmagged"
+/// A surgical tool; when in hand in help intent (and with a surgery in progress) won't attack the user
+#define TRAIT_SURGICAL "surgical_tool"
+
+/// An advanced surgical tool. If a surgical tool has this flag, it will be able to automatically repeat steps until they succeed.
+#define TRAIT_ADVANCED_SURGICAL "advanced_surgical"
+
+
//
// common trait sources
#define TRAIT_GENERIC "generic"
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index a84ca90c352..3ef96ef6087 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -223,7 +223,8 @@
animals lunging, etc.
*/
/mob/proc/RangedAttack(atom/A, params)
- SEND_SIGNAL(src, COMSIG_MOB_ATTACK_RANGED, A, params)
+ if(SEND_SIGNAL(src, COMSIG_MOB_ATTACK_RANGED, A, params) & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return TRUE
/*
Restrained ClickOn
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index dcf5dc51771..43c69084ec0 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -14,11 +14,15 @@
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
- if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) & COMPONENT_NO_INTERACT)
+ var/signal_ret = SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user)
+ if(signal_ret & COMPONENT_NO_INTERACT)
return
- return
+ if(signal_ret & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return TRUE
/obj/item/proc/pre_attack(atom/A, mob/living/user, params) //do stuff before attackby!
+ if(SEND_SIGNAL(src, COMSIG_ITEM_PRE_ATTACK, A, user, params) & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return TRUE
if(is_hot(src) && A.reagents && !ismob(A))
to_chat(user, "You heat [A] with [src].")
A.reagents.temperature_reagents(is_hot(src))
@@ -40,29 +44,11 @@
return I.attack(src, user)
/obj/item/proc/attack(mob/living/M, mob/living/user, def_zone)
- SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user)
+ if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return TRUE
SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user)
if(flags & (NOBLUDGEON))
return FALSE
- if(can_operate(M)) //Checks if mob is lying down on table for surgery
- if(istype(src,/obj/item/robot_parts))//popup override for direct attach
- if(!attempt_initiate_surgery(src, M, user,1))
- return FALSE
- else
- return TRUE
- if(istype(src,/obj/item/organ/external))
- var/obj/item/organ/external/E = src
- if(E.is_robotic()) // Robot limbs are less messy to attach
- if(!attempt_initiate_surgery(src, M, user,1))
- return FALSE
- else
- return TRUE
- var/obj/item/organ/external/O = M.get_organ(user.zone_selected)
- if((is_sharp(src) || (isscrewdriver(src) && O?.is_robotic())) && user.a_intent == INTENT_HELP)
- if(!attempt_initiate_surgery(src, M, user))
- return FALSE
- else
- return TRUE
if(force && HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, "You don't want to harm other living beings!")
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index af79378a940..5b77d6ff721 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -59,7 +59,8 @@
examinate(A)
/atom/proc/attack_ghost(mob/user)
- return
+ if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_GHOST, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return TRUE
// health + cyborg analyzer for ghosts
/mob/living/attack_ghost(mob/dead/observer/user)
@@ -70,6 +71,7 @@
robot_healthscan(user, src)
else if(ishuman(src))
healthscan(user, src, 1, TRUE)
+ return ..()
// ---------------------------------------
// And here are some good things for free:
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index 7eed9f0e2ef..3667dca5804 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -25,7 +25,8 @@
A.attack_hand(src)
/atom/proc/attack_hand(mob/user as mob)
- return
+ if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_HAND, user) & COMPONENT_CANCEL_ATTACK_CHAIN)
+ return TRUE
/*
/mob/living/carbon/human/RestrainedClickOn(atom/A) -- Handled by carbons
diff --git a/code/datums/components/surgery_initiator.dm b/code/datums/components/surgery_initiator.dm
new file mode 100644
index 00000000000..eba5c0a6633
--- /dev/null
+++ b/code/datums/components/surgery_initiator.dm
@@ -0,0 +1,308 @@
+
+/**
+ * # Surgery Initiator
+ *
+ * Allows an item to start (or prematurely stop) a surgical operation.
+ */
+/datum/component/surgery_initiator
+ /// If present, this surgery TYPE will be attempted when the item is used.
+ /// Useful for things like limb reattachments that don't need a scalpel.
+ var/datum/surgery/forced_surgery
+
+ /// If true, the initial step will be cancellable by just using the tool again. Should be FALSE for any tool that actually has a first surgery step.
+ var/can_cancel_before_first = FALSE
+
+ /// If true, can be used with a cautery in the off-hand to cancel a surgery.
+ var/can_cancel = TRUE
+
+ /// If true, can start surgery on someone while they're standing up.
+ /// Seeing as how we really don't support this (yet), it's much nicer to selectively enable this if we want it.
+ var/can_start_on_stander = FALSE
+
+ /// Bitfield for the types of surgeries that this can start.
+ /// Note that in cases where organs are missing, this will be ignored.
+ /// Also, note that for anything sharp, SURGERY_INITIATOR_ORGANIC should be set as well.
+ var/valid_starting_types = SURGERY_INITIATOR_ORGANIC
+
+/**
+ * Create a new surgery initiating component.
+ *
+ * Arguments:
+ * * forced_surgery - (optional) the surgery that will be started when the parent is used on a mob.
+ */
+/datum/component/surgery_initiator/Initialize(datum/surgery/forced_surgery)
+ . = ..()
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.forced_surgery = forced_surgery
+
+/datum/component/surgery_initiator/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/initiate_surgery_moment)
+ RegisterSignal(parent, COMSIG_ATOM_UPDATE_SHARPNESS, .proc/on_parent_sharpness_change)
+
+/datum/component/surgery_initiator/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_ITEM_ATTACK)
+ UnregisterSignal(parent, COMSIG_ATOM_UPDATE_SHARPNESS)
+
+/// Keep tabs on the attached item's sharpness.
+/// This component gets added in atoms when they're made sharp as well.
+/datum/component/surgery_initiator/proc/on_parent_sharpness_change()
+ SIGNAL_HANDLER // COMSIG_ATOM_UPDATE_SHARPNESS
+ var/obj/item/P = parent
+ if(!P.sharp)
+ RemoveComponent()
+ qdel(src)
+
+/// Does the surgery initiation.
+/datum/component/surgery_initiator/proc/initiate_surgery_moment(datum/source, atom/target, mob/user)
+ SIGNAL_HANDLER // COMSIG_ITEM_ATTACK
+ if(!isliving(user))
+ return
+ var/mob/living/L = target
+ if(!user.Adjacent(target))
+ return
+ if(user.a_intent != INTENT_HELP)
+ return
+ if(!IS_HORIZONTAL(L) && !can_start_on_stander)
+ return
+ if(IS_HORIZONTAL(L) && !on_operable_surface(L))
+ return
+ if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ var/obj/item/organ/external/affected = C.get_organ(user.zone_selected)
+ if(affected)
+ if((affected.status & ORGAN_ROBOT) && !(valid_starting_types & SURGERY_INITIATOR_ROBOTIC))
+ return
+ if(!(affected.status & ORGAN_ROBOT) && !(valid_starting_types & SURGERY_INITIATOR_ORGANIC))
+ return
+
+ if(L.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
+ to_chat(user, "You realise that a ghost probably doesn't have any useful organs.")
+ return //no cult ghost surgery please
+ INVOKE_ASYNC(src, .proc/do_initiate_surgery_moment, target, user)
+ // This signal is actually part of the attack chain, so it needs to return true to stop it
+ return TRUE
+
+/// Meat and potatoes of starting surgery.
+/datum/component/surgery_initiator/proc/do_initiate_surgery_moment(mob/living/target, mob/user)
+ var/datum/surgery/current_surgery
+
+ // Check if we've already got a surgery on our target zone
+ for(var/i_one in target.surgeries)
+ var/datum/surgery/surgeryloop = i_one
+ if(surgeryloop.location == user.zone_selected)
+ current_surgery = surgeryloop
+ break
+
+ if(!isnull(current_surgery) && !current_surgery.step_in_progress)
+ var/datum/surgery_step/current_step = current_surgery.get_surgery_step()
+ if(current_step.try_op(user, target, user.zone_selected, parent, current_surgery))
+ return
+ if(istype(parent, /obj/item/scalpel/laser/manager/debug))
+ return
+ if(attempt_cancel_surgery(current_surgery, target, user))
+ return
+
+ if(!isnull(current_surgery) && current_surgery.step_in_progress)
+ return
+
+ var/list/available_surgeries = get_available_surgeries(user, target)
+
+ var/datum/surgery/procedure
+
+ if(!length(available_surgeries))
+ if(IS_HORIZONTAL(target))
+ to_chat(user, "There aren't any surgeries you can perform there right now.")
+ else
+ to_chat(user, "You can't perform any surgeries there while [target] is standing.")
+ return
+
+ // if we have a surgery that should be performed regardless with this item,
+ // make sure it's available to be done
+ if(forced_surgery)
+ for(var/datum/surgery/S in available_surgeries)
+ if(istype(S, forced_surgery))
+ procedure = S
+ break
+ else
+ procedure = input("Begin which procedure?", "Surgery", null, null) as null|anything in available_surgeries
+
+ if(!procedure)
+ return
+
+ return try_choose_surgery(user, target, procedure)
+
+/datum/component/surgery_initiator/proc/get_available_surgeries(mob/user, mob/living/target)
+ var/list/available_surgeries = list()
+
+ for(var/datum/surgery/surgery in GLOB.surgeries_list)
+ if(surgery.abstract) // no choosing abstract surgeries
+ continue
+ if(!target.can_run_surgery(surgery, user))
+ continue
+ for(var/path in surgery.target_mobtypes)
+ if(istype(target, path))
+ available_surgeries += surgery
+ break
+
+ return available_surgeries
+
+/// Does the surgery de-initiation.
+/datum/component/surgery_initiator/proc/attempt_cancel_surgery(datum/surgery/the_surgery, mob/living/patient, mob/user)
+ var/selected_zone = user.zone_selected
+ /// We haven't even started yet. Any surgery can be cancelled at this point.
+ if(the_surgery.step_number == 1)
+ patient.surgeries -= the_surgery
+ user.visible_message(
+ "[user] stops the surgery on [patient]'s [parse_zone(selected_zone)] with [parent].",
+ "You stop the surgery on [patient]'s [parse_zone(selected_zone)] with [parent].",
+ )
+
+ qdel(the_surgery)
+ return TRUE
+
+ if(!the_surgery.can_cancel)
+ return
+
+ // Don't make a forced surgery implement cancel a surgery.
+ if(istype(the_surgery, forced_surgery))
+ return
+
+ var/obj/item/close_tool
+ var/obj/item/other_hand = user.get_inactive_hand()
+
+ var/is_robotic = !the_surgery.requires_organic_bodypart
+ var/datum/surgery_step/chosen_close_step
+ var/skip_surgery = FALSE // if true, don't even run an operation, just end the surgery.
+
+ if(!the_surgery.requires_bodypart)
+ // special behavior here; if it doesn't require a bodypart just check if there's a limb there or not.
+ // this is a little bit gross and I do apologize
+ if(iscarbon(patient))
+ var/mob/living/carbon/C = patient
+ var/obj/item/organ/external/affected = C.get_organ(user.zone_selected)
+ if(!affected)
+ skip_surgery = TRUE
+
+ else
+ // uh there's no reason this should be hit but let's be safe LOL
+ skip_surgery = TRUE
+
+ if(!skip_surgery)
+ if(is_robotic)
+ chosen_close_step = new /datum/surgery_step/robotics/external/close_hatch/premature()
+ else
+ chosen_close_step = new /datum/surgery_step/generic/cauterize/premature()
+
+ if(skip_surgery)
+ close_tool = user.get_active_hand() // sure, just something so that it isn't null
+ else if(isrobot(user))
+ if(!is_robotic)
+ // borgs need to be able to finish surgeries with just the laser scalpel, no special checks here.
+ close_tool = parent
+ else
+ close_tool = locate(/obj/item/crowbar) in user.get_all_slots()
+ if(!close_tool)
+ to_chat(user, "You need a prying tool in an inactive slot to stop the surgery!")
+ return TRUE
+
+ else if(other_hand)
+ for(var/key in chosen_close_step.allowed_tools)
+ if(ispath(key) && istype(other_hand, key) || other_hand.tool_behaviour == key)
+ close_tool = other_hand
+ break
+
+ if(!close_tool)
+ to_chat(user, "You need a [is_robotic ? "prying": "cauterizing"] tool in your inactive hand to stop the surgery!")
+ return TRUE
+
+ if(skip_surgery || chosen_close_step.try_op(user, patient, selected_zone, close_tool, the_surgery))
+ // logging in case people wonder why they're cut up inside
+ log_attack(user, patient, "Prematurely finished \a [the_surgery] surgery.")
+ qdel(chosen_close_step)
+ patient.surgeries -= the_surgery
+ qdel(the_surgery)
+ return TRUE
+
+/datum/component/surgery_initiator/proc/can_start_surgery(mob/user, mob/living/target)
+ if(!user.Adjacent(target))
+ return FALSE
+
+ // The item was moved somewhere else
+ if(!(parent in user))
+ to_chat(user, "You cannot start an operation if you aren't holding the tool anymore.")
+ return FALSE
+
+ // While we were choosing, another surgery was started at the same location
+ for(var/datum/surgery/surgery in target.surgeries)
+ if(surgery.location == user.zone_selected)
+ to_chat(user, "There's already another surgery in progress on their [parse_zone(surgery.location)].")
+ return FALSE
+
+ return TRUE
+
+/datum/component/surgery_initiator/proc/try_choose_surgery(mob/user, mob/living/target, datum/surgery/surgery)
+ if(!can_start_surgery(user, target))
+ return
+
+ var/obj/item/organ/affecting_limb
+
+ var/selected_zone = user.zone_selected
+
+ if(iscarbon(target))
+ var/mob/living/carbon/carbon_target = target
+ affecting_limb = carbon_target.get_organ(check_zone(selected_zone))
+
+ if(surgery.requires_bodypart == isnull(affecting_limb))
+ if(surgery.requires_bodypart)
+ to_chat(user, "The patient has no [parse_zone(selected_zone)]!")
+ else
+ to_chat(user, "The patient has \a [parse_zone(selected_zone)]!")
+
+ return
+
+ if(!isnull(affecting_limb) && (surgery.requires_organic_bodypart && affecting_limb.is_robotic()) || (!surgery.requires_organic_bodypart && !affecting_limb.is_robotic()))
+ to_chat(user, "That's not the right type of limb for this operation!")
+ return
+
+ if(surgery.lying_required && !on_operable_surface(target))
+ to_chat(user, "Patient must be lying down for this operation.")
+ return
+
+ if(target == user && !surgery.self_operable)
+ to_chat(user, "You can't perform that operation on yourself!")
+ return
+
+ if(!surgery.can_start(user, target))
+ to_chat(user, "Can't start the surgery!")
+ return
+
+ if(surgery_needs_exposure(surgery, target))
+ to_chat(user, "You have to expose [target.p_their()] [parse_zone(selected_zone)] first!")
+ return
+
+ var/datum/surgery/procedure = new surgery.type(target, selected_zone, affecting_limb)
+
+ show_starting_message(user, target, procedure)
+
+ log_attack(user, target, "operated on (OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
+
+/datum/component/surgery_initiator/proc/surgery_needs_exposure(datum/surgery/surgery, mob/living/target)
+ return !surgery.ignore_clothes && !get_location_accessible(target, target.zone_selected)
+
+/// Handle to allow for easily overriding the message shown
+/datum/component/surgery_initiator/proc/show_starting_message(mob/user, mob/living/target, datum/surgery/procedure)
+ user.visible_message(
+ "[user] holds [parent] over [target]'s [parse_zone(user.zone_selected)] to prepare for surgery.",
+ "You hold [parent] over [target]'s [parse_zone(user.zone_selected)] to prepare for \an [procedure.name].",
+ )
+
+/datum/component/surgery_initiator/limb
+ can_cancel = FALSE // don't let a leg cancel a surgery
+
+/datum/component/surgery_initiator/robo
+ valid_starting_types = SURGERY_INITIATOR_ROBOTIC
+
+/datum/component/surgery_initiator/robo/sharp
+ valid_starting_types = SURGERY_INITIATOR_ORGANIC | SURGERY_INITIATOR_ROBOTIC
diff --git a/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm b/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm
index 35a5a667015..882582b7c71 100644
--- a/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm
+++ b/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm
@@ -1,18 +1,20 @@
/datum/surgery/organ_extraction
name = "Experimental Dissection"
- steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/open_encased/saw, /datum/surgery_step/open_encased/retract, /datum/surgery_step/internal/extract_organ, /datum/surgery_step/internal/gland_insert, /datum/surgery_step/generic/cauterize)
- possible_locs = list("chest")
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/open_encased/saw,
+ /datum/surgery_step/open_encased/retract,
+ /datum/surgery_step/internal/extract_organ,
+ /datum/surgery_step/internal/gland_insert,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_CHEST)
+ requires_organic_bodypart = TRUE
+ requires_bodypart = TRUE
/datum/surgery/organ_extraction/can_start(mob/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(!ishuman(user))
- return FALSE
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(target_zone)
- if(!affected)
- return FALSE
- if(affected.is_robotic())
- return FALSE
var/mob/living/carbon/human/H = user
// You must either: Be of the abductor species, or contain an abductor implant
if((isabductor(H) || (locate(/obj/item/implant/abductor) in H)))
@@ -40,17 +42,17 @@
user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "You pull [IC] out of [target]'s [target_zone].")
user.put_in_hands(IC)
IC.remove(target, special = 1)
- return TRUE
+ return SURGERY_STEP_CONTINUE
if(NO_INTORGANS in AB.dna.species.species_traits)
user.visible_message("[user] prepares [target]'s [target_zone] for further dissection!", "You prepare [target]'s [target_zone] for further dissection.")
- return TRUE
+ return SURGERY_STEP_CONTINUE
else
to_chat(user, "You don't find anything in [target]'s [target_zone]!")
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/internal/extract_organ/fail_step(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user]'s hand slips, failing to extract anything!", "Your hand slips, failing to extract anything!")
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/internal/gland_insert
name = "insert gland"
@@ -68,35 +70,26 @@
var/obj/item/organ/internal/heart/gland/gland = tool
gland.insert(target, 2)
affected.mend_fracture() // Look, any sufficiently advanced technology is indistinguishable from magic.
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/internal/gland_insert/fail_step(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user]'s hand slips, failing to insert the gland!", "Your hand slips, failing to insert the gland!")
- return FALSE
+ return SURGERY_STEP_RETRY
//IPC Gland Surgery//
/datum/surgery/organ_extraction/synth
name = "Experimental Robotic Dissection"
- steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/internal/extract_organ/synth,/datum/surgery_step/internal/gland_insert,/datum/surgery_step/robotics/external/close_hatch)
- possible_locs = list("chest")
+ steps = list(
+ /datum/surgery_step/robotics/external/unscrew_hatch,
+ /datum/surgery_step/robotics/external/open_hatch,
+ /datum/surgery_step/internal/extract_organ/synth,
+ /datum/surgery_step/internal/gland_insert,
+ /datum/surgery_step/robotics/external/close_hatch
+ )
+ possible_locs = list(BODY_ZONE_CHEST)
requires_organic_bodypart = FALSE
-
-/datum/surgery/organ_extraction/synth/can_start(mob/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(!ishuman(user))
- return FALSE
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(target_zone)
- if(!affected)
- return FALSE
- if(!affected.is_robotic())
- return FALSE
- var/mob/living/carbon/human/H = user
- // You must either: Be of the abductor species, or contain an abductor implant
- if((isabductor(H) || (locate(/obj/item/implant/abductor) in H)))
- return TRUE
- return FALSE
+ requires_bodypart = TRUE
/datum/surgery_step/internal/extract_organ/synth
name = "remove cell"
diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm
index 8dbb47daf97..fa87540701d 100644
--- a/code/game/machinery/computer/Operating.dm
+++ b/code/game/machinery/computer/Operating.dm
@@ -118,21 +118,26 @@
occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) //copy pasta ends here
occupantData["bloodType"] = occupant.dna.blood_type
- if(occupant.surgeries.len)
+ if(length(occupant.surgeries))
occupantData["inSurgery"] = 1
for(var/datum/surgery/procedure in occupant.surgeries)
occupantData["surgeryName"] = "[capitalize(procedure.name)]"
var/datum/surgery_step/surgery_step = procedure.get_surgery_step()
- occupantData["stepName"] = "[capitalize(surgery_step.name)]"
+ var/surgery_desc = "[capitalize(surgery_step.get_step_information(procedure))]"
+ if(surgery_step.repeatable)
+ var/datum/surgery_step/next = procedure.get_surgery_next_step()
+ if(next)
+ surgery_desc += " or [capitalize(next.get_step_information(procedure))]"
+ occupantData["stepName"] = surgery_desc
data["occupant"] = occupantData
- data["verbose"]=verbose
- data["oxyAlarm"]=oxyAlarm
- data["choice"]=choice
- data["health"]=healthAnnounce
- data["crit"]=crit
- data["healthAlarm"]=healthAlarm
- data["oxy"]=oxy
+ data["verbose"] = verbose
+ data["oxyAlarm"] = oxyAlarm
+ data["choice"] = choice
+ data["health"] = healthAnnounce
+ data["crit"] = crit
+ data["healthAlarm"] = healthAlarm
+ data["oxy"] = oxy
return data
diff --git a/code/game/objects/items/devices/autopsy.dm b/code/game/objects/items/devices/autopsy.dm
index 1672c68b413..4c632e9cb3e 100644
--- a/code/game/objects/items/devices/autopsy.dm
+++ b/code/game/objects/items/devices/autopsy.dm
@@ -154,7 +154,7 @@
if(!istype(M))
return
- if(!can_operate(M))
+ if(!on_operable_surface(M))
return
if(target_UID != M.UID())
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index eb4ba9457e6..cb839e8f237 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -23,6 +23,8 @@
else
name = "robot [initial(name)]"
+ AddComponent(/datum/component/surgery_initiator/limb, forced_surgery = /datum/surgery/attach_robotic_limb)
+
/obj/item/robot_parts/attack_self(mob/user)
var/choice = input(user, "Select the company appearance for this limb.", "Limb Company Selection") as null|anything in GLOB.selectable_robolimbs
if(!choice)
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 79eee757007..61052871f9f 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -145,7 +145,7 @@
var/mob/living/carbon/human/H = M
var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected)
- if(affecting.open == FALSE)
+ if(affecting.open == ORGAN_CLOSED)
affecting.germ_level = 0
if(stop_bleeding)
@@ -204,7 +204,7 @@
var/mob/living/carbon/human/H = M
var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected)
- if(affecting.open == FALSE)
+ if(affecting.open == ORGAN_CLOSED)
affecting.germ_level = 0
heal(H, user)
diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm
index b52fe91f5b5..c2cdfa2d2a7 100644
--- a/code/game/objects/items/tools/crowbar.dm
+++ b/code/game/objects/items/tools/crowbar.dm
@@ -66,6 +66,7 @@
/obj/item/crowbar/power
name = "jaws of life"
desc = "A set of jaws of life, the magic of science has managed to fit it down into a device small enough to fit in a tool belt. It's fitted with a prying head."
+ flags = CONDUCT
icon_state = "jaws_pry"
item_state = "jawsoflife"
belt_icon = "jaws"
@@ -76,6 +77,10 @@
toolspeed = 0.25
var/airlock_open_time = 100 // Time required to open powered airlocks
+/obj/item/crowbar/power/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_ADVANCED_SURGICAL, ROUNDSTART_TRAIT)
+
/obj/item/crowbar/power/suicide_act(mob/user)
user.visible_message("[user] is putting [user.p_their()] head in [src]. It looks like [user.p_theyre()] trying to commit suicide!")
playsound(loc, 'sound/items/jaws_pry.ogg', 50, 1, -1)
diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm
index 4c30f8e82f5..4e6a01c83ef 100644
--- a/code/game/objects/items/tools/screwdriver.dm
+++ b/code/game/objects/items/tools/screwdriver.dm
@@ -23,6 +23,10 @@
tool_behaviour = TOOL_SCREWDRIVER
var/random_color = TRUE //if the screwdriver uses random coloring
+/obj/item/screwdriver/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/surgery_initiator/robo)
+
/obj/item/screwdriver/nuke
name = "screwdriver"
desc = "A screwdriver with an ultra thin tip."
@@ -90,6 +94,10 @@
toolspeed = 0.25
random_color = FALSE
+/obj/item/screwdriver/power/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_ADVANCED_SURGICAL, ROUNDSTART_TRAIT)
+
/obj/item/screwdriver/power/suicide_act(mob/user)
user.visible_message("[user] is putting [src] to [user.p_their()] temple. It looks like [user.p_theyre()] trying to commit suicide!")
return BRUTELOSS
diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index 202ae6c51a5..03154af7030 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -170,6 +170,7 @@
item_color = null
w_class = WEIGHT_CLASS_NORMAL
light_color = LIGHT_COLOR_WHITE
+ tool_behaviour = TOOL_SAW
/obj/item/melee/energy/sword/cyborg/saw/New()
..()
diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm
index 0574719fe9f..115b3be662f 100644
--- a/code/game/objects/items/weapons/twohanded.dm
+++ b/code/game/objects/items/weapons/twohanded.dm
@@ -36,7 +36,7 @@
wielded = FALSE
force = force_unwielded
if(sharp_when_wielded)
- sharp = FALSE
+ set_sharpness(FALSE)
var/sf = findtext(name," (Wielded)")
if(sf)
name = copytext(name, 1, sf)
@@ -75,7 +75,7 @@
wielded = TRUE
force = force_wielded
if(sharp_when_wielded)
- sharp = TRUE
+ set_sharpness(TRUE)
name = "[name] (Wielded)"
update_icon()
if(user)
diff --git a/code/game/objects/items/weapons/whetstone.dm b/code/game/objects/items/weapons/whetstone.dm
index de57555ff94..dbf3e0773bf 100644
--- a/code/game/objects/items/weapons/whetstone.dm
+++ b/code/game/objects/items/weapons/whetstone.dm
@@ -46,7 +46,7 @@
return
user.visible_message("[user] sharpens [I] with [src]!", "You sharpen [I], making it much more deadly than before.")
if(!requires_sharpness)
- I.sharp = TRUE
+ set_sharpness(TRUE)
I.force = clamp(I.force + increment, 0, max)
I.throwforce = clamp(I.throwforce + increment, 0, max)
I.name = "[prefix] [I.name]"
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 38d1eb303ed..809c5b44520 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -54,6 +54,8 @@
armor = getArmor()
else if(!istype(armor, /datum/armor))
stack_trace("Invalid type [armor.type] found in .armor during /obj Initialize()")
+ if(sharp)
+ AddComponent(/datum/component/surgery_initiator)
/obj/Topic(href, href_list, nowindow = FALSE, datum/ui_state/state = GLOB.default_state)
// Calling Topic without a corresponding window open causes runtime errors
@@ -364,6 +366,15 @@ a {
/obj/proc/cult_reveal() //Called by cult reveal spell and chaplain's bible
return
+/// Set whether the item should be sharp or not
+/obj/proc/set_sharpness(new_sharp_val)
+ if(sharp == new_sharp_val)
+ return
+ sharp = new_sharp_val
+ SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_SHARPNESS)
+ if(!sharp && new_sharp_val)
+ AddComponent(/datum/component/surgery_initiator)
+
/obj/proc/force_eject_occupant(mob/target)
// This proc handles safely removing occupant mobs from the object if they must be teleported out (due to being SSD/AFK, by admin teleport, etc) or transformed.
diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm
index b667ed17092..e771ac9d685 100644
--- a/code/modules/hydroponics/hydroitemdefines.dm
+++ b/code/modules/hydroponics/hydroitemdefines.dm
@@ -200,7 +200,7 @@
hitsound = "swing_hit"
//Collapse sound (blade sheath)
playsound(src.loc, 'sound/weapons/blade_sheath.ogg', 50, 1) //Sound credit to Q.K. of Freesound.org
- sharp = extend
+ set_sharpness(extend)
update_icon(UPDATE_ICON_STATE)
if(ishuman(user))
var/mob/living/carbon/human/H = user
diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
index 94f94de2417..837c7a7ab55 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -58,7 +58,7 @@
if(stage == 5 && prob(50))
for(var/datum/surgery/S in owner.surgeries)
- if(S.location == "chest" && istype(S.get_surgery_step(), /datum/surgery_step/internal/manipulate_organs))
+ if(S.location == "chest" && S.organ_to_manipulate.open >= ORGAN_ORGANIC_OPEN)
AttemptGrow(burst_on_success = FALSE)
return
AttemptGrow()
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index b7add0ebf05..6094c5161d3 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -20,11 +20,11 @@
wetlevel = min(wetlevel + 1,5)
/mob/living/carbon/attackby(obj/item/I, mob/user, params)
- if(IS_HORIZONTAL(src) && surgeries.len)
- if(user != src && user.a_intent == INTENT_HELP)
+ if(length(surgeries))
+ if(user.a_intent == INTENT_HELP)
for(var/datum/surgery/S in surgeries)
if(S.next_step(user, src))
- return 1
+ return TRUE
return ..()
/mob/living/carbon/attack_hand(mob/living/carbon/human/user)
@@ -45,8 +45,8 @@
if(user.a_intent == INTENT_HELP)
for(var/datum/surgery/S in surgeries)
if(S.next_step(user, src))
- return 1
- return 0
+ return TRUE
+ return FALSE
/mob/living/carbon/attack_slime(mob/living/simple_animal/slime/M)
if(..()) //successful slime attack
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 326a056b35a..52bec02bd8e 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -178,6 +178,9 @@
else if(E.status & ORGAN_SPLINTED)
wound_flavor_text["[E.limb_name]"] = "[p_they(TRUE)] [p_have()] a splint on [p_their()] [E.name]!\n"
+ else if(!E.properly_attached)
+ wound_flavor_text["[E.limb_name]"] = "[p_their(TRUE)] [E.name] is barely attached!\n"
+
if(E.open)
if(E.is_robotic())
msg += "The maintenance hatch on [p_their()] [ignore_limb_branding(E.limb_name)] is open!\n"
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index fa800639d97..8ab9eb17813 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -71,7 +71,7 @@ emp_act
var/obj/item/organ/external/S = bodyparts_by_name[user.zone_selected]
if(!S)
return
- if(!S.is_robotic() || S.open == 2)
+ if(!S.is_robotic() || S.open == ORGAN_SYNTHETIC_OPEN)
return
. = TRUE
if(S.brute_dam > ROBOLIMB_SELF_REPAIR_CAP)
diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm
index 39a5122b9bd..2995e0fcb17 100644
--- a/code/modules/mob/living/carbon/human/human_organs.dm
+++ b/code/modules/mob/living/carbon/human/human_organs.dm
@@ -46,8 +46,12 @@
for(var/limb_tag in list("l_leg","r_leg","l_foot","r_foot"))
var/obj/item/organ/external/E = bodyparts_by_name[limb_tag]
- if(!E || (E.status & ORGAN_DEAD) || E.is_malfunctioning())
- stance_damage += 2 // let it fail even if just foot&leg. Also malfunctioning happens sporadically so it should impact more when it procs
+ if(!E || (E.status & ORGAN_DEAD) || E.is_malfunctioning() || !E.properly_attached)
+ if(E && !E.properly_attached && life_tick % 24 == 0)
+ to_chat(src, "Your [E] is hanging on by a thread! You need someone to surgically attach it for you!")
+ // let it fail even if just foot&leg. Also malfunctioning happens sporadically so it should impact more when it procs.
+ // Also, if you haven't gotten your leg properly attached surgically, you're not gonna have a good time trying to walk.
+ stance_damage += 2
else if(E.is_broken() || !E.is_usable())
stance_damage += 1
@@ -80,7 +84,8 @@
if(!E || !E.can_grasp || (E.status & ORGAN_SPLINTED))
continue
- if(E.is_broken())
+
+ if(E.is_broken() || !E.properly_attached)
if((E.body_part == HAND_LEFT) || (E.body_part == ARM_LEFT))
if(!l_hand)
continue
@@ -92,6 +97,14 @@
if(!unEquip(r_hand))
continue
+ if(!E.properly_attached)
+ visible_message(
+ "[src]'s [E.name] seems to be hanging loosely from [p_their()] wrist, completely fumbling what [p_they()] [p_were()] holding!",
+ "You feel pain shoot through your [E.name] as it dangles limply from your [E.amputation_point], you need to get it surgically attached before you can hold anything with it!"
+ )
+
+ return
+
var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ")
custom_emote(EMOTE_VISIBLE, "[HAS_TRAIT(src, TRAIT_NOPAIN) ? "" : emote_scream ]drops what [p_they()] [p_were()] holding in [p_their()] [E.name]!")
diff --git a/code/modules/mob/living/carbon/human/species/abductor.dm b/code/modules/mob/living/carbon/human/species/abductor.dm
index 646dbe8ac58..5461b11e314 100644
--- a/code/modules/mob/living/carbon/human/species/abductor.dm
+++ b/code/modules/mob/living/carbon/human/species/abductor.dm
@@ -14,7 +14,7 @@
)
species_traits = list(NO_BLOOD, NO_HAIR)
- inherent_traits = list(TRAIT_VIRUSIMMUNE, TRAIT_CHUNKYFINGERS, TRAIT_NOHUNGER, TRAIT_NOBREATH, TRAIT_NOEXAMINE)
+ inherent_traits = list(TRAIT_VIRUSIMMUNE, TRAIT_CHUNKYFINGERS, TRAIT_NOHUNGER, TRAIT_NOBREATH, TRAIT_NOEXAMINE, TRAIT_REPEATSURGERY)
dies_at_threshold = TRUE
taste_sensitivity = TASTE_SENSITIVITY_NO_TASTE
diff --git a/code/modules/mob/living/carbon/human/species/slime.dm b/code/modules/mob/living/carbon/human/species/slime.dm
index 528071e87e5..b8f2ad6939c 100644
--- a/code/modules/mob/living/carbon/human/species/slime.dm
+++ b/code/modules/mob/living/carbon/human/species/slime.dm
@@ -181,7 +181,7 @@
// Grah this line will leave a "not used" warning, in spite of the fact that the new() proc WILL do the thing.
// Bothersome.
var/obj/item/organ/external/new_limb = new limb_path(H)
- new_limb.open = 0 // This is just so that the compiler won't think that new_limb is unused, because the compiler is horribly stupid.
+ new_limb.open = ORGAN_CLOSED // This is just so that the compiler won't think that new_limb is unused, because the compiler is horribly stupid.
H.adjustBruteLoss(stored_brute)
H.adjustFireLoss(stored_burn)
H.update_body()
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
index 7e7ab0d177a..7c5aa2df737 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
@@ -38,7 +38,7 @@
/obj/item/gripper/medical
name = "medical gripper"
- desc = "A grasping tool used to help patients up once surgery is complete."
+ desc = "A grasping tool used to help patients up once surgery is complete, or to substitute for hands in surgical operations."
can_hold = list()
/obj/item/gripper/medical/attack_self(mob/user)
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 142dc69c1f4..ee76586dd81 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -153,7 +153,6 @@
if(on)
on = FALSE
force = initial(force)
- sharp = FALSE
w_class = initial(w_class)
name = initial(name)
attack_verb = list()
@@ -166,7 +165,6 @@
else
on = TRUE
force = 18
- sharp = TRUE
w_class = WEIGHT_CLASS_NORMAL
name = "energy dagger"
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
@@ -176,6 +174,7 @@
playsound(user, 'sound/weapons/saberon.ogg', 5, 1)
to_chat(user, "[src] is now active.")
set_light(brightness_on, 1)
+ set_sharpness(on)
update_icon()
/obj/item/pen/edagger/update_icon_state()
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index d3d9003780a..8f78cd1adf9 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -534,7 +534,7 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe/cable_restrain
if(!S)
return
- if(!S.is_robotic() || user.a_intent != INTENT_HELP || S.open == 2)
+ if(!S.is_robotic() || user.a_intent != INTENT_HELP || S.open == ORGAN_SYNTHETIC_OPEN)
return ..()
if(S.burn_dam > ROBOLIMB_SELF_REPAIR_CAP)
diff --git a/code/modules/surgery/abstract_steps.dm b/code/modules/surgery/abstract_steps.dm
new file mode 100644
index 00000000000..adefe2cf855
--- /dev/null
+++ b/code/modules/surgery/abstract_steps.dm
@@ -0,0 +1,276 @@
+/**
+ * This file consists of how we manage somewhat non-linear surgeries.
+ * Essentially what goes on here is that we have some "proxy" surgery steps that get inserted into existing surgeries at key points.
+ * Depending on the tool used after that point, the proxy step chooses the next step(s) that should be executed.
+ *
+ * These proxy steps use a list of surgeries, so a full procedure can be inserted into an existing surgery. Just make sure that the user's state
+ * after an intermediate surgery is (in the context of the surgery) identical to before the intermediate surgery. Don't heal things that will
+ * need to be healed during the surgery, for example.
+ *
+ * Adding a new intermediate surgery:
+ * - Define a new intermediate surgery datum with the list of steps that you want to inject. This forms one surgery "branch".
+ * - Define a new proxy surgery step with branches containing the typepath of your new surgery datum.
+ * - Insert that surgery step into an existing surgery.
+ */
+
+#define SURGERY_TOOL_HAND "hand"
+#define SURGERY_TOOL_ANY "any"
+
+/**
+ * A partial surgery that consists of a few steps that may be found in the middle of another operation.
+ * An existing surgery can yield to an intermediate surgery for a few steps by way of a proxy surgery_step.
+ */
+/datum/surgery/intermediate
+ abstract = TRUE
+
+/**
+ * Here's the special sauce: a surgery step that can pretend to be a few different surgery steps.
+ * These proxy steps will, depending on the tool that's used, either continue to the next surgery step, or temporarily spin off a new surgery
+ * by adding new steps to the current surgery.
+ */
+/datum/surgery_step/proxy
+ name = "Intermediate Operation"
+ /// Optional surgery TYPES that we can branch out to
+ /// Note that these must not share any starting tools.
+ var/list/branches = list()
+
+ /// Initialized versions of types specified in branches.
+ /// Don't fill this yourself, instead fill branches with surgery TYPES.
+ var/list/datum/surgery/branches_init = list()
+
+ /// These tools are just...special cases.
+ /// If we're using one of these tools and there's a tool conflict with the original surgery,
+ /// just ignore any branches and continue with the original surgery.
+ var/list/overriding_tools = list(
+ /obj/item/scalpel/laser/manager // IMS
+ )
+
+ /// Whether or not we should add ourselves as a step after we run a branch
+ var/insert_self_after = TRUE
+
+/datum/surgery_step/proxy/New()
+ if(length(branches_init))
+ CRASH("Proxy surgery [src] was given some initialized branches. Branching steps must be specified in var/branches, not var/branches_init.")
+
+ for(var/branch_type in branches)
+ if(!ispath(branch_type, /datum/surgery))
+ CRASH("proxy surgery [src] was given a branch type [branch_type] that isn't a subtype of /datum/surgery!")
+ var/datum/surgery/new_surgery = new branch_type()
+ // Add our proxy step as well so we can choose to perform multiple branches after we finish this one.
+ new_surgery.steps.Add(src)
+ branches_init.Add(new branch_type())
+
+ ..()
+
+/datum/surgery_step/proxy/Destroy(force, ...)
+ QDEL_LIST(branches_init)
+ return ..()
+
+/datum/surgery_step/proxy/get_step_information(datum/surgery/surgery)
+ var/datum/surgery_step/cur = surgery.get_surgery_next_step()
+ var/step_names = list()
+ for(var/datum/surgery/surg in branches_init)
+ step_names += surg.get_surgery_step()
+ step_names += cur // put this one on the end
+
+ return english_list(step_names, "Nothing...? If you see this, tell a coder.", ", or ")
+
+/datum/surgery_step/proxy/try_op(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+
+ var/list/starting_tools = list()
+
+ // pull this out separately. We don't want to move ahead with an any item surgery unless we've exhausted all of our
+ // other options.
+ // There will also only ever be one possibility for this in a surgery step (unless someone screwed up)
+ var/datum/surgery/possible_any_surgery
+ var/datum/surgery/next_surgery
+
+ var/datum/surgery_step/next_surgery_step = surgery.get_surgery_next_step()
+ var/datum/surgery_step/first_step
+
+ // Check the tools that all of our branches expect to use, and see if any of them match the current tool.
+ // sanity checks first though! Make sure we don't have any tool conflicts.
+ // A tool should only lead to one surgery step.
+
+ // (If there's a tool that could be used for a few different steps, though, make sure it's put into overriding steps)
+
+ for(var/datum/surgery/S in branches_init)
+ first_step = S.get_surgery_step()
+
+ if(!tool && first_step.accept_hand)
+ if(SURGERY_TOOL_HAND in starting_tools)
+ CRASH("[src] was provided with multiple branches that allow an empty hand.")
+ next_surgery = S // if there's no tool, just proceed forward.
+ starting_tools.Add(SURGERY_TOOL_HAND)
+
+ else if(first_step.accept_any_item)
+ if(SURGERY_TOOL_ANY in starting_tools)
+ CRASH("[src] was provided with multiple branches that allow any tool.")
+ possible_any_surgery = S
+ starting_tools.Add(SURGERY_TOOL_ANY)
+
+
+ for(var/allowed in first_step.allowed_tools)
+ if(ispath(allowed) && istype(tool, allowed) || (tool && istype(tool) && tool.tool_behaviour == allowed))
+ next_surgery = S
+ if(allowed in starting_tools && !(allowed in overriding_tools))
+ CRASH("[src] was provided with multiple branches that start with tool [allowed].")
+ else
+ starting_tools.Add(allowed)
+
+ // if we didn't set our next surgery (defined by the tool in use), check to see if a catch-all like accept hand or any item work
+ if(!next_surgery)
+ if((SURGERY_TOOL_ANY in starting_tools) && tool)
+ next_surgery = possible_any_surgery
+
+
+ // If this is set to true, the tool in use will force the next step in the main surgery.
+ var/overridden_tool = FALSE
+
+ // Also check the next surgery step.
+ if(!isnull(next_surgery_step))
+
+ 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].")
+
+ 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.")
+
+ if((SURGERY_TOOL_ANY in starting_tools) && next_surgery_step.accept_any_item)
+ CRASH("[src] has a conflict with the next main step [next_surgery_step] in surgery [surgery]: both accept any item.")
+
+ if(!tool && next_surgery_step.accept_hand && !(SURGERY_TOOL_HAND in starting_tools))
+ next_surgery = surgery
+
+ for(var/allowed in next_surgery_step.allowed_tools)
+ // debug IMS stuff, check it here so it forces the next surgery if it's being used
+ if(istype(tool, /obj/item/scalpel/laser/manager/debug))
+ if(!ispath(allowed) && (allowed in GLOB.surgery_tool_behaviors))
+ next_surgery = surgery
+ next_surgery_step.allowed_tools[tool.type] = 100
+ next_surgery_step.implement_type = tool.type
+ overridden_tool = TRUE
+ break
+
+ if(allowed in starting_tools)
+ if(allowed in overriding_tools)
+ overridden_tool = TRUE
+ break
+ else
+ CRASH("[src] has a tool conflict ([allowed]) with the next step [next_surgery_step] in the surgery it was called from ([surgery])")
+
+ if(tool && istype(tool) && (ispath(allowed) && istype(tool, allowed) || tool.tool_behaviour == allowed))
+ next_surgery = surgery
+
+ // Check if we might allow this under the any item rule if it doesn't fit into any other category. We don't want to accidentally miss a tool conflict.
+ if(tool && next_surgery_step.accept_any_item && !(SURGERY_TOOL_ANY in starting_tools))
+ next_surgery = surgery
+
+ if(!next_surgery)
+ // If the tool used doesn't work for any branch, just ignore it.
+ 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)
+
+ if(!target.can_run_surgery(next_surgery, user))
+ // Make sure the target can support the surgery.
+ // note that this should be run before can_start
+ return TRUE
+
+ if(!next_surgery.can_start(user, target))
+ // If we wouldn't be able to start the next surgery anyway, don't move past this step.
+ // 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)
+
+ // Also, bump the status so we skip past this abstract step.
+ surgery.steps.Insert(surgery.step_number + 1, next_surgery.steps)
+ surgery.step_number++
+
+ // 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
+ // don't worry about these names, they won't appear anywhere.
+ name = "Internal Bleeding (abstract)"
+ desc = "An intermediate surgery to fix internal bleeding while a patient is undergoing another procedure."
+ steps = list(/datum/surgery_step/fix_vein)
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT)
+
+/datum/surgery/intermediate/bleeding/can_start(mob/user, mob/living/carbon/target)
+ . = ..()
+ if(!.)
+ return FALSE
+ var/mob/living/carbon/human/H = target
+ var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
+ if(affected.status & ORGAN_INT_BLEEDING)
+ return TRUE
+ else
+ // 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 called like that.
+ to_chat(user, "The veins in [target]'s [parse_zone(affected)] seem to be in perfect condition, they don't need mending.")
+
+ return FALSE
+
+/datum/surgery/intermediate/mendbone
+ name = "Mend Bone (abstract)"
+ desc = "An intermediate surgery to mend bones while a patient is undergoing another procedure."
+ steps = list(/datum/surgery_step/glue_bone, /datum/surgery_step/set_bone, /datum/surgery_step/finish_bone)
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
+
+/datum/surgery/intermediate/mendbone/can_start(mob/user, mob/living/carbon/target)
+ . = ..()
+ if(!.)
+ return FALSE
+ var/mob/living/carbon/human/H = target
+ var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
+ if(HAS_TRAIT(target, TRAIT_NO_BONES))
+ return FALSE
+ if(affected.limb_flags & CANNOT_BREAK)
+ return FALSE
+ if(affected.status & ORGAN_BROKEN)
+ return TRUE
+ else
+ to_chat(user, "The bones in [target]'s [parse_zone(affected)] look fully intact, they don't need mending.")
+ return FALSE
+
+/// Proxy surgery step to allow healing bleeding and mending bones.
+/// Should be added into surgeries just after the first three standard steps.
+/datum/surgery_step/proxy/open_organ
+ name = "mend internal bleeding or mend bone (proxy)"
+ branches = list(
+ /datum/surgery/intermediate/bleeding,
+ /datum/surgery/intermediate/mendbone
+ )
+
+/// Mend IB without healing bones
+/datum/surgery_step/proxy/ib
+ name = "mend internal bleeding (proxy)"
+ branches = list(
+ /datum/surgery/intermediate/bleeding
+ )
+
+/// The robotic equivalent
+/datum/surgery_step/proxy/robotics/repair_limb
+ name = "Repair Limb (proxy)"
+ branches = list(
+ /datum/surgery/intermediate/robotics/repair/burn,
+ /datum/surgery/intermediate/robotics/repair/brute
+ )
+
+#undef SURGERY_TOOL_ANY
+#undef SURGERY_TOOL_HAND
diff --git a/code/modules/surgery/bones.dm b/code/modules/surgery/bones.dm
index df8c4d00efe..a2c1c0ab57c 100644
--- a/code/modules/surgery/bones.dm
+++ b/code/modules/surgery/bones.dm
@@ -5,27 +5,42 @@
///Surgery Datums
/datum/surgery/bone_repair
name = "Bone Repair"
- steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/glue_bone, /datum/surgery_step/set_bone, /datum/surgery_step/finish_bone, /datum/surgery_step/generic/cauterize)
- possible_locs = list("chest", "l_arm", "l_hand", "r_arm", "r_hand","r_leg", "r_foot", "l_leg", "l_foot", "groin")
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/ib, // Only proxy IB here
+ /datum/surgery_step/glue_bone,
+ /datum/surgery_step/set_bone,
+ /datum/surgery_step/finish_bone,
+ /datum/surgery_step/generic/cauterize
+ )
+
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
/datum/surgery/bone_repair/skull
name = "Skull Repair"
- steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/glue_bone, /datum/surgery_step/mend_skull, /datum/surgery_step/finish_bone, /datum/surgery_step/generic/cauterize)
- possible_locs = list("head")
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/ib,
+ /datum/surgery_step/glue_bone,
+ /datum/surgery_step/set_bone/mend_skull,
+ /datum/surgery_step/finish_bone,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_HEAD)
/datum/surgery/bone_repair/can_start(mob/user, mob/living/carbon/target)
- if(istype(target,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(affected.is_robotic())
- return FALSE
- if(affected.limb_flags & CANNOT_BREAK)
- return FALSE
- if(affected.status & ORGAN_BROKEN)
- return TRUE
- return TRUE
+ var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
+ if(affected.limb_flags & CANNOT_BREAK)
+ return FALSE
+ if(!(affected.status & ORGAN_BROKEN))
+ return FALSE
+ if(HAS_TRAIT(target, TRAIT_NO_BONES))
+ return FALSE
+ return TRUE
//surgery steps
@@ -33,143 +48,139 @@
name = "mend bone"
allowed_tools = list(
- /obj/item/bonegel = 100, \
- /obj/item/screwdriver = 90
+ TOOL_BONEGEL = 100,
+ TOOL_SCREWDRIVER = 90
)
can_infect = TRUE
- blood_level = 1
+ blood_level = SURGERY_BLOODSPREAD_HANDS
- time = 24
-
-/datum/surgery_step/glue_bone/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- return affected && !affected.is_robotic() && !(affected.limb_flags & CANNOT_BREAK)
+ time = 2.4 SECONDS
/datum/surgery_step/glue_bone/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 damaged bones in [target]'s [affected.name] with \the [tool]." , \
- "You start applying medication to the damaged bones in [target]'s [affected.name] with \the [tool].")
+ user.visible_message(
+ "[user] starts applying medication to the damaged bones in [target]'s [affected.name] with \the [tool].",
+ "You start applying medication to the damaged bones in [target]'s [affected.name] with \the [tool]."
+ )
target.custom_pain("Something in your [affected.name] is causing you a lot of pain!")
- ..()
+ return ..()
/datum/surgery_step/glue_bone/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(" [user] applies some [tool] to [target]'s bone in [affected.name]", \
- " You apply some [tool] to [target]'s bone in [affected.name] with \the [tool].")
+ user.visible_message(
+ " [user] applies some [tool] to [target]'s bone in [affected.name]",
+ " You apply some [tool] to [target]'s bone in [affected.name] with \the [tool]."
+ )
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/glue_bone/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(" [user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \
- " Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!")
- return FALSE
+ user.visible_message(
+ " [user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!",
+ " Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!"
+ )
+ return SURGERY_STEP_RETRY
/datum/surgery_step/set_bone
name = "set bone"
allowed_tools = list(
- /obj/item/bonesetter = 100, \
- /obj/item/wrench = 90 \
+ TOOL_BONESET = 100,
+ TOOL_WRENCH = 90
)
- time = 32
-
-/datum/surgery_step/set_bone/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- return affected && !affected.is_robotic()
+ time = 3.2 SECONDS
/datum/surgery_step/set_bone/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] is beginning to set the bone in [target]'s [affected.name] in place with \the [tool]." , \
- "You are beginning to set the bone in [target]'s [affected.name] in place with \the [tool].")
+ user.visible_message(
+ "[user] is beginning to set the bone in [target]'s [affected.name] in place with \the [tool].",
+ "You are beginning to set the bone in [target]'s [affected.name] in place with \the [tool]."
+ )
target.custom_pain("The pain in your [affected.name] is going to make you pass out!")
- ..()
+ return ..()
/datum/surgery_step/set_bone/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(affected.status & ORGAN_BROKEN)
- user.visible_message(" [user] sets the bone in [target]'s [affected.name] in place with \the [tool].", \
- " You set the bone in [target]'s [affected.name] in place with \the [tool].")
- return TRUE
- else
- user.visible_message(" [user] sets the bone in [target]'s [affected.name] in place with \the [tool].", \
- " You set the bone in [target]'s [affected.name] in place with \the [tool].")
- return TRUE
+ user.visible_message(
+ " [user] sets the bone in [target]'s [affected.name] in place with \the [tool].",
+ " You set the bone in [target]'s [affected.name] in place with \the [tool]."
+ )
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/set_bone/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(" [user]'s hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!" , \
- " Your hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!")
+ user.visible_message(
+ " [user]'s hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!",
+ " Your hand slips, damaging the bone in [target]'s [affected.name] with \the [tool]!"
+ )
affected.receive_damage(5)
- return FALSE
+ return SURGERY_STEP_RETRY
-/datum/surgery_step/mend_skull
+/datum/surgery_step/set_bone/mend_skull
name = "mend skull"
- allowed_tools = list(
- /obj/item/bonesetter = 100, \
- /obj/item/wrench = 90 \
+/datum/surgery_step/set_bone/mend_skull/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
+ user.visible_message(
+ "[user] is beginning piece together [target]'s skull with \the [tool].",
+ "You are beginning piece together [target]'s skull with \the [tool]."
+ )
+ return ..()
+
+/datum/surgery_step/set_bone/mend_skull/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(
+ " [user] sets [target]'s [affected.encased] with \the [tool].",
+ " You set [target]'s [affected.encased] with \the [tool]."
)
- time = 32
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/mend_skull/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
+/datum/surgery_step/set_bone/mend_skull/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)
- return affected && !affected.is_robotic() && affected.limb_name == "head"
-
-/datum/surgery_step/mend_skull/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- user.visible_message("[user] is beginning piece together [target]'s skull with \the [tool]." , \
- "You are beginning piece together [target]'s skull with \the [tool].")
- ..()
-
-/datum/surgery_step/mend_skull/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(" [user] sets [target]'s [affected.encased] with \the [tool]." , \
- " You set [target]'s [affected.encased] with \the [tool].")
-
- return TRUE
-
-/datum/surgery_step/mend_skull/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("[user]'s hand slips, damaging [target]'s face with \the [tool]!" , \
- "Your hand slips, damaging [target]'s face with \the [tool]!")
- var/obj/item/organ/external/head/h = affected
- h.receive_damage(10)
- h.disfigure()
- return FALSE
+ user.visible_message(
+ "[user]'s hand slips, damaging [target]'s face with \the [tool]!",
+ "Your hand slips, damaging [target]'s face with \the [tool]!"
+ )
+ var/obj/item/organ/external/head/H = affected
+ H.receive_damage(10)
+ H.disfigure()
+ return SURGERY_STEP_RETRY
/datum/surgery_step/finish_bone
name = "medicate bones"
allowed_tools = list(
- /obj/item/bonegel = 100, \
- /obj/item/screwdriver = 90
+ TOOL_BONEGEL = 100,
+ TOOL_SCREWDRIVER = 90
)
can_infect = TRUE
- blood_level = 1
+ blood_level = SURGERY_BLOODSPREAD_HANDS
- time = 24
-
-/datum/surgery_step/finish_bone/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- return affected && !affected.is_robotic()
+ time = 2.4 SECONDS
/datum/surgery_step/finish_bone/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 to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].", \
- "You start to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].")
- ..()
+ user.visible_message(
+ "[user] starts to finish mending the damaged bones in [target]'s [affected.name] with \the [tool].",
+ "You start to finish mending the damaged bones in [target]'s [affected.name] with \the [tool]."
+ )
+ return ..()
-/datum/surgery_step/finish_bone/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/finish_bone/end_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)
- user.visible_message(" [user] has mended the damaged bones in [target]'s [affected.name] with \the [tool]." , \
- " You have mended the damaged bones in [target]'s [affected.name] with \the [tool]." )
+ user.visible_message(
+ " [user] has mended the damaged bones in [target]'s [affected.name] with \the [tool].",
+ " You have mended the damaged bones in [target]'s [affected.name] with \the [tool]."
+ )
affected.mend_fracture()
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/finish_bone/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(" [user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \
- " Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!")
- return FALSE
+ user.visible_message(
+ " [user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!",
+ " Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!"
+ )
+ return SURGERY_STEP_RETRY
diff --git a/code/modules/surgery/cavity_implant.dm b/code/modules/surgery/cavity_implant.dm
index 5c79b51193f..b4683b5f931 100644
--- a/code/modules/surgery/cavity_implant.dm
+++ b/code/modules/surgery/cavity_implant.dm
@@ -1,206 +1,312 @@
/datum/surgery/cavity_implant
name = "Cavity Implant/Removal"
- steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/open_encased/saw,
- /datum/surgery_step/open_encased/retract, /datum/surgery_step/cavity/make_space,/datum/surgery_step/cavity/place_item,/datum/surgery_step/cavity/close_space,/datum/surgery_step/open_encased/close,/datum/surgery_step/glue_bone, /datum/surgery_step/set_bone,/datum/surgery_step/finish_bone,/datum/surgery_step/generic/cauterize)
- possible_locs = list("chest","head")
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/ib, // just do IB here since we're sawing the bone anyway
+ /datum/surgery_step/open_encased/saw,
+ /datum/surgery_step/open_encased/retract,
+ /datum/surgery_step/cavity/make_space,
+ /datum/surgery_step/proxy/cavity_manipulation,
+ /datum/surgery_step/cavity/close_space,
+ /datum/surgery_step/open_encased/close,
+ /datum/surgery_step/glue_bone,
+ /datum/surgery_step/set_bone,
+ /datum/surgery_step/finish_bone,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD)
-
-/datum/surgery/cavity_implant/soft
- name = "Cavity Implant/Removal"
- steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/generic/cut_open, /datum/surgery_step/cavity/make_space,/datum/surgery_step/cavity/place_item,/datum/surgery_step/cavity/close_space,/datum/surgery_step/generic/cauterize)
-
- possible_locs = list("groin")
-
-/datum/surgery/cavity_implant/synth
- name = "Robotic Cavity Implant/Removal"
- steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/cavity/place_item,/datum/surgery_step/robotics/external/close_hatch)
- possible_locs = list("chest","head","groin")
- requires_organic_bodypart = FALSE
-
-/datum/surgery/cavity_implant/can_start(mob/user, mob/living/carbon/human/target)
- if(!istype(target))
- return FALSE
- var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(affected.is_robotic())
+/datum/surgery/cavity_implant/can_start(mob/user, mob/living/carbon/target)
+ if(HAS_TRAIT(target, TRAIT_NO_BONES))
return FALSE
return TRUE
-/datum/surgery/cavity_implant/synth/can_start(mob/user, mob/living/carbon/human/target)
- if(!istype(target))
+/datum/surgery/cavity_implant/soft
+ desc = "Implant an object into a cavity not protected by bone."
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/ib, // just do IB here since we're sawing the bone anyway
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/cavity/make_space,
+ /datum/surgery_step/proxy/cavity_manipulation,
+ /datum/surgery_step/cavity/close_space,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_PRECISE_GROIN)
+
+/datum/surgery/cavity_implant/soft/boneless
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD)
+
+/datum/surgery/cavity_implant/soft/boneless/can_start(mob/user, mob/living/carbon/target)
+ if(!HAS_TRAIT(target, TRAIT_NO_BONES))
return FALSE
- var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- return affected.is_robotic()
+ return TRUE
+
+/datum/surgery/cavity_implant/synth
+ name = "Robotic Cavity Implant/Removal"
+ steps = list(
+ /datum/surgery_step/robotics/external/unscrew_hatch,
+ /datum/surgery_step/robotics/external/open_hatch,
+ /datum/surgery_step/proxy/cavity_manipulation/robotic,
+ /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/cavity
/datum/surgery_step/cavity/proc/get_max_wclass(obj/item/organ/external/affected)
switch(affected.limb_name)
- if("head")
+ if(BODY_ZONE_HEAD)
return WEIGHT_CLASS_TINY
- if("chest")
+ if(BODY_ZONE_CHEST)
return WEIGHT_CLASS_NORMAL
- if("groin")
+ if(BODY_ZONE_PRECISE_GROIN)
return WEIGHT_CLASS_SMALL
return 0
/datum/surgery_step/cavity/proc/get_cavity(obj/item/organ/external/affected)
switch(affected.limb_name)
- if("head")
+ if(BODY_ZONE_HEAD)
return "cranial"
- if("chest")
+ if(BODY_ZONE_CHEST)
return "thoracic"
- if("groin")
+ if(BODY_ZONE_PRECISE_GROIN)
return "abdominal"
return ""
-/datum/surgery_step/cavity/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/cavity/proc/get_item_inside(obj/item/organ/external/affected)
+ var/obj/item/extracting
+ for(var/obj/item/I in affected.contents)
+ if(!istype(I, /obj/item/organ))
+ extracting = I
+ break
+
+ if(!extracting && affected.hidden)
+ extracting = affected.hidden
+
+ return extracting
+
+/datum/surgery_step/cavity/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/chest/affected = target.get_organ(target_zone)
- user.visible_message(" [user]'s hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!", \
- " Your hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!")
+ user.visible_message(
+ " [user]'s hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!",
+ " Your hand slips, scraping around inside [target]'s [affected.name] with \the [tool]!"
+ )
affected.receive_damage(20)
+ return SURGERY_STEP_RETRY
/datum/surgery_step/cavity/make_space
name = "make cavity space"
allowed_tools = list(
- /obj/item/surgicaldrill = 100, \
- /obj/item/pen = 90, \
- /obj/item/stack/rods = 60
+ TOOL_DRILL = 100,
+ /obj/item/screwdriver/power = 90,
+ /obj/item/pen = 90,
+ /obj/item/stack/rods = 60
)
- time = 54
+ time = 5.4 SECONDS
-/datum/surgery_step/cavity/make_space/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/cavity/make_space/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)
- user.visible_message("[user] starts making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].", \
- "You start making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]." )
+ user.visible_message(
+ "[user] starts making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].",
+ "You start making some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]."
+ )
target.custom_pain("The pain in your chest is living hell!")
- ..()
+ return ..()
-/datum/surgery_step/cavity/make_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/cavity/make_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/chest/affected = target.get_organ(target_zone)
- user.visible_message(" [user] makes some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].", \
- " You make some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]." )
+ user.visible_message(
+ " [user] makes some space inside [target]'s [get_cavity(affected)] cavity with \the [tool].",
+ " You make some space inside [target]'s [get_cavity(affected)] cavity with \the [tool]."
+ )
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/cavity/close_space
name = "close cavity space"
allowed_tools = list(
- /obj/item/scalpel/laser = 100, \
- /obj/item/cautery = 100, \
- /obj/item/clothing/mask/cigarette = 90, \
- /obj/item/lighter = 60, \
- /obj/item/weldingtool = 30
+ /obj/item/scalpel/laser = 100,
+ TOOL_CAUTERY = 100,
+ /obj/item/clothing/mask/cigarette = 90,
+ /obj/item/lighter = 60,
+ TOOL_WELDER = 30
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/cavity/close_space/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/cavity/close_space/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)
- user.visible_message("[user] starts mending [target]'s [get_cavity(affected)] cavity wall with \the [tool].", \
- "You start mending [target]'s [get_cavity(affected)] cavity wall with \the [tool]." )
+ user.visible_message(
+ "[user] starts mending [target]'s [get_cavity(affected)] cavity wall with \the [tool].",
+ "You start mending [target]'s [get_cavity(affected)] cavity wall with \the [tool]."
+ )
target.custom_pain("The pain in your chest is living hell!")
- ..()
+ return ..()
-/datum/surgery_step/cavity/close_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/cavity/close_space/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/chest/affected = target.get_organ(target_zone)
- user.visible_message(" [user] mends [target]'s [get_cavity(affected)] cavity walls with \the [tool].", \
- " You mend [target]'s [get_cavity(affected)] cavity walls with \the [tool]." )
+ user.visible_message(
+ " [user] mends [target]'s [get_cavity(affected)] cavity walls with \the [tool].",
+ " You mend [target]'s [get_cavity(affected)] cavity walls with \the [tool]."
+ )
+
+ return SURGERY_STEP_CONTINUE
+
+
+/datum/surgery_step/cavity/remove_item
+ name = "extract object"
+ accept_hand = TRUE
+
+/datum/surgery_step/cavity/remove_item/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ // Check even if there isn't anything inside
+ user.visible_message(
+ "[user] checks for items in [target]'s [target_zone].",
+ "You check for items in [target]'s [target_zone]..."
+ )
+ return ..()
+
+/datum/surgery_step/cavity/remove_item/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/extracting
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+
+ for(var/obj/item/I in affected.contents)
+ if(!istype(I, /obj/item/organ))
+ extracting = I
+ break
+
+ if(!extracting && affected.hidden)
+ extracting = affected.hidden
+
+ if(!extracting)
+ to_chat(user, "You don't find anything in [target]'s [target_zone].")
+ return SURGERY_STEP_CONTINUE
+ user.visible_message(
+ "[user] pulls [extracting] out of [target]'s [target_zone]!",
+ "You pull [extracting] out of [target]'s [target_zone]."
+ )
+ user.put_in_hands(extracting)
+ affected.hidden = null
+ return SURGERY_STEP_CONTINUE
+
+/datum/surgery_step/cavity/remove_item/fail_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)
+ user.visible_message(
+ "[user] grabs onto something else by mistake, damaging it!.",
+ "You grab onto something else inside [target]'s [get_cavity(affected)] cavity by mistake, damaging it!"
+ )
+
+ affected.damage += rand(3, 5)
+
+ 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
+
+ time = 3.2 SECONDS
+
+
+/datum/surgery_step/cavity/place_item/tool_check(mob/user, obj/item/tool)
+ if(istype(tool, /obj/item/disk/nuclear))
+ to_chat(user, "Central command would kill you if you implanted the disk into someone.")
+ return FALSE
+
+ var/obj/item/disk/nuclear/datdisk = locate() in tool
+ if(datdisk)
+ to_chat(user, "Central Command would kill you if you implanted the disk into someone. Especially if in a [tool].")
+ return FALSE
+
+ if(istype(tool, /obj/item/organ))
+ to_chat(user, "This isn't the type of surgery for organ transplants!")
+ return FALSE
+
+ if(!user.canUnEquip(tool, 0))
+ to_chat(user, "[tool] is stuck to your hand!")
+ return FALSE
+
+ if(istype(tool, /obj/item/cautery))
+ // Pass it to the next step
+ return FALSE
return TRUE
-/datum/surgery_step/cavity/place_item
- name = "implant/extract object"
- accept_hand = TRUE
- accept_any_item = TRUE
- var/obj/item/IC = null
- allowed_tools = list(/obj/item = 100)
-
- time = 32
-
-
-/datum/surgery_step/cavity/place_item/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(!ishuman(target))
- return FALSE
+/datum/surgery_step/cavity/place_item/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(!affected)
- to_chat(user, "\The [target] lacks a [parse_zone(target_zone)]!")
- return FALSE
- if(tool)
- var/can_fit = !affected.hidden && tool.w_class <= get_max_wclass(affected)
- if(!can_fit)
- to_chat(user, "\The [tool] won't fit in \The [affected.name]!")
- return FALSE
+
+ var/can_fit = !affected.hidden && tool.w_class <= get_max_wclass(affected)
+ if(!can_fit)
+ to_chat(user, "\The [tool] won't fit in \the [affected]!")
+ return SURGERY_BEGINSTEP_ABORT
+
+ user.visible_message(
+ "[user] starts putting \the [tool] inside [target]'s [get_cavity(affected)] cavity.",
+ "You start putting \the [tool] inside [target]'s [get_cavity(affected)] cavity."
+ )
+ target.custom_pain("The pain in your [target_zone] is living hell!")
return ..()
-/datum/surgery_step/cavity/place_item/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)
- for(var/obj/item/I in affected.contents)
- if(!istype(I, /obj/item/organ))
- IC = I
- break
- if(!IC && affected.hidden)
- IC = affected.hidden
- if(istype(tool,/obj/item/cautery))
- to_chat(user, "You prepare to close the cavity wall.")
- else if(tool)
- user.visible_message("[user] starts putting \the [tool] inside [target]'s [get_cavity(affected)] cavity.", \
- "You start putting \the [tool] inside [target]'s [get_cavity(affected)] cavity." )
- else if(IC)
- user.visible_message("[user] checks for items in [target]'s [target_zone].", "You check for items in [target]'s [target_zone]...")
- else //no internal items..but we still need a message!
- user.visible_message("[user] checks for items in [target]'s [target_zone].", "You check for items in [target]'s [target_zone]...")
-
- target.custom_pain("The pain in your [target_zone] is living hell!")
- ..()
-
-/datum/surgery_step/cavity/place_item/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/cavity/place_item/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/chest/affected = target.get_organ(target_zone)
+ if(get_item_inside(affected))
+ to_chat(user, "There seems to be something in there already!")
+ return SURGERY_STEP_CONTINUE
- if(istype(tool, /obj/item/disk/nuclear))
- to_chat(user, "Central command would kill you if you implanted the disk into someone.")
- return FALSE//fail
-
- var/obj/item/disk/nuclear/datdisk = locate() in tool
- if(datdisk)
- to_chat(user, "Central command would kill you if you implanted the disk into someone. Even if in a box. Especially in a box.")
- return FALSE//fail
-
- if(istype(tool,/obj/item/organ))
- to_chat(user, "This isn't the type of surgery for organ transplants!")
- return FALSE//fail
-
- if(!user.canUnEquip(tool, 0))
- to_chat(user, "[tool] is stuck to your hand, you can't put it in [target]!")
- return FALSE
-
- if(istype(tool,/obj/item/cautery))
- return TRUE//god this is ugly....
- else if(tool)
- if(IC)
- to_chat(user, "There seems to be something in there already!")
- return TRUE
- else
- user.visible_message(" [user] puts \the [tool] inside [target]'s [get_cavity(affected)] cavity.", \
- " You put \the [tool] inside [target]'s [get_cavity(affected)] cavity." )
- if((tool.w_class > get_max_wclass(affected) / 2 && prob(50) && !affected.is_robotic()))
- to_chat(user, " You tear some vessels trying to fit the object in the cavity.")
- affected.cause_internal_bleeding()
- user.drop_item()
- affected.hidden = tool
- tool.forceMove(target)
- return TRUE
- else
- if(IC)
- user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "You pull [IC] out of [target]'s [target_zone].")
- user.put_in_hands(IC)
- affected.hidden = null
- return TRUE
- else
- to_chat(user, "You don't find anything in [target]'s [target_zone].")
- return FALSE
+ user.visible_message(
+ "[user] puts \the [tool] inside [target]'s [get_cavity(affected)] cavity.",
+ "You put \the [tool] inside [target]'s [get_cavity(affected)] cavity."
+ )
+ if((tool.w_class > get_max_wclass(affected) / 2 && prob(50) && !affected.is_robotic()))
+ user.visible_message(
+ "[user] tears some blood vessels trying to fit the object in the cavity!",
+ "You tear some blood vessels trying to fit the object into the cavity!",
+ "You hear some gentle tearing.")
+ affected.cause_internal_bleeding()
+ user.drop_item()
+ affected.hidden = tool
+ tool.forceMove(target)
+ return SURGERY_STEP_CONTINUE
diff --git a/code/modules/surgery/core_removal.dm b/code/modules/surgery/core_removal.dm
index a54aa25d228..75838052415 100644
--- a/code/modules/surgery/core_removal.dm
+++ b/code/modules/surgery/core_removal.dm
@@ -1,42 +1,51 @@
/datum/surgery/core_removal
name = "core removal"
steps = list(/datum/surgery_step/slime/cut_flesh, /datum/surgery_step/slime/extract_core)
- allowed_mob = list(/mob/living/simple_animal/slime)
- possible_locs = list("chest", "head", "l_arm", "l_hand", "r_arm", "r_hand", "r_leg", "r_foot", "l_leg", "l_foot", "groin")
+ target_mobtypes = list(/mob/living/simple_animal/slime)
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
+
+/datum/surgery/core_removal/can_start(mob/user, mob/living/carbon/target)
+ . = ..()
+ return . && target.stat == DEAD
+
/datum/surgery_step/slime
-/datum/surgery_step/slime/is_valid_target(mob/living/simple_animal/slime/target)
- return istype(target, /mob/living/simple_animal/slime)
-
-/datum/surgery_step/slime/can_use(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool)
- return istype(target) && target.stat == DEAD
-
/datum/surgery_step/slime/cut_flesh
- allowed_tools = list(/obj/item/scalpel = 100, /obj/item/melee/energy/sword = 75, /obj/item/kitchen/knife = 65, /obj/item/shard = 45)
- time = 16
+ allowed_tools = list(
+ TOOL_SCALPEL = 100,
+ /obj/item/melee/energy/sword = 75,
+ /obj/item/kitchen/knife = 65,
+ /obj/item/shard = 45
+ )
+ time = 1.6 SECONDS
/datum/surgery_step/slime/cut_flesh/begin_step(mob/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool)
user.visible_message("[user] starts cutting through [target]'s flesh with \the [tool].", "You start cutting through [target]'s flesh with \the [tool].")
+ return ..()
/datum/surgery_step/slime/cut_flesh/end_step(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool)
user.visible_message(" [user] cuts through [target]'s flesh with \the [tool].",
" You cut through [target]'s flesh with \the [tool], revealing its silky innards.")
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/slime/cut_flesh/fail_step(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool)
user.visible_message(" [user]'s hand slips, tearing [target]'s flesh with \the [tool]!", \
" Your hand slips, tearing [target]'s flesh with \the [tool]!")
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/slime/extract_core
name = "extract core"
- allowed_tools = list(/obj/item/hemostat = 100, /obj/item/crowbar = 100)
- time = 16
+ allowed_tools = list(TOOL_HEMOSTAT = 100, TOOL_CROWBAR = 100)
+ time = 1.6 SECONDS
/datum/surgery_step/slime/extract_core/begin_step(mob/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool)
- user.visible_message("[user] begins to extract a core from [target].",
- "You begin to extract a core from [target]...")
+ user.visible_message(
+ "[user] begins to extract a core from [target].",
+ "You begin to extract a core from [target]..."
+ )
+ return ..()
+
/datum/surgery_step/slime/extract_core/end_step(mob/user, mob/living/simple_animal/slime/slime, target_zone, obj/item/tool)
if(slime.cores > 0)
@@ -48,14 +57,14 @@
if(slime.cores <= 0)
slime.icon_state = "[slime.colour] baby slime dead-nocore"
- return TRUE
+ return SURGERY_STEP_CONTINUE
else
- return FALSE
+ return SURGERY_STEP_INCOMPLETE
else
to_chat(user, "There aren't any cores left in [slime]!")
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/slime/extract_core/fail_step(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool)
user.visible_message(" [user]'s hand slips, tearing [target]'s flesh with \the [tool]!", \
" Your hand slips, tearing [target]'s flesh with \the [tool]!")
- return FALSE
+ return SURGERY_STEP_RETRY
diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm
index eee70271ac9..1c571d7fdf2 100644
--- a/code/modules/surgery/dental_implant.dm
+++ b/code/modules/surgery/dental_implant.dm
@@ -1,7 +1,7 @@
/datum/surgery/dental_implant
name = "dental implant"
steps = list(/datum/surgery_step/generic/drill, /datum/surgery_step/insert_pill)
- possible_locs = list("mouth")
+ possible_locs = list(BODY_ZONE_PRECISE_MOUTH)
/datum/surgery/dental_implant/can_start(mob/user, mob/living/carbon/target)
if(istype(target,/mob/living/carbon/human))
@@ -13,22 +13,25 @@
/datum/surgery_step/insert_pill
name = "insert pill"
allowed_tools = list(/obj/item/reagent_containers/food/pill = 100)
- time = 16
+ time = 1.6 SECONDS
/datum/surgery_step/insert_pill/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- user.visible_message("[user] begins to wedge \the [tool] in [target]'s [parse_zone(target_zone)].", "You begin to wedge [tool] in [target]'s [parse_zone(target_zone)]...")
- ..()
+ user.visible_message(
+ "[user] begins to wedge \the [tool] in [target]'s [parse_zone(target_zone)].",
+ "You begin to wedge [tool] in [target]'s [parse_zone(target_zone)]..."
+ )
+ return ..()
/datum/surgery_step/insert_pill/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/reagent_containers/food/pill/tool, datum/surgery/surgery)
if(!istype(tool))
- return FALSE
+ return SURGERY_STEP_INCOMPLETE
var/dental_implants = 0
for(var/obj/item/reagent_containers/food/pill in target.contents) // Can't give them more than 4 dental implants.
dental_implants++
if(dental_implants >= 4)
user.visible_message("[user] pulls \the [tool] back out of [target]'s [parse_zone(target_zone)]!", "You pull \the [tool] back out of [target]'s [parse_zone(target_zone)], there wans't enough room...")
- return FALSE
+ return SURGERY_STEP_INCOMPLETE
user.drop_item()
tool.forceMove(target)
@@ -40,7 +43,7 @@
P.Grant(target)
user.visible_message("[user] wedges \the [tool] into [target]'s [parse_zone(target_zone)]!", "You wedge [tool] into [target]'s [parse_zone(target_zone)].")
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/action/item_action/hands_free/activate_pill
name = "Activate Pill"
diff --git a/code/modules/surgery/encased.dm b/code/modules/surgery/encased.dm
index 2d7e74d5bfc..83f8b0ada7e 100644
--- a/code/modules/surgery/encased.dm
+++ b/code/modules/surgery/encased.dm
@@ -4,202 +4,168 @@
//////////////////////////////////////////////////////////////////
/datum/surgery_step/open_encased
can_infect = TRUE
- blood_level = 1
-
-/datum/surgery_step/open_encased/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return FALSE
-
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(!affected)
- return FALSE
- if(affected.is_robotic())
- return FALSE
- return TRUE
-
+ blood_level = SURGERY_BLOODSPREAD_HANDS
/datum/surgery_step/open_encased/saw
name = "saw bone"
allowed_tools = list(
- /obj/item/circular_saw = 100, \
- /obj/item/melee/energy/sword/cyborg/saw = 100, \
- /obj/item/hatchet = 90
+ TOOL_SAW = 100,
+ /obj/item/hatchet = 90
)
- time = 54
+ time = 5.4 SECONDS
-/datum/surgery_step/open_encased/saw/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/saw/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)
- user.visible_message("[user] begins to cut through [target]'s [affected.encased] with \the [tool].", \
- "You begin to cut through [target]'s [affected.encased] with \the [tool].")
+ user.visible_message(
+ "[user] begins to cut through [target]'s [affected.encased] with \the [tool].",
+ "You begin to cut through [target]'s [affected.encased] with \the [tool]."
+ )
target.custom_pain("Something hurts horribly in your [affected.name]!")
- ..()
+ return ..()
-/datum/surgery_step/open_encased/saw/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/saw/end_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)
- user.visible_message(" [user] has cut [target]'s [affected.encased] open with \the [tool].", \
- " You have cut [target]'s [affected.encased] open with \the [tool].")
- affected.open = 2.5
+ user.visible_message(
+ " [user] has cut [target]'s [affected.encased] open with \the [tool].",
+ " You have cut [target]'s [affected.encased] open with \the [tool]."
+ )
+ affected.open = ORGAN_ORGANIC_ENCASED_OPEN
affected.fracture(silent = TRUE)
- return TRUE
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/open_encased/saw/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/saw/fail_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)
- user.visible_message(" [user]'s hand slips, cracking [target]'s [affected.encased] with \the [tool]!" , \
- " Your hand slips, cracking [target]'s [affected.encased] with \the [tool]!" )
+ user.visible_message(
+ " [user]'s hand slips, cracking [target]'s [affected.encased] with \the [tool]!" ,
+ " Your hand slips, cracking [target]'s [affected.encased] with \the [tool]!"
+ )
affected.receive_damage(20)
affected.fracture()
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/open_encased/retract
name = "retract bone"
allowed_tools = list(
- /obj/item/scalpel/laser/manager = 100, \
- /obj/item/retractor = 100, \
- /obj/item/crowbar = 90
+ /obj/item/scalpel/laser/manager = 100,
+ TOOL_RETRACTOR = 100,
+ TOOL_CROWBAR = 90
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/open_encased/retract/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/retract/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)
- var/msg = "[user] starts to force open the [affected.encased] in [target]'s [affected.name] with \the [tool]."
- var/self_msg = "You start to force open the [affected.encased] in [target]'s [affected.name] with \the [tool]."
- user.visible_message(msg, self_msg)
+ user.visible_message(
+ "[user] starts to force open the [affected.encased] in [target]'s [affected.name] with \the [tool].",
+ "You start to force open the [affected.encased] in [target]'s [affected.name] with \the [tool]."
+ )
target.custom_pain("Something hurts horribly in your [affected.name]!")
- ..()
+ return ..()
-/datum/surgery_step/open_encased/retract/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/open_encased/retract/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
- if(!hasorgans(target))
- return
var/obj/item/organ/external/affected = target.get_organ(target_zone)
- var/msg = " [user] forces open [target]'s [affected.encased] with \the [tool]."
- var/self_msg = " You force open [target]'s [affected.encased] with \the [tool]."
- user.visible_message(msg, self_msg)
+ user.visible_message(
+ " [user] forces open [target]'s [affected.encased] with \the [tool].",
+ " You force open [target]'s [affected.encased] with \the [tool]."
+ )
- affected.open = 3
+ affected.open = ORGAN_ORGANIC_ENCASED_OPEN
- return TRUE
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/open_encased/retract/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/retract/fail_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)
- var/msg = " [user]'s hand slips, cracking [target]'s [affected.encased]!"
- var/self_msg = " Your hand slips, cracking [target]'s [affected.encased]!"
- user.visible_message(msg, self_msg)
+ user.visible_message(
+ " [user]'s hand slips, cracking [target]'s [affected.encased]!",
+ " Your hand slips, cracking [target]'s [affected.encased]!"
+ )
affected.receive_damage(20)
affected.fracture()
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/open_encased/close
name = "unretract bone" //i suck at names okay? give me a new one
allowed_tools = list(
- /obj/item/scalpel/laser/manager = 100, \
- /obj/item/retractor = 100, \
- /obj/item/crowbar = 90
+ /obj/item/scalpel/laser/manager = 100,
+ TOOL_RETRACTOR = 100,
+ TOOL_CROWBAR = 90
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/open_encased/close/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/close/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)
- var/msg = "[user] starts bending [target]'s [affected.encased] back into place with \the [tool]."
- var/self_msg = "You start bending [target]'s [affected.encased] back into place with \the [tool]."
- user.visible_message(msg, self_msg)
+ user.visible_message(
+ "[user] starts bending [target]'s [affected.encased] back into place with \the [tool].",
+ "You start bending [target]'s [affected.encased] back into place with \the [tool]."
+ )
target.custom_pain("Something hurts horribly in your [affected.name]!")
- ..()
+ return ..()
-/datum/surgery_step/open_encased/close/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/close/end_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)
- var/msg = " [user] bends [target]'s [affected.encased] back into place with \the [tool]."
- var/self_msg = " You bend [target]'s [affected.encased] back into place with \the [tool]."
- user.visible_message(msg, self_msg)
+ user.visible_message(
+ "[user] bends [target]'s [affected.encased] back into place with \the [tool].",
+ "You bend [target]'s [affected.encased] back into place with \the [tool]."
+ )
- affected.open = 2.5
+ return SURGERY_STEP_CONTINUE
- return TRUE
-
-/datum/surgery_step/open_encased/close/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/close/fail_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)
- var/msg = " [user]'s hand slips, bending [target]'s [affected.encased] the wrong way!"
- var/self_msg = " Your hand slips, bending [target]'s [affected.encased] the wrong way!"
- user.visible_message(msg, self_msg)
+ user.visible_message(
+ "[user]'s hand slips, bending [target]'s [affected.encased] the wrong way!",
+ "Your hand slips, bending [target]'s [affected.encased] the wrong way!"
+ )
affected.receive_damage(20)
affected.fracture()
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/open_encased/mend
name = "mend bone"
allowed_tools = list(
- /obj/item/bonegel = 100, \
- /obj/item/screwdriver = 90
+ TOOL_BONEGEL = 100,
+ TOOL_SCREWDRIVER = 90
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/open_encased/mend/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/mend/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)
- var/msg = "[user] starts applying \the [tool] to [target]'s [affected.encased]."
- var/self_msg = "You start applying \the [tool] to [target]'s [affected.encased]."
- user.visible_message(msg, self_msg)
+ user.visible_message(
+ "[user] starts applying \the [tool] to [target]'s [affected.encased].",
+ "You start applying \the [tool] to [target]'s [affected.encased]."
+ )
target.custom_pain("Something hurts horribly in your [affected.name]!")
- ..()
+ return ..()
-/datum/surgery_step/open_encased/mend/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(!hasorgans(target))
- return
+/datum/surgery_step/open_encased/mend/end_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)
- var/msg = " [user] applied \the [tool] to [target]'s [affected.encased]."
- var/self_msg = " You applied \the [tool] to [target]'s [affected.encased]."
- user.visible_message(msg, self_msg)
+ user.visible_message(
+ " [user] applied \the [tool] to [target]'s [affected.encased].",
+ " You applied \the [tool] to [target]'s [affected.encased]."
+ )
- affected.open = 2
+ affected.open = ORGAN_ORGANIC_OPEN
- return TRUE
+ return SURGERY_STEP_CONTINUE
diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm
index 054367043d8..d15b4586a95 100644
--- a/code/modules/surgery/generic.dm
+++ b/code/modules/surgery/generic.dm
@@ -3,243 +3,282 @@
// COMMON STEPS //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/datum/surgery_step/generic/
+/datum/surgery_step/generic
can_infect = TRUE
-/datum/surgery_step/generic/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(!hasorgans(target))
- return FALSE
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(affected == null)
- return FALSE
- if(affected.is_robotic())
- return FALSE
- return TRUE
-
-
/datum/surgery_step/generic/cut_open
name = "make incision"
allowed_tools = list(
- /obj/item/scalpel = 100, \
- /obj/item/kitchen/knife = 90, \
- /obj/item/shard = 60, \
- /obj/item/scissors = 12, \
- /obj/item/twohanded/chainsaw = 1, \
- /obj/item/claymore = 6, \
- /obj/item/melee/energy/ = 6, \
- /obj/item/pen/edagger = 6, \
+ TOOL_SCALPEL = 100,
+ /obj/item/kitchen/knife = 90,
+ /obj/item/shard = 60,
+ /obj/item/scissors = 12,
+ /obj/item/twohanded/chainsaw = 1,
+ /obj/item/claymore = 6,
+ /obj/item/melee/energy = 6,
+ /obj/item/pen/edagger = 6,
)
- time = 16
+ time = 1.6 SECONDS
-/datum/surgery_step/generic/cut_open/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)
- user.visible_message("[user] starts the incision on [target]'s [affected.name] with \the [tool].", \
- "You start the incision on [target]'s [affected.name] with \the [tool].")
- target.custom_pain("You feel a horrible pain as if from a sharp knife in your [affected.name]!")
- ..()
-
-/datum/surgery_step/generic/cut_open/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/cut_open/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)
- user.visible_message(" [user] has made an incision on [target]'s [affected.name] with \the [tool].", \
- " You have made an incision on [target]'s [affected.name] with \the [tool].",)
- affected.open = 1
- return TRUE
+ user.visible_message(
+ "[user] starts the incision on [target]'s [affected.name] with \the [tool].",
+ "You start the incision on [target]'s [affected.name] with \the [tool]."
+ )
+ target.custom_pain("You feel a horrible pain as if from a sharp knife in your [affected.name]!")
+ return ..()
-/datum/surgery_step/generic/cut_open/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/cut_open/end_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)
- user.visible_message(" [user]'s hand slips, slicing open [target]'s [affected.name] in a wrong spot with \the [tool]!", \
- " Your hand slips, slicing open [target]'s [affected.name] in a wrong spot with \the [tool]!")
+ user.visible_message(
+ " [user] has made an incision on [target]'s [affected.name] with \the [tool].",
+ " You have made an incision on [target]'s [affected.name] with \the [tool]."
+ )
+ affected.open = ORGAN_ORGANIC_OPEN
+ return SURGERY_STEP_CONTINUE
+
+/datum/surgery_step/generic/cut_open/fail_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)
+ user.visible_message(
+ " [user]'s hand slips, slicing open [target]'s [affected.name] in a wrong spot with \the [tool]!",
+ " Your hand slips, slicing open [target]'s [affected.name] in a wrong spot with \the [tool]!"
+ )
affected.receive_damage(10)
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/generic/clamp_bleeders
name = "clamp bleeders"
allowed_tools = list(
- /obj/item/scalpel/laser = 100, \
- /obj/item/hemostat = 100, \
- /obj/item/stack/cable_coil = 90, \
- /obj/item/assembly/mousetrap = 25
+ TOOL_HEMOSTAT = 100,
+ /obj/item/scalpel/laser = 100,
+ /obj/item/stack/cable_coil = 90,
+ /obj/item/assembly/mousetrap = 25
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/generic/clamp_bleeders/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)
- user.visible_message("[user] starts clamping bleeders in [target]'s [affected.name] with \the [tool].", \
- "You start clamping bleeders in [target]'s [affected.name] with \the [tool].")
- target.custom_pain("The pain in your [affected.name] is maddening!")
- ..()
-
-/datum/surgery_step/generic/clamp_bleeders/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/clamp_bleeders/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)
- user.visible_message(" [user] clamps bleeders in [target]'s [affected.name] with \the [tool].", \
- " You clamp bleeders in [target]'s [affected.name] with \the [tool].")
+ user.visible_message(
+ "[user] starts clamping bleeders in [target]'s [affected.name] with \the [tool].",
+ "You start clamping bleeders in [target]'s [affected.name] with \the [tool]."
+ )
+ target.custom_pain("The pain in your [affected.name] is maddening!")
+ return ..()
+
+/datum/surgery_step/generic/clamp_bleeders/end_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)
+ user.visible_message(
+ " [user] clamps bleeders in [target]'s [affected.name] with \the [tool].",
+ " You clamp bleeders in [target]'s [affected.name] with \the [tool]."
+ )
spread_germs_to_organ(affected, user, tool)
- return TRUE
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/generic/clamp_bleeders/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/clamp_bleeders/fail_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)
- user.visible_message(" [user]'s hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!", \
- " Your hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!",)
+ user.visible_message(
+ " [user]'s hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!",
+ " Your hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!"
+ )
affected.receive_damage(10)
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/generic/retract_skin
name = "retract skin"
allowed_tools = list(
- /obj/item/scalpel/laser/manager = 100, \
- /obj/item/retractor = 100, \
- /obj/item/crowbar = 90, \
- /obj/item/kitchen/utensil/fork = 60
+ TOOL_RETRACTOR = 100,
+ /obj/item/scalpel/laser/manager = 100,
+ /obj/item/retractor = 100,
+ /obj/item/crowbar = 90,
+ /obj/item/kitchen/utensil/fork = 60
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/generic/retract_skin/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/retract_skin/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)
var/msg = "[user] starts to pry open the incision on [target]'s [affected.name] with \the [tool]."
var/self_msg = "You start to pry open the incision on [target]'s [affected.name] with \the [tool]."
- if(target_zone == "chest")
+ if(target_zone == BODY_ZONE_CHEST)
msg = "[user] starts to separate the ribcage and rearrange the organs in [target]'s torso with \the [tool]."
self_msg = "You start to separate the ribcage and rearrange the organs in [target]'s torso with \the [tool]."
- if(target_zone == "groin")
+ if(target_zone == BODY_ZONE_PRECISE_GROIN)
msg = "[user] starts to pry open the incision and rearrange the organs in [target]'s lower abdomen with \the [tool]."
self_msg = "You start to pry open the incision and rearrange the organs in [target]'s lower abdomen with \the [tool]."
user.visible_message(msg, self_msg)
target.custom_pain("It feels like the skin on your [affected.name] is on fire!")
- ..()
+ return ..()
-/datum/surgery_step/generic/retract_skin/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/retract_skin/end_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)
var/msg = " [user] keeps the incision open on [target]'s [affected.name] with \the [tool]."
var/self_msg = " You keep the incision open on [target]'s [affected.name] with \the [tool]."
- if(target_zone == "chest")
+ if(target_zone == BODY_ZONE_CHEST)
msg = " [user] keeps the ribcage open on [target]'s torso with \the [tool]."
self_msg = " You keep the ribcage open on [target]'s torso with \the [tool]."
- if(target_zone == "groin")
+ if(target_zone == BODY_ZONE_PRECISE_GROIN)
msg = " [user] keeps the incision open on [target]'s lower abdomen with \the [tool]."
self_msg = " You keep the incision open on [target]'s lower abdomen with \the [tool]."
user.visible_message(msg, self_msg)
- affected.open = 2
- return TRUE
+ affected.open = ORGAN_ORGANIC_ENCASED_OPEN
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/generic/retract_skin/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/retract_skin/fail_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)
- var/msg = " [user]'s hand slips, tearing the edges of incision on [target]'s [affected.name] with \the [tool]!"
+ var/msg = "[user]'s hand slips, tearing the edges of incision on [target]'s [affected.name] with \the [tool]!"
var/self_msg = " Your hand slips, tearing the edges of incision on [target]'s [affected.name] with \the [tool]!"
- if(target_zone == "chest")
- msg = " [user]'s hand slips, damaging several organs [target]'s torso with \the [tool]!"
+ if(target_zone == BODY_ZONE_CHEST)
+ msg = "[user]'s hand slips, damaging several organs [target]'s torso with \the [tool]!"
self_msg = " Your hand slips, damaging several organs [target]'s torso with \the [tool]!"
- if(target_zone == "groin")
- msg = " [user]'s hand slips, damaging several organs [target]'s lower abdomen with \the [tool]"
+ if(target_zone == BODY_ZONE_PRECISE_GROIN)
+ msg = "[user]'s hand slips, damaging several organs [target]'s lower abdomen with \the [tool]"
self_msg = " Your hand slips, damaging several organs [target]'s lower abdomen with \the [tool]!"
user.visible_message(msg, self_msg)
target.apply_damage(12, BRUTE, affected, sharp = TRUE)
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/generic/cauterize
name = "cauterize incision"
allowed_tools = list(
- /obj/item/scalpel/laser = 100, \
- /obj/item/cautery = 100, \
- /obj/item/clothing/mask/cigarette = 90, \
- /obj/item/lighter = 60, \
- /obj/item/weldingtool = 30
+ /obj/item/scalpel/laser = 100,
+ TOOL_CAUTERY = 100,
+ /obj/item/clothing/mask/cigarette = 90,
+ /obj/item/lighter = 60,
+ TOOL_WELDER = 30
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/generic/cauterize/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/cauterize/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)
- user.visible_message("[user] is beginning to cauterize the incision on [target]'s [affected.name] with \the [tool]." , \
- "You are beginning to cauterize the incision on [target]'s [affected.name] with \the [tool].")
+ user.visible_message(
+ "[user] is beginning to cauterize the incision on [target]'s [affected.name] with \the [tool].",
+ "You are beginning to cauterize the incision on [target]'s [affected.name] with \the [tool]."
+ )
target.custom_pain("Your [affected.name] is being burned!")
- ..()
+ return ..()
-/datum/surgery_step/generic/cauterize/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/cauterize/end_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)
- user.visible_message(" [user] cauterizes the incision on [target]'s [affected.name] with \the [tool].", \
- " You cauterize the incision on [target]'s [affected.name] with \the [tool].")
- affected.open = 0
+ user.visible_message(
+ "[user] cauterizes the incision on [target]'s [affected.name] with \the [tool].",
+ "You cauterize the incision on [target]'s [affected.name] with \the [tool]."
+ )
+ affected.open = ORGAN_CLOSED
affected.germ_level = 0
- return TRUE
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/generic/cauterize/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/cauterize/fail_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)
- user.visible_message(" [user]'s hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!", \
- " Your hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!")
+ user.visible_message(
+ "[user]'s hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!",
+ "Your hand slips, leaving a small burn on [target]'s [affected.name] with \the [tool]!"
+ )
target.apply_damage(3, BURN, affected)
- return FALSE
+ return SURGERY_STEP_RETRY
+
+/datum/surgery_step/generic/cauterize/premature
+ name = "cauterize incision (premature)"
+
+/datum/surgery_step/generic/cauterize/premature/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)
+ user.visible_message(
+ "[user] is beginning to cauterize the incision on [target]'s [affected.name] with \the [tool].",
+ // give a little heads up to the surgeon that they're stopping the surgery prematurely in case that wasn't the intention.
+ "You are interrupting the current surgery, beginning to cauterize the incision on [target]'s [affected.name] with \the [tool]."
+ )
+ target.custom_pain("Your [affected.name] is being burned!")
+ return ..()
+
//drill bone
/datum/surgery_step/generic/drill
name = "drill bone"
- allowed_tools = list(/obj/item/surgicaldrill = 100, /obj/item/pickaxe/drill = 60, /obj/item/mecha_parts/mecha_equipment/drill = 60, /obj/item/screwdriver = 20)
- time = 30
+ allowed_tools = list(
+ TOOL_DRILL = 100,
+ /obj/item/screwdriver/power = 80,
+ /obj/item/pickaxe/drill = 60,
+ /obj/item/mecha_parts/mecha_equipment/drill = 60,
+ /obj/item/screwdriver = 20
+ )
+ time = 3 SECONDS
/datum/surgery_step/generic/drill/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- user.visible_message("[user] begins to drill into the bone in [target]'s [parse_zone(target_zone)].", "You begin to drill into the bone in [target]'s [parse_zone(target_zone)]...")
- ..()
+ user.visible_message(
+ "[user] begins to drill into the bone in [target]'s [parse_zone(target_zone)].",
+ "You begin to drill into the bone in [target]'s [parse_zone(target_zone)]..."
+ )
+ return ..()
/datum/surgery_step/generic/drill/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] drills into [target]'s [parse_zone(target_zone)]!", "You drill into [target]'s [parse_zone(target_zone)].")
- return TRUE
+ return SURGERY_STEP_CONTINUE
+
+/datum/surgery_step/generic/drill/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ user.visible_message(
+ "[user]'s [tool] doesn't get a firm grip and tears at the bone in [target]'s [parse_zone(target_zone)]!",
+ "Your [tool] doesn't get a firm grip and tears at the bone in [target]'s [parse_zone(target_zone)]!"
+ )
+
+ affected.receive_damage(15)
+ return SURGERY_STEP_RETRY
/datum/surgery_step/generic/amputate
name = "amputate limb"
allowed_tools = list(
- /obj/item/circular_saw = 100, \
- /obj/item/melee/energy/sword/cyborg/saw = 100, \
- /obj/item/hatchet = 90, \
- /obj/item/melee/arm_blade = 75
+ TOOL_SAW = 100,
+ /obj/item/hatchet = 90,
+ /obj/item/melee/arm_blade = 75
)
- time = 100
+ time = 10 SECONDS
-/datum/surgery_step/generic/amputate/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(target_zone == "eyes") //there are specific steps for eye surgery
- return FALSE
- if(!hasorgans(target))
- return FALSE
+/datum/surgery_step/generic/amputate/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(affected == null)
- return FALSE
- if(affected.limb_flags & CANNOT_DISMEMBER)
- return FALSE
- return TRUE
-
-/datum/surgery_step/generic/amputate/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)
- user.visible_message("[user] is beginning to amputate [target]'s [affected.name] with \the [tool]." , \
- "You are beginning to cut through [target]'s [affected.amputation_point] with \the [tool].")
+ user.visible_message(
+ "[user] is beginning to amputate [target]'s [affected.name] with \the [tool].",
+ "You are beginning to cut through [target]'s [affected.amputation_point] with \the [tool]."
+ )
target.custom_pain("Your [affected.amputation_point] is being ripped apart!")
- ..()
+ return ..()
-/datum/surgery_step/generic/amputate/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/amputate/end_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)
- user.visible_message(" [user] amputates [target]'s [affected.name] at the [affected.amputation_point] with \the [tool].", \
- " You amputate [target]'s [affected.name] with \the [tool].")
+ user.visible_message(
+ " [user] amputates [target]'s [affected.name] at the [affected.amputation_point] with \the [tool].",
+ " You amputate [target]'s [affected.name] with \the [tool]."
+ )
add_attack_logs(user, target, "Surgically removed [affected.name]. INTENT: [uppertext(user.a_intent)]")//log it
- var/atom/movable/thing = affected.droplimb(1,DROPLIMB_SHARP)
- if(istype(thing,/obj/item))
- user.put_in_hands(thing)
- return TRUE
+ var/atom/movable/thing = affected.droplimb(1, DROPLIMB_SHARP)
-/datum/surgery_step/generic/amputate/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+ if(istype(target) && target.can_feel_pain())
+ // okay if you can feel your arm getting chopped off you aren't gonna be singing
+ to_chat(target, "Your [affected] goes completely numb at the [affected.amputation_point]!")
+ target.emote("scream")
+ if(istype(thing, /obj/item))
+ user.put_in_hands(thing)
+ return SURGERY_STEP_CONTINUE
+
+/datum/surgery_step/generic/amputate/fail_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)
- user.visible_message(" [user]'s hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!", \
- " Your hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!")
+ user.visible_message(
+ " [user]'s hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!",
+ " Your hand slips, sawing through the bone in [target]'s [affected.name] with \the [tool]!"
+ )
affected.receive_damage(30)
affected.fracture()
- return FALSE
+ return SURGERY_STEP_RETRY
diff --git a/code/modules/surgery/helpers.dm b/code/modules/surgery/helpers.dm
index 4c52a5cbb09..dfd0c849afe 100644
--- a/code/modules/surgery/helpers.dm
+++ b/code/modules/surgery/helpers.dm
@@ -1,150 +1,31 @@
-/proc/attempt_initiate_surgery(obj/item/I, mob/living/M, mob/user, override)
- if(istype(M))
- var/mob/living/carbon/human/H
- var/obj/item/organ/external/affecting
- var/selected_zone = user.zone_selected
- var/list/cautery_tools = list(
- /obj/item/scalpel/laser = 100, \
- /obj/item/cautery = 100, \
- /obj/item/clothing/mask/cigarette = 90, \
- /obj/item/lighter = 60, \
- /obj/item/weldingtool = 30
- )
-
- if(M == user)
- return // no self surgery
-
- if(M.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
- to_chat(user, "You realise that a ghost probably doesn't have any useful organs.")
- return //no cult ghost surgery please
-
- if(istype(M, /mob/living/carbon/human))
- H = M
- affecting = H.get_organ(check_zone(selected_zone))
-
- if(can_operate(M) || isslime(M)) //if they're prone or a slime
- var/datum/surgery/current_surgery
- for(var/datum/surgery/S in M.surgeries)
- if(S.location == selected_zone)
- current_surgery = S
-
- if(!current_surgery)
- var/list/all_surgeries = GLOB.surgeries_list.Copy()
- var/list/available_surgeries = list()
-
- for(var/datum/surgery/S in all_surgeries)
- if(!S.possible_locs.Find(selected_zone))
- continue
- if(affecting && S.requires_organic_bodypart && affecting.is_robotic())
- continue
- if(!S.can_start(user, M))
- continue
-
- for(var/path in S.allowed_mob)
- if(istype(M, path))
- // If there are multiple surgeries with the same name,
- // prepare to cry
- available_surgeries[S.name] = S
- break
-
- if(override)
- var/datum/surgery/S
- if(istype(I,/obj/item/robot_parts))
- S = available_surgeries["Apply Robotic Prosthetic"]
- if(istype(I,/obj/item/organ/external))
- var/obj/item/organ/external/E = I
- if(E.is_robotic())
- S = available_surgeries["Synthetic Limb Reattachment"]
- if(S)
- var/datum/surgery/procedure = new S.type
- if(procedure)
- procedure.location = selected_zone
- M.surgeries += procedure
- procedure.organ_ref = affecting
- procedure.next_step(user, M)
-
- else
- var/P = input("Begin which procedure?", "Surgery", null, null) as null|anything in available_surgeries
- if(P && user && user.Adjacent(M) && (I in user))
- var/datum/surgery/S = available_surgeries[P]
- var/datum/surgery/procedure = new S.type
- if(procedure)
- procedure.location = selected_zone
- M.surgeries += procedure
- procedure.organ_ref = affecting
- user.visible_message("[user] prepares to operate on [M]'s [parse_zone(selected_zone)].", \
- "You prepare to operate on [M]'s [parse_zone(selected_zone)].")
-
- else if(!current_surgery.step_in_progress && ishuman(M)) //early surgery cautery
- var/datum/surgery_step/generic/cauterize/C = new
- if(current_surgery.status == 1)
- M.surgeries -= current_surgery
- to_chat(user, "You stop the surgery.")
- qdel(current_surgery)
-
- else if(current_surgery.can_cancel)
- var/cautery_chance = 0
- var/obj/item/cautery_tool = null
-
- if(isrobot(user))
- if(istype(I, /obj/item/scalpel/laser))
- cautery_chance = 100
- cautery_tool = I
-
- else
- for(var/T in cautery_tools)
- if(istype(user.get_inactive_hand(), T))
- cautery_chance = cautery_tools[T]
- cautery_tool = user.get_inactive_hand()
-
- if(cautery_chance)
- C.begin_step(user, H, selected_zone, cautery_tool, current_surgery)
- if(do_after(user, C.time * cautery_tool.toolspeed, target = M))
- if(!isrobot(user))
- cautery_chance *= get_location_modifier(H)
- cautery_chance *= get_pain_modifier(H)
- if(prob(cautery_chance))
- C.end_step(user, H, selected_zone, cautery_tool, current_surgery)
- M.surgeries -= current_surgery
- qdel(current_surgery)
- else
- C.fail_step(user, H, selected_zone, cautery_tool, current_surgery)
-
- else if(!isrobot(user))
- to_chat(user, "You need to hold a cautery or equivalent in your inactive hand to stop the surgery in progress.")
-
- return TRUE
- return FALSE
-
-
-/proc/get_pain_modifier(mob/living/carbon/human/M) //returns modfier to make surgery harder if patient is conscious and feels pain
- if(M.stat == DEAD) // Operating on dead people is easy
+/proc/get_pain_modifier(mob/living/carbon/human/target) //returns modfier to make surgery harder if patient is conscious and feels pain
+ if(target.stat == DEAD) // Operating on dead people is easy
return 1
- var/datum/status_effect/incapacitating/sleeping/S = M.IsSleeping()
- if(M.stat == UNCONSCIOUS && !(S?.voluntary))
+ var/datum/status_effect/incapacitating/sleeping/S = target.IsSleeping()
+ if(target.stat == UNCONSCIOUS && !(S?.voluntary))
// Either unconscious due to something other than sleep,
// or "sleeping" due to being hard knocked out (N2O or similar), rather than just napping.
// Either way, not easily woken up.
return 1
- if(HAS_TRAIT(M, TRAIT_NOPAIN))//if you don't feel pain, you can hold still
+ if(HAS_TRAIT(target, TRAIT_NOPAIN))//if you don't feel pain, you can hold still
return 1
- if(M.reagents.has_reagent("hydrocodone"))//really good pain killer
+ if(target.reagents.has_reagent("hydrocodone"))//really good pain killer
return 0.99
- if(M.reagents.has_reagent("morphine"))//Just as effective as Hydrocodone, but has an addiction chance
+ if(target.reagents.has_reagent("morphine"))//Just as effective as Hydrocodone, but has an addiction chance
return 0.99
- var/drunk = M.get_drunkenness()
+ var/drunk = target.get_drunkenness()
if(drunk >= 80)//really damn drunk
return 0.95
if(drunk >= 40)//pretty drunk
return 0.9
- if(M.reagents.has_reagent("sal_acid")) //it's better than nothing, as far as painkillers go.
+ if(target.reagents.has_reagent("sal_acid")) //it's better than nothing, as far as painkillers go.
return 0.85
if(drunk >= 15)//a little drunk
return 0.85
return 0.8 //20% failure chance
-/proc/get_location_modifier(mob/M)
- var/turf/T = get_turf(M)
+/proc/get_location_modifier(mob/target)
+ var/turf/T = get_turf(target)
if(locate(/obj/machinery/optable, T))
return 1
if(locate(/obj/structure/bed/roller/holo, T))
@@ -155,16 +36,42 @@
return 0.7
return 0.5
-//check if mob is lying down on something we can operate him on.
-/proc/can_operate(mob/living/carbon/M)
- if(locate(/obj/machinery/optable, M.loc) && IS_HORIZONTAL(M))
+//check if mob is lying down on something we can operate on.
+/proc/on_operable_surface(mob/living/carbon/target)
+ if(locate(/obj/machinery/optable, target.loc) && IS_HORIZONTAL(target))
return TRUE
- if(locate(/obj/structure/bed, M.loc) && (IS_HORIZONTAL(M) || M.IsWeakened() || M.IsStunned() || M.IsParalyzed() || M.IsSleeping() || M.stat))
+ if(locate(/obj/structure/bed, target.loc) && (IS_HORIZONTAL(target)))
return TRUE
- if(locate(/obj/structure/table, M.loc) && (IS_HORIZONTAL(M) || M.IsWeakened() || M.IsStunned() || M.IsParalyzed() || M.IsSleeping() || M.stat))
+ if(locate(/obj/structure/table, target.loc) && (IS_HORIZONTAL(target)))
return TRUE
return FALSE
// Called when a limb containing this object is placed back on a body
/atom/movable/proc/attempt_become_organ(obj/item/organ/external/parent,mob/living/carbon/human/H)
return 0
+
+/// Check to see if a surgical operation proposed on ourselves is valid or not. We are the target of the surgery
+/mob/living/proc/can_run_surgery(datum/surgery/surgery, mob/surgeon, obj/item/organ/external/affecting)
+ if(!affecting)
+ // try to pull it if it isn't passed in (it's a parameter mostly for optimization purposes)
+ affecting = get_organ(check_zone(surgeon.zone_selected))
+
+ if(!surgery.possible_locs.Find(surgeon.zone_selected))
+ return
+ if(affecting)
+ if(!surgery.requires_bodypart)
+ return
+ if((surgery.requires_organic_bodypart && affecting.is_robotic()) || (!surgery.requires_organic_bodypart && !affecting.is_robotic()))
+ return
+ if(surgery.requires_real_bodypart && !affecting.is_primary_organ())
+ return
+ else if(surgery.requires_bodypart) //mob with no limb in surgery zone when we need a limb
+ return
+ if(surgery.lying_required && !IS_HORIZONTAL(src))
+ return
+ if(!surgery.self_operable && src == surgeon)
+ return
+ if(!surgery.can_start(surgeon, src))
+ return
+
+ return TRUE
diff --git a/code/modules/surgery/implant_removal.dm b/code/modules/surgery/implant_removal.dm
index 29aacc66150..b8ee89c81e3 100644
--- a/code/modules/surgery/implant_removal.dm
+++ b/code/modules/surgery/implant_removal.dm
@@ -4,56 +4,74 @@
/datum/surgery/implant_removal
name = "Implant Removal"
- steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin,/datum/surgery_step/extract_implant,/datum/surgery_step/generic/cauterize)
- possible_locs = list("chest")
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/open_organ,
+ /datum/surgery_step/extract_implant,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_CHEST)
+ requires_organic_bodypart = TRUE
/datum/surgery/implant_removal/synth
name = "Implant Removal"
- steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/extract_implant,/datum/surgery_step/robotics/external/close_hatch)
- possible_locs = list("chest")
+ steps = list(
+ /datum/surgery_step/robotics/external/unscrew_hatch,
+ /datum/surgery_step/robotics/external/open_hatch,
+ /datum/surgery_step/proxy/robotics/repair_limb,
+ /datum/surgery_step/extract_implant,
+ /datum/surgery_step/robotics/external/close_hatch
+ )
requires_organic_bodypart = FALSE
-/datum/surgery/implant_removal/can_start(mob/user, mob/living/carbon/human/target)
- if(!istype(target))
- return FALSE
- var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(affected.is_robotic())
- return FALSE
- return TRUE
-
-/datum/surgery/implant_removal/synth/can_start(mob/user, mob/living/carbon/human/target)
- if(!istype(target))
- return FALSE
- var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(!affected.is_robotic())
- return FALSE
-
- return TRUE
-
/datum/surgery_step/extract_implant
name = "extract implant"
- allowed_tools = list(/obj/item/hemostat = 100, /obj/item/crowbar = 65)
- time = 64
+ allowed_tools = list(TOOL_HEMOSTAT = 100, TOOL_CROWBAR = 65)
+ time = 6.4 SECONDS
+ repeatable = TRUE
var/obj/item/implant/I = null
+ var/max_times_to_check = 5
+
+/datum/surgery_step/extract_implant/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+
-/datum/surgery_step/extract_implant/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(times_repeated >= max_times_to_check)
+ user.visible_message(
+ "[user] seems to have had enough and stops checking inside [target].",
+ "There doesn't seem to be anything inside, you've checked enough times."
+ )
+ return SURGERY_BEGINSTEP_SKIP
+
I = locate(/obj/item/implant) in target
- user.visible_message("[user] starts poking around inside [target]'s [affected.name] with \the [tool].", \
- "You start poking around inside [target]'s [affected.name] with \the [tool]." )
+ user.visible_message(
+ "[user] starts poking around inside [target]'s [affected.name] with \the [tool].",
+ "You start poking around inside [target]'s [affected.name] with \the [tool]."
+ )
target.custom_pain("The pain in your [affected.name] is living hell!")
- ..()
+ return ..()
-/datum/surgery_step/extract_implant/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/extract_implant/fail_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)
+ user.visible_message(
+ "[user] grips onto [target]'s [affected.name] by mistake, tearing it!",
+ "You think you've found something, but you've grabbed onto [target]'s [affected.name] instead, damaging it!"
+ )
+ affected.receive_damage(10)
+ return SURGERY_STEP_RETRY
+
+/datum/surgery_step/extract_implant/end_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)
I = locate(/obj/item/implant) in target
- if(I && (target_zone == "chest")) //implant removal only works on the chest.
- user.visible_message("[user] takes something out of [target]'s [affected.name] with \the [tool].", \
- "You take [I] out of [target]'s [affected.name]s with \the [tool]." )
+ if(I && prob(80)) //implant removal only works on the chest.
+ user.visible_message(
+ "[user] takes something out of [target]'s [affected.name] with \the [tool].",
+ "You take \an [I] out of [target]'s [affected.name]s with \the [tool]."
+ )
I.removed(target)
@@ -70,10 +88,12 @@
case.imp = I
I.forceMove(case)
case.update_state()
- user.visible_message("[user] places [I] into [case]!", "You place [I] into [case].")
+ user.visible_message("[user] places \the [I] into \the [case]!", "You place \the [I] into \the [case].")
else
qdel(I)
else
- user.visible_message(" [user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out.", \
- "You could not find anything inside [target]'s [affected.name].")
- return TRUE
+ user.visible_message(
+ " [user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out.",
+ "You could not find anything inside [target]'s [affected.name]."
+ )
+ return SURGERY_STEP_CONTINUE
diff --git a/code/modules/surgery/limb_augmentation.dm b/code/modules/surgery/limb_augmentation.dm
index 43d721372d4..3373d40aa2e 100644
--- a/code/modules/surgery/limb_augmentation.dm
+++ b/code/modules/surgery/limb_augmentation.dm
@@ -1,42 +1,51 @@
/datum/surgery/limb_augmentation
name = "Augment Limb"
- steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/augment)
- possible_locs = list("head", "chest","l_arm","r_arm","r_leg","l_leg")
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/open_organ,
+ /datum/surgery_step/augment
+ )
+ possible_locs = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)
/datum/surgery/limb_augmentation/can_start(mob/user, mob/living/carbon/target)
+ . = ..()
+ if(!.)
+ return FALSE
if(ishuman(target))
var/mob/living/carbon/human/H = target
var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
if(affected.status & ORGAN_BROKEN) //The arm has to be in prime condition to augment it.
return FALSE
- if(affected.is_robotic())
- return FALSE
return TRUE
/datum/surgery_step/augment
name = "augment limb with robotic part"
allowed_tools = list(/obj/item/robot_parts = 100)
- time = 32
+ time = 3.2 SECONDS
-/datum/surgery_step/augment/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
+/datum/surgery_step/augment/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
var/obj/item/robot_parts/p = tool
if(p.part)
if(!(target_zone in p.part))
to_chat(user, "[tool] cannot be used to augment this limb!")
- return FALSE
- return TRUE
+ return SURGERY_BEGINSTEP_ABORT
-/datum/surgery_step/augment/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 augmenting [affected] with [tool].", "You start augmenting [affected] with [tool].")
+ user.visible_message(
+ "[user] starts augmenting [affected] with [tool].",
+ "You start augmenting [affected] with [tool]."
+ )
+ return ..()
/datum/surgery_step/augment/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
var/obj/item/robot_parts/L = tool
var/obj/item/organ/external/affected = target.get_organ(target_zone)
- user.visible_message("[user] has finished augmenting [affected] with [tool].", \
- "You augment [affected] with [tool].")
+ user.visible_message(
+ "[user] has finished augmenting [affected] with [tool].",
+ "You augment [affected] with [tool]."
+ )
if(L.part)
for(var/part_name in L.part)
@@ -52,6 +61,6 @@
qdel(tool)
- affected.open = 0
+ affected.open = ORGAN_CLOSED
affected.germ_level = 0
- return TRUE
+ return SURGERY_STEP_CONTINUE
diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm
index 529ab21e116..221414a62cd 100644
--- a/code/modules/surgery/limb_reattach.dm
+++ b/code/modules/surgery/limb_reattach.dm
@@ -6,29 +6,31 @@
/datum/surgery/amputation
name = "Amputation"
steps = list(/datum/surgery_step/generic/amputate)
- possible_locs = list("head","l_arm", "l_hand","r_arm","r_hand","r_leg","r_foot","l_leg","l_foot","groin")
-
+ possible_locs = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
+ requires_organic_bodypart = TRUE
/datum/surgery/amputation/can_start(mob/user, mob/living/carbon/target)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(affected.is_robotic())
- return FALSE
- if(affected.limb_flags & CANNOT_DISMEMBER)
- return FALSE
-
- return TRUE
-
+ . = ..()
+ if(!.)
+ return FALSE
+ var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
+ if(affected.limb_flags & CANNOT_DISMEMBER)
+ return FALSE
+ return TRUE
/datum/surgery/reattach
name = "Limb Reattachment"
- steps = list(/datum/surgery_step/limb/attach,/datum/surgery_step/limb/connect)
- possible_locs = list("head","l_arm", "l_hand","r_arm","r_hand","r_leg","r_foot","l_leg","l_foot","groin")
+ requires_bodypart = FALSE
+ steps = list(
+ /datum/surgery_step/limb/attach,
+ /datum/surgery_step/limb/connect
+ )
+ possible_locs = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
/datum/surgery/reattach/can_start(mob/user, mob/living/carbon/target)
+ . = ..()
+ if(!.)
+ return FALSE
if(ishuman(target))
var/mob/living/carbon/human/H = target
var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
@@ -37,97 +39,97 @@
return FALSE
if(!affected)
return TRUE
+ // make sure they can actually have this limb
+ var/list/organ_data = target.dna.species.has_limbs["[user.zone_selected]"]
+ return !isnull(organ_data)
+
return FALSE
/datum/surgery/reattach_synth
name = "Synthetic Limb Reattachment"
+ requires_bodypart = FALSE
steps = list(/datum/surgery_step/limb/attach/robo)
- possible_locs = list("head","l_arm", "l_hand","r_arm","r_hand","r_leg","r_foot","l_leg","l_foot","groin")
-
-/datum/surgery/reattach_synth/can_start(mob/user, mob/living/carbon/target)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- return TRUE
-
- return FALSE
-
+ abstract = TRUE // these shouldn't show in the list; they'll be replaced by attach_robotic_limb
+ possible_locs = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
/datum/surgery/robo_attach
name = "Apply Robotic Prosthetic"
+ requires_bodypart = FALSE
steps = list(/datum/surgery_step/limb/mechanize)
- possible_locs = list("head","l_arm", "l_hand","r_arm","r_hand","r_leg","r_foot","l_leg","l_foot","groin")
+ abstract = TRUE
+ possible_locs = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
-/datum/surgery/robo_attach/can_start(mob/user, mob/living/carbon/target)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- return TRUE
- return FALSE
+/datum/surgery_step/proxy/robo_limb_attach
+ name = "apply robotic limb (proxy)"
+ branches = list(
+ /datum/surgery/robo_attach,
+ /datum/surgery/reattach_synth
+ )
+ insert_self_after = FALSE
-/datum/surgery_step/limb/
+/datum/surgery/attach_robotic_limb
+ name = "Apply Robotic or Synthetic Limb"
+ requires_bodypart = FALSE
+ steps = list(
+ /datum/surgery_step/proxy/robo_limb_attach
+ )
+
+ possible_locs = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
+
+
+/datum/surgery_step/limb
can_infect = FALSE
-/datum/surgery_step/limb/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- if(!hasorgans(target))
- return FALSE
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(affected)
- return FALSE
- var/list/organ_data = target.dna.species.has_limbs["[target_zone]"]
- return !isnull(organ_data)
/datum/surgery_step/limb/attach
name = "attach limb"
allowed_tools = list(/obj/item/organ/external = 100)
- time = 32
+ time = 3.2 SECONDS
+
+/datum/surgery_step/limb/attach/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
-/datum/surgery_step/limb/attach/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- if(!..())
- return FALSE
- if(!istype(tool, /obj/item/organ/external))
- return FALSE
var/obj/item/organ/external/E = tool
if(target.get_organ(E.limb_name))
// This catches attaching an arm to a missing hand while the arm is still there
to_chat(user, "[target] already has an [E.name]!")
- return FALSE
+ return SURGERY_BEGINSTEP_ABORT
if(E.limb_name != target_zone)
// This ensures you must be aiming at the appropriate location to attach
// this limb. (Can't aim at a missing foot to re-attach a missing arm)
to_chat(user, "The [E.name] does not go there.")
- return FALSE
- // if(E.parent_organ && !target.get_organ(E.parent_organ))
- // // No rayman allowed
- // return FALSE
+ return SURGERY_BEGINSTEP_ABORT
if(!is_correct_limb(E))
to_chat(user, "This is not the correct limb type for this surgery!")
- return FALSE
+ return SURGERY_BEGINSTEP_ABORT
+ var/list/organ_data = target.dna.species.has_limbs["[user.zone_selected]"]
+ if(isnull(organ_data))
+ to_chat(user, "[target.dna.species] don't have the anatomy for [E.name]!")
+ return SURGERY_BEGINSTEP_ABORT
- return TRUE
-
-
-/datum/surgery_step/limb/attach/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- var/obj/item/organ/external/E = tool
- user.visible_message("[user] starts attaching [E.name] to [target]'s [E.amputation_point].", \
- "You start attaching [E.name] to [target]'s [E.amputation_point].")
+ user.visible_message(
+ "[user] starts attaching [E.name] to [target]'s [E.amputation_point].",
+ "You start attaching [E.name] to [target]'s [E.amputation_point]."
+ )
+ return ..()
/datum/surgery_step/limb/attach/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
var/obj/item/organ/external/E = tool
- user.visible_message("[user] has attached [target]'s [E.name] to the [E.amputation_point].", \
- "You have attached [target]'s [E.name] to the [E.amputation_point].")
+ user.visible_message(
+ "[user] has attached [target]'s [E.name] to the [E.amputation_point].",
+ "You have attached [target]'s [E.name] to the [E.amputation_point]."
+ )
attach_limb(user, target, E)
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/limb/attach/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
var/obj/item/organ/external/E = tool
- user.visible_message("[user]'s hand slips, damaging [target]'s [E.amputation_point]!", \
- "Your hand slips, damaging [target]'s [E.amputation_point]!")
+ user.visible_message(
+ "[user]'s hand slips, damaging [target]'s [E.amputation_point]!",
+ "Your hand slips, damaging [target]'s [E.amputation_point]!"
+ )
target.apply_damage(10, BRUTE, null, sharp = TRUE)
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/limb/attach/proc/is_correct_limb(obj/item/organ/external/E)
@@ -138,6 +140,8 @@
/datum/surgery_step/limb/attach/proc/attach_limb(mob/living/user, mob/living/carbon/human/target, obj/item/organ/external/E)
user.unEquip(E)
E.replaced(target)
+ if(!E.is_robotic())
+ E.properly_attached = FALSE
target.update_body()
target.updatehealth()
target.UpdateDamageIcon()
@@ -158,8 +162,8 @@
if(!(E.dna) && E.is_robotic() && target.dna)
E.set_dna(target.dna)
..()
- if(E.limb_name == "head")
- var/obj/item/organ/external/head/H = target.get_organ("head")
+ if(E.limb_name == BODY_ZONE_HEAD)
+ var/obj/item/organ/external/head/H = target.get_organ(BODY_ZONE_HEAD)
var/datum/robolimb/robohead = GLOB.all_robolimbs[H.model]
if(robohead.is_monitor) //Ensures that if an IPC gets a head that's got a human hair wig attached to their body, the hair won't wipe.
H.h_style = "Bald"
@@ -170,64 +174,69 @@
/datum/surgery_step/limb/connect
name = "connect limb"
allowed_tools = list(
- /obj/item/hemostat = 100, \
- /obj/item/stack/cable_coil = 90, \
- /obj/item/assembly/mousetrap = 25
+ TOOL_HEMOSTAT = 100,
+ /obj/item/stack/cable_coil = 90,
+ /obj/item/assembly/mousetrap = 25
)
can_infect = TRUE
- time = 32
-
-
-/datum/surgery_step/limb/connect/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- if(!hasorgans(target))
- return FALSE
- return TRUE
+ time = 3.2 SECONDS
/datum/surgery_step/limb/connect/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
var/obj/item/organ/external/E = target.get_organ(target_zone)
- user.visible_message("[user] starts connecting tendons and muscles in [target]'s [E.amputation_point] with [tool].", \
- "You start connecting tendons and muscle in [target]'s [E.amputation_point].")
+ user.visible_message(
+ "[user] starts connecting tendons and muscles in [target]'s [E.amputation_point] with [tool].",
+ "You start connecting tendons and muscle in [target]'s [E.amputation_point]."
+ )
+ return ..()
/datum/surgery_step/limb/connect/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
var/obj/item/organ/external/E = target.get_organ(target_zone)
- user.visible_message("[user] has connected tendons and muscles in [target]'s [E.amputation_point] with [tool].", \
- "You have connected tendons and muscles in [target]'s [E.amputation_point] with [tool].")
+ user.visible_message(
+ "[user] has connected tendons and muscles in [target]'s [E.amputation_point] with [tool].",
+ "You have connected tendons and muscles in [target]'s [E.amputation_point] with [tool]."
+ )
+ E.properly_attached = TRUE
target.update_body()
target.updatehealth()
target.UpdateDamageIcon()
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/limb/connect/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
var/obj/item/organ/external/E = target.get_organ(target_zone)
- user.visible_message("[user]'s hand slips, damaging [target]'s [E.amputation_point]!", \
- "Your hand slips, damaging [target]'s [E.amputation_point]!")
+ user.visible_message(
+ "[user]'s hand slips, damaging [target]'s [E.amputation_point]!",
+ "Your hand slips, damaging [target]'s [E.amputation_point]!"
+ )
target.apply_damage(10, BRUTE, null, sharp = TRUE)
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/limb/mechanize
name = "apply robotic prosthetic"
allowed_tools = list(/obj/item/robot_parts = 100)
- time = 32
-
-/datum/surgery_step/limb/mechanize/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- if(..())
- var/obj/item/robot_parts/p = tool
- if(p.part)
- if(!(target_zone in p.part))
- to_chat(user, "\The [tool] does not go there!")
- return FALSE
- return isnull(target.get_organ(target_zone))
+ time = 3.2 SECONDS
/datum/surgery_step/limb/mechanize/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- user.visible_message("[user] starts attaching \the [tool] to [target].", \
- "You start attaching \the [tool] to [target].")
+
+ var/obj/item/robot_parts/P = tool
+ if(P.part)
+ if(!(target_zone in P.part))
+ to_chat(user, "\The [tool] does not go there!")
+ return SURGERY_BEGINSTEP_ABORT
+
+ user.visible_message(
+ "[user] starts attaching \the [tool] to [target].",
+ "You start attaching \the [tool] to [target]."
+ )
+ return ..()
/datum/surgery_step/limb/mechanize/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
var/obj/item/robot_parts/L = tool
- user.visible_message("[user] has attached \the [tool] to [target].", \
- "You have attached \the [tool] to [target].")
+ user.visible_message(
+ "[user] has attached \the [tool] to [target].",
+ "You have attached \the [tool] to [target]."
+ )
if(L.part)
for(var/part_name in L.part)
@@ -251,10 +260,12 @@
qdel(tool)
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/limb/mechanize/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
- user.visible_message("[user]'s hand slips, damaging [target]'s flesh!", \
- "Your hand slips, damaging [target]'s flesh!")
+ user.visible_message(
+ "[user]'s hand slips, damaging [target]'s flesh!",
+ "Your hand slips, damaging [target]'s flesh!"
+ )
target.apply_damage(10, BRUTE, null, sharp = TRUE)
- return FALSE
+ return SURGERY_STEP_RETRY
diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm
index 468f8643fa8..d87620b1626 100644
--- a/code/modules/surgery/organs/organ_external.dm
+++ b/code/modules/surgery/organs/organ_external.dm
@@ -35,6 +35,8 @@
var/list/child_icons = list()
var/perma_injury = 0
var/dismember_at_max_damage = FALSE
+ /// If the organ has been properly attached or not. Limbs on mobs and robotic ones
+ var/properly_attached = FALSE
var/obj/item/organ/external/parent
var/list/obj/item/organ/external/children
@@ -118,8 +120,23 @@
icobase = H.dna.species.icobase
replaced(H)
sync_colour_to_human(H)
+ properly_attached = TRUE
+
+ if(is_robotic())
+ // These can just be slapped on.
+ properly_attached = TRUE
+
get_icon()
+ // so you can just smack the limb onto a guy to start the "surgery"
+ var/application_surgery
+ if(!is_robotic())
+ application_surgery = /datum/surgery/reattach
+ else
+ application_surgery = /datum/surgery/attach_robotic_limb
+
+ AddComponent(/datum/component/surgery_initiator/limb, forced_surgery = application_surgery)
+
/obj/item/organ/external/proc/add_limb_flags()
if(HAS_TRAIT(owner, TRAIT_NO_BONES))
@@ -314,7 +331,7 @@ This function completely restores a damaged organ to perfect condition.
perma_injury = 0
brute_dam = 0
burn_dam = 0
- open = 0 //Closing all wounds.
+ open = ORGAN_CLOSED //Closing all wounds.
// handle internal organs
for(var/obj/item/organ/internal/current_organ in internal_organs)
@@ -630,7 +647,7 @@ Note that amputating the affected organ does in fact remove the infection from t
)
to_chat(owner, "Something feels like it shattered in your [name]!")
playsound(owner, "bonebreak", 150, 1)
- if(!HAS_TRAIT(owner, TRAIT_NOPAIN) && !owner.stat)
+ if(owner.can_feel_pain())
owner.emote("scream")
status |= ORGAN_BROKEN
diff --git a/code/modules/surgery/organs/pain.dm b/code/modules/surgery/organs/pain.dm
index 8364c2fd4d4..f78cfb2f8f9 100644
--- a/code/modules/surgery/organs/pain.dm
+++ b/code/modules/surgery/organs/pain.dm
@@ -2,16 +2,27 @@
var/last_pain_message = ""
var/next_pain_time = 0
+/**
+ * Whether or not a mob can feel pain.
+ *
+ * Returns TRUE if the mob can feel pain, FALSE otherwise
+ */
+/mob/living/carbon/human/proc/can_feel_pain()
+ if(stat >= UNCONSCIOUS)
+ return FALSE
+ if(reagents.has_reagent("morphine"))
+ return FALSE
+ if(reagents.has_reagent("hydrocodone"))
+ return FALSE
+ if(HAS_TRAIT(src, TRAIT_NOPAIN))
+ return FALSE
+
+ return TRUE
+
// partname is the name of a body part
// amount is a num from 1 to 100
/mob/living/carbon/human/proc/pain(partname, amount)
- if(stat >= UNCONSCIOUS)
- return
- if(reagents.has_reagent("sal_acid"))
- return
- if(reagents.has_reagent("morphine"))
- return
- if(reagents.has_reagent("hydrocodone"))
+ if(!can_feel_pain())
return
if(world.time < next_pain_time)
return
@@ -31,14 +42,7 @@
// message is the custom message to be displayed
/mob/living/carbon/human/proc/custom_pain(message)
- if(stat >= UNCONSCIOUS)
- return
-
- if(HAS_TRAIT(src, TRAIT_NOPAIN))
- return
- if(reagents.has_reagent("morphine"))
- return
- if(reagents.has_reagent("hydrocodone"))
+ if(!can_feel_pain())
return
var/msg = "[message]"
@@ -52,13 +56,7 @@
/mob/living/carbon/human/proc/handle_pain()
// not when sleeping
- if(stat >= UNCONSCIOUS)
- return
- if(HAS_TRAIT(src, TRAIT_NOPAIN))
- return
- if(reagents.has_reagent("morphine"))
- return
- if(reagents.has_reagent("hydrocodone"))
+ if(!can_feel_pain())
return
var/maxdam = 0
diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm
index 36038f7c807..f7de06c23e8 100644
--- a/code/modules/surgery/organs_internal.dm
+++ b/code/modules/surgery/organs_internal.dm
@@ -1,439 +1,615 @@
-#define GHETTO_DISINFECT_AMOUNT 5 //Amount of units to transfer from the container to the organs during ghetto surgery disinfection step
+/// Amount of units to transfer from the container to the organs during disinfection step.
+#define GHETTO_DISINFECT_AMOUNT 5
+/// Amount of mito necessary to revive an organ
+#define MITO_REVIVAL_COST 1
+
+
/datum/surgery/organ_manipulation
name = "Organ Manipulation"
- steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/open_encased/saw,
- /datum/surgery_step/open_encased/retract, /datum/surgery_step/internal/manipulate_organs, /datum/surgery_step/glue_bone, /datum/surgery_step/set_bone,/datum/surgery_step/finish_bone,/datum/surgery_step/generic/cauterize)
- possible_locs = list("chest","head")
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/open_organ,
+ /datum/surgery_step/open_encased/saw,
+ /datum/surgery_step/open_encased/retract,
+ /datum/surgery_step/proxy/manipulate_organs,
+ /datum/surgery_step/internal/manipulate_organs/finish,
+ /datum/surgery_step/glue_bone,
+ /datum/surgery_step/set_bone,
+ /datum/surgery_step/finish_bone,
+ /datum/surgery_step/proxy/open_organ,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD)
requires_organic_bodypart = TRUE
+ requires_bodypart = TRUE
/datum/surgery/organ_manipulation/soft
- possible_locs = list("groin", "eyes", "mouth")
- steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/internal/manipulate_organs,/datum/surgery_step/generic/cauterize)
- requires_organic_bodypart = TRUE
+ possible_locs = list(BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH)
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/manipulate_organs,
+ /datum/surgery_step/generic/cauterize
+ )
/datum/surgery/organ_manipulation_boneless
name = "Organ Manipulation"
- possible_locs = list("chest","head","groin", "eyes", "mouth", "l_arm", "r_arm")
- steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/internal/manipulate_organs,/datum/surgery_step/generic/cauterize)
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/manipulate_organs,
+ /datum/surgery_step/generic/cauterize
+ )
requires_organic_bodypart = TRUE
/datum/surgery/organ_manipulation/alien
name = "Alien Organ Manipulation"
- possible_locs = list("chest", "head", "groin", "eyes", "mouth")
- allowed_mob = list(/mob/living/carbon/alien/humanoid)
- steps = list(/datum/surgery_step/saw_carapace,/datum/surgery_step/cut_carapace, /datum/surgery_step/retract_carapace,/datum/surgery_step/internal/manipulate_organs)
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH)
+ target_mobtypes = list(/mob/living/carbon/alien/humanoid)
+ steps = list(/datum/surgery_step/saw_carapace,/datum/surgery_step/cut_carapace, /datum/surgery_step/retract_carapace,/datum/surgery_step/proxy/manipulate_organs,)
/datum/surgery/organ_manipulation/can_start(mob/user, mob/living/carbon/target)
- if(istype(target,/mob/living/carbon/human))
+ . = ..()
+ if(!.)
+ return FALSE
+ if(ishuman(target))
var/mob/living/carbon/human/H = target
var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- // I'd like to see you do surgery on LITERALLY NOTHING
- return FALSE
- if(affected.is_robotic())
- return FALSE
if(!affected.encased) //no bone, problem.
return FALSE
+ if(HAS_TRAIT(target, TRAIT_NO_BONES))
+ return FALSE
return TRUE
/datum/surgery/organ_manipulation_boneless/can_start(mob/user, mob/living/carbon/target)
- if(istype(target,/mob/living/carbon/human))
+ . = ..()
+ if(!.)
+ return FALSE
+ if(istype(target, /mob/living/carbon/human))
var/mob/living/carbon/human/H = target
var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
-
- if(affected && affected.is_robotic())
- return FALSE//no operating on robotic limbs in an organic surgery
- if(!affected)
- // I'd like to see you do surgery on LITERALLY NOTHING
- return FALSE
-
if(affected && affected.encased) //no bones no problem.
return FALSE
+ if(HAS_TRAIT(target, TRAIT_NO_BONES))
+ return TRUE
return TRUE
-/datum/surgery/organ_manipulation/alien/can_start(mob/user, mob/living/carbon/target)
- if(istype(target,/mob/living/carbon/alien/humanoid))
- return TRUE
- return FALSE
+// Intermediate steps for branching organ manipulation.
+/datum/surgery/intermediate/manipulate
+ requires_bodypart = TRUE
+
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)
+
+// All these surgeries are necessary for slotting into proxy steps
+
+/datum/surgery/intermediate/manipulate/extract
+ steps = list(/datum/surgery_step/internal/manipulate_organs/extract)
+
+/datum/surgery/intermediate/manipulate/implant
+ steps = list(/datum/surgery_step/internal/manipulate_organs/implant)
+
+/datum/surgery/intermediate/manipulate/mend
+ steps = list(/datum/surgery_step/internal/manipulate_organs/mend)
+
+/datum/surgery/intermediate/manipulate/clean
+ steps = list(/datum/surgery_step/internal/manipulate_organs/clean)
+
+/// The surgery step to trigger this whole situation
+/datum/surgery_step/proxy/manipulate_organs
+ name = "Manipulate Organs (proxy)"
+ branches = list(
+ /datum/surgery/intermediate/manipulate/extract,
+ /datum/surgery/intermediate/manipulate/implant,
+ /datum/surgery/intermediate/manipulate/mend,
+ /datum/surgery/intermediate/manipulate/clean,
+ /datum/surgery/intermediate/bleeding
+ )
+
+/datum/surgery_step/proxy/manipulate_organs/soft
+ name = "Manipulate Organs Soft (proxy)"
+ branches = list(
+ /datum/surgery/intermediate/manipulate/extract,
+ /datum/surgery/intermediate/manipulate/implant,
+ /datum/surgery/intermediate/manipulate/mend,
+ /datum/surgery/intermediate/manipulate/clean,
+ /datum/surgery/intermediate/bleeding
+ )
// Internal surgeries.
/datum/surgery_step/internal
can_infect = TRUE
- blood_level = 1
+ blood_level = SURGERY_BLOODSPREAD_HANDS
+
+/datum/surgery_step/internal/manipulate_organs/mend
+ name = "mend organs"
+ allowed_tools = list(
+ /obj/item/stack/medical/bruise_pack/advanced = 100,
+ /obj/item/stack/medical/bruise_pack = 20,
+ /obj/item/stack/nanopaste = 100
+ )
+
+/datum/surgery_step/internal/manipulate_organs/mend/proc/get_tool_name(obj/item/tool)
+ var/tool_name = "[tool]"
+ if(istype(tool, /obj/item/stack/medical/bruise_pack))
+ tool_name = "the bandaid"
+ if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced))
+ tool_name = "regenerative membrane"
+ else if(istype(tool, /obj/item/stack/nanopaste))
+ tool_name = "[tool]" //what else do you call nanopaste medically?
+
+ return tool_name
+
+/datum/surgery_step/internal/manipulate_organs/mend/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/tool_name = get_tool_name(tool)
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+
+ if(!hasorgans(target))
+ to_chat(user, "They do not have organs to mend!")
+ // note that we want to return skip here so we can go "back" to the proxy step
+ return SURGERY_BEGINSTEP_SKIP
+
+ var/any_organs_damaged = FALSE
-/datum/surgery_step/internal/manipulate_organs
- name = "manipulate organs"
- allowed_tools = list(/obj/item/organ/internal = 100, /obj/item/reagent_containers/food/snacks/organ = 0)
- var/implements_extract = list(/obj/item/hemostat = 100, /obj/item/kitchen/utensil/fork = 70)
- var/implements_mend = list(/obj/item/stack/medical/bruise_pack = 20,/obj/item/stack/medical/bruise_pack/advanced = 100,/obj/item/stack/nanopaste = 100)
- var/implements_clean = list(/obj/item/reagent_containers/dropper = 100,
- /obj/item/reagent_containers/syringe = 100,
- /obj/item/reagent_containers/glass/bottle = 90,
- /obj/item/reagent_containers/food/drinks/drinkingglass = 85,
- /obj/item/reagent_containers/food/drinks/bottle = 80,
- /obj/item/reagent_containers/glass/beaker = 75,
- /obj/item/reagent_containers/spray = 60,
- /obj/item/reagent_containers/glass/bucket = 50)
+ for(var/obj/item/organ/internal/I in affected.internal_organs)
+ if(I && I.damage)
+ any_organs_damaged = TRUE
+ if(!I.is_robotic() && !istype(tool, /obj/item/stack/nanopaste))
+ if(!(I.sterile))
+ spread_germs_to_organ(I, user, tool)
+ user.visible_message("[user] starts treating damage to [target]'s [I.name] with [tool_name].", \
+ "You start treating damage to [target]'s [I.name] with [tool_name]." )
+ else if(I.is_robotic() && istype(tool, /obj/item/stack/nanopaste))
+ user.visible_message("[user] starts treating damage to [target]'s [I.name] with [tool_name].", \
+ "You start treating damage to [target]'s [I.name] with [tool_name]." )
- //Finish is just so you can close up after you do other things.
- var/implements_finsh = list(/obj/item/scalpel/laser/manager = 100,/obj/item/retractor = 100 ,/obj/item/crowbar = 90)
- var/current_type
- var/obj/item/organ/internal/I = null
- var/obj/item/organ/external/affected = null
- time = 64
-
-/datum/surgery_step/internal/manipulate_organs/New()
- ..()
- allowed_tools = allowed_tools + implements_extract + implements_mend + implements_clean + implements_finsh
-
-
-/datum/surgery_step/internal/manipulate_organs/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- I = null
- var/mob/living/carbon/human/H
- if(ishuman(target))
- H = target
- affected = H.get_organ(target_zone)
-
- if(is_int_organ(tool))
- current_type = "insert"
- I = tool
- if(I.requires_robotic_bodypart)
- to_chat(user, "[I] is an organ that requires a robotic interface[target].")
- return -1
- if(target_zone != I.parent_organ || target.get_organ_slot(I.slot))
- to_chat(user, "There is no room for [I] in [target]'s [parse_zone(target_zone)]!")
- return -1
-
- if(I.damage > (I.max_damage * 0.75))
- to_chat(user, " [I] is in no state to be transplanted.")
- return -1
-
- if(target.get_int_organ(I))
- to_chat(user, " [target] already has [I].")
- return -1
-
- if(affected)
- user.visible_message("[user] starts transplanting [tool] into [target]'s [affected.name].", \
- "You start transplanting [tool] into [target]'s [affected.name].")
- H.custom_pain("Someone's rooting around in your [affected.name]!")
else
- user.visible_message("[user] starts transplanting [tool] into [target]'s [parse_zone(target_zone)].", \
- "You start transplanting [tool] into [target]'s [parse_zone(target_zone)].")
+ to_chat(user, "[I] does not appear to be damaged.")
- else if(implement_type in implements_clean)
- current_type = "clean"
+ if(!any_organs_damaged)
+ return SURGERY_BEGINSTEP_SKIP
- if(!istype(tool, /obj/item/reagent_containers))
- return
+ if(affected)
+ var/mob/living/carbon/human/H = target
+ H.custom_pain("The pain in your [affected.name] is living hell!")
- var/obj/item/reagent_containers/C = tool
+ return ..()
- for(var/obj/item/organ/internal/I in affected.internal_organs)
- if(I)
- if(C.reagents.total_volume <= 0) //end_step handles if there is not enough reagent
- user.visible_message("[user] notices [tool] is empty.", \
- "You notice [tool] is empty.")
- return FALSE
+/datum/surgery_step/internal/manipulate_organs/mend/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/tool_name = get_tool_name(tool)
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ if(!hasorgans(target))
+ return SURGERY_STEP_INCOMPLETE
- var/msg = "[user] starts pouring some of [tool] over [target]'s [I.name]."
- var/self_msg = "You start pouring some of [tool] over [target]'s [I.name]."
- if(istype(C,/obj/item/reagent_containers/syringe))
- msg = "[user] begins injecting [tool] into [target]'s [I.name]."
- self_msg = "You begin injecting [tool] into [target]'s [I.name]."
- user.visible_message(msg, self_msg)
- if(H && affected)
- H.custom_pain("Something burns horribly in your [affected.name]!")
+ for(var/obj/item/organ/internal/I in affected.internal_organs)
+ if(I)
+ I.surgeryize()
+ if(I && I.damage)
+ if(!I.is_robotic() && !istype(tool, /obj/item/stack/nanopaste))
+ user.visible_message(
+ " [user] treats damage to [target]'s [I.name] with [tool_name].",
+ " You treat damage to [target]'s [I.name] with [tool_name]."
+ )
+ I.damage = 0
+ else if(I.is_robotic() && istype (tool, /obj/item/stack/nanopaste))
+ user.visible_message(
+ " [user] treats damage to [target]'s [I.name] with [tool_name].",
+ " You treat damage to [target]'s [I.name] with [tool_name]."
+ )
+ I.damage = 0
+ return SURGERY_STEP_CONTINUE
- else if(implement_type in implements_finsh)
- //same as surgery step /datum/surgery_step/open_encased/close/
- current_type = "finish"
+/datum/surgery_step/internal/manipulate_organs/mend/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(!hasorgans(target))
+ return SURGERY_STEP_INCOMPLETE
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(affected && affected.encased)
- var/msg = "[user] starts bending [target]'s [affected.encased] back into place with [tool]."
- var/self_msg = "You start bending [target]'s [affected.encased] back into place with [tool]."
- user.visible_message(msg, self_msg)
- else
- var/msg = "[user] starts pulling [target]'s skin back into place with [tool]."
- var/self_msg = "You start pulling [target]'s skin back into place with [tool]."
- user.visible_message(msg, self_msg)
+ user.visible_message(
+ " [user]'s hand slips, getting mess and tearing the inside of [target]'s [affected.name] with [tool]!",
+ " Your hand slips, getting mess and tearing the inside of [target]'s [affected.name] with [tool]!"
+ )
+ var/dam_amt = 2
+
+ if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced))
+ target.adjustToxLoss(5)
+
+ else if(istype(tool, /obj/item/stack/medical/bruise_pack) || istype(tool, /obj/item/stack/nanopaste))
+ dam_amt = 5
+ target.adjustToxLoss(10)
+ affected.receive_damage(5)
+
+ for(var/obj/item/organ/internal/I in affected.internal_organs)
+ if(I && I.damage && !(I.tough))
+ I.receive_damage(dam_amt,0)
+
+ return SURGERY_STEP_RETRY
+
+/datum/surgery_step/internal/manipulate_organs/extract
+ name = "extract organ"
+ allowed_tools = list(
+ TOOL_HEMOSTAT = 100,
+ /obj/item/kitchen/utensil/fork = 70
+ )
+
+ var/obj/item/organ/internal/extracting = null
+
+/datum/surgery_step/internal/manipulate_organs/extract/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/list/organs = target.get_organs_zone(target_zone)
+ if(!length(organs))
+ to_chat(user, "There are no removeable organs in [target]'s [parse_zone(target_zone)]!")
+ return SURGERY_BEGINSTEP_SKIP
+
+ for(var/obj/item/organ/internal/O in organs)
+ if(O.unremovable)
+ continue
+ O.on_find(user)
+ organs -= O
+ organs[O.name] = O
+
+ var/obj/item/organ/internal/I = input("Remove which organ?", "Surgery", null, null) as null|anything in organs
+ if(I && user && target && user.Adjacent(target) && user.get_active_hand() == tool)
+ extracting = organs[I]
+ if(!extracting)
+ return SURGERY_BEGINSTEP_SKIP
+ user.visible_message(
+ "[user] starts to separate [target]'s [I] with [tool].",
+ "You start to separate [target]'s [I] with [tool] for removal."
+ )
+ var/mob/living/carbon/human/H = target
+ var/obj/item/organ/affected = H.get_organ(user.zone_selected)
if(H && affected)
- H.custom_pain("Something hurts horribly in your [affected.name]!")
-
- else if(implement_type in implements_extract)
- current_type = "extract"
- var/list/organs = target.get_organs_zone(target_zone)
- if(!length(organs))
- to_chat(user, "There are no removeable organs in [target]'s [parse_zone(target_zone)]!")
- return -1
-
- for(var/obj/item/organ/internal/O in organs)
- if(O.unremovable)
- continue
- O.on_find(user)
- organs -= O
- organs[O.name] = O
-
- I = input("Remove which organ?", "Surgery", null, null) as null|anything in organs
- if(I && user && target && user.Adjacent(target) && user.get_active_hand() == tool)
- I = organs[I]
- if(!I)
- return -1
- user.visible_message("[user] starts to separate [target]'s [I] with [tool].", \
- "You start to separate [target]'s [I] with [tool] for removal." )
- if(H && affected)
- H.custom_pain("The pain in your [affected.name] is living hell!")
- else
- return -1
-
- else if(implement_type in implements_mend)
- current_type = "mend"
- var/tool_name = "[tool]"
- if(istype(tool, /obj/item/stack/medical/bruise_pack))
- tool_name = "the bandaid"
- if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced))
- tool_name = "regenerative membrane"
- else if(istype(tool, /obj/item/stack/nanopaste))
- tool_name = "[tool]" //what else do you call nanopaste medically?
-
- if(!hasorgans(target))
- to_chat(user, "They do not have organs to mend!")
- return
-
- for(var/obj/item/organ/internal/I in affected.internal_organs)
- if(I && I.damage)
- if(!I.is_robotic() && !istype (tool, /obj/item/stack/nanopaste))
- if(!(I.sterile))
- spread_germs_to_organ(I, user, tool)
- user.visible_message("[user] starts treating damage to [target]'s [I.name] with [tool_name].", \
- "You start treating damage to [target]'s [I.name] with [tool_name]." )
- else if(I.is_robotic() && istype(tool, /obj/item/stack/nanopaste))
- user.visible_message("[user] starts treating damage to [target]'s [I.name] with [tool_name].", \
- "You start treating damage to [target]'s [I.name] with [tool_name]." )
-
- else
- to_chat(user, "[I] does not appear to be damaged.")
-
- if(affected)
H.custom_pain("The pain in your [affected.name] is living hell!")
+ else
+ return SURGERY_BEGINSTEP_SKIP
- else if(istype(tool, /obj/item/reagent_containers/food/snacks/organ))
- to_chat(user, "[tool] was bitten by someone! It's too damaged to use!")
- return -1
+ return ..()
- ..()
+/datum/surgery_step/internal/manipulate_organs/extract/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(!extracting || extracting.owner != target)
+ user.visible_message(
+ "[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!",
+ "You can't extract anything from [target]'s [parse_zone(target_zone)]!"
+ )
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/internal/manipulate_organs/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(current_type == "mend")
- var/tool_name = "[tool]"
- if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced))
- tool_name = "regenerative membrane"
- if(istype(tool, /obj/item/stack/medical/bruise_pack))
- tool_name = "the bandaid"
- if(istype(tool, /obj/item/stack/nanopaste))
- tool_name = "[tool]" //what else do you call nanopaste medically?
+ user.visible_message(
+ " [user] has separated and extracts [target]'s [extracting] with [tool].",
+ " You have separated and extracted [target]'s [extracting] with [tool]."
+ )
- if(!hasorgans(target))
- return
+ add_attack_logs(user, target, "Surgically removed [extracting.name]. INTENT: [uppertext(user.a_intent)]")
+ spread_germs_to_organ(extracting, user, tool)
+ var/obj/item/thing = extracting.remove(target)
+ if(thing) // some "organs", like egg infections, can have I.remove(target) return null, and so we can't use "thing" in that case
+ if(istype(thing))
+ user.put_in_hands(thing)
+ else
+ thing.forceMove(get_turf(target))
+ target.update_icons()
- for(var/obj/item/organ/internal/I in affected.internal_organs)
- if(I)
- I.surgeryize()
- if(I && I.damage)
- if(!I.is_robotic() && !istype (tool, /obj/item/stack/nanopaste))
- user.visible_message(" [user] treats damage to [target]'s [I.name] with [tool_name].", \
- " You treat damage to [target]'s [I.name] with [tool_name]." )
- I.damage = 0
- else if(I.is_robotic() && istype (tool, /obj/item/stack/nanopaste))
- user.visible_message(" [user] treats damage to [target]'s [I.name] with [tool_name].", \
- " You treat damage to [target]'s [I.name] with [tool_name]." )
- I.damage = 0
-
- else if(current_type == "insert")
- I = tool
- if(I.requires_robotic_bodypart)
- to_chat(user, "[I] is an organ that requires a robotic interface[target].")
- return FALSE
- if(!user.drop_item())
- to_chat(user, "[I] is stuck to your hand, you can't put it in [target]!")
- return FALSE
- I.insert(target)
- spread_germs_to_organ(I, user, tool)
+ return SURGERY_STEP_CONTINUE
+/datum/surgery_step/internal/manipulate_organs/extract/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(extracting && extracting.owner == target)
+ var/mob/living/carbon/human/H = target
+ var/obj/item/organ/affected = H.get_organ(user.zone_selected)
if(affected)
- user.visible_message(" [user] has transplanted [tool] into [target]'s [affected.name].",
- " You have transplanted [tool] into [target]'s [affected.name].")
- else
- user.visible_message(" [user] has transplanted [tool] into [target]'s [parse_zone(target_zone)].",
- " You have transplanted [tool] into [target]'s [parse_zone(target_zone)].")
-
- else if(current_type == "extract")
- if(I && I.owner == target)
- user.visible_message(" [user] has separated and extracts [target]'s [I] with [tool].",
- " You have separated and extracted [target]'s [I] with [tool].")
-
- add_attack_logs(user, target, "Surgically removed [I.name]. INTENT: [uppertext(user.a_intent)]")
- spread_germs_to_organ(I, user, tool)
- var/obj/item/thing = I.remove(target)
- if(thing) // some "organs", like egg infections, can have I.remove(target) return null, and so we can't use "thing" in that case
- if(istype(thing))
- user.put_in_hands(thing)
- else
- thing.forceMove(get_turf(target))
- target.update_icons()
- else
- user.visible_message("[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!",
- "You can't extract anything from [target]'s [parse_zone(target_zone)]!")
-
- else if(current_type == "clean")
- if(!hasorgans(target))
- return
- if(!istype(tool,/obj/item/reagent_containers))
- return
-
- var/obj/item/reagent_containers/C = tool
- var/datum/reagents/R = C.reagents
- var/ethanol = 0 //how much alcohol is in the thing
- var/spaceacillin = 0 //how much actual antibiotic is in the thing
-
- if(R.reagent_list.len)
- for(var/datum/reagent/consumable/ethanol/alcohol in R.reagent_list)
- ethanol += alcohol.alcohol_perc * 300
- ethanol /= R.reagent_list.len
-
- spaceacillin = R.get_reagent_amount("spaceacillin")
-
-
- for(var/obj/item/organ/internal/I in affected.internal_organs)
- if(I)
- if(R.total_volume < GHETTO_DISINFECT_AMOUNT)
- user.visible_message("[user] notices there is not enough in [tool].", \
- "You notice there is not enough in [tool].")
- return FALSE
- if(I.germ_level < INFECTION_LEVEL_ONE / 2)
- to_chat(user, "[I] does not appear to be infected.")
- if(I.germ_level >= INFECTION_LEVEL_ONE / 2)
- if(spaceacillin >= GHETTO_DISINFECT_AMOUNT)
- I.germ_level = 0
- else
- I.germ_level = max(I.germ_level-ethanol, 0)
- if(istype(C,/obj/item/reagent_containers/syringe))
- user.visible_message(" [user] has injected [tool] into [target]'s [I.name].",
- " You have injected [tool] into [target]'s [I.name].")
- else
- user.visible_message(" [user] has poured some of [tool] over [target]'s [I.name].",
- " You have poured some of [tool] over [target]'s [I.name].")
- R.trans_to(target, GHETTO_DISINFECT_AMOUNT)
- R.reaction(target, REAGENT_INGEST)
-
- else if(current_type == "finish")
- if(affected && affected.encased)
- var/msg = " [user] bends [target]'s [affected.encased] back into place with [tool]."
- var/self_msg = " You bend [target]'s [affected.encased] back into place with [tool]."
- user.visible_message(msg, self_msg)
- affected.open = 2.5
- else
- var/msg = "[user] pulls [target]'s flesh back into place with [tool]."
- var/self_msg = "You pull [target]'s flesh back into place with [tool]."
- user.visible_message(msg, self_msg)
-
- return TRUE
-
- return FALSE
-
-/datum/surgery_step/internal/manipulate_organs/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(current_type == "mend")
- if(!hasorgans(target))
- return
-
- user.visible_message(" [user]'s hand slips, getting mess and tearing the inside of [target]'s [affected.name] with [tool]!", \
- " Your hand slips, getting mess and tearing the inside of [target]'s [affected.name] with [tool]!")
-
- var/dam_amt = 2
-
- if(istype(tool, /obj/item/stack/medical/bruise_pack/advanced))
- target.adjustToxLoss(5)
-
- else if(istype(tool, /obj/item/stack/medical/bruise_pack) || istype(tool, /obj/item/stack/nanopaste))
- dam_amt = 5
- target.adjustToxLoss(10)
- affected.receive_damage(5)
-
- for(var/obj/item/organ/internal/I in affected.internal_organs)
- if(I && I.damage && !(I.tough))
- I.receive_damage(dam_amt,0)
-
- return FALSE
-
- else if(current_type == "insert")
- user.visible_message(" [user]'s hand slips, damaging [tool]!", \
- " Your hand slips, damaging [tool]!")
- var/obj/item/organ/internal/I = tool
- if(istype(I) && !I.tough)
- I.receive_damage(rand(3,5),0)
-
- return FALSE
-
- else if(current_type == "clean")
- if(!hasorgans(target))
- return
- if(!istype(tool,/obj/item/reagent_containers))
- return
-
- var/obj/item/reagent_containers/C = tool
- var/datum/reagents/R = C.reagents
- var/ethanol = 0 //how much alcohol is in the thing
-
- if(R.reagent_list.len)
- for(var/datum/reagent/consumable/ethanol/alcohol in R.reagent_list)
- ethanol += alcohol.alcohol_perc * 300
- ethanol /= C.reagents.reagent_list.len
-
- for(var/obj/item/organ/internal/I in affected.internal_organs)
- I.germ_level = max(I.germ_level-ethanol, 0)
- I.receive_damage(rand(4,8),0)
-
- R.trans_to(target, GHETTO_DISINFECT_AMOUNT * 10)
- R.reaction(target, REAGENT_INGEST)
-
- user.visible_message(" [user]'s hand slips, splashing the contents of [tool] all over [target]'s [affected.name] incision!", \
- " Your hand slips, splashing the contents of [tool] all over [target]'s [affected.name] incision!")
- return FALSE
-
- else if(current_type == "extract")
- if(I && I.owner == target)
- if(affected)
- user.visible_message(" [user]'s hand slips, damaging [target]'s [affected.name] with [tool]!", \
- " Your hand slips, damaging [target]'s [affected.name] with [tool]!")
- affected.receive_damage(20)
- else
- user.visible_message(" [user]'s hand slips, damaging [target]'s [parse_zone(target_zone)] with [tool]!", \
- " Your hand slips, damaging [target]'s [parse_zone(target_zone)] with [tool]!")
- else
- user.visible_message("[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!",
- "You can't extract anything from [target]'s [parse_zone(target_zone)]!")
- return FALSE
-
- else if(current_type == "finish")
- if(affected && affected.encased)
- var/msg = " [user]'s hand slips, bending [target]'s [affected.encased] the wrong way!"
- var/self_msg = " Your hand slips, bending [target]'s [affected.encased] the wrong way!"
- user.visible_message(msg, self_msg)
- affected.fracture()
- else
- var/msg = " [user]'s hand slips, tearing the skin!"
- var/self_msg = " Your hand slips, tearing skin!"
- user.visible_message(msg, self_msg)
- if(affected)
+ user.visible_message(
+ " [user]'s hand slips, damaging [target]'s [affected.name] with [tool]!",
+ " Your hand slips, damaging [target]'s [affected.name] with [tool]!"
+ )
affected.receive_damage(20)
- return FALSE
+ else
+ user.visible_message(
+ " [user]'s hand slips, damaging [target]'s [parse_zone(target_zone)] with [tool]!",
+ " Your hand slips, damaging [target]'s [parse_zone(target_zone)] with [tool]!"
+ )
+ return SURGERY_STEP_RETRY
+ else
+ user.visible_message(
+ "[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!",
+ "You can't extract anything from [target]'s [parse_zone(target_zone)]!"
+ )
+ return SURGERY_STEP_CONTINUE
+
+/datum/surgery_step/internal/manipulate_organs/implant
+ name = "implant an organ"
+ allowed_tools = list(
+ /obj/item/organ/internal = 100,
+ /obj/item/reagent_containers/food/snacks/organ = 0 // there for the flavor text
+ )
+
+/datum/surgery_step/internal/manipulate_organs/implant/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(istype(tool, /obj/item/reagent_containers/food/snacks/organ))
+ to_chat(user, "[tool] was bitten by someone! It's too damaged to use!")
+ return SURGERY_BEGINSTEP_SKIP
+
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+
+ var/obj/item/organ/internal/I = tool
+ if(!istype(I))
+ // dunno how you got here but okay
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(I.requires_robotic_bodypart)
+ to_chat(user, "[I] is an organ that requires a robotic interface[target].")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(target_zone != I.parent_organ || target.get_organ_slot(I.slot))
+ to_chat(user, "There is no room for [I] in [target]'s [parse_zone(target_zone)]!")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(I.damage > (I.max_damage * 0.75))
+ to_chat(user, " [I] is in no state to be transplanted.")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(target.get_int_organ(I))
+ to_chat(user, " [target] already has [I].")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(affected)
+ user.visible_message(
+ "[user] starts transplanting [tool] into [target]'s [affected.name].",
+ "You start transplanting [tool] into [target]'s [affected.name]."
+ )
+ var/mob/living/carbon/human/H = target
+ H.custom_pain("Someone's rooting around in your [affected.name]!")
+ else
+ user.visible_message(
+ "[user] starts transplanting [tool] into [target]'s [parse_zone(target_zone)].",
+ "You start transplanting [tool] into [target]'s [parse_zone(target_zone)]."
+ )
+ return ..()
+
+/datum/surgery_step/internal/manipulate_organs/implant/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/internal/I = tool
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ if(!istype(tool))
+ return SURGERY_STEP_INCOMPLETE
+ if(I.requires_robotic_bodypart)
+ to_chat(user, "[I] is an organ that requires a robotic interface[target].")
+ return SURGERY_STEP_INCOMPLETE
+ if(!user.drop_item())
+ to_chat(user, "[I] is stuck to your hand, you can't put it in [target]!")
+ return SURGERY_STEP_INCOMPLETE
+ I.insert(target)
+ spread_germs_to_organ(I, user, tool)
+
+ if(affected)
+ user.visible_message(" [user] has transplanted [tool] into [target]'s [affected.name].",
+ " You have transplanted [tool] into [target]'s [affected.name].")
+ else
+ user.visible_message(" [user] has transplanted [tool] into [target]'s [parse_zone(target_zone)].",
+ " You have transplanted [tool] into [target]'s [parse_zone(target_zone)].")
+
+ return SURGERY_STEP_CONTINUE
+
+/datum/surgery_step/internal/manipulate_organs/implant/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ user.visible_message(
+ "[user]'s hand slips, damaging [tool]!",
+ "Your hand slips, damaging [tool]!"
+ )
+ var/obj/item/organ/internal/I = tool
+ if(istype(I) && !I.tough)
+ I.receive_damage(rand(3,5),0)
+
+ return SURGERY_STEP_RETRY
- return FALSE
+/datum/surgery_step/internal/manipulate_organs/clean
+ name = "clean and/or revive organs"
+ allowed_tools = list(
+ /obj/item/reagent_containers/dropper = 100,
+ /obj/item/reagent_containers/syringe = 100,
+ /obj/item/reagent_containers/glass/bottle = 90,
+ /obj/item/reagent_containers/food/drinks/drinkingglass = 85,
+ /obj/item/reagent_containers/food/drinks/bottle = 80,
+ /obj/item/reagent_containers/glass/beaker = 75,
+ /obj/item/reagent_containers/spray = 60,
+ /obj/item/reagent_containers/glass/bucket = 50
+ )
+/datum/surgery_step/internal/manipulate_organs/clean/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+
+ var/obj/item/reagent_containers/C = tool
+
+ var/mob/living/carbon/human/H = target
+ var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
+
+ for(var/obj/item/organ/internal/I in affected.internal_organs)
+ if(I)
+ if(C.reagents.total_volume <= 0) //end_step handles if there is not enough reagent
+ user.visible_message(
+ "[user] notices [tool] is empty.",
+ "You notice [tool] is empty."
+ )
+ return SURGERY_BEGINSTEP_SKIP
+
+ var/msg = "[user] starts pouring some of [tool] over [target]'s [I.name]."
+ var/self_msg = "You start pouring some of [tool] over [target]'s [I.name]."
+ if(istype(C, /obj/item/reagent_containers/syringe))
+ msg = "[user] begins injecting [tool] into [target]'s [I.name]."
+ self_msg = "You begin injecting [tool] into [target]'s [I.name]."
+ user.visible_message(msg, self_msg)
+ if(H && affected)
+ H.custom_pain("Something burns horribly in your [affected.name]!")
+
+ return ..()
+
+/datum/surgery_step/internal/manipulate_organs/clean/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(!hasorgans(target))
+ return SURGERY_STEP_INCOMPLETE
+ if(!istype(tool, /obj/item/reagent_containers))
+ return SURGERY_STEP_INCOMPLETE
+
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+
+ var/obj/item/reagent_containers/C = tool
+ var/datum/reagents/R = C.reagents
+ var/ethanol = 0 //how much alcohol is in the thing
+ var/spaceacillin = 0 //how much actual antibiotic is in the thing
+ var/mito_tot = 0 // same for mito, thanks farie
+
+ if(length(R.reagent_list))
+ for(var/datum/reagent/consumable/ethanol/alcohol in R.reagent_list)
+ ethanol += alcohol.alcohol_perc * 300
+ ethanol /= length(R.reagent_list)
+
+ mito_tot = R.get_reagent_amount("mitocholide")
+ spaceacillin = R.get_reagent_amount("spaceacillin")
+
+
+ for(var/obj/item/organ/internal/I in affected.internal_organs)
+ if(!I) // if you have a null organ I wish you the best
+ continue
+ if(I.germ_level < INFECTION_LEVEL_ONE / 2 && !(I.status & ORGAN_DEAD)) // not dead, don't need to inject mito either
+ to_chat(user, "[I] does not appear to need chemical treatment.")
+ continue
+ if(!spaceacillin && !ethanol && !mito_tot)
+ to_chat(user, "[C] doesn't have anything in it that would be worth applying!")
+ break
+ var/success = FALSE
+ if(I.germ_level >= INFECTION_LEVEL_ONE / 2)
+ // spacecillin completely cures infections if there is enough, ethanol just reduces the infection strength by the amount used.
+ if(spaceacillin || ethanol)
+ if(spaceacillin >= GHETTO_DISINFECT_AMOUNT)
+ I.germ_level = 0
+ else
+ I.germ_level = max(I.germ_level-ethanol, 0)
+ success = TRUE // we actually injected some chemicals
+
+ else if(!(I.status & ORGAN_DEAD)) // Not dead and got nothing to disinfect the organ with. Don't waste the other chems
+ to_chat(user, "[I] does appear mildly infected but [C] does not seem to contain disinfectants. You decide to not inject the chemicals into [I].")
+ continue
+
+ var/mito_trans
+ if(mito_tot && (I.status & ORGAN_DEAD) && !I.is_robotic())
+ mito_trans = min(mito_tot, C.amount_per_transfer_from_this / length(R.reagent_list)) // How much mito is actually transfered
+ success = TRUE
+ if(!success)
+ to_chat(user, "[C] does not seem to have the chemicals needed to clean [I]. You decide against wasting chemicals.")
+ continue
+
+ // now try actually injecting.
+
+ if(istype(C, /obj/item/reagent_containers/syringe))
+ user.visible_message(
+ " [user] has injected [tool] into [target]'s [I.name].",
+ " You have injected [tool] into [target]'s [I.name]."
+ )
+ else
+ user.visible_message(
+ " [user] has poured some of [tool] over [target]'s [I.name].",
+ " You have poured some of [tool] over [target]'s [I.name]."
+ )
+
+ R.reaction(target, REAGENT_INGEST, R.total_volume / C.amount_per_transfer_from_this)
+ R.trans_to(target, C.amount_per_transfer_from_this)
+
+ if(mito_trans)
+ mito_tot -= mito_trans
+ if(I.is_robotic()) // Get out cyborg people
+ continue
+ if(mito_trans >= MITO_REVIVAL_COST)
+ I.rejuvenate() // Just like splashing it onto it
+ user.visible_message("\The [I] seems to regain its lively luster!")
+ else
+ to_chat(user, "[I] does not seem to respond to the amount of mitocholide inside the injection. Try injecting more next time.")
+
+ return SURGERY_STEP_CONTINUE
+
+
+/datum/surgery_step/internal/manipulate_organs/clean/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(!istype(tool, /obj/item/reagent_containers))
+ return SURGERY_STEP_INCOMPLETE
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ var/obj/item/reagent_containers/C = tool
+ var/datum/reagents/R = C.reagents
+ var/ethanol = 0 //how much alcohol is in the thing
+
+ if(length(R.reagent_list))
+ for(var/datum/reagent/consumable/ethanol/alcohol in R.reagent_list)
+ ethanol += alcohol.alcohol_perc * 300
+ ethanol /= length(C.reagents.reagent_list)
+
+ for(var/obj/item/organ/internal/I in target.get_organs_zone(target_zone))
+ I.germ_level = max(I.germ_level-ethanol, 0)
+ I.receive_damage(rand(4, 8), 0)
+
+ R.trans_to(target, GHETTO_DISINFECT_AMOUNT * 10)
+ R.reaction(target, REAGENT_INGEST)
+
+ user.visible_message(
+ " [user]'s hand slips, splashing the contents of [tool] all over [target][affected ? "'s [affected.name]" : ""] incision!",
+ " Your hand slips, splashing the contents of [tool] all over [target][affected ? "'s [affected.name]" : ""] incision!"
+ )
+ // continue here since we want to keep moving in the surgery
+ return SURGERY_STEP_CONTINUE
+
+// FINISH
+/datum/surgery_step/internal/manipulate_organs/finish
+ name = "finish manipulation"
+ allowed_tools = list(
+ /obj/item/scalpel/laser/manager = 100,
+ TOOL_RETRACTOR = 100,
+ TOOL_CROWBAR = 90
+ )
+
+/datum/surgery_step/internal/manipulate_organs/finish/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/mob/living/carbon/human/H = target
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ if(affected && affected.encased)
+ var/msg = "[user] starts bending [target]'s [affected.encased] back into place with [tool]."
+ var/self_msg = "You start bending [target]'s [affected.encased] back into place with [tool]."
+ user.visible_message(msg, self_msg)
+ else
+ var/msg = "[user] starts pulling [target]'s skin back into place with [tool]."
+ var/self_msg = "You start pulling [target]'s skin back into place with [tool]."
+ user.visible_message(msg, self_msg)
+
+ if(H && affected)
+ H.custom_pain("Something hurts horribly in your [affected.name]!")
+
+ return ..()
+
+/datum/surgery_step/internal/manipulate_organs/finish/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ var/msg
+ var/self_msg
+ if(affected && affected.encased)
+ msg = "[user] bends [target]'s [affected.encased] back into place with [tool]."
+ self_msg = " You bend [target]'s [affected.encased] back into place with [tool]."
+ affected.open = ORGAN_ORGANIC_ENCASED_OPEN
+ else
+ msg = "[user] pulls [target]'s flesh back into place with [tool]."
+ self_msg = "You pull [target]'s flesh back into place with [tool]."
+
+ user.visible_message(msg, self_msg)
+ return SURGERY_STEP_CONTINUE
+
+/datum/surgery_step/internal/manipulate_organs/finish/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ var/msg
+ var/self_msg
+ if(affected && affected.encased)
+ msg = " [user]'s hand slips, bending [target]'s [affected.encased] the wrong way!"
+ self_msg = " Your hand slips, bending [target]'s [affected.encased] the wrong way!"
+ affected.fracture()
+ else
+ msg = " [user]'s hand slips, tearing the skin!"
+ self_msg = " Your hand slips, tearing skin!"
+ if(affected)
+ affected.receive_damage(20)
+ user.visible_message(msg, self_msg)
+ return SURGERY_STEP_RETRY
//////////////////////////////////////////////////////////////////
// SPESHUL AYLIUM STUPS //
@@ -442,109 +618,121 @@
/datum/surgery_step/saw_carapace
name = "saw carapace"
allowed_tools = list(
- /obj/item/circular_saw = 100, \
- /obj/item/melee/energy/sword/cyborg/saw = 100, \
- /obj/item/hatchet = 90
+ TOOL_SAW = 100,
+ /obj/item/melee/energy/sword/cyborg/saw = 100,
+ /obj/item/hatchet = 90
)
- time = 54
+ time = 5.4 SECONDS
-/datum/surgery_step/saw_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/saw_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- user.visible_message("[user] begins to cut through [target]'s [target_zone] with [tool].", \
- "You begin to cut through [target]'s [target_zone] with [tool].")
- ..()
+ user.visible_message(
+ "[user] begins to cut through [target]'s [target_zone] with [tool].",
+ "You begin to cut through [target]'s [target_zone] with [tool]."
+ )
+ return ..()
-/datum/surgery_step/saw_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/saw_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- user.visible_message(" [user] has cut [target]'s [target_zone] open with [tool].", \
- " You have cut [target]'s [target_zone] open with [tool].")
- return TRUE
+ user.visible_message(
+ " [user] has cut [target]'s [target_zone] open with [tool].",
+ " You have cut [target]'s [target_zone] open with [tool]."
+ )
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/saw_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/saw_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- user.visible_message(" [user]'s hand slips, cracking [target]'s [target_zone] with [tool]!" , \
- " Your hand slips, cracking [target]'s [target_zone] with [tool]!" )
- return FALSE
+ user.visible_message(
+ " [user]'s hand slips, cracking [target]'s [target_zone] with [tool]!",
+ " Your hand slips, cracking [target]'s [target_zone] with [tool]!"
+ )
+ return SURGERY_STEP_RETRY
/datum/surgery_step/cut_carapace
name = "cut carapace"
allowed_tools = list(
- /obj/item/scalpel = 100, \
- /obj/item/kitchen/knife = 90, \
- /obj/item/shard = 60, \
- /obj/item/scissors = 12, \
- /obj/item/twohanded/chainsaw = 1, \
- /obj/item/claymore = 6, \
- /obj/item/melee/energy/ = 6, \
- /obj/item/pen/edagger = 6, \
+ TOOL_SCALPEL = 100,
+ /obj/item/kitchen/knife = 90,
+ /obj/item/shard = 60,
+ /obj/item/scissors = 12,
+ /obj/item/twohanded/chainsaw = 1,
+ /obj/item/claymore = 6,
+ /obj/item/melee/energy = 6,
+ /obj/item/pen/edagger = 6
)
- time = 16
+ time = 1.6 SECONDS
-/datum/surgery_step/cut_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/cut_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] starts the incision on [target]'s [target_zone] with [tool].", \
"You start the incision on [target]'s [target_zone] with [tool].")
- ..()
+ return ..()
-/datum/surgery_step/cut_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/cut_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- user.visible_message(" [user] has made an incision on [target]'s [target_zone] with [tool].", \
- " You have made an incision on [target]'s [target_zone] with [tool].",)
- return TRUE
+ user.visible_message(
+ " [user] has made an incision on [target]'s [target_zone] with [tool].",
+ " You have made an incision on [target]'s [target_zone] with [tool]."
+ )
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/cut_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/cut_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- user.visible_message(" [user]'s hand slips, slicing open [target]'s [target_zone] in a wrong spot with [tool]!", \
- " Your hand slips, slicing open [target]'s [target_zone] in a wrong spot with [tool]!")
- return FALSE
+ user.visible_message(
+ "[user]'s hand slips, slicing open [target]'s [target_zone] in a wrong spot with [tool]!",
+ "Your hand slips, slicing open [target]'s [target_zone] in a wrong spot with [tool]!"
+ )
+ return SURGERY_STEP_RETRY
/datum/surgery_step/retract_carapace
name = "retract carapace"
allowed_tools = list(
- /obj/item/scalpel/laser/manager = 100, \
- /obj/item/retractor = 100, \
- /obj/item/crowbar = 90, \
- /obj/item/kitchen/utensil/fork = 60
+ /obj/item/scalpel/laser/manager = 100,
+ TOOL_RETRACTOR = 100,
+ /obj/item/crowbar = 90,
+ /obj/item/kitchen/utensil/fork = 60
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/retract_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/retract_carapace/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/msg = "[user] starts to pry open the incision on [target]'s [target_zone] with [tool]."
var/self_msg = "You start to pry open the incision on [target]'s [target_zone] with [tool]."
- if(target_zone == "chest")
+ if(target_zone == BODY_ZONE_CHEST)
msg = "[user] starts to separate the ribcage and rearrange the organs in [target]'s torso with [tool]."
self_msg = "You start to separate the ribcage and rearrange the organs in [target]'s torso with [tool]."
- if(target_zone == "groin")
+ if(target_zone == BODY_ZONE_PRECISE_GROIN)
msg = "[user] starts to pry open the incision and rearrange the organs in [target]'s lower abdomen with [tool]."
self_msg = "You start to pry open the incision and rearrange the organs in [target]'s lower abdomen with [tool]."
user.visible_message(msg, self_msg)
- ..()
+ return ..()
-/datum/surgery_step/retract_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/retract_carapace/end_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/msg = " [user] keeps the incision open on [target]'s [target_zone] with [tool]."
var/self_msg = " You keep the incision open on [target]'s [target_zone] with [tool]."
- if(target_zone == "chest")
+ if(target_zone == BODY_ZONE_CHEST)
msg = " [user] keeps the ribcage open on [target]'s torso with [tool]."
self_msg = " You keep the ribcage open on [target]'s torso with [tool]."
- if(target_zone == "groin")
+ if(target_zone == BODY_ZONE_PRECISE_GROIN)
msg = " [user] keeps the incision open on [target]'s lower abdomen with [tool]."
self_msg = " You keep the incision open on [target]'s lower abdomen with [tool]."
user.visible_message(msg, self_msg)
- return TRUE
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/generic/retract_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/generic/retract_carapace/fail_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/msg = " [user]'s hand slips, tearing the edges of incision on [target]'s [target_zone] with [tool]!"
var/self_msg = " Your hand slips, tearing the edges of incision on [target]'s [target_zone] with [tool]!"
- if(target_zone == "chest")
- msg = " [user]'s hand slips, damaging several organs [target]'s torso with [tool]!"
- self_msg = " Your hand slips, damaging several organs [target]'s torso with [tool]!"
- if(target_zone == "groin")
- msg = " [user]'s hand slips, damaging several organs [target]'s lower abdomen with [tool]"
- self_msg = " Your hand slips, damaging several organs [target]'s lower abdomen with [tool]!"
+ if(target_zone == BODY_ZONE_CHEST)
+ msg = " [user]'s hand slips, damaging several organs in [target]'s torso with [tool]!"
+ self_msg = " Your hand slips, damaging several organs in [target]'s torso with [tool]!"
+ if(target_zone == BODY_ZONE_PRECISE_GROIN)
+ msg = " [user]'s hand slips, damaging several organs in [target]'s lower abdomen with [tool]"
+ self_msg = " Your hand slips, damaging several organs in [target]'s lower abdomen with [tool]!"
user.visible_message(msg, self_msg)
- return FALSE
+ return SURGERY_STEP_RETRY
+
+#undef MITO_REVIVAL_COST
diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm
index 2c748e1c625..fbbfd10fd0a 100644
--- a/code/modules/surgery/other.dm
+++ b/code/modules/surgery/other.dm
@@ -6,152 +6,142 @@
/datum/surgery/infection
name = "External Infection Treatment"
steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/cauterize)
- possible_locs = list("chest","head","groin", "l_arm", "r_arm", "l_leg", "r_leg", "r_hand", "l_hand", "r_foot", "l_foot")
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT)
/datum/surgery/bleeding
name = "Internal Bleeding"
- steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders,/datum/surgery_step/generic/retract_skin,/datum/surgery_step/fix_vein,/datum/surgery_step/generic/cauterize)
- possible_locs = list("chest","head","groin", "l_arm", "r_arm", "l_leg", "r_leg", "r_hand", "l_hand", "r_foot", "l_foot")
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/open_organ,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT)
/datum/surgery/debridement
name = "Debridement"
- steps = list(/datum/surgery_step/generic/cut_open,/datum/surgery_step/generic/clamp_bleeders,/datum/surgery_step/generic/retract_skin,/datum/surgery_step/fix_dead_tissue,/datum/surgery_step/treat_necrosis,/datum/surgery_step/generic/cauterize)
- possible_locs = list("chest","head","groin", "l_arm", "r_arm", "l_leg", "r_leg", "r_hand", "l_hand", "r_foot", "l_foot")
-
-/datum/surgery/infection/can_start(mob/user, mob/living/carbon/target)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(affected.is_robotic())
- return FALSE
- return TRUE
- return FALSE
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/open_organ,
+ /datum/surgery_step/fix_dead_tissue,
+ /datum/surgery_step/treat_necrosis,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT)
+ requires_organic_bodypart = TRUE
/datum/surgery/bleeding/can_start(mob/user, mob/living/carbon/target)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
-
- if(affected.status & ORGAN_INT_BLEEDING)
- return TRUE
+ . = ..()
+ if(!.)
return FALSE
+ var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
+ if(affected.status & ORGAN_INT_BLEEDING)
+ return TRUE
+ return FALSE
/datum/surgery/debridement/can_start(mob/user, mob/living/carbon/target)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
+ . = ..()
+ if(!.)
+ return FALSE
+ var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
+ if(!(affected.status & ORGAN_DEAD))
+ return FALSE
+ return TRUE
- if(!hasorgans(target))
- return FALSE
-
- if(!affected)
- return FALSE
-
- if(!(affected.status & ORGAN_DEAD))
- return FALSE
-
- return TRUE
-
- return FALSE
/datum/surgery_step/fix_vein
name = "mend internal bleeding"
allowed_tools = list(
- /obj/item/FixOVein = 100, \
- /obj/item/stack/cable_coil = 90
+ TOOL_FIXOVEIN = 100,
+ /obj/item/stack/cable_coil = 90
)
can_infect = TRUE
- blood_level = 1
+ blood_level = SURGERY_BLOODSPREAD_HANDS
- time = 32
+ time = 3.2 SECONDS
-/datum/surgery_step/fix_vein/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/fix_vein/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(!affected)
- return FALSE
+ if(!(affected.status & ORGAN_INT_BLEEDING))
+ to_chat(user, "The veins in [affected] seem to be in perfect shape!")
+ return SURGERY_BEGINSTEP_SKIP
- if(affected.status & ORGAN_INT_BLEEDING)
- return TRUE
-
-/datum/surgery_step/fix_vein/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)
- user.visible_message("[user] starts patching the damaged vein in [target]'s [affected.name] with \the [tool]." , \
- "You start patching the damaged vein in [target]'s [affected.name] with \the [tool].")
+ user.visible_message(
+ "[user] starts patching the damaged vein in [target]'s [affected.name] with \the [tool].",
+ "You start patching the damaged vein in [target]'s [affected.name] with \the [tool]."
+ )
target.custom_pain("The pain in your [affected.name] is unbearable!")
- ..()
+ return ..()
-/datum/surgery_step/fix_vein/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+
+/datum/surgery_step/fix_vein/end_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)
- user.visible_message(" [user] has patched the damaged vein in [target]'s [affected.name] with \the [tool].", \
- " You have patched the damaged vein in [target]'s [affected.name] with \the [tool].")
+ user.visible_message(
+ " [user] has patched the damaged vein in [target]'s [affected.name] with \the [tool].",
+ " You have patched the damaged vein in [target]'s [affected.name] with \the [tool]."
+ )
affected.fix_internal_bleeding()
if(ishuman(user) && prob(40))
var/mob/living/carbon/human/U = user
U.bloody_hands(target, 0)
- return TRUE
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/fix_vein/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/fix_vein/fail_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)
- user.visible_message(" [user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!" , \
- " Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!")
+ user.visible_message(
+ " [user]'s hand slips, smearing [tool] in the incision in [target]'s [affected.name]!",
+ " Your hand slips, smearing [tool] in the incision in [target]'s [affected.name]!"
+ )
affected.receive_damage(5, 0)
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/fix_dead_tissue //Debridement
name = "remove dead tissue"
allowed_tools = list(
- /obj/item/scalpel = 100, \
- /obj/item/kitchen/knife = 90, \
- /obj/item/shard = 60, \
+ TOOL_SCALPEL = 100,
+ /obj/item/kitchen/knife = 90,
+ /obj/item/shard = 60
)
can_infect = TRUE
- blood_level = 1
+ blood_level = SURGERY_BLOODSPREAD_HANDS
- time = 16
-
-/datum/surgery_step/fix_dead_tissue/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(!hasorgans(target))
- return FALSE
+ time = 1.6 SECONDS
+/datum/surgery_step/fix_dead_tissue/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(!affected)
- return FALSE
-
- if(!(affected.status & ORGAN_DEAD))
- return FALSE
- return TRUE
-
-/datum/surgery_step/fix_dead_tissue/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)
- user.visible_message("[user] starts cutting away necrotic tissue in [target]'s [affected.name] with \the [tool]." , \
- "You start cutting away necrotic tissue in [target]'s [affected.name] with \the [tool].")
+ user.visible_message(
+ "[user] starts cutting away necrotic tissue in [target]'s [affected.name] with \the [tool].",
+ "You start cutting away necrotic tissue in [target]'s [affected.name] with \the [tool]."
+ )
target.custom_pain("The pain in [affected.name] is unbearable!")
- ..()
+ return ..()
-/datum/surgery_step/fix_dead_tissue/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/fix_dead_tissue/end_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)
- user.visible_message(" [user] has cut away necrotic tissue in [target]'s [affected.name] with \the [tool].", \
- " You have cut away necrotic tissue in [target]'s [affected.name] with \the [tool].")
- affected.open = 3
+ user.visible_message(
+ " [user] has cut away necrotic tissue in [target]'s [affected.name] with \the [tool].",
+ " You have cut away necrotic tissue in [target]'s [affected.name] with \the [tool]."
+ )
+ affected.open = ORGAN_ORGANIC_OPEN
- return TRUE
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/fix_dead_tissue/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/fix_dead_tissue/fail_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)
- user.visible_message(" [user]'s hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!", \
- " Your hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!")
+ user.visible_message(
+ " [user]'s hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!",
+ " Your hand slips, slicing an artery inside [target]'s [affected.name] with \the [tool]!"
+ )
affected.receive_damage(20)
- return FALSE
+ return SURGERY_STEP_RETRY
/datum/surgery_step/treat_necrosis
name = "treat necrosis"
@@ -166,46 +156,43 @@
)
can_infect = FALSE
- blood_level = 0
+ blood_level = SURGERY_BLOODSPREAD_NONE
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/treat_necrosis/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(!istype(tool, /obj/item/reagent_containers))
- return FALSE
+/datum/surgery_step/treat_necrosis/tool_check(mob/user, obj/item/tool)
+ . = ..()
var/obj/item/reagent_containers/container = tool
if(!container.reagents.has_reagent("mitocholide"))
- user.visible_message("[user] looks at \the [tool] and ponders." , \
- "You are not sure if \the [tool] contains mitocholide to treat the necrosis.")
+ user.visible_message(
+ "[user] looks at \the [tool] and ponders.",
+ "You are not sure if \the [tool] contains the mitocholide necessary to treat the necrosis.")
return FALSE
- if(!hasorgans(target))
- return FALSE
+/datum/surgery_step/treat_necrosis/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(!(affected.status & ORGAN_DEAD))
- return FALSE
- return TRUE
+ to_chat(user, "The [affected] seems to already be in fine condition!")
+ return SURGERY_BEGINSTEP_SKIP
-/datum/surgery_step/treat_necrosis/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)
- 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].")
+ 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!")
- ..()
+ return ..()
-/datum/surgery_step/treat_necrosis/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/treat_necrosis/end_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(!istype(tool, /obj/item/reagent_containers))
- return FALSE
-
var/obj/item/reagent_containers/container = tool
- var/mitocholide = 0
+ var/mitocholide = FALSE
if(container.reagents.has_reagent("mitocholide"))
- mitocholide = 1
+ mitocholide = TRUE
var/trans = container.reagents.trans_to(target, container.amount_per_transfer_from_this)
if(trans > 0)
@@ -213,25 +200,32 @@
if(mitocholide)
affected.status &= ~ORGAN_DEAD
+ affected.germ_level = 0
target.update_body()
- user.visible_message(" [user] applies [trans] units of the solution to affected tissue in [target]'s [affected.name]", \
- " You apply [trans] units of the solution to affected tissue in [target]'s [affected.name] with \the [tool].")
+ user.visible_message(
+ " [user] applies [trans] units of the solution to affected tissue in [target]'s [affected.name]",
+ " You apply [trans] units of the solution to affected tissue in [target]'s [affected.name] with \the [tool]."
+ )
- return TRUE
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/treat_necrosis/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/treat_necrosis/fail_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(!istype(tool, /obj/item/reagent_containers))
- return
+ return SURGERY_STEP_INCOMPLETE
var/obj/item/reagent_containers/container = tool
var/trans = container.reagents.trans_to(target, container.amount_per_transfer_from_this)
container.reagents.reaction(target, REAGENT_INGEST) //technically it's contact, but the reagents are being applied to internal tissue
- user.visible_message(" [user]'s hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!" , \
- " Your hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!")
+ user.visible_message(
+ " [user]'s hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!",
+ " 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
+
+ return SURGERY_STEP_RETRY
diff --git a/code/modules/surgery/plastic_surgery.dm b/code/modules/surgery/plastic_surgery.dm
index a75f35f7de3..e195a2fb071 100644
--- a/code/modules/surgery/plastic_surgery.dm
+++ b/code/modules/surgery/plastic_surgery.dm
@@ -1,26 +1,23 @@
/datum/surgery/plastic_surgery
name = "Plastic Surgery"
- steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/reshape_face, /datum/surgery_step/generic/cauterize)
- possible_locs = list("head")
-
-/datum/surgery/plastic_surgery/can_start(mob/user, mob/living/carbon/target)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/head/head = H.get_organ(user.zone_selected)
- if(!head)
- return FALSE
- if(head.is_robotic())
- return FALSE
- return TRUE
-
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/reshape_face,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_HEAD)
+ requires_organic_bodypart = TRUE
/datum/surgery_step/reshape_face
name = "reshape face"
- allowed_tools = list(/obj/item/scalpel = 100, /obj/item/kitchen/knife = 50, /obj/item/wirecutters = 35)
- time = 64
+ allowed_tools = list(TOOL_SCALPEL = 100, /obj/item/kitchen/knife = 50, /obj/item/wirecutters = 35)
+ time = 6.4 SECONDS
/datum/surgery_step/reshape_face/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
user.visible_message("[user] begins to alter [target]'s appearance.", "You begin to alter [target]'s appearance...")
+ return ..()
/datum/surgery_step/reshape_face/end_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/head/head = target.get_organ(target_zone)
@@ -79,12 +76,14 @@
var/newname = target.real_name //something about how the code handles names required that I use this instead of target.real_name
user.visible_message("[user] alters [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname]!", "You alter [oldname]'s appearance completely, [target.p_they()] [target.p_are()] now [newname].")
target.sec_hud_set_ID()
- return TRUE
+ return SURGERY_STEP_CONTINUE
/datum/surgery_step/reshape_face/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/head/head = target.get_organ(target_zone)
- user.visible_message(" [user]'s hand slips, tearing skin on [target]'s face with [tool]!", \
- " Your hand slips, tearing skin on [target]'s face with [tool]!")
+ user.visible_message(
+ " [user]'s hand slips, tearing skin on [target]'s face with [tool]!",
+ " Your hand slips, tearing skin on [target]'s face with [tool]!"
+ )
target.apply_damage(10, BRUTE, head, sharp = TRUE)
- return FALSE
+ return SURGERY_STEP_RETRY
diff --git a/code/modules/surgery/remove_embedded_object.dm b/code/modules/surgery/remove_embedded_object.dm
index 499e48882d5..07240c281a6 100644
--- a/code/modules/surgery/remove_embedded_object.dm
+++ b/code/modules/surgery/remove_embedded_object.dm
@@ -1,13 +1,29 @@
/datum/surgery/embedded_removal
name = "Removal of Embedded Objects"
- steps = list(/datum/surgery_step/generic/cut_open, /datum/surgery_step/generic/clamp_bleeders, /datum/surgery_step/generic/retract_skin, /datum/surgery_step/remove_object,/datum/surgery_step/generic/cauterize)
- possible_locs = list("head", "chest", "l_arm", "l_hand", "r_arm", "r_hand","r_leg", "r_foot", "l_leg", "l_foot", "groin")
+ steps = list(
+ /datum/surgery_step/generic/cut_open,
+ /datum/surgery_step/generic/clamp_bleeders,
+ /datum/surgery_step/generic/retract_skin,
+ /datum/surgery_step/proxy/open_organ,
+ /datum/surgery_step/remove_object,
+ /datum/surgery_step/generic/cauterize
+ )
+ possible_locs = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
/datum/surgery/embedded_removal/synth
- steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/remove_object,/datum/surgery_step/robotics/external/close_hatch)
+ steps = list(
+ /datum/surgery_step/robotics/external/unscrew_hatch,
+ /datum/surgery_step/robotics/external/open_hatch,
+ /datum/surgery_step/remove_object,
+ /datum/surgery_step/robotics/external/close_hatch
+ )
requires_organic_bodypart = FALSE
+
/datum/surgery/embedded_removal/can_start(mob/user, mob/living/carbon/human/target)
+ . = ..()
+ if(!.)
+ return FALSE
if(!istype(target))
return FALSE
var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
@@ -18,6 +34,9 @@
return TRUE
/datum/surgery/embedded_removal/synth/can_start(mob/user, mob/living/carbon/human/target)
+ . = ..()
+ if(!.)
+ return FALSE
if(!istype(target))
return FALSE
var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
@@ -30,37 +49,40 @@
/datum/surgery_step/remove_object
name = "Remove Embedded Objects"
- time = 32
- accept_hand = 1
+ time = 3.2 SECONDS
+ accept_hand = TRUE
var/obj/item/organ/external/L = null
+ repeatable = TRUE
/datum/surgery_step/remove_object/begin_step(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- L = surgery.organ_ref
+ L = surgery.organ_to_manipulate
if(L)
user.visible_message("[user] looks for objects embedded in [target]'s [parse_zone(user.zone_selected)].", "You look for objects embedded 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)]...")
+ return ..()
/datum/surgery_step/remove_object/end_step(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
if(L)
- if(ishuman(target))
- var/mob/living/carbon/human/H = target
- var/objects = 0
- for(var/obj/item/I in L.embedded_objects)
- objects++
- L.remove_embedded_object(I)
- I.forceMove(get_turf(H))
- if(!H.has_embedded_objects())
- H.clear_alert("embeddedobject")
+ var/mob/living/carbon/human/H = target
+ var/objects = 0
+ for(var/obj/item/I in L.embedded_objects)
+ objects++
+ L.remove_embedded_object(I)
+ I.forceMove(get_turf(H))
+ if(!H.has_embedded_objects())
+ H.clear_alert("embeddedobject")
- if(objects > 0)
- user.visible_message("[user] sucessfully removes [objects] objects from [H]'s [L]!", "You successfully remove [objects] objects from [H]'s [L.name].")
- else
- to_chat(user, "You find no objects embedded in [H]'s [L]!")
+ if(objects > 0)
+ user.visible_message("[user] sucessfully removes [objects] object\s from [H]'s [parse_zone(user.zone_selected)]!", "You successfully remove [objects] object\s from [H]'s [L.name].")
+ else
+ to_chat(user, "You find no objects embedded in [H]'s [parse_zone(user.zone_selected)]!")
else
to_chat(user, "You can't find [target]'s [parse_zone(user.zone_selected)], let alone any objects embedded in it!")
- return TRUE
+ return SURGERY_STEP_CONTINUE
+
+// this could use a fail_step...
diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm
index 2e62b5d365c..83031d61501 100644
--- a/code/modules/surgery/robotics.dm
+++ b/code/modules/surgery/robotics.dm
@@ -3,581 +3,648 @@
// COMMON STEPS //
//////////////////////////////////////////////////////////////////
-/datum/surgery/cybernetic_repair
+
+/datum/surgery_step/proxy/robotics
+
+/datum/surgery/robotics
+ requires_organic_bodypart = FALSE
+
+/datum/surgery/robotics/cybernetic_repair
name = "Cybernetic Repair"
- steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/robotics/external/repair)
- possible_locs = list("chest","head","l_arm", "l_hand","r_arm","r_hand","r_leg","r_foot","l_leg","l_foot","groin")
+ steps = list(
+ /datum/surgery_step/robotics/external/unscrew_hatch,
+ /datum/surgery_step/robotics/external/open_hatch,
+ /datum/surgery_step/proxy/robotics/repair_limb,
+ /datum/surgery_step/robotics/external/close_hatch
+ )
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
requires_organic_bodypart = FALSE
-/datum/surgery/cybernetic_repair/internal
+/datum/surgery/robotics/cybernetic_repair/internal
name = "Internal Component Manipulation"
- steps = list(/datum/surgery_step/robotics/external/unscrew_hatch,/datum/surgery_step/robotics/external/open_hatch,/datum/surgery_step/robotics/manipulate_robotic_organs)
- possible_locs = list("eyes", "mouth", "chest","head","groin","l_arm","r_arm")
- requires_organic_bodypart = FALSE
+ steps = list(
+ /datum/surgery_step/robotics/external/unscrew_hatch,
+ /datum/surgery_step/robotics/external/open_hatch,
+ // burn/brute are squished into here as well
+ /datum/surgery_step/proxy/robotics/manipulate_organs,
+ /datum/surgery_step/robotics/external/close_hatch
+ )
+ possible_locs = list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)
-/datum/surgery/cybernetic_amputation
+/datum/surgery/robotics/cybernetic_amputation
name = "Robotic Limb Amputation"
steps = list(/datum/surgery_step/robotics/external/amputate)
- possible_locs = list("chest","head","l_arm", "l_hand","r_arm","r_hand","r_leg","r_foot","l_leg","l_foot","groin")
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
+
+/datum/surgery/robotics/cybernetic_amputation/can_start(mob/user, mob/living/carbon/human/target)
+ . = ..()
+ if(!.)
+ return FALSE
+ var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
+ if(affected.limb_flags & CANNOT_DISMEMBER)
+ return FALSE
+ return TRUE
+
+/datum/surgery/robotics/cybernetic_customization
+ name = "Cybernetic Appearance Customization"
+ steps = list(
+ /datum/surgery_step/robotics/external/unscrew_hatch,
+ /datum/surgery_step/robotics/external/open_hatch,
+ /datum/surgery_step/robotics/external/customize_appearance,
+ /datum/surgery_step/robotics/external/close_hatch
+ )
+ possible_locs = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
+
+/datum/surgery/robotics/cybernetic_customization/can_start(mob/user, mob/living/carbon/human/target)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(ishuman(target))
+ var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
+ if(!(affected.status & ORGAN_ROBOT))
+ return FALSE
+ return TRUE
+
+// Intermediate repair surgeries, for fixing up internal maladies mid-surgery.
+
+/datum/surgery/intermediate/robotics
+ requires_bodypart = TRUE
requires_organic_bodypart = FALSE
-/datum/surgery/cybernetic_repair/can_start(mob/user, mob/living/carbon/target)
+/datum/surgery/intermediate/robotics/repair
+ possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_L_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_GROIN)
- if(istype(target,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(!affected.is_robotic())
- return FALSE
- return TRUE
+/datum/surgery/intermediate/robotics/repair/burn
+ steps = list(/datum/surgery_step/robotics/external/repair/burn)
-/datum/surgery/cybernetic_amputation/can_start(mob/user, mob/living/carbon/target)
- if(istype(target,/mob/living/carbon/human))
- var/mob/living/carbon/human/H = target
- var/obj/item/organ/external/affected = H.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(!affected.is_robotic())
- return FALSE
- if(affected.limb_flags & CANNOT_DISMEMBER)
- return FALSE
- return TRUE
+/datum/surgery/intermediate/robotics/repair/brute
+ steps = list(/datum/surgery_step/robotics/external/repair/brute)
+
+// Manipulate organs sub-surgeries
+
+/datum/surgery/intermediate/robotics/manipulate_organs
+ possible_locs = list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)
+
+/datum/surgery/intermediate/robotics/manipulate_organs/extract
+ steps = list(/datum/surgery_step/robotics/manipulate_robotic_organs/extract)
+
+/datum/surgery/intermediate/robotics/manipulate_organs/implant
+ steps = list(/datum/surgery_step/robotics/manipulate_robotic_organs/implant)
+
+/datum/surgery/intermediate/robotics/manipulate_organs/mend
+ steps = list(/datum/surgery_step/robotics/manipulate_robotic_organs/mend)
+/datum/surgery/intermediate/robotics/manipulate_organs/install_mmi
+ steps = list(/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi)
+ possible_locs = list(BODY_ZONE_CHEST)
-//to do, moar surgerys or condense down ala manipulate organs.
/datum/surgery_step/robotics
can_infect = FALSE
-/datum/surgery_step/robotics/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(!istype(target))
- return FALSE
- if(!hasorgans(target))
- return FALSE
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(affected == null)
- return FALSE
- return TRUE
+/datum/surgery_step/robotics/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ . = ..()
+ if(tool && tool.tool_behaviour)
+ tool.play_tool_sound(user, 30)
/datum/surgery_step/robotics/external
-
-/datum/surgery_step/robotics/external/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(!..())
- return FALSE
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(!affected.is_robotic())
- return FALSE
- return TRUE
+ name = "external robotics surgery"
/datum/surgery_step/robotics/external/unscrew_hatch
name = "unscrew hatch"
allowed_tools = list(
- /obj/item/screwdriver = 100,
+ TOOL_SCREWDRIVER = 100,
/obj/item/coin = 50,
/obj/item/kitchen/knife = 50
)
- time = 16
+ time = 1.6 SECONDS
-/datum/surgery_step/robotics/external/unscrew_hatch/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(..())
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(!affected)
- return FALSE
- return TRUE
-
-/datum/surgery_step/robotics/external/unscrew_hatch/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/unscrew_hatch/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)
- user.visible_message("[user] starts to unscrew the maintenance hatch on [target]'s [affected.name] with \the [tool].", \
- "You start to unscrew the maintenance hatch on [target]'s [affected.name] with \the [tool].")
- ..()
+ user.visible_message(
+ "[user] starts to unscrew the maintenance hatch on [target]'s [affected.name] with \the [tool].",
+ "You start to unscrew the maintenance hatch on [target]'s [affected.name] with \the [tool]."
+ )
+ return ..()
-/datum/surgery_step/robotics/external/unscrew_hatch/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/unscrew_hatch/end_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)
- user.visible_message(" [user] has opened the maintenance hatch on [target]'s [affected.name] with \the [tool].", \
- " You have opened the maintenance hatch on [target]'s [affected.name] with \the [tool].",)
- affected.open = 1
- return TRUE
+ user.visible_message(
+ " [user] has opened the maintenance hatch on [target]'s [affected.name] with \the [tool].",
+ " You have opened the maintenance hatch on [target]'s [affected.name] with \the [tool]."
+ )
+ affected.open = ORGAN_SYNTHETIC_LOOSENED
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/robotics/external/unscrew_hatch/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/unscrew_hatch/fail_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)
- user.visible_message(" [user]'s [tool.name] slips, failing to unscrew [target]'s [affected.name].", \
- " Your [tool] slips, failing to unscrew [target]'s [affected.name].")
- return FALSE
+ user.visible_message(
+ " [user]'s [tool.name] slips, failing to unscrew [target]'s [affected.name].",
+ " Your [tool] slips, failing to unscrew [target]'s [affected.name]."
+ )
+ return SURGERY_STEP_RETRY
/datum/surgery_step/robotics/external/open_hatch
name = "open hatch"
allowed_tools = list(
- /obj/item/retractor = 100,
- /obj/item/crowbar = 100,
- /obj/item/kitchen/utensil/ = 50
+ TOOL_RETRACTOR = 100,
+ TOOL_CROWBAR = 100,
+ /obj/item/kitchen/utensil = 50
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/robotics/external/open_hatch/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(..())
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(!affected)
- return FALSE
- return TRUE
-
-/datum/surgery_step/robotics/external/open_hatch/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/open_hatch/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)
- user.visible_message("[user] starts to pry open the maintenance hatch on [target]'s [affected.name] with \the [tool].",
- "You start to pry open the maintenance hatch on [target]'s [affected.name] with \the [tool].")
- ..()
+ user.visible_message(
+ "[user] starts to pry open the maintenance hatch on [target]'s [affected.name] with \the [tool].",
+ "You start to pry open the maintenance hatch on [target]'s [affected.name] with \the [tool]."
+ )
+ return ..()
-/datum/surgery_step/robotics/external/open_hatch/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/open_hatch/end_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)
- user.visible_message(" [user] opens the maintenance hatch on [target]'s [affected.name] with \the [tool].", \
- " You open the maintenance hatch on [target]'s [affected.name] with \the [tool]." )
- affected.open = 2
- return TRUE
+ user.visible_message(
+ " [user] opens the maintenance hatch on [target]'s [affected.name] with \the [tool].",
+ " You open the maintenance hatch on [target]'s [affected.name] with \the [tool]."
+ )
+ affected.open = ORGAN_SYNTHETIC_OPEN
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/robotics/external/open_hatch/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/open_hatch/fail_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)
- user.visible_message(" [user]'s [tool.name] slips, failing to open the hatch on [target]'s [affected.name].",
- " Your [tool] slips, failing to open the hatch on [target]'s [affected.name].")
- return FALSE
+ user.visible_message(
+ " [user]'s [tool.name] slips, failing to open the hatch on [target]'s [affected.name].",
+ " Your [tool] slips, failing to open the hatch on [target]'s [affected.name]."
+ )
+ return SURGERY_STEP_RETRY
/datum/surgery_step/robotics/external/close_hatch
name = "close hatch"
allowed_tools = list(
- /obj/item/retractor = 100,
- /obj/item/crowbar = 100,
+ TOOL_RETRACTOR = 100,
+ TOOL_CROWBAR = 100,
/obj/item/kitchen/utensil = 50
)
- time = 24
+ time = 2.4 SECONDS
-/datum/surgery_step/robotics/external/close_hatch/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(..())
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(!affected)
- return FALSE
- return TRUE
-
-/datum/surgery_step/robotics/external/close_hatch/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/close_hatch/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)
- user.visible_message("[user] begins to close and secure the hatch on [target]'s [affected.name] with \the [tool]." , \
- "You begin to close and secure the hatch on [target]'s [affected.name] with \the [tool].")
- ..()
+ user.visible_message(
+ "[user] begins to close and secure the hatch on [target]'s [affected.name] with \the [tool].",
+ "You begin to close and secure the hatch on [target]'s [affected.name] with \the [tool]."
+ )
+ return ..()
-/datum/surgery_step/robotics/external/close_hatch/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/close_hatch/end_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)
- user.visible_message(" [user] closes and secures the hatch on [target]'s [affected.name] with \the [tool].", \
- " You close and secure the hatch on [target]'s [affected.name] with \the [tool].")
- affected.open = 0
- return TRUE
+ user.visible_message(
+ " [user] closes and secures the hatch on [target]'s [affected.name] with \the [tool].",
+ " You close and secure the hatch on [target]'s [affected.name] with \the [tool]."
+ )
+ tool.play_tool_sound(target)
+ affected.open = ORGAN_CLOSED
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/robotics/external/close_hatch/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/close_hatch/fail_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)
user.visible_message(" [user]'s [tool.name] slips, failing to close the hatch on [target]'s [affected.name].",
" Your [tool.name] slips, failing to close the hatch on [target]'s [affected.name].")
- return FALSE
+ return SURGERY_STEP_RETRY
+
+/datum/surgery_step/robotics/external/close_hatch/premature
+ name = "close hatch prematurely"
+
+/datum/surgery_step/robotics/external/close_hatch/premature/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)
+ user.visible_message(
+ "[user] begins to close and secure the hatch on [target]'s [affected.name] with \the [tool].",
+ "You are interrupting the current surgery, beginning to close and secure the hatch on [target]'s [affected.name] with \the [tool]."
+ )
+ return ..()
+
/datum/surgery_step/robotics/external/repair
- name = "repair damage internally"
- allowed_tools = list()
+ name = "repair damage"
+ time = 3.2 SECONDS
- var/list/implements_finish = list(
- /obj/item/retractor = 100,
- /obj/item/crowbar = 100,
- /obj/item/kitchen/utensil = 50
- )
- var/list/implements_heal_burn = list(
+/datum/surgery_step/robotics/external/repair/burn
+ name = "repair burn damage"
+ allowed_tools = list(
/obj/item/stack/cable_coil = 100
)
- var/list/implements_heal_brute = list(
- /obj/item/weldingtool = 100,
+
+/datum/surgery_step/robotics/external/repair/burn/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)
+ var/obj/item/stack/cable_coil/C = tool
+ if(!(affected.burn_dam > 0))
+ to_chat(user, "\The [affected] does not have any burn damage!")
+ return SURGERY_BEGINSTEP_SKIP
+ if(!istype(C))
+ return SURGERY_BEGINSTEP_SKIP
+ if(C.get_amount() < 3)
+ to_chat(user, "You need three or more cable pieces to repair this damage.")
+ return SURGERY_BEGINSTEP_SKIP
+ C.use(3)
+ user.visible_message(
+ "[user] begins to splice new cabling into [target]'s [affected.name].",
+ "You begin to splice new cabling into [target]'s [affected.name]."
+ )
+ return ..()
+
+
+
+/datum/surgery_step/robotics/external/repair/burn/end_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)
+ user.visible_message(
+ " [user] finishes splicing cable into [target]'s [affected.name].",
+ " You finishes splicing new cable into [target]'s [affected.name]."
+ )
+ affected.heal_damage(0, rand(30, 50), 1, 1)
+ if(affected.burn_dam)
+ return SURGERY_STEP_RETRY_ALWAYS
+ else
+ return SURGERY_STEP_CONTINUE
+
+/datum/surgery_step/robotics/external/repair/burn/fail_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)
+ user.visible_message(
+ " [user] causes a short circuit in [target]'s [affected.name]!",
+ " You cause a short circuit in [target]'s [affected.name]!"
+ )
+ target.apply_damage(rand(5, 10), BURN, affected)
+ return SURGERY_STEP_RETRY
+
+/datum/surgery_step/robotics/external/repair/brute
+ name = "repair brute damage"
+ allowed_tools = list(
+ TOOL_WELDER = 100,
/obj/item/gun/energy/plasmacutter = 50
)
- var/current_type
- time = 32
-/datum/surgery_step/robotics/external/repair/New()
- ..()
- allowed_tools = implements_heal_burn + implements_heal_brute + implements_finish
-
-
-/datum/surgery_step/robotics/external/repair/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/repair/brute/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(!affected)
- return -1
+ if(!(affected.brute_dam > 0 || (affected.status & ORGAN_DISFIGURED)))
+ to_chat(user, "\The [affected] does not require welding repair!")
+ return SURGERY_BEGINSTEP_SKIP
+ if(tool.tool_behaviour == TOOL_WELDER)
+ if(!tool.use(1))
+ return SURGERY_BEGINSTEP_SKIP
+ user.visible_message(
+ "[user] begins to patch damage to [target]'s [affected.name]'s support structure with \the [tool].",
+ "You begin to patch damage to [target]'s [affected.name]'s support structure with \the [tool]."
+ )
+ return ..()
- if(implement_type in implements_heal_burn)
- current_type = "burn"
- var/obj/item/stack/cable_coil/C = tool
- if(!(affected.burn_dam > 0))
- to_chat(user, "\The [affected] does not have any burn damage!")
- return -1
- if(!istype(C))
- return -1
- if(!C.get_amount() >= 3)
- to_chat(user, "You need three or more cable pieces to repair this damage.")
- return -1
- C.use(3)
- user.visible_message("[user] begins to splice new cabling into [target]'s [affected.name]." , \
- "You begin to splice new cabling into [target]'s [affected.name].")
- else if(implement_type in implements_heal_brute)
- current_type = "brute"
- if(!(affected.brute_dam > 0 || (affected.status & ORGAN_DISFIGURED)))
- to_chat(user, "\The [affected] does not require welding repair!")
- return -1
- if(tool.tool_behaviour == TOOL_WELDER)
- if(!tool.use(1))
- return -1
- user.visible_message("[user] begins to patch damage to [target]'s [affected.name]'s support structure with \the [tool]." , \
- "You begin to patch damage to [target]'s [affected.name]'s support structure with \the [tool].")
-
- else if(implement_type in implements_finish)
- current_type = "finish"
- user.visible_message("[user] begins to close and secure the hatch on [target]'s [affected.name] with \the [tool]." , \
- "You begin to close and secure the hatch on [target]'s [affected.name] with \the [tool].")
+/datum/surgery_step/robotics/external/repair/brute/end_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)
+ user.visible_message(
+ " [user] finishes patching damage to [target]'s [affected.name] with \the [tool].",
+ " You finish patching damage to [target]'s [affected.name] with \the [tool]."
+ )
+ affected.heal_damage(rand(30, 50), 0, 1, 1)
+ affected.status &= ~ORGAN_DISFIGURED
+ if(affected.brute_dam)
+ // Keep trying until there's nothing left to patch up.
+ return SURGERY_STEP_RETRY_ALWAYS
else
- . = -1
- CRASH("Invalid tool: '[implement_type]'")
- ..()
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/robotics/external/repair/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/repair/brute/fail_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)
- switch(current_type)
- if("brute")
- user.visible_message(" [user] finishes patching damage to [target]'s [affected.name] with \the [tool].", \
- " You finish patching damage to [target]'s [affected.name] with \the [tool].")
- affected.heal_damage(rand(30,50),0,1,1)
- affected.status &= ~ORGAN_DISFIGURED
- if("burn")
- user.visible_message(" [user] finishes splicing cable into [target]'s [affected.name].", \
- " You finishes splicing new cable into [target]'s [affected.name].")
- affected.heal_damage(0,rand(30,50),1,1)
- if("finish")
- user.visible_message(" [user] closes and secures the hatch on [target]'s [affected.name] with \the [tool].", \
- " You close and secure the hatch on [target]'s [affected.name] with \the [tool].")
- affected.open = 0
- return TRUE
- return FALSE
+ user.visible_message(
+ "[user]'s [tool.name] slips, damaging the internal structure of [target]'s [affected.name].",
+ "Your [tool.name] slips, damaging the internal structure of [target]'s [affected.name]."
+ )
+ target.apply_damage(rand(5, 10), BURN, affected)
+ return SURGERY_STEP_RETRY
+
+/datum/surgery_step/proxy/robotics/manipulate_organs
+ name = "manipulate robotic organs (proxy)"
+
+ branches = list(
+ /datum/surgery/intermediate/robotics/manipulate_organs/extract,
+ /datum/surgery/intermediate/robotics/manipulate_organs/implant,
+ /datum/surgery/intermediate/robotics/manipulate_organs/install_mmi,
+ /datum/surgery/intermediate/robotics/manipulate_organs/mend,
+ /datum/surgery/intermediate/robotics/repair/brute,
+ /datum/surgery/intermediate/robotics/repair/burn
+ )
-/datum/surgery_step/robotics/external/repair/fail_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)
- switch(current_type)
- if("brute")
- user.visible_message(" [user]'s [tool.name] slips, damaging the internal structure of [target]'s [affected.name].",
- " Your [tool.name] slips, damaging the internal structure of [target]'s [affected.name].")
- target.apply_damage(rand(5,10), BURN, affected)
- if("burn")
- user.visible_message(" [user] causes a short circuit in [target]'s [affected.name]!",
- " You cause a short circuit in [target]'s [affected.name]!")
- target.apply_damage(rand(5,10), BURN, affected)
- if("finish")
- user.visible_message(" [user]'s [tool.name] slips, failing to close the hatch on [target]'s [affected.name].",
- " Your [tool.name] slips, failing to close the hatch on [target]'s [affected.name].")
- return FALSE
-///////condenseing remove/extract/repair here. /////////////
/datum/surgery_step/robotics/manipulate_robotic_organs
+ time = 3.2 SECONDS
- name = "internal part manipulation"
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/mend
+ name = "Mend cybernetic organs"
+ allowed_tools = list(
+ /obj/item/stack/nanopaste = 100,
+ TOOL_BONEGEL = 30,
+ TOOL_SCREWDRIVER = 70
+ )
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/mend/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(!hasorgans(target))
+ return
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+
+ var/found_damaged_organ = FALSE
+ for(var/obj/item/organ/internal/I in affected.internal_organs)
+ if(I && I.damage && I.is_robotic())
+ user.visible_message(
+ "[user] starts mending the damage to [target]'s [I.name]'s mechanisms.",
+ "You start mending the damage to [target]'s [I.name]'s mechanisms."
+ )
+ found_damaged_organ = TRUE
+
+ if(!found_damaged_organ)
+ to_chat(user, "There are no damaged components in [affected].")
+ return SURGERY_BEGINSTEP_SKIP
+
+ target.custom_pain("The pain in your [affected.name] is living hell!")
+ return ..()
+
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/mend/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(!hasorgans(target))
+ return
+ 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.damage)
+ if(I.is_robotic())
+ user.visible_message(
+ " [user] repairs [target]'s [I.name] with [tool].",
+ " You repair [target]'s [I.name] with [tool]."
+ )
+ I.damage = 0
+ I.surgeryize()
+ return ..()
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/mend/fail_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)
+
+ user.visible_message(" [user]'s hand slips, gumming up the mechanisms inside of [target]'s [affected.name] with \the [tool]!", \
+ " Your hand slips, gumming up the mechanisms inside of [target]'s [affected.name] with \the [tool]!")
+
+ target.adjustToxLoss(5)
+ affected.receive_damage(5)
+
+ for(var/obj/item/organ/internal/I in affected.internal_organs)
+ if(I)
+ I.receive_damage(rand(3, 5), 0)
+ return SURGERY_STEP_RETRY
+
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/implant
+ name = "Implant cybernetic organ"
+ allowed_tools = list(
+ /obj/item/organ/internal = 100
+ )
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/implant/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/internal/I = tool
+
+ if(!I.is_robotic())
+ to_chat(user, "You can only implant cybernetic organs.")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(target_zone != I.parent_organ || target.get_organ_slot(I.slot))
+ to_chat(user, "There is no room for [I] in [target]'s [parse_zone(target_zone)]!")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(I.damage > (I.max_damage * 0.75))
+ to_chat(user, " \The [I] is in no state to be transplanted.")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(target.get_int_organ(I))
+ to_chat(user, " \The [target] already has [I].")
+ return SURGERY_BEGINSTEP_SKIP
+
+ user.visible_message(
+ "[user] begins reattaching [target]'s [tool].",
+ "You start reattaching [target]'s [tool]."
+ )
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ target.custom_pain("Someone's rooting around in your [affected.name]!")
+ return ..()
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/implant/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/obj/item/organ/internal/I = tool
+
+ if(!user.canUnEquip(I, 0))
+ to_chat(user, "[I] is stuck to your hand, you can't put it in [target]!")
+ return SURGERY_STEP_INCOMPLETE
+
+ user.drop_item()
+ I.insert(target)
+ user.visible_message(
+ " [user] has reattached [target]'s [I].",
+ " You have reattached [target]'s [I]."
+ )
+ return ..()
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/implant/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ user.visible_message(
+ " [user]'s hand slips, disconnecting \the [tool].",
+ " Your hand slips, disconnecting \the [tool]."
+ )
+ return SURGERY_STEP_RETRY
+
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/extract
+ name = "extract cybernetic organ"
+ allowed_tools = list(TOOL_MULTITOOL = 100)
+ var/obj/item/organ/internal/I
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/extract/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ var/list/organs = target.get_organs_zone(target_zone)
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ if(!(affected && affected.is_robotic()))
+ return SURGERY_BEGINSTEP_SKIP
+ if(!length(organs))
+ to_chat(user, "There is no removeable organs in [target]'s [parse_zone(target_zone)]!")
+ return SURGERY_BEGINSTEP_SKIP
+ else
+ for(var/obj/item/organ/internal/O in organs)
+ O.on_find(user)
+ organs -= O
+ organs[O.name] = O
+
+ I = input("Remove which organ?", "Surgery", null, null) as null|anything in organs
+ if(I && user && target && user.Adjacent(target) && user.get_active_hand() == tool)
+ I = organs[I]
+ if(!I)
+ return SURGERY_BEGINSTEP_SKIP
+ user.visible_message(
+ "[user] starts to decouple [target]'s [I] with \the [tool].",
+ "You start to decouple [target]'s [I] with \the [tool]."
+ )
+
+ target.custom_pain("The pain in your [affected.name] is living hell!")
+ else
+ return SURGERY_BEGINSTEP_SKIP
+
+ return ..()
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/extract/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(!I || I.owner != target)
+ user.visible_message(
+ "[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!",
+ "You can't extract anything from [target]'s [parse_zone(target_zone)]!"
+ )
+ return SURGERY_STEP_CONTINUE
+
+ user.visible_message(
+ " [user] has decoupled [target]'s [I] with \the [tool].",
+ " You have decoupled [target]'s [I] with \the [tool]."
+ )
+
+ add_attack_logs(user, target, "Surgically removed [I.name]. INTENT: [uppertext(user.a_intent)]")
+ spread_germs_to_organ(I, user)
+ var/obj/item/thing = I.remove(target)
+ if(!istype(thing))
+ thing.forceMove(get_turf(target))
+ else
+ user.put_in_hands(thing)
+ return ..()
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/extract/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ user.visible_message(
+ " [user]'s hand slips, disconnecting \the [tool].",
+ " Your hand slips, disconnecting \the [tool]."
+ )
+
+ return SURGERY_STEP_RETRY
+
+
+/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi
+ name = "insert robotic brain"
allowed_tools = list(/obj/item/mmi = 100)
- var/implements_extract = list(/obj/item/multitool = 100)
- var/implements_mend = list( /obj/item/stack/nanopaste = 100,/obj/item/bonegel = 30, /obj/item/screwdriver = 70)
- var/implements_insert = list(/obj/item/organ/internal = 100)
- var/implements_finish =list(/obj/item/retractor = 100,/obj/item/crowbar = 100,/obj/item/kitchen/utensil = 50)
- var/current_type
- var/obj/item/organ/internal/I = null
- var/obj/item/organ/external/affected = null
- time = 32
-/datum/surgery_step/robotics/manipulate_robotic_organs/New()
- ..()
- allowed_tools = allowed_tools + implements_extract + implements_mend + implements_insert + implements_finish
+/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(target_zone != BODY_ZONE_CHEST)
+ to_chat(user, " You must target the chest cavity.")
+
+ return SURGERY_BEGINSTEP_SKIP
+
+ var/obj/item/organ/external/affected = target.get_organ(target_zone)
+ var/obj/item/mmi/M = tool
+
+ if(!affected)
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(!istype(M))
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(!M.brainmob || !M.brainmob.client || !M.brainmob.ckey || M.brainmob.stat >= DEAD)
+ to_chat(user, "That brain is not usable.")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(!affected.is_robotic())
+ to_chat(user, "You cannot install a computer brain into a meat enclosure.")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(!target.dna.species)
+ to_chat(user, "You have no idea what species this person is. Report this on the bug tracker.")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(!target.dna.species.has_organ["brain"])
+ to_chat(user, "You're pretty sure [target.dna.species.name_plural] don't normally have a brain.")
+ return SURGERY_BEGINSTEP_SKIP
+
+ if(target.get_int_organ(/obj/item/organ/internal/brain))
+ to_chat(user, "Your subject already has a brain.")
+ return SURGERY_BEGINSTEP_SKIP
+
+ user.visible_message(
+ "[user] starts installing \the [tool] into [target]'s [affected.name].",
+ "You start installing \the [tool] into [target]'s [affected.name]."
+ )
+ return ..()
+/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi/end_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)
+ user.visible_message(
+ " [user] has installed \the [tool] into [target]'s [affected.name].",
+ " You have installed \the [tool] into [target]'s [affected.name]."
+ )
+ var/obj/item/mmi/M = tool
-/datum/surgery_step/robotics/manipulate_robotic_organs/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- I = null
- affected = target.get_organ(target_zone)
- if(implement_type in implements_insert)
- current_type = "insert"
- var/obj/item/organ/internal/I = tool
-
- if(!I.is_robotic())
- to_chat(user, "You can only implant cybernetic organs.")
-
- if(target_zone != I.parent_organ || target.get_organ_slot(I.slot))
- to_chat(user, "There is no room for [I] in [target]'s [parse_zone(target_zone)]!")
- return -1
-
- if(I.damage > (I.max_damage * 0.75))
- to_chat(user, " \The [I] is in no state to be transplanted.")
- return -1
-
- if(target.get_int_organ(I))
- to_chat(user, " \The [target] already has [I].")
- return -1
-
- user.visible_message("[user] begins reattaching [target]'s [tool].", \
- "You start reattaching [target]'s [tool].")
- target.custom_pain("Someone's rooting around in your [affected.name]!")
- else if(istype(tool,/obj/item/mmi))
- current_type = "install"
-
- if(target_zone != "chest")
- to_chat(user, " You must target the chest cavity.")
-
- return -1
- var/obj/item/mmi/M = tool
-
-
- if(!affected)
- return -1
-
- if(!istype(M))
- return -1
-
- if(!M.brainmob || !M.brainmob.client || !M.brainmob.ckey || M.brainmob.stat >= DEAD)
- to_chat(user, "That brain is not usable.")
- return -1
-
- if(!affected.is_robotic())
- to_chat(user, "You cannot install a computer brain into a meat enclosure.")
- return -1
-
- if(!target.dna.species)
- to_chat(user, "You have no idea what species this person is. Report this on the bug tracker.")
- return -1
-
- if(!target.dna.species.has_organ["brain"])
- to_chat(user, "You're pretty sure [target.dna.species.name_plural] don't normally have a brain.")
- return -1
-
- if(target.get_int_organ(/obj/item/organ/internal/brain/))
- to_chat(user, "Your subject already has a brain.")
- return -1
-
- user.visible_message("[user] starts installing \the [tool] into [target]'s [affected.name].", \
- "You start installing \the [tool] into [target]'s [affected.name].")
-
- else if(implement_type in implements_extract)
- current_type = "extract"
- var/list/organs = target.get_organs_zone(target_zone)
- if(!(affected && affected.is_robotic()))
- return -1
- if(!organs.len)
- to_chat(user, "There is no removeable organs in [target]'s [parse_zone(target_zone)]!")
- return -1
- else
- for(var/obj/item/organ/internal/O in organs)
- O.on_find(user)
- organs -= O
- organs[O.name] = O
-
- I = input("Remove which organ?", "Surgery", null, null) as null|anything in organs
- if(I && user && target && user.Adjacent(target) && user.get_active_hand() == tool)
- I = organs[I]
- if(!I) return -1
- user.visible_message("[user] starts to decouple [target]'s [I] with \the [tool].", \
- "You start to decouple [target]'s [I] with \the [tool]." )
-
- target.custom_pain("The pain in your [affected.name] is living hell!")
- else
- return -1
-
- else if(implement_type in implements_mend)
- current_type = "mend"
- if(!hasorgans(target))
- return
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
-
- var/found_damaged_organ = FALSE
- for(var/obj/item/organ/internal/I in affected.internal_organs)
- if(I && I.damage && I.is_robotic())
- user.visible_message("[user] starts mending the damage to [target]'s [I.name]'s mechanisms.", \
- "You start mending the damage to [target]'s [I.name]'s mechanisms.")
- found_damaged_organ = TRUE
-
- if(!found_damaged_organ)
- to_chat(user, "There are no damaged components in [affected].")
- return -1
-
- target.custom_pain("The pain in your [affected.name] is living hell!")
-
- else if(implement_type in implements_finish)
- current_type = "finish"
- user.visible_message("[user] begins to close and secure the hatch on [target]'s [affected.name] with \the [tool]." , \
- "You begin to close and secure the hatch on [target]'s [affected.name] with \the [tool].")
-
-
- ..()
-
-/datum/surgery_step/robotics/manipulate_robotic_organs/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(current_type == "mend")
-
- if(!hasorgans(target))
- return
- for(var/obj/item/organ/internal/I in affected.internal_organs)
- if(I && I.damage)
- if(I.is_robotic())
- user.visible_message(" [user] repairs [target]'s [I.name] with [tool].", \
- " You repair [target]'s [I.name] with [tool]." )
- I.damage = 0
- I.surgeryize()
- else if(current_type == "insert")
- var/obj/item/organ/internal/I = tool
-
- if(!user.canUnEquip(I, 0))
- to_chat(user, "[I] is stuck to your hand, you can't put it in [target]!")
- return FALSE
-
- user.drop_item()
- I.insert(target)
- user.visible_message(" [user] has reattached [target]'s [I]." , \
- " You have reattached [target]'s [I].")
-
- else if(current_type == "install")
- user.visible_message(" [user] has installed \the [tool] into [target]'s [affected.name].", \
- " You have installed \the [tool] into [target]'s [affected.name].")
-
- var/obj/item/mmi/M = tool
-
- user.unEquip(tool)
- M.attempt_become_organ(affected,target)
-
- else if(current_type == "extract")
- if(I && I.owner == target)
- user.visible_message(" [user] has decoupled [target]'s [I] with \the [tool]." , \
- " You have decoupled [target]'s [I] with \the [tool].")
-
- add_attack_logs(user, target, "Surgically removed [I.name]. INTENT: [uppertext(user.a_intent)]")
- spread_germs_to_organ(I, user)
- var/obj/item/thing = I.remove(target)
- if(!istype(thing))
- thing.forceMove(get_turf(target))
- else
- user.put_in_hands(thing)
- else
- user.visible_message("[user] can't seem to extract anything from [target]'s [parse_zone(target_zone)]!",
- "You can't extract anything from [target]'s [parse_zone(target_zone)]!")
- else if(current_type == "finish")
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- user.visible_message(" [user] closes and secures the hatch on [target]'s [affected.name] with \the [tool].", \
- " You close and secure the hatch on [target]'s [affected.name] with \the [tool].")
- affected.open = 0
- affected.germ_level = 0
- return TRUE
- return FALSE
-
-/datum/surgery_step/robotics/manipulate_robotic_organs/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
-
- if(current_type == "mend")
- if(!hasorgans(target))
- return
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
-
- user.visible_message(" [user]'s hand slips, gumming up the mechanisms inside of [target]'s [affected.name] with \the [tool]!", \
- " Your hand slips, gumming up the mechanisms inside of [target]'s [affected.name] with \the [tool]!")
-
- target.adjustToxLoss(5)
- affected.receive_damage(5)
-
- for(var/obj/item/organ/internal/I in affected.internal_organs)
- if(I)
- I.receive_damage(rand(3,5),0)
-
- else if(current_type == "insert")
- user.visible_message(" [user]'s hand slips, disconnecting \the [tool].", \
- " Your hand slips, disconnecting \the [tool].")
-
- else if(current_type == "extract")
- user.visible_message(" [user]'s hand slips, disconnecting \the [tool].", \
- " Your hand slips, disconnecting \the [tool].")
-
- else if(current_type == "install")
- user.visible_message(" [user]'s hand slips!.", \
- " Your hand slips!")
- else if(current_type == "finish")
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- user.visible_message(" [user]'s [tool.name] slips, failing to close the hatch on [target]'s [affected.name].",
- " Your [tool.name] slips, failing to close the hatch on [target]'s [affected.name].")
- return FALSE
+ user.unEquip(tool)
+ M.attempt_become_organ(affected,target)
+ return ..()
+/datum/surgery_step/robotics/manipulate_robotic_organs/install_mmi/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ user.visible_message(
+ " [user]'s hand slips!.",
+ " Your hand slips!"
+ )
+ return SURGERY_STEP_RETRY
/datum/surgery_step/robotics/external/amputate
name = "remove robotic limb"
allowed_tools = list(
- /obj/item/multitool = 100)
+ TOOL_MULTITOOL = 100
+ )
- time = 100
+ time = 10 SECONDS
-/datum/surgery_step/robotics/external/amputate/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/amputate/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)
- user.visible_message("[user] starts to decouple [target]'s [affected.name] with \the [tool].", \
- "You start to decouple [target]'s [affected.name] with \the [tool]." )
+ user.visible_message(
+ "[user] starts to decouple [target]'s [affected.name] with \the [tool].",
+ "You start to decouple [target]'s [affected.name] with \the [tool]."
+ )
target.custom_pain("Your [affected.amputation_point] is being ripped apart!")
- ..()
+ return ..()
-/datum/surgery_step/robotics/external/amputate/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/amputate/end_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)
- user.visible_message(" [user] has decoupled [target]'s [affected.name] with \the [tool]." , \
- " You have decoupled [target]'s [affected.name] with \the [tool].")
+ user.visible_message(
+ " [user] has decoupled [target]'s [affected.name] with \the [tool].",
+ " You have decoupled [target]'s [affected.name] with \the [tool]."
+ )
add_attack_logs(user, target, "Surgically removed [affected.name] from. INTENT: [uppertext(user.a_intent)]")//log it
- var/atom/movable/thing = affected.droplimb(1,DROPLIMB_SHARP)
- if(istype(thing,/obj/item))
+ var/atom/movable/thing = affected.droplimb(1, DROPLIMB_SHARP)
+ if(istype(thing, /obj/item))
user.put_in_hands(thing)
- return TRUE
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/robotics/external/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/amputate/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
- user.visible_message(" [user]'s hand slips!", \
- " Your hand slips!")
- return FALSE
-
-/datum/surgery/cybernetic_customization
- name = "Cybernetic Appearance Customization"
- steps = list(/datum/surgery_step/robotics/external/unscrew_hatch, /datum/surgery_step/robotics/external/customize_appearance)
- possible_locs = list("head", "chest", "l_arm", "l_hand", "r_arm", "r_hand", "r_leg", "r_foot", "l_leg", "l_foot", "groin")
- requires_organic_bodypart = FALSE
-
-/datum/surgery/cybernetic_customization/can_start(mob/user, mob/living/carbon/human/target)
- if(ishuman(target))
- var/obj/item/organ/external/affected = target.get_organ(user.zone_selected)
- if(!affected)
- return FALSE
- if(!(affected.status & ORGAN_ROBOT))
- return FALSE
- return TRUE
+ user.visible_message(
+ " [user]'s hand slips!",
+ " Your hand slips!"
+ )
+ return SURGERY_STEP_RETRY
/datum/surgery_step/robotics/external/customize_appearance
name = "reprogram limb"
- allowed_tools = list(/obj/item/multitool = 100)
- time = 48
+ allowed_tools = list(TOOL_MULTITOOL = 100)
+ time = 4.8 SECONDS
-/datum/surgery_step/robotics/external/customize_appearance/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- if(..())
- var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(!affected)
- return FALSE
- return TRUE
-
-/datum/surgery_step/robotics/external/customize_appearance/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/customize_appearance/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)
- user.visible_message("[user] begins to reprogram the appearance of [target]'s [affected.name] with [tool]." , \
- "You begin to reprogram the appearance of [target]'s [affected.name] with [tool].")
- ..()
+ user.visible_message(
+ "[user] begins to reprogram the appearance of [target]'s [affected.name] with [tool].",
+ "You begin to reprogram the appearance of [target]'s [affected.name] with [tool]."
+ )
+ return ..()
-/datum/surgery_step/robotics/external/customize_appearance/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/customize_appearance/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/chosen_appearance = input(user, "Select the company appearance for this limb.", "Limb Company Selection") as null|anything in GLOB.selectable_robolimbs
if(!chosen_appearance)
- return FALSE
+ return SURGERY_STEP_INCOMPLETE
var/obj/item/organ/external/affected = target.get_organ(target_zone)
affected.robotize(chosen_appearance, convert_all = FALSE)
if(istype(affected, /obj/item/organ/external/head))
@@ -587,13 +654,15 @@
target.update_body()
target.updatehealth()
target.UpdateDamageIcon()
- user.visible_message(" [user] reprograms the appearance of [target]'s [affected.name] with [tool].", \
- " You reprogram the appearance of [target]'s [affected.name] with [tool].")
- affected.open = 0
- return TRUE
+ user.visible_message(
+ " [user] reprograms the appearance of [target]'s [affected.name] with [tool].",
+ " You reprogram the appearance of [target]'s [affected.name] with [tool]."
+ )
+ affected.open = ORGAN_CLOSED
+ return SURGERY_STEP_CONTINUE
-/datum/surgery_step/robotics/external/customize_appearance/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/robotics/external/customize_appearance/fail_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)
user.visible_message(" [user]'s [tool.name] slips, failing to reprogram [target]'s [affected.name].",
" Your [tool.name] slips, failing to reprogram [target]'s [affected.name].")
- return FALSE
+ return SURGERY_STEP_RETRY
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index b7678479abc..9da4313d171 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -1,37 +1,132 @@
///Datum Surgery Helpers//
-/datum/surgery
- var/name
- var/status = 1
- var/list/steps = list() //Steps in a surgery
- var/can_cancel = TRUE
- var/step_in_progress = FALSE
- var/list/in_progress = list() //Actively performing a Surgery
- var/location = "chest" //Surgery location
- var/requires_organic_bodypart = TRUE //Prevents you from performing an operation on robotic limbs
- var/list/possible_locs = list() //Multiple locations -- c0
- var/obj/item/organ/organ_ref //Operable body part
- var/current_organ = "organ"
- var/list/allowed_mob = list(/mob/living/carbon/human)
+
+/datum/surgery
+ /// Name of the surgery
+ var/name
+ /// Description of the surgery
+ var/desc
+ /// How far along we are in a surgery being performed.
+ var/step_number = 1
+ /// Surgical steps that go into performing this procedure
+ var/list/steps = list()
+ /// Whether this surgery can be stopped after the first step with a cautery
+ var/can_cancel = TRUE
+ /// If we're currently performing a step
+ var/step_in_progress = FALSE
+ /// Location of the surgery
+ var/location = BODY_ZONE_CHEST
+ /// Whether we can perform the surgery on a robotic limb
+ var/requires_organic_bodypart = TRUE //Prevents you from performing an operation on robotic limbs
+ /// Whether you need to remove clothes to perform the surgery
+ var/ignore_clothes = TRUE
+ /// Body locations this surgery can be performed on
+ var/list/possible_locs = list()
+ /// Surgery is only available if the target bodypart is present (or if it's missing)
+ var/requires_bodypart = TRUE
+ /// Surgery step speed modifier
+ var/speed_modifier = 0
+ /// Some surgeries might work on limbs that don't really exist (like chainsaw arms or flashlight eyes)
+ var/requires_real_bodypart = TRUE
+ /// Does the victim (patient) need to be lying down?
+ var/lying_required = TRUE
+ /// Can the surgery be performed on yourself?
+ var/self_operable = FALSE
+ /// Don't show this surgery if this type exists. Set to /datum/surgery if you want to hide a "base" surgery.
+ var/replaced_by
+ /// Mobs on which this can be performed
+ var/list/target_mobtypes = list(/mob/living/carbon/human)
+ /// Target of the surgery
+ var/mob/living/carbon/target
+ /// Body part the surgery is currently being performed on. Useful for checking to see if the organ desired is still in the body after the surgery has begun.
+ var/obj/item/organ/external/organ_to_manipulate
+ /// Whether or not this should be a selectable surgery at all
+ var/abstract = FALSE
+
+
+/datum/surgery/New(atom/surgery_target, surgery_location, surgery_bodypart)
+ ..()
+ if(!surgery_target)
+ return
+ target = surgery_target
+ target.surgeries += src
+ if(surgery_location)
+ location = surgery_location
+ if(!surgery_bodypart)
+ return
+ organ_to_manipulate = surgery_bodypart
+
+/datum/surgery/Destroy()
+ if(target)
+ target.surgeries -= src
+ target = null
+ organ_to_manipulate = null
+ return ..()
+
+/// Get whether the target organ is compatible with the current surgery.
+/datum/surgery/proc/is_organ_compatible(obj/item/organ/external/affecting)
+ if(!affecting || !istype(affecting))
+ return FALSE
+ return requires_organic_bodypart && affecting.is_robotic() || !requires_organic_bodypart && !affecting.is_robotic()
+
+
+/**
+ * Whether or not we can start this surgery.
+ * If this returns FALSE, this surgery won't show up in the list.
+ */
/datum/surgery/proc/can_start(mob/user, mob/living/carbon/target)
- // if FALSE surgery wont show up in list
- // put special restrictions here
+ if(replaced_by == /datum/surgery)
+ return FALSE
+
return TRUE
-
+/**
+ * Try to perform the next step in the current operation.
+ * This gets called in the attack chain, and as such returning FALSE in here means that the target
+ * will be hit with whatever's in your hand.
+ *
+ * The return is passed to the attack chain, so return TRUE to stop any sort of afterattack.
+ */
/datum/surgery/proc/next_step(mob/user, mob/living/carbon/target)
- if(step_in_progress) return
+ if(location != user.zone_selected)
+ return FALSE
- var/datum/surgery_step/S = get_surgery_step()
- if(S)
- if(S.try_op(user, target, user.zone_selected, user.get_active_hand(), src))
+ if(step_in_progress)
+ return FALSE
+
+ if(!self_operable && user == target)
+ return FALSE
+
+ var/datum/surgery_step/step = get_surgery_step()
+ if(step)
+ var/obj/item/tool = user.get_active_hand()
+ if(step.try_op(user, target, user.zone_selected, tool, src))
+ return TRUE
+ // If it's a surgery initiator, make sure it calls its attack chain down the line.
+ // Make sure this comes after the operation though, especially for things like scalpels
+ if(tool && tool.GetComponent(/datum/component/surgery_initiator))
+ return FALSE
+ if(tool && HAS_TRAIT(tool, TRAIT_SURGICAL))
+ to_chat(user, "This step requires a different tool!")
return TRUE
return FALSE
+/**
+ * Get the current surgery step we're on
+ */
/datum/surgery/proc/get_surgery_step()
- var/step_type = steps[status]
+ var/step_type = steps[step_number]
return new step_type
+/**
+ * Get the next step in the current surgery, or null if we're on the last one.
+ */
+/datum/surgery/proc/get_surgery_next_step()
+ if(step_number < length(steps))
+ var/step_type = steps[step_number + 1]
+ return new step_type
+ else
+ return null
/datum/surgery/proc/complete(mob/living/carbon/human/target)
target.surgeries -= src
@@ -41,146 +136,333 @@
/* SURGERY STEPS */
/datum/surgery_step
- // type path referencing tools that can be used for this step, and how well are they suited for it
- var/list/allowed_tools = null
- var/implement_type = null
-
- // duration of the step
- var/time = 10
-
var/name
- var/accept_hand = FALSE //does the surgery step require an open hand? If true, ignores implements. Compatible with accept_any_item.
+ /// Type path of tools that can be used to complete this step. Format is `path = probability of success`.
+ /// If the tool has a specific surgery tooltype, you can use that as a key as well.
+ var/list/allowed_tools = null
+ /// The current type of implement from allowed_tools in use. This has to be stored, as the typepath of the tool might not match the list type (such as if we're using tool behavior)
+ var/implement_type = null
+ /// does the surgery step require an open hand? If true, ignores implements. Compatible with accept_any_item.
+ var/accept_hand = FALSE
+ /// Does the surgery step accept any item? If true, ignores implements. Compatible with accept_hand.
var/accept_any_item = FALSE
+ /// duration of the step
+ var/time = 1 SECONDS
+ /// Is this step repeatable by using the same tool again after it's finished?
+ /// Make sure it isn't the last step, or it's used in a cancellable surgery. Otherwise, you might get stuck in a loop!
+ var/repeatable = FALSE
+ /// List of chems needed in the mob to complete the step. Even on success, this step will have no effect if the required chems aren't in the mob.
+ var/list/chems_needed = list()
+ /// Do we require any of the needed chems, or all of them?
+ var/require_all_chems = TRUE
+ /// Whether silicons ignore any probabilities (and are therefore "perfect" surgeons)
+ var/silicons_obey_prob = FALSE
+ /// How many times this step has been automatically repeated.
+ var/times_repeated = 0
// evil infection stuff that will make everyone hate me
- var/can_infect = FALSE
- //How much blood this step can get on surgeon. 1 - hands, 2 - full body.
- var/blood_level = 0
-/datum/surgery_step/proc/try_op(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ /// Whether this surgery step can cause an infection.
+ var/can_infect = FALSE
+ /// How much blood this step can get on surgeon. See SURGERY_BLOODSPREAD_* defines
+ var/blood_level = SURGERY_BLOODSPREAD_NONE
+
+/**
+ * Whether or not the tool being used is usable for the surgery.
+ * Checks both the tool itself as well as any tool behaviors defined in allowed_tools.
+ * Arguments:
+ * * user - User handling the tool.
+ * * tool - The tool (or item) being used in this surgery step.
+ * Returns TRUE if the tool can be used, or FALSE otherwise
+ */
+/datum/surgery_step/proc/is_valid_tool(mob/living/user, obj/item/tool)
+
var/success = FALSE
if(accept_hand)
if(!tool)
success = TRUE
- if(accept_any_item)
- if(tool && tool_quality(tool))
+ if(isrobot(user) && istype(tool, /obj/item/gripper/medical))
success = TRUE
- else
- for(var/path in allowed_tools)
- if(istype(tool, path))
- implement_type = path
- if(tool_quality(tool))
- success = TRUE
- if(success)
+ if(accept_any_item)
+ if(tool && tool_check(user, tool))
+ success = TRUE
+ else if(tool)
+ if(istype(tool, /obj/item/scalpel/laser/manager/debug))
+ // ok this is a meme but only use it if we'd actually be replacing a tool
+ for(var/key in allowed_tools)
+ if(!ispath(key) && (key in GLOB.surgery_tool_behaviors))
+ allowed_tools[tool.type] = 100
+ implement_type = tool.type
+ success = TRUE
+ break
+
+ for(var/key in allowed_tools)
+ var/match = FALSE
+ if(ispath(key) && istype(tool, key))
+ match = TRUE
+ else if(tool.tool_behaviour == key)
+ match = TRUE
+
+ if(match)
+ implement_type = key
+ if(tool_check(user, tool))
+ success = TRUE
+ break
+
+ return success
+
+/**
+ * Try to perform an operation on a user.
+ * Arguments:
+ * * user - The user performing the surgery.
+ * * target - The user on whom the surgery is being performed.
+ * * target_zone - the zone the user is targeting for the surgery.
+ * * tool - The object that the user is using to perform the surgery (optional)
+ * * surgery - The surgery being performed.
+ * Returns TRUE if the step was a success, or FALSE if the step can't be performed for some reason.
+ */
+/datum/surgery_step/proc/try_op(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+
+ if(is_valid_tool(user, tool))
if(target_zone == surgery.location)
- initiate(user, target, target_zone, tool, surgery)
+ if(get_location_accessible(target, target_zone) || surgery.ignore_clothes)
+ initiate(user, target, target_zone, tool, surgery)
+ else
+ to_chat(user, "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.
+
+ if(repeatable)
+ // you can continuously, manually, perform a step, so long as you continue to use the correct tool.
+ // if you use the wrong tool, though, it'll try to start the next surgery step with the tool you have in your hand.
+ // Note that this separate from returning the surgery_step_* defines in end/fail_step, which will automatically retry the surgery.
+ var/datum/surgery_step/next_step = surgery.get_surgery_next_step()
+ next_step.times_repeated = times_repeated + 1
+ if(next_step)
+ surgery.step_number++
+ if(next_step.try_op(user, target, user.zone_selected, user.get_active_hand(), surgery))
+ return TRUE
+ else
+ surgery.step_number--
+
return FALSE
-/datum/surgery_step/proc/initiate(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
- if(!can_use(user, target, target_zone, tool, surgery))
- return
+/**
+ * Determines whether or not this surgery step can repeat if its end/fail steps returned SURGERY_STEP_RETRY.
+ *
+ * Arguments:
+ * * user - mob performing the surgery
+ * * target - mob the surgery is being performed on
+ * * target_zone - body zone of the surgery
+ * * tool - tool used for the surgery
+ * * surgery - the operation this surgery step is a part of
+ *
+ * If this returns TRUE, the step will automatically retry. If not, the user will have to manually start the step again.
+ *
+ */
+/datum/surgery_step/proc/can_repeat(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ if(tool && istype(tool) && HAS_TRAIT(tool, TRAIT_ADVANCED_SURGICAL))
+ return TRUE
+ if(HAS_TRAIT(user, TRAIT_REPEATSURGERY))
+ return TRUE
+ return FALSE
+
+/**
+ * Initiate and really perform the surgery itself.
+ * This includes the main do-after and the checking of probabilities for successful surgeries.
+ * If try_to_fail is TRUE, then this surgery will be deliberately failed out of.
+ *
+ * Returns TRUE if the surgery should proceed to the next step, or FALSE otherwise.
+ */
+/datum/surgery_step/proc/initiate(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, try_to_fail = FALSE)
+
surgery.step_in_progress = TRUE
var/speed_mod = 1
+ var/advance = FALSE
+ var/retry = FALSE
+ var/prob_success = 100
- if(begin_step(user, target, target_zone, tool, surgery) == -1)
+ 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
+ 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
if(tool)
speed_mod = tool.toolspeed
- if(do_after(user, time * speed_mod, target = target))
- var/advance = FALSE
- var/prob_chance = 100
+ // Using an unoptimal tool slows down your surgery
+ var/implement_speed_mod = 1
+ if(implement_type)
+ implement_speed_mod = allowed_tools[implement_type] / 100.0
- if(implement_type) //this means it isn't a require nd or any item step.
- prob_chance = allowed_tools[implement_type]
- prob_chance *= get_location_modifier(target)
+ // They also have some interesting ways that surgery success/fail prob get evaluated, maybe worth looking at
+ speed_mod /= (get_location_modifier(target) * 1 + surgery.speed_modifier) * implement_speed_mod
+ var/modded_time = time * speed_mod
+ if(slowdown_immune(user))
+ modded_time = time
- if(!ispath(surgery.steps[surgery.status], /datum/surgery_step/robotics))//Repairing robotic limbs doesn't hurt, and neither does cutting someone out of a rig
- if(ishuman(target))
- var/mob/living/carbon/human/H = target //typecast to human
- var/pain_mod = get_pain_modifier(H)
- var/datum/status_effect/incapacitating/sleeping/S = H.IsSleeping()
- if(S?.voluntary)
- H.SetSleeping(0) // wake up people who are napping through the surgery
- if(pain_mod < 0.95)
- to_chat(H, "The surgery on your [parse_zone(target_zone)] is agonizingly painful, and rips you out of your shallow slumber!")
- else
- // Still wake people up, but they shouldn't be as alarmed.
- to_chat(H, "The surgery being performed on your [parse_zone(target_zone)] wakes you up.")
- prob_chance *= pain_mod //operating on conscious people is hard.
+ if(implement_type) // If this is set, we aren't in an allow_hand or allow_any_item step.
+ prob_success = allowed_tools[implement_type]
+ prob_success *= get_location_modifier(target)
- if(prob(prob_chance) || isrobot(user))
- if(end_step(user, target, target_zone, tool, surgery))
- advance = TRUE
+ if(do_after(user, modded_time, target = target))
+
+ 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
+
+ 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
- if(fail_step(user, target, target_zone, tool, surgery))
+ 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(advance)
- surgery.status++
- if(surgery.status > surgery.steps.len)
+ 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)
surgery.step_in_progress = FALSE
+ return advance
-//returns how well tool is suited for this step
-/datum/surgery_step/proc/tool_quality(obj/item/tool)
- for(var/T in allowed_tools)
- if(istype(tool,T))
- return allowed_tools[T]
- return 0
+/**
+ * Try to inflict pain during a surgery, a surgeon's dream come true.
+ * This will wake up the user if they're voluntarily sleeping.
+ *
+ * Returns the success rate that the user's amount of pain would deal, while also handling extra pain behavior.
+ */
+/datum/surgery_step/proc/deal_pain(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, try_to_fail = FALSE)
+ . = 1
+ if(!surgery.requires_organic_bodypart)
+ return
+ if(!ishuman(target))
+ return
-// Checks if this step applies to the user mob at all
-/datum/surgery_step/proc/is_valid_target(mob/living/carbon/human/target)
- if(!hasorgans(target))
- return FALSE
+ var/mob/living/carbon/human/H = target
+ var/pain_mod = get_pain_modifier(H)
+
+ // don't let people sit on the optable and sleep verb
+ var/datum/status_effect/incapacitating/sleeping/S = H.IsSleeping()
+ if(S?.voluntary)
+ H.SetSleeping(0) // wake up people who are napping through the surgery
+ if(pain_mod < 0.95)
+ to_chat(H, "The surgery on your [parse_zone(target_zone)] is agonizingly painful, and rips you out of your shallow slumber!")
+ else
+ // Still wake people up, but they shouldn't be as alarmed.
+ to_chat(H, "The surgery being performed on your [parse_zone(target_zone)] wakes you up.")
+ return pain_mod //operating on conscious people is hard.
+
+/**
+ * Get whether the tool should be usable in its current state. Useful for checks to see if a welder is on, for example.
+ *
+ * Arguments:
+ * * user - The user using the tool.
+ * * tool - The tool in use.
+ *
+ */
+/datum/surgery_step/proc/tool_check(mob/user, obj/item/tool)
return TRUE
-// checks whether this step can be applied with the given user and target
-/datum/surgery_step/proc/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- return TRUE
+/**
+ * Check for mobs that would be immune to surgery slowdowns/speedups.
+ */
+/datum/surgery_step/proc/slowdown_immune(mob/living/user)
+ if(isrobot(user))
+ return TRUE
+ return FALSE
// does stuff to begin the step, usually just printing messages. Moved germs transfering and bloodying here too
-/datum/surgery_step/proc/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
+/datum/surgery_step/proc/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ SHOULD_CALL_PARENT(TRUE)
if(ishuman(target))
var/obj/item/organ/external/affected = target.get_organ(target_zone)
if(can_infect && affected)
spread_germs_to_organ(affected, user, tool)
- if(ishuman(user) && !(istype(target,/mob/living/carbon/alien)) && prob(60))
+ if(ishuman(user) && !istype(target, /mob/living/carbon/alien) && prob(60))
var/mob/living/carbon/human/H = user
- if(blood_level)
- H.bloody_hands(target,0)
- if(blood_level > 1)
- H.bloody_body(target,0)
+ switch(blood_level)
+ if(SURGERY_BLOODSPREAD_HANDS)
+ H.bloody_hands(target, 0)
+ if(SURGERY_BLOODSPREAD_FULLBODY)
+ H.bloody_body(target)
return
-// does stuff to end the step, which is normally print a message + do whatever this step changes
-/datum/surgery_step/proc/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- return
+/**
+ * Finish a surgery step, performing anything that runs on the tail-end of a successful surgery.
+ * This runs if the surgery step passes the probability check, and therefore is a success.
+ *
+ * Should return SURGERY_STEP_CONTINUE to advance the surgery, though may return SURGERY_STEP_INCOMPLETE to keep the surgery step from advancing.
+ */
+/datum/surgery_step/proc/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ return SURGERY_STEP_CONTINUE
-// stuff that happens when the step fails
-/datum/surgery_step/proc/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery)
- return null
+/**
+ * Play out the failure state of a surgery step.
+ * This runs if the surgery step fails the probability check, the right chems weren't present, or if the user deliberately failed the surgery.
+ *
+ * Should return SURGERY_STEP_INCOMPLETE to prevent the surgery step from advancing, though may return SURGERY_STEP_CONTINUE to advance to the next step regardless.
+ */
+/datum/surgery_step/proc/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
+ return SURGERY_STEP_INCOMPLETE
-/proc/spread_germs_to_organ(obj/item/organ/E, mob/living/carbon/human/user, obj/item/tool)
- if(!istype(user) || !istype(E) || E.is_robotic() || E.sterile)
+/**
+ * Get the action that will be performed during this surgery step, in context of the surgery it is a part of.
+ *
+ * * surgery - A surgery in progress.
+ */
+/datum/surgery_step/proc/get_step_information(datum/surgery/surgery)
+ return name
+
+/**
+ * Spread some nasty germs to an organ.
+ *
+ * * target_organ - The organ to try spreading germs to.
+ * * user - The user who's manipulating the organ.
+ * * tool - The tool the user is using to mess with the organ.
+ */
+/proc/spread_germs_to_organ(obj/item/organ/target_organ, mob/living/carbon/human/user, obj/item/tool)
+ if(!istype(user) || !istype(target_organ) || target_organ.is_robotic() || target_organ.sterile)
return
var/germ_level = user.germ_level
- //germ spread from surgeon touching the patient
+ // germ spread from surgeon touching the patient
if(user.gloves)
germ_level = user.gloves.germ_level
- E.germ_level = max(germ_level, E.germ_level)
- spread_germs_by_incision(E, tool) //germ spread from environement to patient
+ target_organ.germ_level = max(germ_level, target_organ.germ_level)
+ spread_germs_by_incision(target_organ, tool) //germ spread from environement to patient
-/proc/spread_germs_by_incision(obj/item/organ/external/E,obj/item/tool)
+/**
+ * Spread germs directly from a tool.
+ *
+ * * E - An external organ being operated on.
+ * * tool - The tool performing the operation.
+ */
+/proc/spread_germs_by_incision(obj/item/organ/external/E, obj/item/tool)
if(!istype(E, /obj/item/organ/external))
return
@@ -195,13 +477,31 @@
if(AStar(E.loc, M.loc, /turf/proc/Distance, 2, simulated_only = 0))
germs++
- if(tool && tool.blood_DNA && tool.blood_DNA.len) //germs from blood-stained tools
+ if(tool && tool.blood_DNA && length(tool.blood_DNA)) //germs from blood-stained tools
germs += 30
- if(E.internal_organs.len)
- germs = germs / (E.internal_organs.len + 1) // +1 for the external limb this eventually applies to; let's not multiply germs now.
+ if(length(E.internal_organs))
+ germs = germs / (length(E.internal_organs) + 1) // +1 for the external limb this eventually applies to; let's not multiply germs now.
for(var/obj/item/organ/internal/O in E.internal_organs)
if(!O.is_robotic())
O.germ_level += germs
E.germ_level += germs
+
+/**
+ * Check that the target contains the chems we expect them to.
+ */
+/datum/surgery_step/proc/chem_check(mob/living/target)
+ if(!LAZYLEN(chems_needed))
+ return TRUE
+
+ if(require_all_chems)
+ . = TRUE
+ for(var/reagent in chems_needed)
+ if(!target.reagents.has_reagent(reagent))
+ return FALSE
+ else
+ . = FALSE
+ for(var/reagent in chems_needed)
+ if(target.reagents.has_reagent(reagent))
+ return TRUE
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index eeb8e92d14c..acefb7034ee 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -7,6 +7,11 @@
flags = CONDUCT
w_class = WEIGHT_CLASS_SMALL
origin_tech = "materials=1;biotech=1"
+ tool_behaviour = TOOL_RETRACTOR
+
+/obj/item/retractor/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
/obj/item/retractor/augment
desc = "Micro-mechanical manipulator for retracting stuff."
@@ -23,6 +28,11 @@
w_class = WEIGHT_CLASS_TINY
origin_tech = "materials=1;biotech=1"
attack_verb = list("attacked", "pinched")
+ tool_behaviour = TOOL_HEMOSTAT
+
+/obj/item/hemostat/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
/obj/item/hemostat/augment
desc = "Tiny servos power a pair of pincers to stop bleeding."
@@ -38,6 +48,11 @@
w_class = WEIGHT_CLASS_TINY
origin_tech = "materials=1;biotech=1"
attack_verb = list("burnt")
+ tool_behaviour = TOOL_CAUTERY
+
+/obj/item/cautery/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
/obj/item/cautery/augment
desc = "A heated element that cauterizes wounds."
@@ -56,6 +71,11 @@
w_class = WEIGHT_CLASS_NORMAL
origin_tech = "materials=1;biotech=1"
attack_verb = list("drilled")
+ tool_behaviour = TOOL_DRILL
+
+/obj/item/surgicaldrill/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
/obj/item/surgicaldrill/suicide_act(mob/user)
to_chat(viewers(user), pick("[user] is pressing [src] to [user.p_their()] temple and activating it! It looks like [user.p_theyre()] trying to commit suicide.",
@@ -86,6 +106,13 @@
origin_tech = "materials=1;biotech=1"
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
hitsound = 'sound/weapons/bladeslice.ogg'
+ tool_behaviour = TOOL_SCALPEL
+
+/obj/item/scalpel/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
+ AddComponent(/datum/component/surgery_initiator)
+
/obj/item/scalpel/suicide_act(mob/user)
to_chat(viewers(user), pick("[user] is slitting [user.p_their()] wrists with [src]! It looks like [user.p_theyre()] trying to commit suicide.",
@@ -132,6 +159,22 @@
icon_state = "scalpel_manager_on"
toolspeed = 0.2
+/obj/item/scalpel/laser/manager/Initialize(mapload)
+ . = ..()
+ // this one can automatically retry its steps, too!
+ ADD_TRAIT(src, TRAIT_ADVANCED_SURGICAL, ROUNDSTART_TRAIT)
+
+/obj/item/scalpel/laser/manager/debug
+ name = "debug IMS"
+ desc = "A wonder of modern medicine. This tool functions as any other sort of surgery tool, and finishes in only a fraction of the time. Hey, how'd you get your hands on this, anyway?"
+ toolspeed = 0.01
+
+/obj/item/scalpel/laser/manager/debug/attack_self(mob/user)
+ . = ..()
+ toolspeed = toolspeed == 0.5 ? 0.01 : 0.5
+ to_chat(user, "[src] is now set to toolspeed [toolspeed]")
+ playsound(src, 'sound/effects/pop.ogg', 50, 0) //Change the mode
+
/obj/item/circular_saw
name = "circular saw"
desc = "For heavy duty cutting."
@@ -149,6 +192,11 @@
materials = list(MAT_METAL=10000, MAT_GLASS=6000)
origin_tech = "biotech=1;combat=1"
attack_verb = list("attacked", "slashed", "sawed", "cut")
+ tool_behaviour = TOOL_SAW
+
+/obj/item/circular_saw/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
/obj/item/circular_saw/augment
desc = "A small but very fast spinning saw. Edges dulled to prevent accidental cutting inside of the surgeon."
@@ -165,6 +213,11 @@
w_class = WEIGHT_CLASS_SMALL
throwforce = 1.0
origin_tech = "materials=1;biotech=1"
+ tool_behaviour = TOOL_BONEGEL
+
+/obj/item/bonegel/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
/obj/item/bonegel/augment
toolspeed = 0.5
@@ -177,6 +230,11 @@
throwforce = 1.0
origin_tech = "materials=1;biotech=1"
w_class = WEIGHT_CLASS_SMALL
+ tool_behaviour = TOOL_FIXOVEIN
+
+/obj/item/FixOVein/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
/obj/item/FixOVein/augment
toolspeed = 0.5
@@ -192,6 +250,11 @@
w_class = WEIGHT_CLASS_SMALL
attack_verb = list("attacked", "hit", "bludgeoned")
origin_tech = "materials=1;biotech=1"
+ tool_behaviour = TOOL_BONESET
+
+/obj/item/bonesetter/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
/obj/item/bonesetter/augment
toolspeed = 0.5
diff --git a/paradise.dme b/paradise.dme
index 1c3b64e937d..1e9b12d81bc 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -91,6 +91,7 @@
#include "code\__DEFINES\station_goals.dm"
#include "code\__DEFINES\status_effects.dm"
#include "code\__DEFINES\subsystems.dm"
+#include "code\__DEFINES\surgery.dm"
#include "code\__DEFINES\text.dm"
#include "code\__DEFINES\tgs.dm"
#include "code\__DEFINES\tgui.dm"
@@ -360,6 +361,7 @@
#include "code\datums\components\spawner.dm"
#include "code\datums\components\spooky.dm"
#include "code\datums\components\squeak.dm"
+#include "code\datums\components\surgery_initiator.dm"
#include "code\datums\components\swarming.dm"
#include "code\datums\diseases\_disease.dm"
#include "code\datums\diseases\_MobProcs.dm"
@@ -2477,6 +2479,7 @@
#include "code\modules\station_goals\station_goal.dm"
#include "code\modules\store\items.dm"
#include "code\modules\store\store.dm"
+#include "code\modules\surgery\abstract_steps.dm"
#include "code\modules\surgery\bones.dm"
#include "code\modules\surgery\cavity_implant.dm"
#include "code\modules\surgery\core_removal.dm"