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"