From 99a084fbd7431e42b0bb54f0627f819d3c8e4c95 Mon Sep 17 00:00:00 2001
From: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Date: Mon, 3 Aug 2020 08:48:34 +0200
Subject: [PATCH] [MIRROR] Componentizes Surgery-Initiation Devices (#174)
* Componentizes Surgery-Initiation Devices (#52295)
* meow
* doc moment
* meow
* Componentizes Surgery-Initiation Devices
Co-authored-by: girl <11748095+ExcessiveUseOfCobblestone@users.noreply.github.com>
---
code/datums/components/surgery_initiator.dm | 159 +++++++++++++++++++
code/game/objects/structures/bedsheet_bin.dm | 6 +-
code/modules/surgery/surgery_helpers.dm | 120 --------------
code/modules/surgery/tools.dm | 7 +-
tgstation.dme | 1 +
5 files changed, 167 insertions(+), 126 deletions(-)
create mode 100644 code/datums/components/surgery_initiator.dm
diff --git a/code/datums/components/surgery_initiator.dm b/code/datums/components/surgery_initiator.dm
new file mode 100644
index 00000000000..284400931eb
--- /dev/null
+++ b/code/datums/components/surgery_initiator.dm
@@ -0,0 +1,159 @@
+/**
+ *
+ * Allows parent (obj) to initiate surgeries.
+ *
+ */
+/datum/component/surgery_initiator
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ ///allows for post-selection manipulation of parent
+ var/datum/callback/after_select_cb
+
+/datum/component/surgery_initiator/Initialize(_after_select_cb)
+ . = ..()
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+ after_select_cb = _after_select_cb
+
+/datum/component/surgery_initiator/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/initiate_surgery_moment)
+
+/datum/component/surgery_initiator/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_ITEM_ATTACK)
+
+/datum/component/surgery_initiator/Destroy()
+ if(after_select_cb)
+ QDEL_NULL(after_select_cb)
+ return ..()
+
+ /**
+ *
+ * Does the surgery initiation.
+ *
+ */
+/datum/component/surgery_initiator/proc/initiate_surgery_moment(datum/source, atom/target, mob/user)
+ if(!isliving(target))
+ return
+ var/mob/living/livingtarget = target
+ var/mob/living/carbon/carbontarget
+ var/obj/item/bodypart/affecting
+ var/selected_zone = user.zone_selected
+ . = COMPONENT_ITEM_NO_ATTACK
+
+ if(iscarbon(livingtarget))
+ carbontarget = livingtarget
+ affecting = carbontarget.get_bodypart(check_zone(selected_zone))
+
+ var/datum/surgery/current_surgery
+
+ for(var/i_one in livingtarget.surgeries)
+ var/datum/surgery/surgeryloop = i_one
+ if(surgeryloop.location == selected_zone)
+ current_surgery = surgeryloop
+
+ if(!current_surgery)
+ var/list/all_surgeries = GLOB.surgeries_list.Copy()
+ var/list/available_surgeries = list()
+
+ for(var/i_two in all_surgeries)
+ var/datum/surgery/surgeryloop_two = i_two
+ if(!surgeryloop_two.possible_locs.Find(selected_zone))
+ continue
+ if(affecting)
+ if(!surgeryloop_two.requires_bodypart)
+ continue
+ if(surgeryloop_two.requires_bodypart_type && affecting.status != surgeryloop_two.requires_bodypart_type)
+ continue
+ if(surgeryloop_two.requires_real_bodypart && affecting.is_pseudopart)
+ continue
+ else if(carbontarget && surgeryloop_two.requires_bodypart) //mob with no limb in surgery zone when we need a limb
+ continue
+ if(surgeryloop_two.lying_required && (livingtarget.mobility_flags & MOBILITY_STAND))
+ continue
+ if(!surgeryloop_two.can_start(user, livingtarget))
+ continue
+ for(var/path in surgeryloop_two.target_mobtypes)
+ if(istype(livingtarget, path))
+ available_surgeries[surgeryloop_two.name] = surgeryloop_two
+ break
+
+ if(!available_surgeries.len)
+ return
+
+ var/pick_your_surgery = input("Begin which procedure?", "Surgery", null, null) as null|anything in sortList(available_surgeries)
+ if(pick_your_surgery && user?.Adjacent(livingtarget) && (parent in user))
+ var/datum/surgery/surgeryinstance_notonmob = available_surgeries[pick_your_surgery]
+
+ for(var/i_three in livingtarget.surgeries)
+ var/datum/surgery/surgeryloop_three = i_three
+ if(surgeryloop_three.location == selected_zone)
+ return //during the input() another surgery was started at the same location.
+
+ //we check that the surgery is still doable after the input() wait.
+ if(carbontarget)
+ affecting = carbontarget.get_bodypart(check_zone(selected_zone))
+ if(affecting)
+ if(!surgeryinstance_notonmob.requires_bodypart)
+ return
+ if(surgeryinstance_notonmob.requires_bodypart_type && affecting.status != surgeryinstance_notonmob.requires_bodypart_type)
+ return
+ else if(carbontarget && surgeryinstance_notonmob.requires_bodypart)
+ return
+ if(surgeryinstance_notonmob.lying_required && (livingtarget.mobility_flags & MOBILITY_STAND))
+ return
+ if(!surgeryinstance_notonmob.can_start(user, livingtarget))
+ return
+
+ if(surgeryinstance_notonmob.ignore_clothes || get_location_accessible(livingtarget, selected_zone))
+ var/datum/surgery/procedure = new surgeryinstance_notonmob.type(livingtarget, selected_zone, affecting)
+ user.visible_message("[user] drapes [parent] over [livingtarget]'s [parse_zone(selected_zone)] to prepare for surgery.", \
+ "You drape [parent] over [livingtarget]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name].")
+
+ log_combat(user, livingtarget, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
+ after_select_cb?.Invoke()
+ else
+ to_chat(user, "You need to expose [livingtarget]'s [parse_zone(selected_zone)] first!")
+
+ else if(!current_surgery.step_in_progress)
+ attempt_cancel_surgery(current_surgery, parent, livingtarget, user)
+
+ /**
+ *
+ * Does the surgery de-initiation.
+ *
+ */
+/datum/component/surgery_initiator/proc/attempt_cancel_surgery(datum/surgery/the_surgery, obj/item/the_item, mob/living/the_patient, mob/user)
+ var/selected_zone = user.zone_selected
+
+ if(the_surgery.status == 1)
+ the_patient.surgeries -= the_surgery
+ user.visible_message("[user] removes [the_item] from [the_patient]'s [parse_zone(selected_zone)].", \
+ "You remove [the_item] from [the_patient]'s [parse_zone(selected_zone)].")
+ qdel(the_surgery)
+ return
+
+ if(!the_surgery.can_cancel)
+ return
+
+ var/required_tool_type = TOOL_CAUTERY
+ var/obj/item/close_tool = user.get_inactive_held_item()
+ var/is_robotic = the_surgery.requires_bodypart_type == BODYPART_ROBOTIC
+
+ if(is_robotic)
+ required_tool_type = TOOL_SCREWDRIVER
+
+ if(iscyborg(user))
+ close_tool = locate(/obj/item/cautery) in user.held_items
+ if(!close_tool)
+ to_chat(user, "You need to equip a cautery in an inactive slot to stop [the_patient]'s surgery!")
+ return
+ else if(!close_tool || close_tool.tool_behaviour != required_tool_type)
+ to_chat(user, "You need to hold a [is_robotic ? "screwdriver" : "cautery"] in your inactive hand to stop [the_patient]'s surgery!")
+ return
+
+ if(the_surgery.operated_bodypart)
+ the_surgery.operated_bodypart.generic_bleedstacks -= 5
+
+ the_patient.surgeries -= the_surgery
+ user.visible_message("[user] closes [the_patient]'s [parse_zone(selected_zone)] with [close_tool] and removes [the_item].", \
+ "You close [the_patient]'s [parse_zone(selected_zone)] with [close_tool] and remove [the_item].")
+ qdel(the_surgery)
diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm
index 886d9bea95a..f21f0b27c43 100644
--- a/code/game/objects/structures/bedsheet_bin.dm
+++ b/code/game/objects/structures/bedsheet_bin.dm
@@ -24,9 +24,9 @@ LINEN BINS
dog_fashion = /datum/dog_fashion/head/ghost
var/list/dream_messages = list("white")
-/obj/item/bedsheet/attack(mob/living/M, mob/user)
- if(!attempt_initiate_surgery(src, M, user))
- ..()
+/obj/item/bedsheet/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/surgery_initiator, null)
/obj/item/bedsheet/attack_self(mob/user)
if(!user.CanReach(src)) //No telekenetic grabbing.
diff --git a/code/modules/surgery/surgery_helpers.dm b/code/modules/surgery/surgery_helpers.dm
index f2ae03c15de..71106d66ddc 100644
--- a/code/modules/surgery/surgery_helpers.dm
+++ b/code/modules/surgery/surgery_helpers.dm
@@ -1,122 +1,3 @@
-/proc/attempt_initiate_surgery(obj/item/I, mob/living/M, mob/user)
- if(!istype(M))
- return
-
- var/mob/living/carbon/C
- var/obj/item/bodypart/affecting
- var/selected_zone = user.zone_selected
-
- if(iscarbon(M))
- C = M
- affecting = C.get_bodypart(check_zone(selected_zone))
-
- 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)
- if(!S.requires_bodypart)
- continue
- if(S.requires_bodypart_type && affecting.status != S.requires_bodypart_type)
- continue
- if(S.requires_real_bodypart && affecting.is_pseudopart)
- continue
- else if(C && S.requires_bodypart) //mob with no limb in surgery zone when we need a limb
- continue
- if(S.lying_required && (M.mobility_flags & MOBILITY_STAND))
- continue
- if(!S.can_start(user, M))
- continue
- for(var/path in S.target_mobtypes)
- if(istype(M, path))
- available_surgeries[S.name] = S
- break
-
- if(!available_surgeries.len)
- return
-
- var/P = input("Begin which procedure?", "Surgery", null, null) as null|anything in sortList(available_surgeries)
- if(P && user && user.Adjacent(M) && (I in user))
- var/datum/surgery/S = available_surgeries[P]
-
- for(var/datum/surgery/other in M.surgeries)
- if(other.location == selected_zone)
- return //during the input() another surgery was started at the same location.
-
- //we check that the surgery is still doable after the input() wait.
- if(C)
- affecting = C.get_bodypart(check_zone(selected_zone))
- if(affecting)
- if(!S.requires_bodypart)
- return
- if(S.requires_bodypart_type && affecting.status != S.requires_bodypart_type)
- return
- else if(C && S.requires_bodypart)
- return
- if(S.lying_required && (M.mobility_flags & MOBILITY_STAND))
- return
- if(!S.can_start(user, M))
- return
-
- if(S.ignore_clothes || get_location_accessible(M, selected_zone))
- var/datum/surgery/procedure = new S.type(M, selected_zone, affecting)
- user.visible_message("[user] drapes [I] over [M]'s [parse_zone(selected_zone)] to prepare for surgery.", \
- "You drape [I] over [M]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name].")
-
- log_combat(user, M, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
- else
- to_chat(user, "You need to expose [M]'s [parse_zone(selected_zone)] first!")
-
- else if(!current_surgery.step_in_progress)
- attempt_cancel_surgery(current_surgery, I, M, user)
-
- return TRUE
-
-/proc/attempt_cancel_surgery(datum/surgery/S, obj/item/I, mob/living/M, mob/user)
- var/selected_zone = user.zone_selected
-
- if(S.status == 1)
- M.surgeries -= S
- user.visible_message("[user] removes [I] from [M]'s [parse_zone(selected_zone)].", \
- "You remove [I] from [M]'s [parse_zone(selected_zone)].")
- qdel(S)
- return
-
- if(S.can_cancel)
- var/required_tool_type = TOOL_CAUTERY
- var/obj/item/close_tool = user.get_inactive_held_item()
- var/is_robotic = S.requires_bodypart_type == BODYPART_ROBOTIC
-
- if(is_robotic)
- required_tool_type = TOOL_SCREWDRIVER
-
- if(iscyborg(user))
- close_tool = locate(/obj/item/cautery) in user.held_items
- if(!close_tool)
- to_chat(user, "You need to equip a cautery in an inactive slot to stop [M]'s surgery!")
- return
- else if(!close_tool || close_tool.tool_behaviour != required_tool_type)
- to_chat(user, "You need to hold a [is_robotic ? "screwdriver" : "cautery"] in your inactive hand to stop [M]'s surgery!")
- return
-
- if(S.operated_bodypart)
- S.operated_bodypart.generic_bleedstacks -= 5
-
- M.surgeries -= S
- user.visible_message("[user] closes [M]'s [parse_zone(selected_zone)] with [close_tool] and removes [I].", \
- "You close [M]'s [parse_zone(selected_zone)] with [close_tool] and remove [I].")
- qdel(S)
-
-
/proc/get_location_modifier(mob/M)
var/turf/T = get_turf(M)
if(locate(/obj/structure/table/optable, T))
@@ -190,4 +71,3 @@
return 0
return 1
-
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index defacbe1cd4..581a27eb84a 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -174,9 +174,10 @@
w_class = WEIGHT_CLASS_TINY
attack_verb = list("slapped")
-/obj/item/surgical_drapes/attack(mob/living/M, mob/user)
- if(!attempt_initiate_surgery(src, M, user))
- ..()
+/obj/item/surgical_drapes/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/surgery_initiator, null)
+
/obj/item/organ_storage //allows medical cyborgs to manipulate organs without hands
name = "organ storage bag"
diff --git a/tgstation.dme b/tgstation.dme
index 5314c581883..b91deb7e12f 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -452,6 +452,7 @@
#include "code\datums\components\stationloving.dm"
#include "code\datums\components\stationstuck.dm"
#include "code\datums\components\summoning.dm"
+#include "code\datums\components\surgery_initiator.dm"
#include "code\datums\components\swarming.dm"
#include "code\datums\components\tackle.dm"
#include "code\datums\components\tactical.dm"