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"