diff --git a/code/__DEFINES/bodyparts.dm b/code/__DEFINES/bodyparts.dm
index 2f396cfbabc..ffeff072f45 100644
--- a/code/__DEFINES/bodyparts.dm
+++ b/code/__DEFINES/bodyparts.dm
@@ -67,6 +67,9 @@
#define BODYPART_OVERLAY_CSS_SUICIDE 3
#define BODYPART_OVERLAY_VOIDWALKER_CURSE 4
+/// This limb cannot be disabled via damage thresholds
+#define LIMB_NO_DISABLE -1
+
// Bodypart surgery state
/// An incision has been made into the skin
#define SURGERY_SKIN_CUT (1<<0)
@@ -174,3 +177,8 @@ DEFINE_BITFIELD(surgery_state, list(
#define BIOSTATE_HAS_VESSELS (BIO_BLOODED|BIO_WIRED)
/// Checks if a bodypart lacks both blood and wires, meaning it has no vessels to manipulate.
#define LIMB_HAS_VESSELS(limb) (limb?.biological_state & BIOSTATE_HAS_VESSELS)
+
+/// How much blood is lost from unclamped vessels?
+#define UNCLAMPED_VESSELS_BLEEDING 1.5
+/// How much blood is lost from clamped vessels or cut organs?
+#define CLAMPED_VESSELS_BLEEDING 0.2
diff --git a/code/__DEFINES/dcs/signals/signals_medical.dm b/code/__DEFINES/dcs/signals/signals_medical.dm
index de3814adb4a..4f79b79ddd7 100644
--- a/code/__DEFINES/dcs/signals/signals_medical.dm
+++ b/code/__DEFINES/dcs/signals/signals_medical.dm
@@ -1,11 +1,11 @@
/// From /datum/surgery_operation/try_perform(): (datum/surgery_operation/operation, atom/movable/operating_on, tool)
-#define COMSIG_LIVING_SURGERY_STARTED "mob_surgery_started"
+#define COMSIG_ATOM_SURGERY_STARTED "atom_surgery_started"
/// From /datum/surgery_operation/try_perform(): (datum/surgery_operation/operation, atom/movable/operating_on, tool)
-#define COMSIG_LIVING_SURGERY_FINISHED "mob_surgery_finished"
+#define COMSIG_ATOM_SURGERY_FINISHED "atom_surgery_finished"
/// From /datum/surgery_operation/success(): (datum/surgery_operation/operation, atom/movable/operating_on, tool)
-#define COMSIG_LIVING_SURGERY_SUCCESS "mob_surgery_step_success"
+#define COMSIG_ATOM_SURGERY_SUCCESS "atom_surgery_step_success"
/// From /datum/surgery_operation/failure(): (datum/surgery_operation/operation, atom/movable/operating_on, tool)
-#define COMSIG_LIVING_SURGERY_FAILED "mob_surgery_step_failed"
+#define COMSIG_ATOM_SURGERY_FAILED "atom_surgery_step_failed"
/// From /obj/item/shockpaddles/do_help, after the defib do_after is complete, but before any effects are applied: (mob/living/defibber, obj/item/shockpaddles/source)
#define COMSIG_DEFIBRILLATOR_PRE_HELP_ZAP "carbon_being_defibbed"
@@ -19,7 +19,7 @@
/// From /obj/item/shockpaddles/proc/do_disarm(), sent to the shock-ee in non-revival scenarios: (obj/item/shockpaddles/source)
#define COMSIG_HEARTATTACK_DEFIB "heartattack_defib"
-/// Sent from /mob/living/perform_surgery: (mob/living/patient, list/possible_operations)
+/// Sent from /mob/living/perform_surgery: (atom/movable/operating_on, list/possible_operations)
#define COMSIG_LIVING_OPERATING_ON "living_operating_on"
-/// Sent from /mob/living/perform_surgery: (mob/living/patient, list/possible_operations)
-#define COMSIG_LIVING_BEING_OPERATED_ON "living_being_operated_on"
+/// Sent from /mob/living/perform_surgery: (mob/living/surgeon, list/possible_operations)
+#define COMSIG_ATOM_BEING_OPERATED_ON "atom_being_operated_on"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
index 03f8a044085..a463510a438 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
@@ -60,6 +60,9 @@
/// Called from bodypart being removed /obj/item/bodypart/proc/drop_limb(mob/living/carbon/old_owner, special, dismembered)
#define COMSIG_BODYPART_REMOVED "bodypart_removed"
+/// Sent to a limb when something *attempts* to change its surgery state (old_state, new_state, changed_states)
+#define COMSIG_BODYPART_UPDATING_SURGERY_STATE "bodypart_updating_surgery_state"
+
///from /item/organ/proc/Insert() (/obj/item/organ/)
#define COMSIG_CARBON_GAIN_ORGAN "carbon_gain_organ"
///from /item/organ/proc/Remove() (/obj/item/organ/)
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
index 86bfadb0428..e6d71fefae7 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -318,7 +318,7 @@
/// From /obj/item/melee/baton/baton_effect(): (datum/source, mob/living/user, /obj/item/melee/baton)
#define COMSIG_MOB_BATONED "mob_batoned"
-/// From /obj/machinery/gibber/startgibbing(): (mob/living/user, /obj/machinery/gibber, list/results)
+/// From /obj/machinery/gibber/start_gibbing(): (mob/living/user, /obj/machinery/gibber, list/results)
#define COMSIG_LIVING_GIBBER_ACT "living_gibber_act"
/// From /mob/living/get_eye_protection() (list/reflist)
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index 58ffdc916ed..ad7f6bba889 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -287,3 +287,7 @@ DEFINE_BITFIELD(job_flags, list(
#define HEAD_OF_STAFF_MIND_TRAITS TRAIT_FAST_TYING, TRAIT_HIGH_VALUE_RANSOM
#define MEDICAL_MIND_TRAITS TRAIT_DESENSITIZED
#define SECURITY_MIND_TRAITS TRAIT_DESENSITIZED
+
+/// Statistically average probability of a random cop or cop-adjacent person consisting of meat of sus domesticus of suidae family,
+/// also known as swine or hogs, calculated by the university of random numbers
+#define PIG_COP_PROBABILITY 40
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index b2281a93e4e..c76410bb289 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -209,6 +209,7 @@
#define BODYPART_ID_PSYKER "psyker"
#define BODYPART_ID_MEAT "meat"
#define BODYPART_ID_PEG "peg"
+#define BODYPART_ID_BONE "bone"
//See: datum/species/var/digitigrade_customization
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
index dc54c139dba..1a7945b2bf7 100644
--- a/code/__DEFINES/surgery.dm
+++ b/code/__DEFINES/surgery.dm
@@ -117,6 +117,8 @@
#define OPERATION_MECHANIC (1<<10)
/// Hides the operation from autowiki generation
#define OPERATION_NO_WIKI (1<<11)
+/// This operation can be performed on a detached limb
+#define OPERATION_NO_PATIENT_REQUIRED (1<<12)
DEFINE_BITFIELD(operation_flags, list(
"AFFECTS MOOD" = OPERATION_AFFECTS_MOOD,
@@ -170,3 +172,8 @@ DEFINE_BITFIELD(operation_flags, list(
#define OPERATION_BURN_HEAL "burn_heal"
#define OPERATION_BRUTE_MULTIPLIER "brute_multiplier"
#define OPERATION_BURN_MULTIPLIER "burn_multiplier"
+
+/// Used in string formatting to print a limb as "John's right arm" or "the human right arm"
+#define FORMAT_LIMB_OWNER(limb) (limb.owner ? "[limb.owner]'s [limb.plaintext_zone]" : limb)
+/// Used in string formatting to print an organ's location as "John" or "the human chest"
+#define FORMAT_ORGAN_OWNER(organ) (organ.owner || organ.loc)
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index a3f7c0738b4..33510904c75 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -470,8 +470,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_PERCEPTUAL_TRAUMA_BYPASS "trauma_bypass"
/// mob is immune to hallucinations
#define TRAIT_HALLUCINATION_IMMUNE "hallucination_immune"
-/// Increases chance of getting special traumas, makes them harder to cure
-#define TRAIT_SPECIAL_TRAUMA_BOOST "special_trauma_boost"
//---- Brain trauma resists
/// Unable to gain any brain trauma whatsoever
@@ -977,6 +975,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_DUCT_TAPE_UNREPAIRABLE "duct_tape_unrepairable"
/// An item is ALWAYS considered baseline reachable and will pipe into CanBeReached().
#define TRAIT_SKIP_BASIC_REACH_CHECK "skip_basic_reach_check"
+/// Increases chance of this brain getting special traumas, makes them harder to cure
+#define TRAIT_SPECIAL_TRAUMA_BOOST "special_trauma_boost"
//---- Heretic Traits
/// Hides the heretic overlay that outs them as the heretic
diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm
index f68a242e565..153e7c389f0 100644
--- a/code/__DEFINES/traits/sources.dm
+++ b/code/__DEFINES/traits/sources.dm
@@ -329,3 +329,9 @@
/// Permanent trait from an overdose effect
#define OVERDOSE_TRAIT "overdose"
+
+/// Trait gained from bioware surgeries
+#define BIOWARE_TRAIT "bioware"
+
+/// Trait gained from skeleton "limbs", such as husking from a butchered chest
+#define SKELETON_TRAIT "skeleton"
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 2bf0398d272..f099ddc3587 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -554,7 +554,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SPARRING" = TRAIT_SPARRING,
"TRAIT_SPAWNED_MOB" = TRAIT_SPAWNED_MOB,
"TRAIT_SPEAKS_CLEARLY" = TRAIT_SPEAKS_CLEARLY,
- "TRAIT_SPECIAL_TRAUMA_BOOST" = TRAIT_SPECIAL_TRAUMA_BOOST,
"TRAIT_SPELLS_LOTTERY" = TRAIT_SPELLS_LOTTERY,
"TRAIT_SPIDER_CONSUMED" = TRAIT_SPIDER_CONSUMED,
"TRAIT_SPIRITUAL" = TRAIT_SPIRITUAL,
@@ -781,6 +780,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/obj/item/organ/brain = list(
"TRAIT_BRAIN_DAMAGE_NODEATH" = TRAIT_BRAIN_DAMAGE_NODEATH,
+ "TRAIT_SPECIAL_TRAUMA_BOOST" = TRAIT_SPECIAL_TRAUMA_BOOST,
),
/obj/item/organ/liver = list(
"TRAIT_BALLMER_SCIENTIST" = TRAIT_BALLMER_SCIENTIST,
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index 5fe6c87c6fa..748e64ef643 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -290,7 +290,6 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_SOOTHED_THROAT" = TRAIT_SOOTHED_THROAT,
"TRAIT_SOOTHED_HEADACHE" = TRAIT_SOOTHED_HEADACHE,
"TRAIT_SPACEWALK" = TRAIT_SPACEWALK,
- "TRAIT_SPECIAL_TRAUMA_BOOST" = TRAIT_SPECIAL_TRAUMA_BOOST,
"TRAIT_SPIRITUAL" = TRAIT_SPIRITUAL,
"TRAIT_STABLEHEART" = TRAIT_STABLEHEART,
"TRAIT_STABLELIVER" = TRAIT_STABLELIVER,
@@ -406,6 +405,9 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_LIVING_HEART" = TRAIT_LIVING_HEART,
"TRAIT_USED_ORGAN" = TRAIT_USED_ORGAN,
),
+ /obj/item/organ/brain = list(
+ "TRAIT_SPECIAL_TRAUMA_BOOST" = TRAIT_SPECIAL_TRAUMA_BOOST,
+ ),
/obj/item/organ/liver = list(
"TRAIT_BALLMER_SCIENTIST" = TRAIT_BALLMER_SCIENTIST,
"TRAIT_COMEDY_METABOLISM" = TRAIT_COMEDY_METABOLISM,
diff --git a/code/datums/components/bayonet_attachable.dm b/code/datums/components/bayonet_attachable.dm
index 9841185ea7f..8da8d61aac3 100644
--- a/code/datums/components/bayonet_attachable.dm
+++ b/code/datums/components/bayonet_attachable.dm
@@ -100,7 +100,8 @@
if (isnull(bayonet) || !user.combat_mode)
return NONE
- INVOKE_ASYNC(bayonet, TYPE_PROC_REF(/obj/item, melee_attack_chain), user, target, modifiers)
+ // RMB is used for butchering, and bayonets are usually are knives
+ INVOKE_ASYNC(bayonet, TYPE_PROC_REF(/obj/item, melee_attack_chain), user, target, modifiers - RIGHT_CLICK)
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/bayonet_attachable/proc/on_attackby(obj/item/source, obj/item/attacking_item, mob/attacker, list/modifiers)
diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm
index 1f432efc734..dd14cd4dcde 100644
--- a/code/datums/components/butchering.dm
+++ b/code/datums/components/butchering.dm
@@ -27,72 +27,357 @@
src.effectiveness = effectiveness
src.bonus_modifier = bonus_modifier
src.butcher_sound = butcher_sound
- if(disabled)
+ if (disabled)
src.butchering_enabled = FALSE
src.can_be_blunt = can_be_blunt
src.butcher_callback = butcher_callback
- if(isitem(parent))
- RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(onItemAttack))
/datum/component/butchering/Destroy(force)
butcher_callback = null
return ..()
-/datum/component/butchering/proc/onItemAttack(obj/item/source, mob/living/M, mob/living/user)
+/datum/component/butchering/RegisterWithParent()
+ if (!isitem(parent))
+ return
+ var/obj/item/item_parent = parent
+ item_parent.item_flags |= ITEM_HAS_CONTEXTUAL_SCREENTIPS
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(on_item_attack))
+ RegisterSignal(parent, COMSIG_ITEM_INTERACTING_WITH_ATOM, PROC_REF(on_item_interaction))
+ RegisterSignal(parent, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, PROC_REF(add_item_context))
+
+/datum/component/butchering/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_ITEM_ATTACK, COMSIG_ITEM_INTERACTING_WITH_ATOM))
+
+/datum/component/butchering/proc/on_item_attack(obj/item/source, mob/living/victim, mob/living/user, list/modifiers)
SIGNAL_HANDLER
- if(!user.combat_mode)
- return
- if(M.stat == DEAD && (M.butcher_results || M.guaranteed_butcher_results)) //can we butcher it?
- if(butchering_enabled && (can_be_blunt || source.get_sharpness()))
- INVOKE_ASYNC(src, PROC_REF(startButcher), source, M, user)
- return COMPONENT_CANCEL_ATTACK_CHAIN
-
- if(ishuman(M) && source.force && source.get_sharpness())
- var/mob/living/carbon/human/H = M
- if((user.pulling == H && user.grab_state >= GRAB_AGGRESSIVE) && user.zone_selected == BODY_ZONE_HEAD) // Only aggressive grabbed can be sliced.
- if(HAS_TRAIT(user, TRAIT_PACIFISM))
- to_chat(user, span_warning("You don't want to harm other living beings!"))
- return COMPONENT_CANCEL_ATTACK_CHAIN
-
- if(H.has_status_effect(/datum/status_effect/neck_slice))
- return
-
- INVOKE_ASYNC(src, PROC_REF(startNeckSlice), source, H, user)
- return COMPONENT_CANCEL_ATTACK_CHAIN
-
-/datum/component/butchering/proc/startButcher(obj/item/source, mob/living/M, mob/living/user)
- to_chat(user, span_notice("You begin to butcher [M]..."))
- playsound(M.loc, butcher_sound, 50, TRUE, -1)
- if(do_after(user, speed, M) && M.Adjacent(source))
- on_butchering(user, M)
-
-/datum/component/butchering/proc/startNeckSlice(obj/item/source, mob/living/carbon/human/H, mob/living/user)
- if(DOING_INTERACTION_WITH_TARGET(user, H))
- to_chat(user, span_warning("You're already interacting with [H]!"))
+ if (!source.get_sharpness() && !can_be_blunt)
return
- user.visible_message(span_danger("[user] is slitting [H]'s throat!"), \
- span_danger("You start slicing [H]'s throat!"), \
- span_hear("You hear a cutting noise!"), ignored_mobs = H)
- H.show_message(span_userdanger("Your throat is being slit by [user]!"), MSG_VISUAL, \
- span_userdanger("Something is cutting into your neck!"), NONE)
- log_combat(user, H, "attempted throat slitting", source)
+ if (!user.combat_mode)
+ return
- playsound(H.loc, butcher_sound, 50, TRUE, -1)
- if(do_after(user, clamp(500 / source.force, 30, 100), H) && H.Adjacent(source))
- if(H.has_status_effect(/datum/status_effect/neck_slice))
- user.show_message(span_warning("[H]'s neck has already been already cut, you can't make the bleeding any worse!"), MSG_VISUAL, \
- span_warning("Their neck has already been already cut, you can't make the bleeding any worse!"))
+ // Can we butcher it?
+ if (victim.stat == DEAD && (victim.butcher_results || victim.guaranteed_butcher_results) && butchering_enabled)
+ INVOKE_ASYNC(src, PROC_REF(start_butcher), source, victim, user)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+ if (!ishuman(victim) || !source.force)
+ return
+
+ if (LAZYACCESS(modifiers, RIGHT_CLICK) && butchering_enabled)
+ INVOKE_ASYNC(src, PROC_REF(butcher_human), source, victim, user)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+ // Neckslicing requires aggro grabs
+ if (user.pulling != victim || user.grab_state < GRAB_AGGRESSIVE || user.zone_selected != BODY_ZONE_HEAD)
+ return
+
+ if (HAS_TRAIT(user, TRAIT_PACIFISM))
+ to_chat(user, span_warning("You don't want to harm other living beings!"))
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+ if (victim.has_status_effect(/datum/status_effect/neck_slice))
+ return
+
+ INVOKE_ASYNC(src, PROC_REF(start_neck_slice), source, victim, user)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+/datum/component/butchering/proc/on_item_interaction(obj/item/source, mob/living/user, atom/target)
+ SIGNAL_HANDLER
+
+ if (!source.get_sharpness() && !can_be_blunt)
+ return
+
+ if (!user.combat_mode || !butchering_enabled)
+ return
+
+ if (isbodypart(target))
+ INVOKE_ASYNC(src, PROC_REF(butcher_limb), source, target, user)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+/datum/component/butchering/proc/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
+ SIGNAL_HANDLER
+
+ if (!source.get_sharpness() && !can_be_blunt)
+ return NONE
+
+ if (!butchering_enabled)
+ return NONE
+
+ if (isbodypart(target))
+ context[SCREENTIP_CONTEXT_LMB] = "(Combat Mode) Butcher limb"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ if (!isliving(target))
+ return NONE
+
+ var/mob/living/victim = target
+ if (victim.stat != DEAD && (victim.butcher_results || victim.guaranteed_butcher_results))
+ context[SCREENTIP_CONTEXT_LMB] = "(Combat Mode) Butcher"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ if (ishuman(victim))
+ context[SCREENTIP_CONTEXT_RMB] = "(Combat Mode) Butcher"
+ return CONTEXTUAL_SCREENTIP_SET
+
+ return NONE
+
+/datum/component/butchering/proc/butcher_limb(obj/item/source, obj/item/bodypart/target, mob/living/user)
+ target.add_fingerprint(user)
+ if (LIMB_HAS_SKIN(target) && !HAS_ANY_SURGERY_STATE(target.surgery_state, SURGERY_SKIN_CUT | SURGERY_SKIN_OPEN))
+ to_chat(user, span_warning("[target]'s skin is still intact!"))
+ return
+
+ if (LIMB_HAS_BONES(target) && !HAS_ANY_SURGERY_STATE(target.surgery_state, SURGERY_BONE_DRILLED | SURGERY_BONE_SAWED))
+ // We need to gut the limb before turning it into meat, otherwise just cut around the bone I guess
+ if (length(target.contents))
+ to_chat(user, span_warning("[target]'s bones are still intact!"))
return
- H.visible_message(span_danger("[user] slits [H]'s throat!"), \
- span_userdanger("[user] slits your throat..."))
- log_combat(user, H, "wounded via throat slitting", source)
- H.apply_damage(source.force, BRUTE, BODY_ZONE_HEAD, wound_bonus=CANT_WOUND) // easy tiger, we'll get to that in a sec
- var/obj/item/bodypart/slit_throat = H.get_bodypart(BODY_ZONE_HEAD)
- if (H.cause_wound_of_type_and_severity(WOUND_SLASH, slit_throat, WOUND_SEVERITY_CRITICAL))
- H.apply_status_effect(/datum/status_effect/neck_slice)
+ var/speed_modifier = 1
+ if (!target.owner)
+ speed_modifier = 0.5
+ else if (target.owner.stat < UNCONSCIOUS)
+ speed_modifier = 1.5 // yeowch
+
+ var/limb_descriptor = (target.owner ? "[target.owner]'s [target.plaintext_zone]" : target)
+ // Okay *hopefully* they're already dead at this point
+ if (target.body_zone == BODY_ZONE_CHEST && target.owner)
+ // Butchering the chest gibs the victim
+ limb_descriptor = target.owner
+
+ if (target.owner)
+ log_combat(user, target.owner, "attempted to butcher", source)
+
+ if (length(target.contents))
+ user.visible_message(span_warning("[user] begins to gut [limb_descriptor]!"), span_notice("You begin to gut [limb_descriptor]..."), ignored_mobs = target.owner)
+ if (target.owner)
+ to_chat(target.owner, span_warning("[user] begins to gut your [target.plaintext_zone]!"))
+
+ playsound(target.loc, butcher_sound, 50, TRUE, -1)
+ if (!do_after(user, speed * speed_modifier, target.owner || target))
+ return
+ target.drop_organs(user, TRUE)
+ return
+
+ if (!length(target.butcher_drops))
+ to_chat(user, span_warning("There is nothing left inside [limb_descriptor]!"))
+ return
+
+ if (target.body_zone == BODY_ZONE_CHEST && target.owner)
+ // Cannot butcher the chest until we hack off all the other limbs
+ for (var/obj/item/bodypart/limb as anything in target.owner.bodyparts)
+ if (limb != target && limb.butcher_drops && limb.butcher_replacement)
+ to_chat(user, span_warning("You need to butcher all other limbs first!"))
+ return
+
+ user.visible_message(span_warning("[user] begins to cut [limb_descriptor] apart!"), span_notice("You begin to cut [limb_descriptor] apart..."), ignored_mobs = target.owner)
+ if (target.owner)
+ to_chat(target.owner, span_warning("[user] begins to cut your [target.plaintext_zone] apart!"))
+
+ playsound(target.loc, butcher_sound, 50, TRUE, -1)
+ if (!do_after(user, speed * speed_modifier, target.owner || target))
+ return
+
+ var/list/results = list()
+ var/turf/drop_loc = target.owner?.drop_location() || target.drop_location()
+ var/bonus_chance = max(0, (effectiveness - 100) + bonus_modifier) //so 125 total effectiveness = 25% extra chance
+
+ var/list/failures = list()
+ var/list/bonuses = list()
+
+ for (var/obj/item/drop_type as anything in target.butcher_drops)
+ var/amount = target.butcher_drops[drop_type] || 1
+ var/is_stack = ispath(drop_type, /obj/item/stack)
+
+ for (var/i in 1 to amount)
+ if (!prob(effectiveness))
+ failures |= drop_type::name
+ amount -= 1
+ continue
+
+ if (prob(bonus_chance))
+ if (!is_stack)
+ if (ispath(drop_type, /obj/item/food/meat))
+ results += new drop_type(drop_loc, target.blood_dna_info?.Copy())
+ else
+ var/obj/item/butcher_result = new drop_type(drop_loc)
+ if (target.blood_dna_info)
+ butcher_result.add_blood_DNA(target.blood_dna_info.Copy())
+ results += butcher_result
+ amount += 1
+ bonuses |= drop_type::name
+
+ if (is_stack)
+ continue
+
+ if (ispath(drop_type, /obj/item/food/meat))
+ results += new drop_type(drop_loc, target.blood_dna_info?.Copy())
+ continue
+
+ var/obj/item/butcher_result = new drop_type(drop_loc)
+ butcher_result.add_blood_DNA(target.blood_dna_info.Copy())
+ results += butcher_result
+
+ if (is_stack && amount)
+ var/obj/item/stack/butcher_result = new drop_type(drop_loc, amount)
+ if (target.blood_dna_info)
+ butcher_result.add_blood_DNA(target.blood_dna_info.Copy())
+ results += butcher_result
+
+ if (target.reagents)
+ var/meat_produced = 0
+ for (var/obj/item/food/target_meat in results)
+ meat_produced += 1
+
+ for (var/obj/item/food/target_meat in results)
+ target.reagents.trans_to(target_meat, target.reagents.total_volume / meat_produced, remove_blacklisted = TRUE)
+
+ if (target.owner)
+ var/reagents_in_produced = 0
+ for(var/obj/item/result as anything in results)
+ if(result.reagents)
+ reagents_in_produced += 1
+
+ var/list/diseases = target.owner.get_static_viruses()
+
+ for(var/obj/item/result as anything in results)
+ if (reagents_in_produced)
+ if (target.owner.reagents)
+ target.owner.reagents.trans_to(result, target.owner.reagents.total_volume / reagents_in_produced / length(target.owner.bodyparts), remove_blacklisted = TRUE)
+ result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, target.owner.nutrition / 15 / reagents_in_produced)
+
+ if(LAZYLEN(diseases))
+ var/list/datum/disease/diseases_to_add = list()
+ for(var/datum/disease/disease as anything in diseases)
+ // Admin or special viruses that should not be reproduced
+ if(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS))
+ continue
+
+ diseases_to_add += disease
+
+ if(LAZYLEN(diseases_to_add))
+ result.AddComponent(/datum/component/infective, diseases_to_add)
+
+ for (var/obj/item/food/meat/meat in results)
+ meat.name = "[target.owner.real_name]'s [meat.name]"
+ meat.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, target.owner) = 4 * SHEET_MATERIAL_AMOUNT))
+ meat.subjectname = target.owner.real_name
+ meat.subjectjob = target.owner.job
+
+ user.visible_message(span_warning("[user] butchers [limb_descriptor]!"), span_notice("You butcher [limb_descriptor]."), ignored_mobs = target.owner)
+ if (!target.owner)
+ target.drop_organs(violent_removal = TRUE) // Should not happen, but just in case
+ create_replacement_limb(target, drop_loc)
+ qdel(target)
+ return
+
+ var/wound_type = null
+ if (source.damtype == BURN)
+ wound_type = WOUND_BURN
+ else
+ switch (source.get_sharpness())
+ if (SHARP_EDGED)
+ wound_type = WOUND_SLASH
+ if (SHARP_POINTY)
+ wound_type = WOUND_PIERCE
+ else
+ wound_type = WOUND_BLUNT
+
+ to_chat(target.owner, span_userdanger("[user] hacks the meat off your [target.plaintext_zone]!"))
+ var/mob/living/carbon/victim = target.owner
+
+ if (!target.butcher_replacement)
+ target.dismember(source.damtype, wound_type)
+ target.drop_organs(violent_removal = TRUE) // Should not happen, but just in case
+ qdel(target)
+ return
+
+ var/obj/item/bodypart/replacement = create_replacement_limb(target, drop_loc)
+ target.dismember(source.damtype, wound_type)
+ target.drop_organs(violent_removal = TRUE)
+ replacement.replace_limb(victim)
+ replacement.update_limb(is_creating = TRUE)
+ qdel(target)
+
+/// Creates a replacement (usually skeleton) limb for the butchered one
+/datum/component/butchering/proc/create_replacement_limb(obj/item/bodypart/target, drop_loc)
+ var/drop_type = target.butcher_replacement
+ var/obj/item/bodypart/replacement = new drop_type(drop_loc)
+ replacement.set_initial_damage(target.brute_dam, target.burn_dam)
+ if (IS_ORGANIC_LIMB(replacement) && target.owner)
+ replacement.blood_dna_info = target.owner.get_blood_dna_list()
+
+ for (var/datum/wound/wound as anything in target.wounds)
+ wound.remove_wound()
+ wound.apply_wound(replacement, silent = TRUE)
+
+ return replacement
+
+/datum/component/butchering/proc/start_butcher(obj/item/source, mob/living/target, mob/living/user)
+ to_chat(user, span_notice("You begin to butcher [target]..."))
+ playsound(target.loc, butcher_sound, 50, TRUE, -1)
+ if (do_after(user, speed, target) && target.Adjacent(source))
+ on_butchering(user, target)
+
+/datum/component/butchering/proc/butcher_human(obj/item/source, mob/living/carbon/human/victim, mob/living/user)
+ if (DOING_INTERACTION_WITH_TARGET(user, victim))
+ to_chat(user, span_warning("You're already interacting with [victim]!"))
+ return
+
+ var/static/list/butcher_spots = typecacheof(list(
+ /obj/structure/table,
+ /obj/structure/bed,
+ /obj/machinery/stasis,
+ /obj/structure/kitchenspike,
+ ))
+
+ var/found_spot = FALSE
+ for (var/obj/thing in victim.loc)
+ if (is_type_in_typecache(thing, butcher_spots))
+ found_spot = TRUE
+ break
+
+ if (!found_spot)
+ to_chat(user, span_warning("You need a better spot to butcher [victim]!"))
+ return
+
+ var/obj/item/bodypart/limb = victim.get_bodypart(deprecise_zone(user.zone_selected))
+ if (!limb)
+ to_chat(user, span_warning("[victim] doesn't have a [parse_zone(deprecise_zone(user.zone_selected))]!"))
+ return
+
+ butcher_limb(source, limb, user)
+
+/datum/component/butchering/proc/start_neck_slice(obj/item/source, mob/living/carbon/human/victim, mob/living/user)
+ if (DOING_INTERACTION_WITH_TARGET(user, victim))
+ to_chat(user, span_warning("You're already interacting with [victim]!"))
+ return
+
+ user.visible_message(span_danger("[user] is slitting [victim]'s throat!"), \
+ span_danger("You start slicing [victim]'s throat!"), \
+ span_hear("You hear a cutting noise!"), ignored_mobs = victim)
+ victim.show_message(span_userdanger("Your throat is being slit by [user]!"), MSG_VISUAL, \
+ span_userdanger("Something is cutting into your neck!"), NONE)
+ log_combat(user, victim, "attempted throat slitting", source)
+
+ playsound(victim.loc, butcher_sound, 50, TRUE, -1)
+ if (!do_after(user, clamp(500 / source.force, 30, 100), victim) && victim.Adjacent(source))
+ return
+
+ if (victim.has_status_effect(/datum/status_effect/neck_slice))
+ user.show_message(span_warning("[victim]'s neck has already been already cut, you can't make the bleeding any worse!"), MSG_VISUAL, \
+ span_warning("Their neck has already been already cut, you can't make the bleeding any worse!"))
+ return
+
+ victim.visible_message(span_danger("[user] slits [victim]'s throat!"), \
+ span_userdanger("[user] slits your throat..."))
+ log_combat(user, victim, "wounded via throat slitting", source)
+ victim.apply_damage(source.force, BRUTE, BODY_ZONE_HEAD, wound_bonus=CANT_WOUND) // easy tiger, we'll get to that in a sec
+ var/obj/item/bodypart/slit_throat = victim.get_bodypart(BODY_ZONE_HEAD)
+ if (victim.cause_wound_of_type_and_severity(WOUND_SLASH, slit_throat, WOUND_SEVERITY_CRITICAL))
+ victim.apply_status_effect(/datum/status_effect/neck_slice)
/**
* Handles a user butchering a target
@@ -107,67 +392,85 @@
var/final_effectiveness = effectiveness - target.butcher_difficulty
var/bonus_chance = max(0, (final_effectiveness - 100) + bonus_modifier) //so 125 total effectiveness = 25% extra chance
- if(target.flags_1 & HOLOGRAM_1)
+ if (target.flags_1 & HOLOGRAM_1)
butcher.visible_message(span_notice("[butcher] tries to butcher [target], but it vanishes."), \
span_notice("You try to butcher [target], but it vanishes."))
qdel(target)
return
- for(var/result_typepath in target.butcher_results)
- var/obj/remains = result_typepath
- var/amount = target.butcher_results[remains]
- for(var/_i in 1 to amount)
- if(!prob(final_effectiveness))
- if(butcher)
- to_chat(butcher, span_warning("You fail to harvest some of the [initial(remains.name)] from [target]."))
+ var/list/failures = list()
+ var/list/bonuses = list()
+ for (var/obj/remains as anything in target.butcher_results)
+ var/amount = target.butcher_results[remains] || 1
+ var/is_stack = ispath(remains, /obj/item/stack)
+
+ for (var/i in 1 to amount)
+ if (!prob(final_effectiveness))
+ failures |= remains::name
+ amount -= 1
continue
- if(prob(bonus_chance))
- if(butcher)
- to_chat(butcher, span_info("You harvest some extra [initial(remains.name)] from [target]!"))
- results += new remains (location)
- results += new remains (location)
+ if (prob(bonus_chance))
+ if (!is_stack)
+ results += new remains(location)
+ amount += 1
+ bonuses |= remains::name
- target.butcher_results.Remove(remains) //in case you want to, say, have it drop its results on gib
+ if (!is_stack)
+ results += new remains(location)
- for(var/guaranteed_result_typepath in target.guaranteed_butcher_results)
- var/obj/guaranteed_remains = guaranteed_result_typepath
+ if (is_stack && amount)
+ results += new remains(location, amount)
+
+ target.butcher_results?.Cut()
+
+ if (butcher)
+ if (length(failures))
+ to_chat(butcher, span_warning("You fail to harvest some of the [english_list(failures)] from [target]."))
+ if (length(bonuses))
+ to_chat(butcher, span_info("You harvest some extra [english_list(bonuses)] from [target]!"))
+
+ for (var/obj/guaranteed_remains as anything in target.guaranteed_butcher_results)
var/amount = target.guaranteed_butcher_results[guaranteed_remains]
- for(var/i in 1 to amount)
- results += new guaranteed_remains (location)
- target.guaranteed_butcher_results.Remove(guaranteed_remains)
+ if (ispath(guaranteed_remains, /obj/item/stack))
+ results += new guaranteed_remains(location, amount)
+ continue
- for(var/obj/item/carrion in results)
+ for (var/i in 1 to amount)
+ results += new guaranteed_remains(location)
+
+ target.guaranteed_butcher_results?.Cut()
+
+ for (var/obj/item/carrion in results)
var/list/meat_mats = carrion.has_material_type(/datum/material/meat)
- if(!length(meat_mats))
+ if (!length(meat_mats))
continue
carrion.set_custom_materials((carrion.custom_materials - meat_mats) + list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, target) = counterlist_sum(meat_mats)))
- // transfer delicious reagents to meat
- if(target.reagents)
+ // Transfer delicious reagents to meat
+ if (target.reagents)
var/meat_produced = 0
- for(var/obj/item/food/meat/slab/target_meat in results)
+ for (var/obj/item/food/target_meat in results)
meat_produced += 1
- for(var/obj/item/food/meat/slab/target_meat in results)
+ for (var/obj/item/food/target_meat in results)
target.reagents.trans_to(target_meat, target.reagents.total_volume / meat_produced, remove_blacklisted = TRUE)
- // dont forget yummy diseases either!
- if(iscarbon(target))
+ // Don't forget yummy diseases either!
+ if (iscarbon(target))
var/mob/living/carbon/host_target = target
var/list/diseases = host_target.get_static_viruses()
- if(LAZYLEN(diseases))
+ if (LAZYLEN(diseases))
var/list/datum/disease/diseases_to_add = list()
- for(var/datum/disease/disease as anything in diseases)
+ for (var/datum/disease/disease as anything in diseases)
// admin or special viruses that should not be reproduced
- if(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS))
- continue
+ if (!(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)))
+ diseases_to_add += disease
- diseases_to_add += disease
- if(LAZYLEN(diseases_to_add))
- for(var/obj/diseased_remains in results)
+ if (LAZYLEN(diseases_to_add))
+ for (var/obj/diseased_remains in results)
diseased_remains.AddComponent(/datum/component/infective, diseases_to_add)
- if(butcher)
+ if (butcher)
butcher.visible_message(span_notice("[butcher] butchers [target]."), \
span_notice("You butcher [target]."))
butcher_callback?.Invoke(butcher, target)
@@ -175,20 +478,9 @@
target.log_message("has been butchered by [key_name(butcher)]", LOG_ATTACK)
target.gib(DROP_BRAIN|DROP_ORGANS)
-///Enables the butchering mechanic for the mob who has equipped us.
-/datum/component/butchering/proc/enable_butchering(datum/source)
- SIGNAL_HANDLER
- butchering_enabled = TRUE
-
-///Disables the butchering mechanic for the mob who has dropped us.
-/datum/component/butchering/proc/disable_butchering(datum/source)
- SIGNAL_HANDLER
- butchering_enabled = FALSE
-
-///Special snowflake component only used for the recycler.
+/// Special snowflake component only used for the recycler.
/datum/component/butchering/recycler
-
/datum/component/butchering/recycler/Initialize(
speed,
effectiveness,
@@ -198,10 +490,10 @@
can_be_blunt,
butcher_callback,
)
- if(!istype(parent, /obj/machinery/recycler)) //EWWW
+ if (!istype(parent, /obj/machinery/recycler)) //EWWW
return COMPONENT_INCOMPATIBLE
. = ..()
- if(. == COMPONENT_INCOMPATIBLE)
+ if (. == COMPONENT_INCOMPATIBLE)
return
var/static/list/loc_connections = list(
@@ -212,13 +504,13 @@
/datum/component/butchering/recycler/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
SIGNAL_HANDLER
- if(!isliving(arrived))
+ if (!isliving(arrived))
return
var/mob/living/victim = arrived
var/obj/machinery/recycler/eater = parent
- if(eater.safety_mode || (eater.machine_stat & (BROKEN|NOPOWER))) //I'm so sorry.
+ if (eater.safety_mode || (eater.machine_stat & (BROKEN|NOPOWER))) //I'm so sorry.
return
- if(victim.stat == DEAD && (victim.butcher_results || victim.guaranteed_butcher_results))
+ if (victim.stat == DEAD && (victim.butcher_results || victim.guaranteed_butcher_results))
on_butchering(parent, victim)
/datum/component/butchering/mecha
@@ -237,6 +529,16 @@
COMSIG_MECHA_EQUIPMENT_DETACHED,
))
+/// Enables the butchering mechanic for the mecha who has equipped us.
+/datum/component/butchering/mecha/proc/enable_butchering(datum/source)
+ SIGNAL_HANDLER
+ butchering_enabled = TRUE
+
+/// Disables the butchering mechanic for the mecha who has dropped us.
+/datum/component/butchering/mecha/proc/disable_butchering(datum/source)
+ SIGNAL_HANDLER
+ butchering_enabled = FALSE
+
///When we are ready to drill through a mob
/datum/component/butchering/mecha/proc/on_drill(datum/source, obj/vehicle/sealed/mecha/chassis, mob/living/target)
SIGNAL_HANDLER
@@ -256,16 +558,16 @@
COMSIG_ITEM_DROPPED,
))
-///Same as enable_butchering but for worn items
+/// Same as enable_butchering but for worn items
/datum/component/butchering/wearable/proc/worn_enable_butchering(obj/item/source, mob/user, slot)
SIGNAL_HANDLER
//check if the item is being not worn
- if(!(slot & source.slot_flags))
+ if (!(slot & source.slot_flags))
return
butchering_enabled = TRUE
RegisterSignal(user, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(butcher_target))
-///Same as disable_butchering but for worn items
+/// Same as disable_butchering but for worn items
/datum/component/butchering/wearable/proc/worn_disable_butchering(obj/item/source, mob/user)
SIGNAL_HANDLER
butchering_enabled = FALSE
@@ -273,6 +575,6 @@
/datum/component/butchering/wearable/proc/butcher_target(mob/user, atom/target, proximity)
SIGNAL_HANDLER
- if(!isliving(target))
+ if (!isliving(target))
return NONE
- return onItemAttack(parent, target, user)
+ return on_item_attack(parent, target, user)
diff --git a/code/datums/components/free_operation.dm b/code/datums/components/free_operation.dm
new file mode 100644
index 00000000000..f1bddc05c65
--- /dev/null
+++ b/code/datums/components/free_operation.dm
@@ -0,0 +1,33 @@
+/// Allows mobs with this component to have all of their limbs operated on without needing drapes
+/datum/component/free_operation
+ dupe_mode = COMPONENT_DUPE_SOURCES
+
+/datum/component/free_operation/Initialize(check)
+ if (!iscarbon(parent))
+ return COMPONENT_INCOMPATIBLE
+ ADD_TRAIT(parent, TRAIT_READY_TO_OPERATE, REF(src))
+ var/mob/living/carbon/owner = parent
+ for (var/obj/item/bodypart/limb as anything in owner.bodyparts)
+ ADD_TRAIT(limb, TRAIT_READY_TO_OPERATE, REF(src))
+
+/datum/component/free_operation/Destroy(force)
+ REMOVE_TRAIT(parent, TRAIT_READY_TO_OPERATE, REF(src))
+ var/mob/living/carbon/owner = parent
+ for (var/obj/item/bodypart/limb as anything in owner.bodyparts)
+ REMOVE_TRAIT(limb, TRAIT_READY_TO_OPERATE, REF(src))
+ return ..()
+
+/datum/component/free_operation/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_CARBON_ATTACH_LIMB, PROC_REF(flag_limb))
+ RegisterSignal(parent, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(unflag_limb))
+
+/datum/component/free_operation/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_CARBON_ATTACH_LIMB, COMSIG_CARBON_REMOVE_LIMB))
+
+/datum/component/free_operation/proc/flag_limb(mob/living/carbon/source, obj/item/bodypart/limb)
+ SIGNAL_HANDLER
+ ADD_TRAIT(limb, TRAIT_READY_TO_OPERATE, REF(src))
+
+/datum/component/free_operation/proc/unflag_limb(mob/living/carbon/source, obj/item/bodypart/limb)
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(limb, TRAIT_READY_TO_OPERATE, REF(src))
diff --git a/code/datums/elements/corrupted_organ.dm b/code/datums/elements/corrupted_organ.dm
index ccf56b17c30..7ba5b3ae8b6 100644
--- a/code/datums/elements/corrupted_organ.dm
+++ b/code/datums/elements/corrupted_organ.dm
@@ -30,7 +30,7 @@
/// When we're taken out of someone, do something spooky
/datum/element/corrupted_organ/proc/on_removed(atom/organ, mob/living/remover, mob/living/carbon/loser)
SIGNAL_HANDLER
- if (loser.has_reagent(/datum/reagent/water/holywater) || loser.can_block_magic(MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY) || prob(20))
+ if (loser?.has_reagent(/datum/reagent/water/holywater) || loser?.can_block_magic(MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY) || prob(20))
return
if (prob(75))
organ.AddComponent(\
diff --git a/code/datums/status_effects/buffs/bioware/cortex.dm b/code/datums/status_effects/buffs/bioware/cortex.dm
index 6134b9d7f6b..91c5fee9e9f 100644
--- a/code/datums/status_effects/buffs/bioware/cortex.dm
+++ b/code/datums/status_effects/buffs/bioware/cortex.dm
@@ -2,19 +2,15 @@
/datum/status_effect/bioware/cortex
id = "cortex"
-// Folded brain - Grants a bonus chance to getting special traumas via lobotomy
-/datum/status_effect/bioware/cortex/folded
-
-/datum/status_effect/bioware/cortex/folded/bioware_gained()
- ADD_TRAIT(owner, TRAIT_SPECIAL_TRAUMA_BOOST, TRAIT_STATUS_EFFECT(id))
-
-/datum/status_effect/bioware/cortex/folded/bioware_lost()
- REMOVE_TRAIT(owner, TRAIT_SPECIAL_TRAUMA_BOOST, TRAIT_STATUS_EFFECT(id))
+// All cortex bioware removes the trauma boost as it is not granted via a status
+/datum/status_effect/bioware/cortex/bioware_gained()
+ REMOVE_TRAIT(owner, TRAIT_SPECIAL_TRAUMA_BOOST, BIOWARE_TRAIT)
// Imprinted brain - Cures basic traumas continuously
/datum/status_effect/bioware/cortex/imprinted
/datum/status_effect/bioware/cortex/imprinted/bioware_gained()
+ . = ..()
var/mob/living/carbon/human/human_owner = owner
human_owner.cure_all_traumas(resilience = TRAUMA_RESILIENCE_BASIC)
RegisterSignal(human_owner, COMSIG_CARBON_GAIN_TRAUMA, PROC_REF(on_gain_trauma))
diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm
index 31ca19429f7..8eda251d1ae 100644
--- a/code/datums/wounds/_wounds.dm
+++ b/code/datums/wounds/_wounds.dm
@@ -78,6 +78,8 @@
var/limp_chance
/// How much we're contributing to this limb's bleed_rate
var/blood_flow
+ /// Surgical states we're applying to our limb
+ var/surgery_states = NONE
/// How much having this wound will add to all future check_wounding() rolls on this limb, to allow progression to worse injuries with repeated damage
var/threshold_penalty
@@ -112,6 +114,14 @@
/// The actionspeed modifier we will use in case we are on the arms and have a interaction penalty. Qdelled on destroy.
var/datum/actionspeed_modifier/wound_interaction_inefficiency/actionspeed_mod
+ /// List of states -> other states that override them and prevent them from being added by wounds
+ var/static/list/exclusive_surgery_states = list(
+ "[SURGERY_SKIN_CUT]" = SURGERY_SKIN_OPEN,
+ "[SURGERY_VESSELS_UNCLAMPED]" = SURGERY_VESSELS_CLAMPED,
+ "[SURGERY_BONE_SAWED]" = SURGERY_BONE_DRILLED,
+ "[SURGERY_BONE_DRILLED]" = SURGERY_BONE_SAWED,
+ )
+
/datum/wound/New()
. = ..()
@@ -284,16 +294,28 @@
/datum/wound/proc/set_limb(obj/item/bodypart/new_value, replaced = FALSE, destroying = FALSE)
if(limb == new_value)
return FALSE //Limb can either be a reference to something or `null`. Returning the number variable makes it clear no change was made.
+
. = limb
+
if(limb) // if we're nulling limb, we're basically detaching from it, so we should remove ourselves in that case
- UnregisterSignal(limb, COMSIG_QDELETING)
- UnregisterSignal(limb, list(COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_UNGAUZED))
+ UnregisterSignal(limb, list(COMSIG_QDELETING, COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_UNGAUZED, COMSIG_BODYPART_UPDATING_SURGERY_STATE))
LAZYREMOVE(limb.wounds, src)
if (!destroying)
limb.update_wounds(replaced)
if (disabling)
limb.remove_traits(list(TRAIT_PARALYSIS, TRAIT_DISABLED_BY_WOUND), REF(src))
+ if(surgery_states)
+ for (var/state in exclusive_surgery_states)
+ if (!(limb.surgery_state & exclusive_surgery_states[state]))
+ continue
+ var/actual_state = text2num(state)
+ if (actual_state & surgery_states)
+ surgery_states &= ~actual_state
+
+ if (surgery_states)
+ limb.remove_surgical_state(surgery_states)
+
limb = new_value
// POST-CHANGE
@@ -301,15 +323,32 @@
if (limb)
RegisterSignal(limb, COMSIG_QDELETING, PROC_REF(source_died))
RegisterSignals(limb, list(COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_UNGAUZED), PROC_REF(gauze_state_changed))
+ RegisterSignal(limb, COMSIG_BODYPART_UPDATING_SURGERY_STATE, PROC_REF(on_surgery_state_change))
if (disabling)
limb.add_traits(list(TRAIT_PARALYSIS, TRAIT_DISABLED_BY_WOUND), REF(src))
+ if(surgery_states)
+ for (var/state in exclusive_surgery_states)
+ if (!(limb.surgery_state & exclusive_surgery_states[state]))
+ continue
+ var/actual_state = text2num(state)
+ if (actual_state & surgery_states)
+ surgery_states &= ~actual_state
+
+ if (surgery_states)
+ limb.add_surgical_state(surgery_states)
+
if (victim)
start_limping_if_we_should() // the status effect already handles removing itself
add_or_remove_actionspeed_mod()
update_inefficiencies(replaced)
+/// Used to remove states applied or removed by operations from ourselves as to not remove them if we heal mid-surgery
+/datum/wound/proc/on_surgery_state_change(old_state, surgery_state, changed_states)
+ SIGNAL_HANDLER
+ surgery_states &= ~changed_states
+
/datum/wound/proc/add_or_remove_actionspeed_mod()
update_actionspeed_modifier()
if (actionspeed_mod)
diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm
index 12a47139473..a2307a927d5 100644
--- a/code/datums/wounds/bones.dm
+++ b/code/datums/wounds/bones.dm
@@ -430,6 +430,7 @@
internal_bleeding_chance = 60
wound_flags = (ACCEPTS_GAUZE | MANGLES_INTERIOR)
regen_ticks_needed = 240 // ticks every 2 seconds, 480 seconds, so roughly 8 minutes default
+ surgery_states = SURGERY_SKIN_CUT | SURGERY_BONE_SAWED // Bad enough to count as a busted skull/ribcage
simple_desc = "Patient's bones have effectively shattered completely, causing total immobilization of the limb."
simple_treat_text = "Bandaging the wound will slightly reduce its impact until surgically treated with bone gel and surgical tape."
diff --git a/code/datums/wounds/cranial_fissure.dm b/code/datums/wounds/cranial_fissure.dm
index f61e5386fb5..e26e3a97d9d 100644
--- a/code/datums/wounds/cranial_fissure.dm
+++ b/code/datums/wounds/cranial_fissure.dm
@@ -39,6 +39,7 @@
severity = WOUND_SEVERITY_CRITICAL
sound_effect = 'sound/effects/dismember.ogg'
+ surgery_states = SURGERY_SKIN_OPEN | SURGERY_BONE_SAWED // Literally a cracked open skull, no vessels as it... doesn't bleed?
/// If TRUE we have been prepped for surgery (to repair)
VAR_FINAL/prepped = FALSE
diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm
index 1379b1bbc0d..953dfcdac2f 100644
--- a/code/datums/wounds/pierce.dm
+++ b/code/datums/wounds/pierce.dm
@@ -404,6 +404,7 @@
threshold_penalty = 15
status_effect_type = /datum/status_effect/wound/pierce/critical
scar_keyword = "piercecritical"
+ surgery_states = SURGERY_SKIN_CUT | SURGERY_VESSELS_UNCLAMPED // Bad enough to count
wound_flags = (ACCEPTS_GAUZE | MANGLES_EXTERIOR | CAN_BE_GRASPED)
simple_treat_text = "Bandaging the wound is of utmost importance, as is seeking direct medical attention - Death will ensue if treatment is delayed whatsoever, with lack of oxygen killing the patient, thus Food, Iron, and saline solution is always recommended after treatment. This wound will not naturally seal itself."
diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm
index a4e66f0188b..3e43a1081d0 100644
--- a/code/datums/wounds/slash.dm
+++ b/code/datums/wounds/slash.dm
@@ -342,6 +342,7 @@
demotes_to = /datum/wound/slash/flesh/moderate
status_effect_type = /datum/status_effect/wound/slash/flesh/severe
scar_keyword = "slashsevere"
+ surgery_states = SURGERY_SKIN_CUT | SURGERY_VESSELS_UNCLAMPED
simple_treat_text = "Bandaging the wound is essential, and will reduce blood loss. Afterwards, the wound can be sutured shut, preferably while the patient is resting and/or grasping their wound."
homemade_treat_text = "Bed sheets can be ripped up to make makeshift gauze. Flour, table salt, or salt mixed with water can be applied directly to stem the flow, though unmixed salt will irritate the skin and worsen natural healing. Resting and grabbing your wound will also reduce bleeding."
@@ -375,6 +376,7 @@
demotes_to = /datum/wound/slash/flesh/severe
status_effect_type = /datum/status_effect/wound/slash/flesh/critical
scar_keyword = "slashcritical"
+ surgery_states = SURGERY_SKIN_OPEN | SURGERY_VESSELS_UNCLAMPED
wound_flags = (ACCEPTS_GAUZE | MANGLES_EXTERIOR | CAN_BE_GRASPED)
simple_treat_text = "Bandaging the wound is of utmost importance, as is seeking direct medical attention - Death will ensue if treatment is delayed whatsoever, with lack of oxygen killing the patient, thus Food, Iron, and saline solution is always recommended after treatment. This wound will not naturally seal itself."
homemade_treat_text = "Bed sheets can be ripped up to make makeshift gauze. Flour, salt, and saltwater topically applied will help. Dropping to the ground and grabbing your wound will reduce blood flow."
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index ad6b4259f41..567f0933d12 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -9,6 +9,8 @@
var/buckle_requires_restraints = FALSE
/// The mobs currently buckled to this atom
var/list/mob/living/buckled_mobs = null //list()
+ /// How much time it takes to buckle a mob to us?
+ var/buckle_delay = 2 SECONDS
/// The maximum number of mob/livings allowed to be buckled to this atom at once
var/max_buckled_mobs = 1
/// Whether things buckled to this atom can be pulled while they're buckled
@@ -321,7 +323,7 @@
M.visible_message(span_warning("[user] starts buckling [M] to [src]!"),\
span_userdanger("[user] starts buckling you to [src]!"),\
span_hear("You hear metal clanking."))
- if(!do_after(user, 2 SECONDS, M))
+ if(!do_after(user, buckle_delay, M))
return FALSE
// Sanity check before we attempt to buckle. Is everything still in a kosher state for buckling after the 3 seconds have elapsed?
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index f8382c876eb..446dc45ab66 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -879,7 +879,6 @@
. = ..()
if(!isliving(hit_atom)) //Living mobs handle hit sounds differently.
-
throw_drop_sound_chain(YEET_SOUND_VOLUME)
return
diff --git a/code/game/objects/items/devices/scanners/autopsy_scanner.dm b/code/game/objects/items/devices/scanners/autopsy_scanner.dm
index 61bc6c4d262..c541e513a0d 100644
--- a/code/game/objects/items/devices/scanners/autopsy_scanner.dm
+++ b/code/game/objects/items/devices/scanners/autopsy_scanner.dm
@@ -190,6 +190,8 @@
autopsy_information += "Subject is husked by: "
if(HAS_TRAIT_FROM(scanned, TRAIT_HUSK, CHANGELING_DRAIN))
autopsy_information += "Desiccation, commonly caused by Changelings."
+ else if(HAS_TRAIT_FROM(scanned, TRAIT_HUSK, SKELETON_TRAIT))
+ autopsy_information += "Stripped flesh."
else if(!HAS_TRAIT_FROM(scanned, TRAIT_HUSK, BURN)) // prioritize showing unknown causes over burns
autopsy_information += "Unknown causes."
else
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index 56fab997cf7..e1f4bc6d792 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -180,6 +180,8 @@
if(advanced)
if(HAS_TRAIT_FROM(target, TRAIT_HUSK, CHANGELING_DRAIN))
render_list += "Subject has been husked by [conditional_tooltip("desiccation", "Irreparable. Under normal circumstances, revival can only proceed via brain transplant.", tochat)].
"
+ else if(HAS_TRAIT_FROM(target, TRAIT_HUSK, SKELETON_TRAIT))
+ render_list += "Subject has been husked due to severe flesh loss.
"
else if(!HAS_TRAIT_FROM(target, TRAIT_HUSK, BURN)) // prioritize showing unknown causes over burns
render_list += "Subject has been husked by mysterious causes.
"
else
diff --git a/code/game/objects/items/knives.dm b/code/game/objects/items/knives.dm
index fcda07ed897..b94a91e3456 100644
--- a/code/game/objects/items/knives.dm
+++ b/code/game/objects/items/knives.dm
@@ -47,7 +47,7 @@
///Adds the butchering component, used to override stats for special cases
/obj/item/knife/proc/set_butchering()
AddComponent(/datum/component/butchering, \
- speed = 8 SECONDS - force, \
+ speed = 9 SECONDS - force * 2, \
effectiveness = 100, \
bonus_modifier = force - 10, \
)
@@ -134,7 +134,7 @@
/obj/item/knife/hunting/set_butchering()
AddComponent(/datum/component/butchering, \
- speed = 8 SECONDS - force, \
+ speed = 9 SECONDS - force * 2, \
effectiveness = 100, \
bonus_modifier = force + 10, \
)
diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm
index f1492da1267..2988c564754 100644
--- a/code/game/objects/structures/kitchen_spike.dm
+++ b/code/game/objects/structures/kitchen_spike.dm
@@ -72,10 +72,12 @@
desc = "A spike for collecting meat from animals."
density = TRUE
anchored = TRUE
- buckle_lying = FALSE
+ buckle_lying = 180
+ buckle_dir = SOUTH
can_buckle = TRUE
max_integrity = 250
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 7)
+ buckle_delay = 10 SECONDS
/obj/structure/kitchenspike/Initialize(mapload)
. = ..()
@@ -114,8 +116,12 @@
/obj/structure/kitchenspike/user_buckle_mob(mob/living/target, mob/user, check_loc = TRUE)
if(!iscarbon(target) && !isanimal_or_basicmob(target))
return
+ if(target != user || target.loc == loc)
+ return ..()
if(!do_after(user, 10 SECONDS, target))
return
+ if(!is_user_buckle_possible(target, user, check_loc))
+ return FALSE
return ..()
/obj/structure/kitchenspike/post_buckle_mob(mob/living/target)
@@ -123,12 +129,11 @@
target.emote("scream")
target.add_splatter_floor()
target.adjust_brute_loss(30)
- target.setDir(2)
- var/matrix/m180 = matrix(target.transform)
- m180.Turn(180)
- animate(target, transform = m180, time = 3)
- target.add_offsets(type, y_add = -6, animate = FALSE)
+ target.add_offsets(type, x_add = -1)
+ target.set_lying_angle(buckle_lying)
ADD_TRAIT(target, TRAIT_MOVE_UPSIDE_DOWN, REF(src))
+ // So you can butcher people too
+ target.AddComponentFrom(REF(src), /datum/component/free_operation)
/obj/structure/kitchenspike/user_unbuckle_mob(mob/living/buckled_mob, mob/user)
if(buckled_mob != user)
@@ -156,11 +161,9 @@
buckled_mob.adjust_brute_loss(30)
INVOKE_ASYNC(buckled_mob, TYPE_PROC_REF(/mob, emote), "scream")
buckled_mob.AdjustParalyzed(20)
- var/matrix/m180 = matrix(buckled_mob.transform)
- m180.Turn(180)
- animate(buckled_mob, transform = m180, time = 3)
- buckled_mob.remove_offsets(type, animate = FALSE)
+ buckled_mob.remove_offsets(type)
REMOVE_TRAIT(buckled_mob, TRAIT_MOVE_UPSIDE_DOWN, REF(src))
+ buckled_mob.RemoveComponentSource(REF(src), /datum/component/free_operation)
/obj/structure/kitchenspike/atom_deconstruct(disassembled = TRUE)
if(disassembled)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index d171da22326..21d2fa893cc 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -1086,8 +1086,8 @@
UnregisterSignal(patient, list(
SIGNAL_ADDTRAIT(TRAIT_READY_TO_OPERATE),
SIGNAL_REMOVETRAIT(TRAIT_READY_TO_OPERATE),
- COMSIG_LIVING_BEING_OPERATED_ON,
- COMSIG_LIVING_SURGERY_FINISHED,
+ COMSIG_ATOM_BEING_OPERATED_ON,
+ COMSIG_ATOM_SURGERY_FINISHED,
COMSIG_LIVING_UPDATING_SURGERY_STATE,
))
if (patient.external && patient.external == air_tank)
@@ -1101,10 +1101,10 @@
RegisterSignals(patient, list(
SIGNAL_ADDTRAIT(TRAIT_READY_TO_OPERATE),
SIGNAL_REMOVETRAIT(TRAIT_READY_TO_OPERATE),
- COMSIG_LIVING_SURGERY_FINISHED,
+ COMSIG_ATOM_SURGERY_FINISHED,
COMSIG_LIVING_UPDATING_SURGERY_STATE,
), PROC_REF(on_surgery_change))
- RegisterSignal(patient, COMSIG_LIVING_BEING_OPERATED_ON, PROC_REF(get_surgeries))
+ RegisterSignal(patient, COMSIG_ATOM_BEING_OPERATED_ON, PROC_REF(get_surgeries))
/obj/structure/table/optable/proc/on_surgery_change(datum/source)
SIGNAL_HANDLER
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 42d3645300b..a1cf2f26502 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -129,7 +129,7 @@
var/mob/living/glorp = mob_override || owner.current
UnregisterSignal(glorp, COMSIG_LIVING_OPERATING_ON)
-/datum/antagonist/abductor/scientist/proc/add_surgery(datum/source, mob/living/patient, list/possible_operations)
+/datum/antagonist/abductor/scientist/proc/add_surgery(datum/source, atom/movable/operating_on, list/possible_operations)
SIGNAL_HANDLER
var/static/list/ayy_operations
diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm
index d2a1e299cad..4900924da36 100644
--- a/code/modules/food_and_drinks/machinery/gibber.dm
+++ b/code/modules/food_and_drinks/machinery/gibber.dm
@@ -13,8 +13,8 @@
var/dirty = FALSE
/// Time from starting until meat appears
var/gibtime = 40
- /// How much meat we meet when we meat the meat
- var/meat_produced = 2
+ /// Average efficiency multiplier for how much meat we meet when we meat the meat per meat of each of meat's meat limbs
+ var/efficiency = 0.25
/// If the gibber should give the 'Subject may not have abiotic items on' message
var/ignore_clothing = FALSE
/// The DNA info of the last gibbed mob
@@ -35,9 +35,9 @@
/obj/machinery/gibber/RefreshParts()
. = ..()
gibtime = 40
- meat_produced = initial(meat_produced)
+ efficiency = initial(efficiency)
for(var/datum/stock_part/matter_bin/matter_bin in component_parts)
- meat_produced += matter_bin.tier
+ efficiency += matter_bin.tier * 0.25
for(var/datum/stock_part/servo/servo in component_parts)
gibtime -= 5 * servo.tier
if(servo.tier >= 2)
@@ -46,7 +46,7 @@
/obj/machinery/gibber/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
- . += span_notice("The status display reads: Outputting [meat_produced] meat slab(s) after [gibtime*0.1] seconds of processing.")
+ . += span_notice("The status display reads: Outputting [efficiency]% of meat after [gibtime*0.1] seconds of processing.")
for(var/datum/stock_part/servo/servo in component_parts)
if(servo.tier >= 2)
. += span_notice("[src] has been upgraded to process inorganic materials.")
@@ -127,7 +127,7 @@
set_occupant(C)
update_appearance()
else
- startgibbing(user)
+ start_gibbing(user)
/obj/machinery/gibber/wrench_act(mob/living/user, obj/item/tool)
. = ..()
@@ -162,12 +162,14 @@
dump_inventory_contents()
update_appearance()
-/obj/machinery/gibber/proc/startgibbing(mob/user)
+/obj/machinery/gibber/proc/start_gibbing(mob/user)
if(operating)
return
+
if(!occupant)
audible_message(span_hear("You hear a loud metallic grinding sound."))
return
+
if(occupant.flags_1 & HOLOGRAM_1)
audible_message(span_hear("You hear a very short metallic grinding sound."))
playsound(loc, 'sound/machines/hiss.ogg', 20, TRUE)
@@ -180,123 +182,153 @@
playsound(loc, 'sound/machines/juicer.ogg', 50, TRUE)
operating = TRUE
update_appearance()
-
Shake(pixelshiftx = 1, pixelshifty = 0, duration = gibtime)
- var/mob/living/mob_occupant = occupant
- var/sourcename = mob_occupant.real_name
- var/sourcejob
- if(ishuman(occupant))
- var/mob/living/carbon/human/gibee = occupant
- sourcejob = gibee.job
- var/sourcenutriment = mob_occupant.nutrition / 15
- var/gibtypes = null
- var/typeofmeat = /obj/item/food/meat/slab/human
- var/typeofskin
+ var/mob/living/victim = occupant
var/list/results = list()
- var/obj/item/stack/sheet/animalhide/skin
- var/list/datum/disease/diseases = mob_occupant.get_static_viruses()
+ var/list/datum/disease/diseases = list()
+ for(var/datum/disease/disease as anything in victim.get_static_viruses())
+ // admin or special viruses that should not be reproduced
+ if(!(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)))
+ diseases += disease
- // We cannot initial() lists on types, so we need to create in nullspace, take the list and then delete our mob's gibspawner
- var/spawner_type = mob_occupant.get_gibs_type()
- if (spawner_type)
- // No need to handle cleanup as it returns INITIALIZE_HINT_QDEL by default
- var/obj/effect/gibspawner/spawner = new spawner_type()
- gibtypes = spawner.gibtypes
+ blood_dna_info = victim.get_blood_dna_list()
- if(ishuman(occupant))
- var/mob/living/carbon/human/gibee = occupant
- if(prob(40) && (sourcejob in list(JOB_SECURITY_OFFICER,JOB_WARDEN,JOB_HEAD_OF_SECURITY)))
- typeofmeat = /obj/item/food/meat/slab/pig
- else if(gibee.dna && gibee.dna.species)
- typeofmeat = gibee.dna.species.meat
- typeofskin = gibee.dna.species.skinned_type
- blood_dna_info = gibee.get_blood_dna_list()
+ if (!ishuman(victim))
+ var/meat_type = /obj/item/food/meat/slab
+ var/skin_type = null
+ var/meat_produced = 2
+ var/skin_produced = 1
+ if (victim.butcher_results || victim.guaranteed_butcher_results)
+ for (var/possible_drop in victim.butcher_results | victim.guaranteed_butcher_results)
+ if (ispath(possible_drop, /obj/item/food/meat/slab))
+ meat_type = possible_drop
+ meat_produced = victim.butcher_results[possible_drop] + victim.guaranteed_butcher_results[possible_drop]
+ else if (ispath(possible_drop, /obj/item/stack/sheet/animalhide))
+ skin_type = possible_drop
+ skin_produced = victim.butcher_results[possible_drop] + victim.guaranteed_butcher_results[possible_drop]
- else if(iscarbon(occupant))
- var/mob/living/carbon/carbon_occupant = occupant
- typeofmeat = carbon_occupant.type_of_meat
- if(isalien(carbon_occupant))
- typeofskin = /obj/item/stack/sheet/animalhide/xeno
- blood_dna_info = carbon_occupant.get_blood_dna_list()
+ else if (iscarbon(victim))
+ var/mob/living/carbon/as_carbon = victim
+ meat_type = as_carbon.type_of_meat
+ if (isalien(victim))
+ skin_type = /obj/item/stack/sheet/animalhide/xeno
- if (typeofmeat)
- for (var/i in 1 to meat_produced)
- var/obj/item/food/meat/slab/newmeat = new typeofmeat(null, blood_dna_info)
- newmeat.name = "[sourcename] [newmeat.name]"
- newmeat.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, occupant) = 4 * SHEET_MATERIAL_AMOUNT))
- if(!istype(newmeat))
+ for (var/i in 1 to rand(floor(meat_produced * efficiency / initial(efficiency)), ceil(meat_produced * efficiency / initial(efficiency))))
+ results += spawn_meat(victim, meat_type, diseases)
+
+ if (skin_type && skin_produced)
+ for (var/i in 1 to rand(floor(skin_produced * efficiency / initial(efficiency)), ceil(skin_produced * efficiency / initial(efficiency))))
+ results += new skin_type(src)
+
+ SEND_SIGNAL(victim, COMSIG_LIVING_GIBBER_ACT, user, src, results)
+ process_results(victim, results)
+ addtimer(CALLBACK(src, PROC_REF(finish_gibbing), results, victim.get_gibs_type(), diseases), gibtime)
+ log_combat(user, victim, "gibbed")
+ victim.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS)
+ victim.death(TRUE)
+ victim.ghostize()
+ set_occupant(null)
+ qdel(victim)
+ return
+
+ var/mob/living/carbon/human/agent_whiskey = victim
+ var/drop_chance = 0
+ for (var/obj/item/bodypart/limb as anything in agent_whiskey.bodyparts)
+ if (!limb.butcher_drops)
+ continue
+
+ for (var/obj/item/drop_type as anything in limb.butcher_drops)
+ var/amount = limb.butcher_drops[drop_type] || 1
+ drop_chance += amount * efficiency
+ if (drop_chance > 1)
+ amount = floor(drop_chance)
+ if (prob(drop_chance * 100))
+ amount += 1
+ else
+ amount = 1
+ if (!prob(drop_chance * 100))
+ continue
+ drop_chance = 0
+
+ if (ispath(drop_type, /obj/item/food/meat))
+ for (var/i in 1 to amount)
+ results += spawn_meat(victim, drop_type, diseases)
continue
- newmeat.subjectname = sourcename
- if(sourcejob)
- newmeat.subjectjob = sourcejob
- results += newmeat
+ for (var/i in 1 to amount)
+ results += new drop_type(src)
- SEND_SIGNAL(occupant, COMSIG_LIVING_GIBBER_ACT, user, src, results)
+ SEND_SIGNAL(victim, COMSIG_LIVING_GIBBER_ACT, user, src, results)
+ process_results(victim, results)
+ addtimer(CALLBACK(src, PROC_REF(finish_gibbing), results, victim.get_gibs_type(), diseases), gibtime)
+ log_combat(user, victim, "gibbed")
+ victim.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS)
+ victim.death(TRUE)
+ victim.ghostize()
+ set_occupant(null)
+ qdel(victim)
+/obj/machinery/gibber/proc/spawn_meat(mob/living/victim, meat_type = /obj/item/food/meat/slab, list/datum/disease/diseases)
+ var/obj/item/food/meat/meat = new meat_type(src, blood_dna_info)
+ meat.name = "[victim.real_name]'s [meat.name]"
+ meat.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, victim) = 4 * SHEET_MATERIAL_AMOUNT))
+ if (!istype(meat))
+ return
+ meat.subjectname = victim.real_name
+ meat.subjectjob = victim.job
+ if (length(diseases))
+ meat.AddComponent(/datum/component/infective, diseases)
+ return meat
+
+/obj/machinery/gibber/proc/process_results(mob/living/victim, list/obj/item/results)
var/reagents_in_produced = 0
for(var/obj/item/result as anything in results)
if(result.reagents)
- reagents_in_produced++
+ reagents_in_produced += 1
for(var/obj/item/result as anything in results)
- occupant.reagents.trans_to(result, occupant.reagents.total_volume / reagents_in_produced, remove_blacklisted = TRUE)
- result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, sourcenutriment / reagents_in_produced) // Thehehe. Fat guys go first
+ if (victim.reagents)
+ victim.reagents.trans_to(result, victim.reagents.total_volume / reagents_in_produced, remove_blacklisted = TRUE)
+ result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, victim.nutrition / 15 / reagents_in_produced)
-
- if(typeofskin)
- skin = new typeofskin
-
- log_combat(user, occupant, "gibbed")
- mob_occupant.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS)
- mob_occupant.death(TRUE)
- mob_occupant.ghostize()
- set_occupant(null)
- qdel(mob_occupant)
- addtimer(CALLBACK(src, PROC_REF(make_meat), skin, results, meat_produced, gibtypes, diseases, blood_dna_info), gibtime)
-
-/obj/machinery/gibber/proc/make_meat(obj/item/stack/sheet/animalhide/skin, list/results, meat_produced, list/gibtypes, list/datum/disease/diseases, blood_dna_info)
+/obj/machinery/gibber/proc/finish_gibbing(list/obj/item/results, gibs_type, list/datum/disease/diseases)
playsound(src.loc, 'sound/effects/splat.ogg', 50, TRUE)
operating = FALSE
if (!dirty && prob(50))
dirty = TRUE
+
if(blood_dna_info)
add_blood_DNA(blood_dna_info)
+
var/turf/our_turf = get_turf(src)
var/list/turf/nearby_turfs = RANGE_TURFS(3, our_turf) - our_turf
- if(skin)
- skin.forceMove(loc)
- skin.throw_at(pick(nearby_turfs), meat_produced, 3)
+ for (var/obj/item/result as anything in results)
+ result.forceMove(our_turf)
+ result.throw_at(pick(nearby_turfs), rand(1, length(results)), 1)
- var/iteration = 1
- for (var/obj/item/meatslab in results)
- if(LAZYLEN(diseases))
- var/list/datum/disease/diseases_to_add = list()
- for(var/datum/disease/disease as anything in diseases)
- // admin or special viruses that should not be reproduced
- if(disease.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS))
- continue
+ if (!gibs_type)
+ pixel_x = base_pixel_x
+ update_appearance()
+ return
- diseases_to_add += disease
- if(LAZYLEN(diseases_to_add))
- meatslab.AddComponent(/datum/component/infective, diseases_to_add)
- meatslab.forceMove(loc)
- meatslab.throw_at(pick(nearby_turfs), iteration, 3)
+ var/obj/effect/gibspawner/spawner = new gibs_type()
+ var/list/gibs = spawner.gibtypes
+ if (!length(gibs))
+ pixel_x = base_pixel_x
+ update_appearance()
+ return
- iteration++
+ for (var/i in 1 to length(results))
+ var/gib_dir = pick(GLOB.alldirs)
+ var/turf/gib_turf = get_step(src, gib_dir)
+ if (gib_turf.is_blocked_turf(TRUE))
+ continue
+ var/gib_type = pick(gibs)
+ var/obj/effect/decal/cleanable/blood/gibs/gib = new gib_type(gib_turf, diseases, blood_dna_info)
+ gib.streak(gib_dir)
- if (length(gibtypes))
- for (var/i in 1 to meat_produced**2) //2 slabs: 4 giblets, 3 slabs: 9, etc.
- var/gibdir = pick(GLOB.alldirs)
- var/turf/gibturf = get_step(src, gibdir)
- if (!gibturf.is_blocked_turf(exclude_mobs = TRUE))
- var/list/gibtype = pick(gibtypes)
- var/obj/effect/decal/cleanable/blood/gibs/gib = new gibtype(gibturf, diseases, blood_dna_info)
- gib.streak(gibdir)
-
- pixel_x = base_pixel_x //return to its spot after shaking
- operating = FALSE
+ pixel_x = base_pixel_x
update_appearance()
//auto-gibs anything that bumps into it
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 64fcb70c9fe..4fcad1b97af 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -40,10 +40,23 @@
voice_of_god_power = 1.4 //Command staff has authority
-
/datum/job/head_of_security/get_captaincy_announcement(mob/living/captain)
return "Due to staffing shortages, newly promoted Acting Captain [captain.real_name] on deck!"
+/datum/job/head_of_security/after_spawn(mob/living/spawned, client/player_client)
+ . = ..()
+ if(!ishuman(spawned) || !prob(PIG_COP_PROBABILITY))
+ return
+ var/mob/living/carbon/human/piggy = spawned
+ for (var/obj/item/bodypart/ham as anything in piggy.bodyparts)
+ // These are string lists
+ ham.butcher_drops = ham.butcher_drops.Copy()
+ for (var/meat_type in ham.butcher_drops)
+ if (!ispath(meat_type, /obj/item/food/meat/slab))
+ continue
+ ham.butcher_drops[/obj/item/food/meat/slab/pig] = ham.butcher_drops[meat_type]
+ ham.butcher_drops -= meat_type
+ ham.butcher_drops = string_list(ham.butcher_drops)
/datum/outfit/job/hos
name = "Head of Security"
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index bc8dcfe16a1..983d7a56c5a 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -57,6 +57,20 @@ GLOBAL_LIST_INIT(available_depts, list(SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, S
*/
GLOBAL_LIST_EMPTY(security_officer_distribution)
+/datum/job/security_officer/after_spawn(mob/living/spawned, client/player_client)
+ . = ..()
+ if(!ishuman(spawned) || !prob(PIG_COP_PROBABILITY))
+ return
+ var/mob/living/carbon/human/piggy = spawned
+ for (var/obj/item/bodypart/ham as anything in piggy.bodyparts)
+ // These are string lists
+ ham.butcher_drops = ham.butcher_drops.Copy()
+ for (var/meat_type in ham.butcher_drops)
+ if (!ispath(meat_type, /obj/item/food/meat/slab))
+ continue
+ ham.butcher_drops[/obj/item/food/meat/slab/pig] = ham.butcher_drops[meat_type]
+ ham.butcher_drops -= meat_type
+ ham.butcher_drops = string_list(ham.butcher_drops)
/datum/job/security_officer/after_roundstart_spawn(mob/living/spawning, client/player_client)
. = ..()
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index d71857d0502..c759552c049 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -42,6 +42,21 @@
rpg_title = "Jailor"
job_flags = STATION_JOB_FLAGS | JOB_BOLD_SELECT_TEXT | JOB_ANTAG_PROTECTED
+/datum/job/warden/after_spawn(mob/living/spawned, client/player_client)
+ . = ..()
+ if(!ishuman(spawned) || !prob(PIG_COP_PROBABILITY))
+ return
+ var/mob/living/carbon/human/piggy = spawned
+ for (var/obj/item/bodypart/ham as anything in piggy.bodyparts)
+ // These are string lists
+ ham.butcher_drops = ham.butcher_drops.Copy()
+ for (var/meat_type in ham.butcher_drops)
+ if (!ispath(meat_type, /obj/item/food/meat/slab))
+ continue
+ ham.butcher_drops[/obj/item/food/meat/slab/pig] = ham.butcher_drops[meat_type]
+ ham.butcher_drops -= meat_type
+ ham.butcher_drops = string_list(ham.butcher_drops)
+
/datum/outfit/job/warden
name = "Warden"
jobtype = /datum/job/warden
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 45e3364fd35..466b8d066de 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -362,7 +362,7 @@
if(prob(delta_dam * (1 + max(0, (damage - BRAIN_DAMAGE_MILD)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1% //learn how to do your bloody math properly goddamnit
gain_trauma_type(BRAIN_TRAUMA_MILD, natural_gain = TRUE)
- var/is_boosted = (owner && HAS_TRAIT(owner, TRAIT_SPECIAL_TRAUMA_BOOST))
+ var/is_boosted = (owner && HAS_TRAIT(src, TRAIT_SPECIAL_TRAUMA_BOOST))
if(damage < BRAIN_DAMAGE_SEVERE)
return
if(prob(delta_dam * (1 + max(0, (damage - BRAIN_DAMAGE_SEVERE)/100)))) //Base chance is the hit damage; for every point of damage past the threshold the chance is increased by 1%
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index fb155e0e419..4adc765d3a9 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -2575,6 +2575,9 @@ GLOBAL_LIST_EMPTY(fire_appearances)
/mob/living/proc/set_usable_hands(new_value)
if(usable_hands == new_value)
return
+ if(new_value < 0) // Sanity check
+ stack_trace("[src] had set_usable_hands() called on them with a negative value!")
+ new_value = 0
. = usable_hands
usable_hands = new_value
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 512aecdc28b..04005ff4eff 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -78,8 +78,8 @@
var/can_be_disabled = FALSE //Defaults to FALSE, as only human limbs can be disabled, and only the appendages.
///Controls if the limb is disabled. TRUE means it is disabled (similar to being removed, but still present for the sake of targeted interactions).
var/bodypart_disabled = FALSE
- ///Handles limb disabling by damage. If 0 (0%), a limb can't be disabled via damage. If 1 (100%), it is disabled at max limb damage. Anything between is the percentage of damage against maximum limb damage needed to disable the limb.
- var/disabling_threshold_percentage = 0
+ ///Handles limb disabling by damage. If LIMB_NO_DISABLE (-1), a limb can't be disabled via damage. If 1 (100%), it is disabled at max limb damage. Anything between is the percentage of damage against maximum limb damage needed to disable the limb.
+ var/disabling_threshold_percentage = LIMB_NO_DISABLE
// Damage variables
///A mutiplication of the burn and brute damage that the limb's stored damage contributes to its attached mob's overall wellbeing.
@@ -222,7 +222,15 @@
var/list/bodypart_effects
/// The cached info about the blood this organ belongs to, set during on_removal()
var/list/blood_dna_info
-
+ /// What items we drop whenever we're butchered
+ /// If unset, the bodyparot cannot be butchered
+ var/list/butcher_drops = null
+ /// What skeleton limb, if any, we replace ourselves with when butchered?
+ var/obj/item/bodypart/butcher_replacement = null
+ /// How much meat do we add to butcher_drops when automatically generating them from our species datum?
+ var/base_meat_amount = 1
+ /// Init cache for our butcher drops for sanic speed
+ var/static/list/butcher_drop_cache = list()
/// What state is the bodypart in for determining surgery availability
VAR_FINAL/surgery_state = NONE
@@ -268,6 +276,11 @@
add_surgical_state(innate_state)
name = "[limb_id] [parse_zone(body_zone)]"
+ // There's a lot of bodyparts in the world, and we don't need to have separate drops on each and every one of them
+ var/list/drop_results = get_butcher_drops()
+ if (length(drop_results))
+ butcher_drops = string_list(drop_results)
+ butcher_drop_cache[type] = butcher_drops
update_icon_dropped()
refresh_bleed_rate()
@@ -301,6 +314,16 @@
return FALSE
return ..()
+/obj/item/bodypart/proc/get_butcher_drops()
+ if(!isnull(butcher_drops))
+ return butcher_drops
+ if (butcher_drop_cache[type])
+ return butcher_drop_cache[type]
+ var/datum/species/species = GLOB.species_list[limb_id]
+ if (!species || !species.meat || !base_meat_amount)
+ return null
+ return list(species.meat = base_meat_amount)
+
/obj/item/bodypart/proc/on_forced_removal(atom/old_loc, dir, forced, list/old_locs)
SIGNAL_HANDLER
@@ -326,6 +349,10 @@
if(wound_desc)
. += wound_desc
+ var/surgery_examine = get_surgery_examine()
+ if(surgery_examine)
+ . += surgery_examine
+
/**
* Called when a bodypart is checked for injuries.
*/
@@ -475,8 +502,8 @@
/// Returns surgery examine information for this bodypart
/obj/item/bodypart/proc/get_surgery_examine()
- var/t_his = owner.p_their()
- var/t_His = owner.p_Their()
+ var/lowercase_zone = owner ? "[owner.p_their()] [plaintext_zone]" : "[src]"
+ var/capital_zone = owner ? "[owner.p_Their()] [plaintext_zone]" : capitalize("[src]")
var/single_message = ""
var/list/sub_messages = list()
var/reported_state = surgery_state
@@ -489,42 +516,42 @@
if(HAS_SURGERY_STATE(reported_state, SURGERY_SKIN_CUT))
sub_messages += "skin has been incised"
- single_message = "The skin on [t_his] [plaintext_zone] has been incised."
+ single_message = "The skin on [lowercase_zone] has been incised."
if(HAS_SURGERY_STATE(reported_state, SURGERY_SKIN_OPEN))
sub_messages += "skin is opened"
- single_message = "The skin on [t_his] [plaintext_zone] is opened."
+ single_message = "The skin on [lowercase_zone] is opened."
// We can only see these if the skin is open
// And we check the real state rather than reported_state
if(LIMB_HAS_ANY_SURGERY_STATE(src, ALL_SURGERY_SKIN_STATES))
if(HAS_SURGERY_STATE(reported_state, SURGERY_VESSELS_UNCLAMPED))
sub_messages += "blood vessels are unclamped[cached_bleed_rate ? " and bleeding" : ""]"
- single_message = "The blood vessels in [t_his] [plaintext_zone] are unclamped[cached_bleed_rate ? " and bleeding!" : "."]"
+ single_message = "The blood vessels in [lowercase_zone] are unclamped[cached_bleed_rate ? " and bleeding!" : "."]"
if(HAS_SURGERY_STATE(reported_state, SURGERY_VESSELS_CLAMPED))
sub_messages += "blood vessels are clamped shut"
- single_message = "The blood vessels in [t_his] [plaintext_zone] are clamped shut."
+ single_message = "The blood vessels in [lowercase_zone] are clamped shut."
if(HAS_SURGERY_STATE(reported_state, SURGERY_ORGANS_CUT))
sub_messages += "the organs within have been incised"
- single_message = "The organs in [t_his] [plaintext_zone] have been incised."
+ single_message = "The organs in [lowercase_zone] have been incised."
if(HAS_SURGERY_STATE(reported_state, SURGERY_BONE_SAWED))
sub_messages += "the bones within have been sawed apart"
- single_message = "The bones in [t_his] [plaintext_zone] have been sawed apart."
+ single_message = "The bones in [lowercase_zone] have been sawed apart."
if(HAS_SURGERY_STATE(reported_state, SURGERY_BONE_DRILLED))
sub_messages += "the bones within have been drilled through"
- single_message = "The bones in [t_his] [plaintext_zone] have been drilled through."
+ single_message = "The bones in [lowercase_zone] have been drilled through."
if(HAS_SURGERY_STATE(reported_state, SURGERY_PROSTHETIC_UNSECURED))
sub_messages += "prosthetic item is unsecured"
- single_message = "[t_His] [plaintext_zone] is unsecured and loose!"
+ single_message = "[capital_zone] is unsecured and loose!"
if(HAS_SURGERY_STATE(reported_state, SURGERY_PLASTIC_APPLIED))
sub_messages += "got a layer of plastic applied to it"
- single_message = "A layer of plastic has been applied to [t_his] [plaintext_zone]."
+ single_message = "A layer of plastic has been applied to [lowercase_zone]."
if(HAS_SURGERY_STATE(reported_state, SURGERY_CAVITY_WIDENED))
sub_messages += "the chest cavity is wide open"
- single_message = "[t_His] chest cavity is wide open!"
+ single_message = "[owner?.p_Their() || "The"] chest cavity is wide open!"
if(length(sub_messages) >= 2)
- return span_danger("[t_His] [plaintext_zone]'s [english_list(sub_messages)].")
+ return span_danger("[capital_zone]'s [english_list(sub_messages)].")
if(single_message)
return span_danger(single_message)
return ""
@@ -555,31 +582,34 @@
return
return ..()
-/obj/item/bodypart/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
- SHOULD_CALL_PARENT(TRUE)
-
- if(weapon.get_sharpness())
- add_fingerprint(user)
- if(!contents.len)
- to_chat(user, span_warning("There is nothing left inside [src]!"))
- return
- playsound(loc, 'sound/items/weapons/slice.ogg', 50, TRUE, -1)
- user.visible_message(span_warning("[user] begins to cut open [src]."),\
- span_notice("You begin to cut open [src]..."))
- if(do_after(user, 5.4 SECONDS, target = src))
- drop_organs(user, TRUE)
- else
- return ..()
-
/obj/item/bodypart/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
SHOULD_CALL_PARENT(TRUE)
- ..()
+ . = ..()
if(IS_ORGANIC_LIMB(src))
playsound(get_turf(src), 'sound/misc/splort.ogg', 50, TRUE, -1)
pixel_x = rand(-3, 3)
pixel_y = rand(-3, 3)
+/obj/item/bodypart/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(user.combat_mode)
+ return NONE
+
+ var/operation_zone = user.zone_selected
+ if (deprecise_zone(operation_zone) != body_zone)
+ operation_zone = body_zone
+ return user.perform_surgery(src, tool, LAZYACCESS(modifiers, RIGHT_CLICK), operation_zone)
+
+/obj/item/bodypart/examine_more(mob/user)
+ . = ..()
+ var/list/operations = GLOB.operations.get_instances_from(GLOB.operations.unlocked)
+ var/operation_zone = user.zone_selected
+ if (deprecise_zone(operation_zone) != body_zone)
+ operation_zone = body_zone
+ for(var/datum/surgery_operation/operation as anything in operations)
+ if ((operation.operation_flags & OPERATION_NO_PATIENT_REQUIRED) && operation.show_as_next_step(src, operation_zone))
+ . += span_notice("You could perform [operation] on [src] with \a [operation.get_recommended_tool()]...")
+
//empties the bodypart from its organs and other things inside it
/obj/item/bodypart/proc/drop_organs(mob/user, violent_removal)
SHOULD_CALL_PARENT(TRUE)
@@ -593,12 +623,18 @@
for(var/obj/item/organ/bodypart_organ in contents)
if(bodypart_organ.organ_flags & ORGAN_UNREMOVABLE)
continue
+
+ if(violent_removal)
+ bodypart_organ.apply_organ_damage(bodypart_organ.maxHealth * 0.5)
+
if(owner)
- bodypart_organ.Remove(bodypart_organ.owner)
- else
- if(bodypart_organ.bodypart_remove(src))
- if(drop_loc) //can be null if being deleted
- bodypart_organ.forceMove(get_turf(drop_loc))
+ if(!bodypart_organ.Remove(bodypart_organ.owner))
+ continue
+ else if(!bodypart_organ.bodypart_remove(src))
+ continue
+
+ if(drop_loc) //can be null if being deleted
+ bodypart_organ.forceMove(get_turf(drop_loc))
if(drop_loc) //can be null during deletion
for(var/atom/movable/movable as anything in src)
@@ -870,31 +906,31 @@
return brute_dam + burn_dam
//Checks disabled status thresholds
-/obj/item/bodypart/proc/update_disabled()
+/obj/item/bodypart/proc/update_disabled(update_limbs = TRUE)
SHOULD_CALL_PARENT(TRUE)
if(!owner)
return
if(!can_be_disabled)
- set_disabled(FALSE)
+ set_disabled(FALSE, update_limbs)
CRASH("update_disabled called with can_be_disabled false")
if(HAS_TRAIT(src, TRAIT_PARALYSIS))
- set_disabled(TRUE)
+ set_disabled(TRUE, update_limbs)
return
var/total_damage = brute_dam + burn_dam
// this block of checks is for limbs that can be disabled, but not through pure damage (AKA limbs that suffer wounds, human/monkey parts and such)
- if(!disabling_threshold_percentage)
+ if(disabling_threshold_percentage == LIMB_NO_DISABLE)
if(total_damage < max_damage)
last_maxed = FALSE
else
if(!last_maxed && owner.stat < UNCONSCIOUS)
INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), "scream")
last_maxed = TRUE
- set_disabled(FALSE) // we only care about the paralysis trait
+ set_disabled(FALSE, update_limbs) // we only care about the paralysis trait
return
// we're now dealing solely with limbs that can be disabled through pure damage, AKA robot parts
@@ -903,15 +939,15 @@
if(owner.stat < UNCONSCIOUS)
INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob, emote), "scream")
last_maxed = TRUE
- set_disabled(TRUE)
+ set_disabled(TRUE, update_limbs)
return
- if(bodypart_disabled && total_damage <= max_damage * 0.5) // reenable the limb at 50% health
+ if(bodypart_disabled && total_damage < max_damage * disabling_threshold_percentage * 0.5) // reenable the limb at 50% of the threshold
last_maxed = FALSE
- set_disabled(FALSE)
+ set_disabled(FALSE, update_limbs)
///Proc to change the value of the `disabled` variable and react to the event of its change.
-/obj/item/bodypart/proc/set_disabled(new_disabled)
+/obj/item/bodypart/proc/set_disabled(new_disabled, update_limbs = TRUE)
SHOULD_CALL_PARENT(TRUE)
PROTECTED_PROC(TRUE)
@@ -990,7 +1026,7 @@
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_NOBLOOD), PROC_REF(on_owner_nobleed_gain))
if(can_be_disabled)
- update_disabled()
+ update_disabled(FALSE)
RegisterSignal(owner, COMSIG_ATOM_RESTYLE, PROC_REF(on_attempt_feature_restyle_mob))
RegisterSignal(owner, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_owner_clean))
@@ -1458,14 +1494,14 @@
// In 99% of situations we won't get to this point if we aren't wired or blooded
// But I'm covering my ass in case someone adds some weird new species
+ var/surgery_bloodloss = 0
if(biological_state & BIOSTATE_HAS_VESSELS)
- var/surgery_bloodloss = 0
// better clamp those up quick
if(HAS_ANY_SURGERY_STATE(surgery_state, SURGERY_VESSELS_UNCLAMPED))
- surgery_bloodloss += 1.5
+ surgery_bloodloss += UNCLAMPED_VESSELS_BLEEDING
// better, but still not exactly ideal
else if(HAS_ANY_SURGERY_STATE(surgery_state, SURGERY_VESSELS_CLAMPED|SURGERY_ORGANS_CUT))
- surgery_bloodloss += 0.2
+ surgery_bloodloss += CLAMPED_VESSELS_BLEEDING
// modify rate so cutting everything open won't nuke people
if(body_zone == BODY_ZONE_HEAD)
@@ -1484,6 +1520,12 @@
for(var/datum/wound/iter_wound as anything in wounds)
cached_bleed_rate += iter_wound.blood_flow
+ if (!(iter_wound.surgery_states & SURGERY_VESSELS_UNCLAMPED) || !surgery_bloodloss)
+ continue
+ // Consider it contirubuted by the wound itself
+ // Not -surgery_bloodloss as this way clamping the vessels reduces the overall bleeding
+ cached_bleed_rate -= UNCLAMPED_VESSELS_BLEEDING
+ surgery_bloodloss = 0
if(owner.body_position == LYING_DOWN)
cached_bleed_rate *= 0.75
@@ -1700,9 +1742,6 @@
/obj/item/bodypart/proc/add_surgical_state(new_states)
if(!new_states)
CRASH("add_surgical_state called with no new states to add")
- if((surgery_state & new_states) == new_states)
- return
-
var/old_states = surgery_state
surgery_state |= new_states
update_surgical_state(old_states, new_states)
@@ -1730,11 +1769,16 @@
/// Called when surgical state changes so we can react to it
/obj/item/bodypart/proc/update_surgical_state(old_state, changed_states)
+ SEND_SIGNAL(src, COMSIG_BODYPART_UPDATING_SURGERY_STATE, old_state, surgery_state, changed_states)
+ if((surgery_state & changed_states) == changed_states)
+ return
+
if(HAS_ANY_SURGERY_STATE(changed_states, SURGERY_ORGANS_CUT|ALL_SURGERY_VESSEL_STATES))
refresh_bleed_rate()
if(isnull(owner))
return
+
SEND_SIGNAL(owner, COMSIG_LIVING_UPDATING_SURGERY_STATE, old_state, surgery_state, changed_states)
if(HAS_SURGERY_STATE(surgery_state, ALL_SURGERY_FISH_STATES(body_zone)))
owner.AddComponent(/datum/component/fishing_spot, /datum/fish_source/surgery) // no-op if they already have one
diff --git a/code/modules/surgery/bodyparts/ghetto_parts.dm b/code/modules/surgery/bodyparts/ghetto_parts.dm
index 5e8858c68c2..e22386b62e0 100644
--- a/code/modules/surgery/bodyparts/ghetto_parts.dm
+++ b/code/modules/surgery/bodyparts/ghetto_parts.dm
@@ -18,6 +18,7 @@
bodypart_flags = BODYPART_UNHUSKABLE
biological_state = (BIO_WOOD|BIO_JOINTED)
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
+ butcher_replacement = null
/obj/item/bodypart/arm/left/ghetto/Initialize(mapload, ...)
. = ..()
@@ -43,6 +44,7 @@
bodypart_flags = BODYPART_UNHUSKABLE
biological_state = (BIO_WOOD|BIO_JOINTED)
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
+ butcher_replacement = null
/obj/item/bodypart/arm/right/ghetto/Initialize(mapload, ...)
. = ..()
@@ -66,6 +68,7 @@
bodypart_flags = BODYPART_UNHUSKABLE
biological_state = (BIO_WOOD|BIO_JOINTED)
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
+ butcher_replacement = null
/obj/item/bodypart/leg/left/ghetto/Initialize(mapload, ...)
. = ..()
@@ -89,6 +92,7 @@
bodypart_flags = BODYPART_UNHUSKABLE
biological_state = (BIO_WOOD|BIO_JOINTED)
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
+ butcher_replacement = null
/obj/item/bodypart/leg/right/ghetto/Initialize(mapload, ...)
. = ..()
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 3b85fe54345..9e59cb744ec 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -25,6 +25,8 @@
unarmed_damage_high = 3
unarmed_effectiveness = 0
bodypart_trait_source = HEAD_TRAIT
+ butcher_replacement = /obj/item/bodypart/head/skeleton/nonfunctional
+ base_meat_amount = 0
/// Do we show the information about missing organs upon being examined? Defaults to TRUE, useful for Dullahan heads.
var/show_organs_on_examine = TRUE
@@ -104,6 +106,14 @@
QDEL_NULL(worn_face_offset)
return ..()
+/obj/item/bodypart/head/get_butcher_drops()
+ if(butcher_drops)
+ return butcher_drops
+ var/datum/species/species = GLOB.species_list[limb_id]
+ if (!species || !species.skinned_type)
+ return null
+ return list(species.skinned_type = 1)
+
/obj/item/bodypart/head/grind_results()
return null
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index 23e1056d716..026fb0b268b 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -12,6 +12,8 @@
px_y = 0
wound_resistance = 10
bodypart_trait_source = CHEST_TRAIT
+ base_meat_amount = 2
+ butcher_replacement = /obj/item/bodypart/chest/skeleton/nonfunctional
///The bodyshape(s) allowed to attach to this chest.
var/acceptable_bodyshape = BODYSHAPE_HUMANOID
///The bodytype(s) allowed to attach to this chest.
@@ -36,6 +38,17 @@
/// Which functional (i.e. flightpotion) wing types (if any) does this bodypart support? If count is >1 a radial menu is used to choose between all icons in list
var/list/wing_types = list(/obj/item/organ/wings/functional/angel)
+/obj/item/bodypart/chest/get_butcher_drops()
+ . = ..()
+ if(butcher_drops)
+ return
+ var/datum/species/species = GLOB.species_list[limb_id]
+ if (!species || !species.skinned_type)
+ return
+ if (!islist(.))
+ . = list()
+ .[species.skinned_type] = 1
+
/obj/item/bodypart/chest/grind_results()
return null
@@ -201,6 +214,25 @@
var/atom/movable/screen/inventory/hand/hand = new_owner.hud_used.hand_slots["[held_index]"]
hand?.update_appearance()
+/obj/item/bodypart/arm/set_disabled(new_disabled)
+ . = ..()
+ if(isnull(.) || !owner)
+ return
+
+ if(!.)
+ if(bodypart_disabled)
+ owner.set_usable_hands(owner.usable_hands - 1)
+ if(owner.stat < UNCONSCIOUS)
+ to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!"))
+ if(held_index)
+ owner.dropItemToGround(owner.get_item_for_held_index(held_index))
+ else if(!bodypart_disabled)
+ owner.set_usable_hands(owner.usable_hands + 1)
+
+ if(owner.hud_used)
+ var/atom/movable/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
+ hand_screen_object?.update_appearance()
+
/obj/item/bodypart/arm/left
name = "left arm"
desc = "Did you know that the word 'sinister' stems originally from the \
@@ -216,6 +248,7 @@
px_x = -6
px_y = 0
bodypart_trait_source = LEFT_ARM_TRAIT
+ butcher_replacement = /obj/item/bodypart/arm/left/skeleton/nonfunctional
/obj/item/bodypart/arm/left/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_ARM))
@@ -248,25 +281,6 @@
UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_ARM))
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_ARM), PROC_REF(on_owner_paralysis_gain))
-/obj/item/bodypart/arm/left/set_disabled(new_disabled)
- . = ..()
- if(isnull(.) || !owner)
- return
-
- if(!.)
- if(bodypart_disabled)
- owner.set_usable_hands(owner.usable_hands - 1)
- if(owner.stat < UNCONSCIOUS)
- to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!"))
- if(held_index)
- owner.dropItemToGround(owner.get_item_for_held_index(held_index))
- else if(!bodypart_disabled)
- owner.set_usable_hands(owner.usable_hands + 1)
-
- if(owner.hud_used)
- var/atom/movable/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
- hand_screen_object?.update_appearance()
-
/obj/item/bodypart/arm/left/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
@@ -316,6 +330,7 @@
px_x = 6
px_y = 0
bodypart_trait_source = RIGHT_ARM_TRAIT
+ butcher_replacement = /obj/item/bodypart/arm/right/skeleton/nonfunctional
/obj/item/bodypart/arm/right/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_ARM))
@@ -348,25 +363,6 @@
UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_R_ARM))
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_ARM), PROC_REF(on_owner_paralysis_gain))
-/obj/item/bodypart/arm/right/set_disabled(new_disabled)
- . = ..()
- if(isnull(.) || !owner)
- return
-
- if(!.)
- if(bodypart_disabled)
- owner.set_usable_hands(owner.usable_hands - 1)
- if(owner.stat < UNCONSCIOUS)
- to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!"))
- if(held_index)
- owner.dropItemToGround(owner.get_item_for_held_index(held_index))
- else if(!bodypart_disabled)
- owner.set_usable_hands(owner.usable_hands + 1)
-
- if(owner.hud_used)
- var/atom/movable/screen/inventory/hand/hand_screen_object = owner.hud_used.hand_slots["[held_index]"]
- hand_screen_object?.update_appearance()
-
/obj/item/bodypart/arm/right/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
@@ -449,6 +445,19 @@
QDEL_NULL(worn_foot_offset)
return ..()
+/obj/item/bodypart/leg/set_disabled(new_disabled, update_limbs = TRUE)
+ . = ..()
+ if(isnull(.) || !owner || !update_limbs)
+ return
+
+ if(!.)
+ if(bodypart_disabled)
+ owner.set_usable_legs(owner.usable_legs - 1)
+ if(owner.stat < UNCONSCIOUS)
+ to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!"))
+ else if(!bodypart_disabled)
+ owner.set_usable_legs(owner.usable_legs + 1)
+
/obj/item/bodypart/leg/left
name = "left leg"
desc = "Some athletes prefer to tie their left shoelaces first for good \
@@ -461,6 +470,7 @@
px_y = 12
can_be_disabled = TRUE
bodypart_trait_source = LEFT_LEG_TRAIT
+ butcher_replacement = /obj/item/bodypart/leg/left/skeleton/nonfunctional
/obj/item/bodypart/leg/left/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_LEG))
@@ -493,19 +503,6 @@
UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_PARALYSIS_L_LEG))
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_L_LEG), PROC_REF(on_owner_paralysis_gain))
-/obj/item/bodypart/leg/left/set_disabled(new_disabled)
- . = ..()
- if(isnull(.) || !owner)
- return
-
- if(!.)
- if(bodypart_disabled)
- owner.set_usable_legs(owner.usable_legs - 1)
- if(owner.stat < UNCONSCIOUS)
- to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!"))
- else if(!bodypart_disabled)
- owner.set_usable_legs(owner.usable_legs + 1)
-
/obj/item/bodypart/leg/left/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
@@ -552,6 +549,7 @@
px_x = 2
px_y = 12
bodypart_trait_source = RIGHT_LEG_TRAIT
+ butcher_replacement = /obj/item/bodypart/leg/right/skeleton/nonfunctional
/obj/item/bodypart/leg/right/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_LEG))
@@ -585,19 +583,6 @@
RegisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_PARALYSIS_R_LEG), PROC_REF(on_owner_paralysis_gain))
-/obj/item/bodypart/leg/right/set_disabled(new_disabled)
- . = ..()
- if(isnull(.) || !owner)
- return
-
- if(!.)
- if(bodypart_disabled)
- owner.set_usable_legs(owner.usable_legs - 1)
- if(owner.stat < UNCONSCIOUS)
- to_chat(owner, span_userdanger("You lose control of your [plaintext_zone]!"))
- else if(!bodypart_disabled)
- owner.set_usable_legs(owner.usable_legs + 1)
-
/obj/item/bodypart/leg/right/monkey
icon = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm
index 7b8ed90c238..5548d063461 100644
--- a/code/modules/surgery/bodyparts/robot_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm
@@ -44,6 +44,7 @@
damage_examines = list(BRUTE = ROBOTIC_BRUTE_EXAMINE_TEXT, BURN = ROBOTIC_BURN_EXAMINE_TEXT)
disabling_threshold_percentage = 1
bodypart_flags = BODYPART_UNHUSKABLE
+ butcher_replacement = null
/obj/item/bodypart/arm/right/robot
name = "cyborg right arm"
@@ -79,6 +80,7 @@
damage_examines = list(BRUTE = ROBOTIC_BRUTE_EXAMINE_TEXT, BURN = ROBOTIC_BURN_EXAMINE_TEXT)
bodypart_flags = BODYPART_UNHUSKABLE
+ butcher_replacement = null
/obj/item/bodypart/leg/left/robot
name = "cyborg left leg"
@@ -114,6 +116,7 @@
damage_examines = list(BRUTE = ROBOTIC_BRUTE_EXAMINE_TEXT, BURN = ROBOTIC_BURN_EXAMINE_TEXT)
bodypart_flags = BODYPART_UNHUSKABLE
+ butcher_replacement = null
/obj/item/bodypart/leg/left/robot/emp_effect(severity, protection)
. = ..()
@@ -163,6 +166,7 @@
damage_examines = list(BRUTE = ROBOTIC_BRUTE_EXAMINE_TEXT, BURN = ROBOTIC_BURN_EXAMINE_TEXT)
bodypart_flags = BODYPART_UNHUSKABLE
+ butcher_replacement = null
/obj/item/bodypart/leg/right/robot/emp_effect(severity, protection)
. = ..()
@@ -209,6 +213,7 @@
damage_examines = list(BRUTE = ROBOTIC_BRUTE_EXAMINE_TEXT, BURN = ROBOTIC_BURN_EXAMINE_TEXT)
bodypart_flags = BODYPART_UNHUSKABLE
+ butcher_replacement = null
robotic_emp_paralyze_damage_percent_threshold = 0.6
@@ -387,6 +392,7 @@
head_flags = HEAD_EYESPRITES
bodypart_flags = BODYPART_UNHUSKABLE
+ butcher_replacement = null
var/obj/item/assembly/flash/handheld/flash1 = null
var/obj/item/assembly/flash/handheld/flash2 = null
diff --git a/code/modules/surgery/bodyparts/species_parts/ghost_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/ghost_bodyparts.dm
index 765dd8d465b..f3247ab84dc 100644
--- a/code/modules/surgery/bodyparts/species_parts/ghost_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/ghost_bodyparts.dm
@@ -11,6 +11,7 @@
head_flags = HEAD_HAIR | HEAD_FACIAL_HAIR | HEAD_DEBRAIN
teeth_count = 0
+ butcher_replacement = null
/obj/item/bodypart/chest/ghost
icon = 'icons/mob/human/species/ghost.dmi'
@@ -24,6 +25,7 @@
should_draw_greyscale = FALSE
dmg_overlay_type = null
wing_types = null
+ butcher_replacement = null
/obj/item/bodypart/arm/left/ghost
icon = 'icons/mob/human/species/ghost.dmi'
@@ -34,6 +36,7 @@
limb_id = SPECIES_GHOST
should_draw_greyscale = FALSE
dmg_overlay_type = null
+ butcher_replacement = null
/obj/item/bodypart/arm/right/ghost
icon = 'icons/mob/human/species/ghost.dmi'
@@ -44,3 +47,4 @@
limb_id = SPECIES_GHOST
should_draw_greyscale = FALSE
dmg_overlay_type = null
+ butcher_replacement = null
diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
index 5ded9a2e6e5..113f5f3e936 100644
--- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
@@ -91,6 +91,7 @@
dmg_overlay_type = null
burn_modifier = 0.5 // = 1/2x generic burn damage
head_flags = HEAD_EYECOLOR | HEAD_EYESPRITES | HEAD_HAIR | HEAD_FACIAL_HAIR
+ butcher_replacement = null
/obj/item/bodypart/chest/jelly
biological_state = (BIO_FLESH|BIO_BLOODED)
@@ -99,6 +100,7 @@
dmg_overlay_type = null
burn_modifier = 0.5 // = 1/2x generic burn damage
wing_types = list(/obj/item/organ/wings/functional/slime)
+ butcher_replacement = null
/obj/item/bodypart/chest/jelly/get_butt_sprite()
return icon('icons/mob/butts.dmi', BUTT_SPRITE_SLIME)
@@ -108,24 +110,28 @@
limb_id = SPECIES_JELLYPERSON
dmg_overlay_type = null
burn_modifier = 0.5 // = 1/2x generic burn damage
+ butcher_replacement = null
/obj/item/bodypart/arm/right/jelly
biological_state = (BIO_FLESH|BIO_BLOODED)
limb_id = SPECIES_JELLYPERSON
dmg_overlay_type = null
burn_modifier = 0.5 // = 1/2x generic burn damage
+ butcher_replacement = null
/obj/item/bodypart/leg/left/jelly
biological_state = (BIO_FLESH|BIO_BLOODED)
limb_id = SPECIES_JELLYPERSON
dmg_overlay_type = null
burn_modifier = 0.5 // = 1/2x generic burn damage
+ butcher_replacement = null
/obj/item/bodypart/leg/right/jelly
biological_state = (BIO_FLESH|BIO_BLOODED)
limb_id = SPECIES_JELLYPERSON
dmg_overlay_type = null
burn_modifier = 0.5 // = 1/2x generic burn damage
+ butcher_replacement = null
///SLIME
/obj/item/bodypart/head/jelly/slime
@@ -377,6 +383,8 @@
dmg_overlay_type = null
head_flags = NONE
bodypart_flags = BODYPART_UNHUSKABLE
+ scarrable = FALSE
+ butcher_replacement = null
/obj/item/bodypart/chest/skeleton
biological_state = BIO_BONE
@@ -386,6 +394,8 @@
dmg_overlay_type = null
bodypart_flags = BODYPART_UNHUSKABLE
wing_types = list(/obj/item/organ/wings/functional/skeleton)
+ scarrable = FALSE
+ butcher_replacement = null
/obj/item/bodypart/arm/left/skeleton
biological_state = (BIO_BONE|BIO_JOINTED)
@@ -393,6 +403,8 @@
should_draw_greyscale = FALSE
dmg_overlay_type = null
bodypart_flags = BODYPART_UNHUSKABLE
+ scarrable = FALSE
+ butcher_replacement = null
/obj/item/bodypart/arm/right/skeleton
biological_state = (BIO_BONE|BIO_JOINTED)
@@ -400,6 +412,8 @@
should_draw_greyscale = FALSE
dmg_overlay_type = null
bodypart_flags = BODYPART_UNHUSKABLE
+ scarrable = FALSE
+ butcher_replacement = null
/obj/item/bodypart/leg/left/skeleton
biological_state = (BIO_BONE|BIO_JOINTED)
@@ -407,6 +421,8 @@
should_draw_greyscale = FALSE
dmg_overlay_type = null
bodypart_flags = BODYPART_UNHUSKABLE
+ scarrable = FALSE
+ butcher_replacement = null
/obj/item/bodypart/leg/right/skeleton
biological_state = (BIO_BONE|BIO_JOINTED)
@@ -414,6 +430,44 @@
should_draw_greyscale = FALSE
dmg_overlay_type = null
bodypart_flags = BODYPART_UNHUSKABLE
+ scarrable = FALSE
+ butcher_replacement = null
+
+/// Degloved bone "limbs"
+/obj/item/bodypart/head/skeleton/nonfunctional
+ limb_id = BODYPART_ID_BONE
+ // These are always disabled
+ disabling_threshold_percentage = 0
+
+/obj/item/bodypart/chest/skeleton/nonfunctional
+ limb_id = BODYPART_ID_BONE
+ disabling_threshold_percentage = 0
+
+/obj/item/bodypart/chest/skeleton/nonfunctional/on_adding(mob/living/carbon/new_owner)
+ . = ..()
+ // Treat people with bone chests as husks for all purposes for now
+ // Ideally husking should be per-bodypart but this simplifies a lot of behaviors
+ new_owner.become_husk(SKELETON_TRAIT)
+
+/obj/item/bodypart/chest/skeleton/nonfunctional/on_removal(mob/living/carbon/old_owner)
+ . = ..()
+ old_owner.cure_husk(SKELETON_TRAIT)
+
+/obj/item/bodypart/arm/left/skeleton/nonfunctional
+ limb_id = BODYPART_ID_BONE
+ disabling_threshold_percentage = 0
+
+/obj/item/bodypart/arm/right/skeleton/nonfunctional
+ limb_id = BODYPART_ID_BONE
+ disabling_threshold_percentage = 0
+
+/obj/item/bodypart/leg/left/skeleton/nonfunctional
+ limb_id = BODYPART_ID_BONE
+ disabling_threshold_percentage = 0
+
+/obj/item/bodypart/leg/right/skeleton/nonfunctional
+ limb_id = BODYPART_ID_BONE
+ disabling_threshold_percentage = 0
///MUSHROOM
/obj/item/bodypart/head/mushroom
@@ -496,6 +550,7 @@
teeth_count = 0
brute_modifier = 0.5
burn_modifier = 0.5
+ butcher_replacement = null
/obj/item/bodypart/head/golem/Initialize(mapload)
worn_ears_offset = new(
@@ -538,6 +593,7 @@
wing_types = null
brute_modifier = 0.5
burn_modifier = 0.5
+ butcher_replacement = null
/obj/item/bodypart/chest/golem/Initialize(mapload)
worn_belt_offset = new(
@@ -563,6 +619,7 @@
unarmed_effectiveness = 20
brute_modifier = 0.5
burn_modifier = 0.5
+ butcher_replacement = null
/obj/item/bodypart/arm/left/golem/Initialize(mapload)
held_hand_offset = new(
@@ -599,6 +656,7 @@
unarmed_effectiveness = 20
brute_modifier = 0.5
burn_modifier = 0.5
+ butcher_replacement = null
/obj/item/bodypart/arm/right/golem/Initialize(mapload)
held_hand_offset = new(
@@ -634,6 +692,7 @@
unarmed_effectiveness = 25
brute_modifier = 0.5
burn_modifier = 0.5
+ butcher_replacement = null
/obj/item/bodypart/leg/right/golem
icon = 'icons/mob/human/species/golems.dmi'
diff --git a/code/modules/surgery/operations/_operation.dm b/code/modules/surgery/operations/_operation.dm
index aa785b95713..ab8989494fb 100644
--- a/code/modules/surgery/operations/_operation.dm
+++ b/code/modules/surgery/operations/_operation.dm
@@ -2,23 +2,22 @@
* Attempts to perform a surgery with whatever tool is passed
*
* * src - the surgeon
- * * patient - the mob being operated on
+ * * operating_on - the main thing being operated on, usually a mob, but can be a detached limb
* * potential_tool - the tool being used for the operation (can be null / IMPLEMENT_HAND)
* * intentionally_fail - if TRUE, forces the operation to fail (for testing purposes)
*
* Returns an ITEM_INTERACT_* flag
*/
-/mob/living/proc/perform_surgery(mob/living/patient, potential_tool = IMPLEMENT_HAND, intentionally_fail = FALSE)
- if(DOING_INTERACTION(src, (HAS_TRAIT(src, TRAIT_HIPPOCRATIC_OATH) ? patient : DOAFTER_SOURCE_SURGERY)))
- patient.balloon_alert(src, "already performing surgery!")
+/mob/living/proc/perform_surgery(atom/movable/operating_on, potential_tool = IMPLEMENT_HAND, intentionally_fail = FALSE, operating_zone = zone_selected)
+ if(DOING_INTERACTION(src, (HAS_TRAIT(src, TRAIT_HIPPOCRATIC_OATH) ? operating_on : DOAFTER_SOURCE_SURGERY)))
+ operating_on.balloon_alert(src, "already performing surgery!")
return ITEM_INTERACT_BLOCKING
// allow cyborgs to use "hands"
if(istype(potential_tool, /obj/item/borg/cyborghug))
potential_tool = IMPLEMENT_HAND
- var/operating_zone = zone_selected
- var/list/operations = get_available_operations(patient, potential_tool, operating_zone)
+ var/list/operations = get_available_operations(operating_on, potential_tool, operating_zone)
// we failed to undertake any operations?
if(!length(operations))
@@ -26,7 +25,7 @@
return NONE
var/obj/item/realtool = potential_tool
// try self-cauterization if applicable
- if(src == patient)
+ if(src == operating_on)
var/manual_cauterization = try_manual_cauterize(realtool)
if(manual_cauterization & ITEM_INTERACT_ANY_BLOCKER)
return manual_cauterization
@@ -34,16 +33,25 @@
if(!(realtool.item_flags & SURGICAL_TOOL))
return NONE
// if the targeted limb isn't prepped for surgery, i suppose we can allow an attack
- var/obj/item/bodypart/operating = patient.get_bodypart(operating_zone)
+ var/obj/item/bodypart/operating = null
+ if (isbodypart(operating_on))
+ operating = operating_on
+ else if (isliving(operating_on))
+ var/mob/living/patient = operating_on
+ operating = patient.get_bodypart(operating_zone)
if(operating && !HAS_TRAIT(operating, TRAIT_READY_TO_OPERATE))
return NONE
// at this point we can be relatively sure they messed up so let's give a feedback message...
- if(!patient.is_location_accessible(operating_zone, IGNORED_OPERATION_CLOTHING_SLOTS))
- patient.balloon_alert(src, "operation site is obstructed!")
- else if(!IS_LYING_OR_CANNOT_LIE(patient))
- patient.balloon_alert(src, "not lying down!")
+ if (isliving(operating_on))
+ var/mob/living/patient = operating_on
+ if(!patient.is_location_accessible(operating_zone, IGNORED_OPERATION_CLOTHING_SLOTS))
+ patient.balloon_alert(src, "operation site is obstructed!")
+ else if(!IS_LYING_OR_CANNOT_LIE(patient))
+ patient.balloon_alert(src, "not lying down!")
+ else
+ patient.balloon_alert(src, "nothing to do with [realtool.name]!")
else
- patient.balloon_alert(src, "nothing to do with [realtool.name]!")
+ operating.balloon_alert(src, "nothing to do with [realtool.name]!")
// ...then, block attacking. prevents the surgeon from viciously stabbing the patient on a mistake
return ITEM_INTERACT_BLOCKING
@@ -55,29 +63,29 @@
var/picked = show_radial_menu(
user = src,
- anchor = patient,
+ anchor = operating_on,
choices = radial_operations,
require_near = TRUE,
autopick_single_option = TRUE,
radius = 56,
- custom_check = CALLBACK(src, PROC_REF(surgery_check), potential_tool, patient),
+ custom_check = CALLBACK(src, PROC_REF(surgery_check), potential_tool, operating_on),
)
if(isnull(picked))
return ITEM_INTERACT_BLOCKING
var/datum/surgery_operation/picked_op = operations[picked][1]
- var/atom/movable/operating_on = operations[picked][2]
+ var/atom/movable/operating = operations[picked][2]
var/list/op_info = operations[picked][3]
op_info[OPERATION_TARGET_ZONE] = operating_zone
op_info[OPERATION_FORCE_FAIL] = intentionally_fail
- return picked_op.try_perform(operating_on, src, potential_tool, op_info)
+ return picked_op.try_perform(operating, src, potential_tool, op_info)
/**
- * Returns a list of all surgery operations the mob can currently perform on the patient with the potential tool
+ * Returns a list of all surgery operations the mob can currently perform on the target with the potential tool
*
* * src - the surgeon
- * * patient - the mob being operated on
+ * * operating_on - the mob being operated on, or possibly a detached limb
* * potential_tool - the tool being used for the operation (can be null / IMPLEMENT_HAND)
* * operating_zone - the body zone being operated on
*
@@ -86,17 +94,17 @@
* * [1] - the atom being operated on
* * [2] - a list of option-specific info
*/
-/mob/living/proc/get_available_operations(mob/living/patient, potential_tool = IMPLEMENT_HAND, operating_zone = zone_selected)
+/mob/living/proc/get_available_operations(atom/movable/operating_on, potential_tool = IMPLEMENT_HAND, operating_zone = zone_selected)
// List of typepaths of operations we *can* do
var/list/possible_operations = GLOB.operations.unlocked.Copy()
// Signals can add operation types to the list to unlock special ones
- SEND_SIGNAL(src, COMSIG_LIVING_OPERATING_ON, patient, possible_operations)
- SEND_SIGNAL(patient, COMSIG_LIVING_BEING_OPERATED_ON, patient, possible_operations)
+ SEND_SIGNAL(src, COMSIG_LIVING_OPERATING_ON, operating_on, possible_operations)
+ SEND_SIGNAL(operating_on, COMSIG_ATOM_BEING_OPERATED_ON, src, possible_operations)
var/list/operations = list()
for(var/datum/surgery_operation/operation as anything in GLOB.operations.get_instances_from(possible_operations))
- var/atom/movable/operate_on = operation.get_operation_target(patient, operating_zone)
- if(!operation.check_availability(patient, operate_on, src, potential_tool, operating_zone))
+ var/atom/movable/operate_on = operation.get_operation_target(operating_on, operating_zone)
+ if(!operation.check_availability(isliving(operating_on) ? operating_on : null, operate_on, src, potential_tool, operating_zone))
continue
var/potential_options = operation.get_radial_options(operate_on, potential_tool, operating_zone)
if(!islist(potential_options))
@@ -111,15 +119,16 @@
return operations
/// Callback for checking if the surgery radial can be kept open
-/mob/living/proc/surgery_check(obj/item/tool, mob/living/patient)
+/mob/living/proc/surgery_check(obj/item/tool, atom/movable/operating_on)
var/obj/item/holding = get_active_held_item()
if(tool == IMPLEMENT_HAND)
return isnull(holding) || istype(holding, /obj/item/borg/cyborghug) // still holding nothing (or "hands")
+
if(QDELETED(holding))
return FALSE // i dunno, a stack item? not our problem
- return tool == holding.get_proxy_attacker_for(patient, src) // tool (or its proxy) is still being held
+ return tool == holding.get_proxy_attacker_for(operating_on, src) // tool (or its proxy) is still being held
/// src attempts to cauterize themselves to reset their surgery state. Basically a manual form of the real "close skin" operation
/mob/living/proc/try_manual_cauterize(obj/item/tool)
@@ -463,13 +472,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
SHOULD_NOT_SLEEP(TRUE)
SHOULD_BE_PURE(TRUE)
- if(isnull(patient) || isnull(operating_on))
- return FALSE
-
- if(!(operation_flags & OPERATION_STANDING_ALLOWED) && !IS_LYING_OR_CANNOT_LIE(patient))
- return FALSE
-
- if(!(operation_flags & OPERATION_SELF_OPERABLE) && patient == surgeon && !HAS_TRAIT(surgeon, TRAIT_SELF_SURGERY))
+ if(isnull(operating_on))
return FALSE
if(get_tool_quality(tool) <= 0)
@@ -478,6 +481,17 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
if(!is_available(operating_on, operated_zone))
return FALSE
+ if(isnull(patient))
+ if(operation_flags & OPERATION_NO_PATIENT_REQUIRED)
+ return snowflake_check_availability(operating_on, surgeon, tool, operated_zone)
+ return FALSE
+
+ if(!(operation_flags & OPERATION_STANDING_ALLOWED) && !IS_LYING_OR_CANNOT_LIE(patient))
+ return FALSE
+
+ if(!(operation_flags & OPERATION_SELF_OPERABLE) && patient == surgeon && !HAS_TRAIT(surgeon, TRAIT_SELF_SURGERY))
+ return FALSE
+
return snowflake_check_availability(operating_on, surgeon, tool, operated_zone)
/**
@@ -537,10 +551,15 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
if(any_surgery_states_blocked && has_any_surgery_state(operating_on, any_surgery_states_blocked))
return FALSE
+ var/mob/living/patient = get_patient(operating_on)
+ if(isnull(patient))
+ if (!(operation_flags & OPERATION_NO_PATIENT_REQUIRED))
+ return FALSE
+ return state_check(operating_on)
+
if(!state_check(operating_on))
return FALSE
- var/mob/living/patient = get_patient(operating_on)
if(!(operation_flags & OPERATION_IGNORE_CLOTHES) && !patient.is_location_accessible(operated_zone, IGNORED_OPERATION_CLOTHING_SLOTS))
return FALSE
@@ -723,11 +742,12 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
// Ignore alllll the penalties (but also all the bonuses)
if(!HAS_TRAIT(surgeon, TRAIT_IGNORE_SURGERY_MODIFIERS))
var/mob/living/patient = get_patient(operating_on)
- total_mod *= get_location_modifier(get_turf(patient))
total_mod *= get_morbid_modifier(surgeon, tool)
- total_mod *= get_mob_surgery_speed_mod(patient)
+ if(!isnull(patient)) // Some surgeries can lack patients
+ total_mod *= get_location_modifier(get_turf(patient))
+ total_mod *= get_mob_surgery_speed_mod(patient)
// Using TRAIT_SELF_SURGERY on a surgery which doesn't normally allow self surgery imparts a penalty
- if(patient == surgeon && HAS_TRAIT(surgeon, TRAIT_SELF_SURGERY) && !(operation_flags & OPERATION_SELF_OPERABLE))
+ if(operating_on == surgeon && HAS_TRAIT(surgeon, TRAIT_SELF_SURGERY) && !(operation_flags & OPERATION_SELF_OPERABLE))
total_mod *= 1.5
return round(total_mod, 0.01)
@@ -766,6 +786,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
/obj/structure/table/optable/abductor = 0.85,
/obj/machinery/stasis = 1.15,
/obj/structure/bed = 1.5,
+ /obj/structure/kitchenspike = 1.5,
))
var/mod = 2.0
for(var/obj/thingy in operation_turf)
@@ -777,19 +798,19 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
* Determines what gets passed into the try_perform() proc
* If null is returned, the operation cannot be performed
*
- * * patient - The mob being operated on
+ * * operating_on - The mob or detached bodypart being operated on
* * body_zone - The body zone being operated on
*
* Returns the atom/movable being operated on
*/
-/datum/surgery_operation/proc/get_operation_target(mob/living/patient, body_zone)
- return patient
+/datum/surgery_operation/proc/get_operation_target(atom/movable/operating_on, body_zone)
+ return operating_on
/**
* Called by operating computers to hint that this surgery could come next given the target's current state
*/
-/datum/surgery_operation/proc/show_as_next_step(mob/living/potential_patient, operated_zone)
- var/atom/movable/operate_on = get_operation_target(potential_patient, operated_zone)
+/datum/surgery_operation/proc/show_as_next_step(atom/movable/potential_target, operated_zone)
+ var/atom/movable/operate_on = get_operation_target(potential_target, operated_zone)
return !isnull(operate_on) && is_available(operate_on, operated_zone)
@@ -809,14 +830,17 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
if(!check_availability(patient, operating_on, surgeon, tool, operation_args[OPERATION_TARGET_ZONE]))
return ITEM_INTERACT_BLOCKING
+
if(!start_operation(operating_on, surgeon, tool, operation_args))
return ITEM_INTERACT_BLOCKING
- var/was_sleeping = (patient.stat != DEAD && HAS_TRAIT(patient, TRAIT_KNOCKEDOUT))
+ var/was_sleeping = FALSE
var/result = NONE
- update_surgery_mood(patient, SURGERY_STATE_STARTED)
- SEND_SIGNAL(patient, COMSIG_LIVING_SURGERY_STARTED, src, operating_on, tool)
+ if(patient)
+ was_sleeping = (patient.stat != DEAD && HAS_TRAIT(patient, TRAIT_KNOCKEDOUT))
+ update_surgery_mood(patient, SURGERY_STATE_STARTED)
+ SEND_SIGNAL(patient, COMSIG_ATOM_SURGERY_STARTED, src, operating_on, tool)
do
operation_args[OPERATION_SPEED] = get_time_modifiers(operating_on, surgeon, tool)
@@ -825,20 +849,23 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
user = surgeon,
// Actual delay is capped - think of the excess time as being added to failure chance instead
delay = time * min(operation_args[OPERATION_SPEED], SURGERY_MODIFIER_FAILURE_THRESHOLD),
- target = patient,
+ target = patient || operating_on,
extra_checks = CALLBACK(src, PROC_REF(operate_check), patient, operating_on, surgeon, tool, operation_args),
// You can only operate on one mob at a time without a hippocratic oath
- interaction_key = HAS_TRAIT(surgeon, TRAIT_HIPPOCRATIC_OATH) ? patient : DOAFTER_SOURCE_SURGERY,
+ interaction_key = HAS_TRAIT(surgeon, TRAIT_HIPPOCRATIC_OATH) ? (patient || operating_on) : DOAFTER_SOURCE_SURGERY,
))
result |= ITEM_INTERACT_BLOCKING
- update_surgery_mood(patient, SURGERY_STATE_FAILURE)
+ if (patient)
+ update_surgery_mood(patient, SURGERY_STATE_FAILURE)
break
- if(ishuman(surgeon))
- var/mob/living/carbon/human/surgeon_human = surgeon
- surgeon_human.add_blood_DNA_to_items(patient.get_blood_dna_list(), ITEM_SLOT_GLOVES)
- else
- surgeon.add_mob_blood(patient)
+ var/list/patient_dna = get_patient_blood_dna(patient, operating_on)
+ if (patient_dna)
+ if (ishuman(surgeon))
+ var/mob/living/carbon/human/surgeon_human = surgeon
+ surgeon_human.add_blood_DNA_to_items(patient_dna, ITEM_SLOT_GLOVES)
+ else
+ surgeon.add_blood_DNA(patient_dna)
// This will annoy doctors immensely
// if(isitem(tool))
@@ -860,11 +887,13 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
if(operation_args[OPERATION_FORCE_FAIL] || prob(clamp(GET_FAILURE_CHANCE(time, operation_args[OPERATION_SPEED]), 0, 99)))
failure(operating_on, surgeon, tool, operation_args)
result |= ITEM_INTERACT_FAILURE
- update_surgery_mood(patient, SURGERY_STATE_FAILURE)
+ if (patient)
+ update_surgery_mood(patient, SURGERY_STATE_FAILURE)
else
success(operating_on, surgeon, tool, operation_args)
result |= ITEM_INTERACT_SUCCESS
- update_surgery_mood(patient, SURGERY_STATE_SUCCESS)
+ if (patient)
+ update_surgery_mood(patient, SURGERY_STATE_SUCCESS)
if(isstack(tool))
var/obj/item/stack/tool_stack = tool
@@ -872,13 +901,27 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
while ((operation_flags & OPERATION_LOOPING) && can_loop(patient, operating_on, surgeon, tool, operation_args))
- SEND_SIGNAL(patient, COMSIG_LIVING_SURGERY_FINISHED, src, operating_on, tool)
+ if (!patient)
+ return result
+ SEND_SIGNAL(patient, COMSIG_ATOM_SURGERY_FINISHED, src, operating_on, tool)
if(patient.stat == DEAD && was_sleeping)
surgeon.client?.give_award(/datum/award/achievement/jobs/sandman, surgeon)
return result
+/datum/surgery_operation/proc/get_patient_blood_dna(mob/living/patient, atom/movable/operating_on)
+ if (patient)
+ return patient.get_blood_dna_list()
+
+ if (isbodypart(operating_on))
+ var/obj/item/bodypart/limb = operating_on
+ return limb.blood_dna_info?.Copy()
+
+ if (isorgan(operating_on))
+ var/obj/item/organ/guts = operating_on
+ return guts.blood_dna_info?.Copy()
+
/// Called after an operation to check if it can be repeated/looped
/datum/surgery_operation/proc/can_loop(mob/living/patient, atom/movable/operating_on, mob/living/surgeon, tool, list/operation_args)
PROTECTED_PROC(TRUE)
@@ -893,7 +936,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
if(tool_stack.amount <= 0)
return FALSE
- if(!surgeon.surgery_check(tool, patient))
+ if(!surgeon.surgery_check(tool, (operation_flags & OPERATION_NO_PATIENT_REQUIRED) ? patient : (patient || operating_on)))
return FALSE
if(!check_availability(patient, operating_on, surgeon, tool, operation_args[OPERATION_TARGET_ZONE]))
@@ -929,9 +972,15 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
vision_distance = 1,
ignored_mobs = target_detailed ? null : target
)
+
if(target_detailed)
return
+ if (!target)
+ if (operation_flags & OPERATION_NO_PATIENT_REQUIRED)
+ return
+ CRASH("[type] operation attempted to call display_results without a patient, despite requiring one!")
+
var/you_feel = pick("a brief pain", "your body tense up", "an unnerving sensation")
target.show_message(
msg = vague_message || detailed_message || span_notice("You feel [you_feel] as you are operated on."),
@@ -947,6 +996,11 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
if(!pain_message)
return
+ if (!target)
+ if (operation_flags & OPERATION_NO_PATIENT_REQUIRED)
+ return
+ CRASH("[type] operation attempted to call display_pain without a patient, despite requiring one!")
+
// Determine how drunk our patient is
var/drunken_patient = target.get_drunk_amount()
// Create a probability to ignore the pain based on drunkenness level
@@ -1043,7 +1097,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
if(!pre_preop(operating_on, surgeon, tool, operation_args))
return FALSE
// if pre_preop slept, sanity check that everything is still valid
- if(preop_time != world.time && (patient != get_patient(operating_on) || !surgeon.Adjacent(patient) || !surgeon.is_holding(tool) || !operate_check(patient, operating_on, surgeon, tool, operation_args)))
+ if(preop_time != world.time && (patient != get_patient(operating_on) || !surgeon.Adjacent(patient || operating_on) || !surgeon.is_holding(tool) || !operate_check(patient, operating_on, surgeon, tool, operation_args)))
return FALSE
play_operation_sound(operating_on, surgeon, tool, preop_sound)
@@ -1056,13 +1110,13 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
/datum/surgery_operation/proc/on_preop(atom/movable/operating_on, mob/living/surgeon, tool, list/operation_args)
PROTECTED_PROC(TRUE)
var/mob/living/patient = get_patient(operating_on)
-
+ var/atom/movable/display_target = patient || operating_on
display_results(
surgeon,
patient,
- span_notice("You begin to operate on [patient]..."),
- span_notice("[surgeon] begins to operate on [patient]."),
- span_notice("[surgeon] begins to operate on [patient]."),
+ span_notice("You begin to operate on [display_target]..."),
+ span_notice("[surgeon] begins to operate on [display_target]."),
+ span_notice("[surgeon] begins to operate on [display_target]."),
)
/**
@@ -1078,7 +1132,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
SSblackbox.record_feedback("tally", "surgeries_completed", 1, type)
surgeon.add_mob_memory(/datum/memory/surgery, deuteragonist = surgeon, surgery_type = name)
- SEND_SIGNAL(surgeon, COMSIG_LIVING_SURGERY_SUCCESS, src, operating_on, tool)
+ SEND_SIGNAL(surgeon, COMSIG_ATOM_SURGERY_SUCCESS, src, operating_on, tool)
play_operation_sound(operating_on, surgeon, tool, success_sound)
on_success(operating_on, surgeon, tool, operation_args)
@@ -1109,7 +1163,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
if(operation_flags & OPERATION_NOTABLE)
SSblackbox.record_feedback("tally", "surgeries_failed", 1, type)
- SEND_SIGNAL(surgeon, COMSIG_LIVING_SURGERY_FAILED, src, operating_on, tool)
+ SEND_SIGNAL(surgeon, COMSIG_ATOM_SURGERY_FAILED, src, operating_on, tool)
play_operation_sound(operating_on, surgeon, tool, failure_sound)
on_failure(operating_on, surgeon, tool, operation_args)
@@ -1239,8 +1293,13 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
else if(required_bodytype & BODYTYPE_ORGANIC)
. += "the limb must not be cybernetic"
-/datum/surgery_operation/limb/get_operation_target(mob/living/patient, body_zone)
- return patient.get_bodypart(deprecise_zone(body_zone))
+/datum/surgery_operation/limb/get_operation_target(atom/movable/operating_on, body_zone)
+ if (isliving(operating_on))
+ var/mob/living/patient = operating_on
+ return patient.get_bodypart(deprecise_zone(body_zone))
+ if (!isbodypart(operating_on))
+ return null
+ return operating_on
/datum/surgery_operation/limb/is_available(obj/item/bodypart/limb, operated_zone)
SHOULD_NOT_OVERRIDE(TRUE)
@@ -1248,10 +1307,9 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
// targeting groin will redirect you to the chest
if(limb.body_zone != deprecise_zone(operated_zone))
return FALSE
+
if(required_bodytype && !(limb.bodytype & required_bodytype))
return FALSE
- if(!HAS_TRAIT(limb, TRAIT_READY_TO_OPERATE))
- return FALSE
return ..()
@@ -1292,8 +1350,15 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
/datum/surgery_operation/organ/get_default_radial_image()
return get_generic_limb_radial_image(target_type::zone)
-/datum/surgery_operation/organ/get_operation_target(mob/living/patient, body_zone)
- return patient.get_organ_by_type(target_type)
+/datum/surgery_operation/organ/get_operation_target(atom/movable/operating_on, body_zone)
+ if (isliving(operating_on))
+ var/mob/living/patient = operating_on
+ return patient.get_organ_by_type(target_type)
+
+ if (!isbodypart(operating_on))
+ return null
+
+ return locate(target_type) in operating_on
/datum/surgery_operation/organ/get_patient(obj/item/organ/organ)
return organ.owner
diff --git a/code/modules/surgery/operations/operation_add_limb.dm b/code/modules/surgery/operations/operation_add_limb.dm
index 744fdbef880..018e32d1067 100644
--- a/code/modules/surgery/operations/operation_add_limb.dm
+++ b/code/modules/surgery/operations/operation_add_limb.dm
@@ -10,7 +10,8 @@
)
time = 3.2 SECONDS
operation_flags = OPERATION_STANDING_ALLOWED | OPERATION_PRIORITY_NEXT_STEP | OPERATION_NOTABLE | OPERATION_IGNORE_CLOTHES
- all_surgery_states_required = SURGERY_SKIN_OPEN | SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/// List of items that are always allowed to be an arm replacement, even if they fail another requirement.
var/list/always_accepted_prosthetics = list(
/obj/item/chainsaw, // the OG, too large otherwise
@@ -49,7 +50,10 @@
return option
-/datum/surgery_operation/prosthetic_replacement/get_operation_target(mob/living/patient, body_zone)
+/datum/surgery_operation/prosthetic_replacement/get_operation_target(atom/movable/operating_on, body_zone)
+ if (!isliving(operating_on))
+ return null
+ var/mob/living/patient = operating_on
// We always operate on the chest even if we're targeting left leg or w/e
return patient.get_bodypart(BODY_ZONE_CHEST)
diff --git a/code/modules/surgery/operations/operation_amputation.dm b/code/modules/surgery/operations/operation_amputation.dm
index 48b7962c9ed..100f9f42862 100644
--- a/code/modules/surgery/operations/operation_amputation.dm
+++ b/code/modules/surgery/operations/operation_amputation.dm
@@ -20,7 +20,8 @@
/obj/item = 'sound/items/handling/surgery/scalpel1.ogg',
)
success_sound = 'sound/items/handling/surgery/organ2.ogg'
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/limb/amputate/get_recommended_tool()
return TOOL_SAW
diff --git a/code/modules/surgery/operations/operation_bioware.dm b/code/modules/surgery/operations/operation_bioware.dm
index 1ea6f7ea820..084ba2903c6 100644
--- a/code/modules/surgery/operations/operation_bioware.dm
+++ b/code/modules/surgery/operations/operation_bioware.dm
@@ -247,9 +247,25 @@
name = "cortex folding"
rnd_name = "Encephalofractoplasty (Cortex Folding)" // it's a stretch - "brain fractal reshaping"
desc = "A biological upgrade which folds a patient's cerebral cortex into a fractal pattern, increasing neural density and flexibility."
- status_effect_gained = /datum/status_effect/bioware/cortex/folded
+ operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_NOTABLE | OPERATION_MORBID | OPERATION_LOCKED | OPERATION_NO_PATIENT_REQUIRED
+ status_effect_gained = /datum/status_effect/bioware/cortex // Not actually applied, simply for compatibility checks
required_zone = BODY_ZONE_HEAD
+/datum/surgery_operation/limb/bioware/cortex_folding/state_check(obj/item/bodypart/limb)
+ . = ..()
+ if (!.)
+ return
+ var/obj/item/organ/brain/brain = locate() in limb
+ if(isnull(brain))
+ return FALSE
+ return !HAS_TRAIT_FROM(brain, TRAIT_SPECIAL_TRAUMA_BOOST, BIOWARE_TRAIT)
+
+/datum/surgery_operation/limb/bioware/cortex_folding/on_success(obj/item/bodypart/limb, mob/living/surgeon, tool, list/operation_args)
+ . = ..()
+ var/obj/item/organ/brain/brain = locate() in limb
+ if(!isnull(brain))
+ ADD_TRAIT(brain, TRAIT_SPECIAL_TRAUMA_BOOST, BIOWARE_TRAIT)
+
/datum/surgery_operation/limb/bioware/cortex_folding/on_preop(obj/item/bodypart/limb, mob/living/surgeon, tool)
display_results(
surgeon,
@@ -272,7 +288,8 @@
display_pain(limb.owner, "Your brain feels stronger... and more flexible!")
/datum/surgery_operation/limb/bioware/cortex_folding/on_failure(obj/item/bodypart/limb, mob/living/surgeon, tool)
- if(!limb.owner.get_organ_slot(ORGAN_SLOT_BRAIN))
+ var/obj/item/organ/brain/brain = locate() in limb
+ if(isnull(brain))
return ..()
display_results(
surgeon,
@@ -282,8 +299,8 @@
span_notice("[surgeon] completes the surgery on [limb.owner]'s brain."),
)
display_pain(limb.owner, "Your head throbs with excruciating pain!")
- limb.owner.adjust_organ_loss(ORGAN_SLOT_BRAIN, 60)
- limb.owner.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_LOBOTOMY)
+ brain.apply_organ_damage(60)
+ brain.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_LOBOTOMY)
/datum/surgery_operation/limb/bioware/cortex_folding/mechanic
rnd_name = "Wetware OS Labyrinthian Programming (Cortex Folding)"
diff --git a/code/modules/surgery/operations/operation_bone_repair.dm b/code/modules/surgery/operations/operation_bone_repair.dm
index 47ec6dbf44f..2129d1b969e 100644
--- a/code/modules/surgery/operations/operation_bone_repair.dm
+++ b/code/modules/surgery/operations/operation_bone_repair.dm
@@ -1,7 +1,7 @@
/datum/surgery_operation/limb/repair_hairline
name = "repair hairline fracture"
desc = "Mend a hairline fracture in a patient's bone."
- operation_flags = OPERATION_PRIORITY_NEXT_STEP
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP | OPERATION_NO_PATIENT_REQUIRED
implements = list(
TOOL_BONESET = 1,
/obj/item/stack/medical/bone_gel = 1,
@@ -27,9 +27,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to repair the fracture in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to repair the fracture in [limb.owner]'s [limb.plaintext_zone] with [tool]."),
- span_notice("[surgeon] begins to repair the fracture in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to repair the fracture in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to repair the fracture in [FORMAT_LIMB_OWNER(limb)] with [tool]."),
+ span_notice("[surgeon] begins to repair the fracture in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "Your [limb.plaintext_zone] aches with pain!")
@@ -40,15 +40,15 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully repair the fracture in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully repairs the fracture in [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] successfully repairs the fracture in [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You successfully repair the fracture in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully repairs the fracture in [FORMAT_LIMB_OWNER(limb)]!"),
+ span_notice("[surgeon] successfully repairs the fracture in [FORMAT_LIMB_OWNER(limb)]!"),
)
/datum/surgery_operation/limb/reset_compound
name = "reset compound fracture"
desc = "Reset a compound fracture in a patient's bone, preparing it for proper healing."
- operation_flags = OPERATION_PRIORITY_NEXT_STEP
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP | OPERATION_NO_PATIENT_REQUIRED
implements = list(
TOOL_BONESET = 1,
/obj/item/stack/sticky_tape/surgical = 1.66,
@@ -56,7 +56,8 @@
/obj/item/stack/sticky_tape = 5,
)
time = 6 SECONDS
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/limb/reset_compound/get_default_radial_image()
return image(/obj/item/bonesetter)
@@ -74,9 +75,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to reset the bone in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to reset the bone in [limb.owner]'s [limb.plaintext_zone] with [tool]."),
- span_notice("[surgeon] begins to reset the bone in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to reset the bone in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to reset the bone in [FORMAT_LIMB_OWNER(limb)] with [tool]."),
+ span_notice("[surgeon] begins to reset the bone in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "The aching pain in your [limb.plaintext_zone] is overwhelming!")
@@ -87,15 +88,15 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully reset the bone in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully resets the bone in [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] successfully resets the bone in [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You successfully reset the bone in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully resets the bone in [FORMAT_LIMB_OWNER(limb)] with [tool]!"),
+ span_notice("[surgeon] successfully resets the bone in [FORMAT_LIMB_OWNER(limb)]!"),
)
/datum/surgery_operation/limb/repair_compound
name = "repair compound fracture"
desc = "Mend a compound fracture in a patient's bone."
- operation_flags = OPERATION_PRIORITY_NEXT_STEP
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP | OPERATION_NO_PATIENT_REQUIRED
implements = list(
/obj/item/stack/medical/bone_gel = 1,
/obj/item/stack/sticky_tape/surgical = 1,
@@ -121,9 +122,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to repair the fracture in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to repair the fracture in [limb.owner]'s [limb.plaintext_zone] with [tool]."),
- span_notice("[surgeon] begins to repair the fracture in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to repair the fracture in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to repair the fracture in [FORMAT_LIMB_OWNER(limb)] with [tool]."),
+ span_notice("[surgeon] begins to repair the fracture in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "The aching pain in your [limb.plaintext_zone] is overwhelming!")
@@ -133,15 +134,15 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully repair the fracture in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully repairs the fracture in [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] successfully repairs the fracture in [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You successfully repair the fracture in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully repairs the fracture in [FORMAT_LIMB_OWNER(limb)] with [tool]!"),
+ span_notice("[surgeon] successfully repairs the fracture in [FORMAT_LIMB_OWNER(limb)]!"),
)
/datum/surgery_operation/limb/prepare_cranium_repair
name = "discard skull debris"
desc = "Clear away bone fragments and debris from a patient's cranial fissure in preparation for repair."
- operation_flags = OPERATION_PRIORITY_NEXT_STEP
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP | OPERATION_NO_PATIENT_REQUIRED
implements = list(
TOOL_HEMOSTAT = 1,
TOOL_WIRECUTTER = 2.5,
@@ -166,9 +167,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to discard the smaller skull debris in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to discard the smaller skull debris in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to poke around in [limb.owner]'s [limb.plaintext_zone]..."),
+ span_notice("You begin to discard the smaller skull debris in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to discard the smaller skull debris in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to poke around in [FORMAT_LIMB_OWNER(limb)]..."),
)
display_pain(limb.owner, "Your brain feels like it's getting stabbed by little shards of glass!")
@@ -180,7 +181,7 @@
/datum/surgery_operation/limb/repair_cranium
name = "repair cranium"
desc = "Mend a cranial fissure in a patient's skull."
- operation_flags = OPERATION_PRIORITY_NEXT_STEP
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP | OPERATION_NO_PATIENT_REQUIRED
implements = list(
/obj/item/stack/medical/bone_gel = 1,
/obj/item/stack/sticky_tape/surgical = 1,
@@ -205,9 +206,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to repair [limb.owner]'s skull as best you can..."),
- span_notice("[surgeon] begins to repair [limb.owner]'s skull with [tool]."),
- span_notice("[surgeon] begins to repair [limb.owner]'s skull."),
+ span_notice("You begin to repair [limb.owner || limb]'s skull as best you can..."),
+ span_notice("[surgeon] begins to repair [limb.owner || limb]'s skull with [tool]."),
+ span_notice("[surgeon] begins to repair [limb.owner || limb]'s skull."),
)
display_pain(limb.owner, "You can feel pieces of your skull rubbing against your brain!")
@@ -219,7 +220,7 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully repair [limb.owner]'s skull."),
- span_notice("[surgeon] successfully repairs [limb.owner]'s skull with [tool]."),
- span_notice("[surgeon] successfully repairs [limb.owner]'s skull.")
+ span_notice("You successfully repair [limb.owner || limb]'s skull."),
+ span_notice("[surgeon] successfully repairs [limb.owner || limb]'s skull with [tool]."),
+ span_notice("[surgeon] successfully repairs [limb.owner || limb]'s skull.")
)
diff --git a/code/modules/surgery/operations/operation_brainwash.dm b/code/modules/surgery/operations/operation_brainwash.dm
index eece83b3142..2f74ce14602 100644
--- a/code/modules/surgery/operations/operation_brainwash.dm
+++ b/code/modules/surgery/operations/operation_brainwash.dm
@@ -73,7 +73,7 @@
span_notice("[surgeon] completes the surgery on [organ.owner]'s brain."),
)
display_pain(organ.owner, "Your head throbs with horrible pain!")
- organ.owner.adjust_organ_loss(ORGAN_SLOT_BRAIN, 40)
+ organ.apply_organ_damage(40)
/datum/surgery_operation/organ/brainwash/mechanic
name = "reprogram"
diff --git a/code/modules/surgery/operations/operation_cavity_implant.dm b/code/modules/surgery/operations/operation_cavity_implant.dm
index 8c54a19eed1..c6032e93403 100644
--- a/code/modules/surgery/operations/operation_cavity_implant.dm
+++ b/code/modules/surgery/operations/operation_cavity_implant.dm
@@ -24,9 +24,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to open [limb.owner]'s [limb.plaintext_zone] cavity wide..."),
- span_notice("[surgeon] begins to open [limb.owner]'s [limb.plaintext_zone] cavity wide."),
- span_notice("[surgeon] begins to open [limb.owner]'s [limb.plaintext_zone] cavity wide."),
+ span_notice("You begin to open [FORMAT_LIMB_OWNER(limb)] cavity wide..."),
+ span_notice("[surgeon] begins to open [FORMAT_LIMB_OWNER(limb)] cavity wide."),
+ span_notice("[surgeon] begins to open [FORMAT_LIMB_OWNER(limb)] cavity wide."),
)
display_pain(limb.owner, "You can feel pressure as your [limb.plaintext_zone] is being opened wide!")
@@ -91,9 +91,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to insert [tool] into [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to insert [tool] into [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to insert [tool.w_class > WEIGHT_CLASS_SMALL ? tool : "something"] into [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to insert [tool] into [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to insert [tool] into [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to insert [tool.w_class > WEIGHT_CLASS_SMALL ? tool : "something"] into [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You can feel something being inserted into your [limb.plaintext_zone], it hurts like hell!")
@@ -113,9 +113,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You stuff [tool] into [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] stuffs [tool] into [limb.owner]'s [limb.plaintext_zone]!"),
- span_notice("[surgeon] stuffs [tool.w_class > WEIGHT_CLASS_SMALL ? tool : "something"] into [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You stuff [tool] into [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] stuffs [tool] into [FORMAT_LIMB_OWNER(limb)]!"),
+ span_notice("[surgeon] stuffs [tool.w_class > WEIGHT_CLASS_SMALL ? tool : "something"] into [FORMAT_LIMB_OWNER(limb)]."),
)
@@ -163,9 +163,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to extract [limb.cavity_item] from [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to extract [limb.cavity_item] from [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to extract something from [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to extract [limb.cavity_item] from [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to extract [limb.cavity_item] from [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to extract something from [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a serious pain in your [limb.plaintext_zone]!")
@@ -174,9 +174,9 @@
display_results(
surgeon,
limb.owner,
- span_warning("You find nothing to remove from [limb.owner]'s [limb.plaintext_zone]."),
- span_warning("[surgeon] finds nothing to remove from [limb.owner]'s [limb.plaintext_zone]."),
- span_warning("[surgeon] finds nothing to remove from [limb.owner]'s [limb.plaintext_zone]."),
+ span_warning("You find nothing to remove from [FORMAT_LIMB_OWNER(limb)]."),
+ span_warning("[surgeon] finds nothing to remove from [FORMAT_LIMB_OWNER(limb)]."),
+ span_warning("[surgeon] finds nothing to remove from [FORMAT_LIMB_OWNER(limb)]."),
)
return
@@ -186,9 +186,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You pull [implant] out of [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] pulls [implant] out of [limb.owner]'s [limb.plaintext_zone]!"),
- span_notice("[surgeon] pulls something out of [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You pull [implant] out of [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] pulls [implant] out of [FORMAT_LIMB_OWNER(limb)]!"),
+ span_notice("[surgeon] pulls something out of [FORMAT_LIMB_OWNER(limb)]!"),
)
display_pain(limb.owner, "You can feel [implant.name] being pulled out of you!")
surgeon.put_in_hands(implant)
diff --git a/code/modules/surgery/operations/operation_debride.dm b/code/modules/surgery/operations/operation_debride.dm
index 5283a836af8..de58e19a7c3 100644
--- a/code/modules/surgery/operations/operation_debride.dm
+++ b/code/modules/surgery/operations/operation_debride.dm
@@ -9,7 +9,7 @@
TOOL_WIRECUTTER = 2.5,
)
time = 3 SECONDS
- operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_LOOPING | OPERATION_PRIORITY_NEXT_STEP
+ operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_LOOPING | OPERATION_PRIORITY_NEXT_STEP | OPERATION_NO_PATIENT_REQUIRED
preop_sound = list(
TOOL_SCALPEL = 'sound/items/handling/surgery/scalpel1.ogg',
TOOL_HEMOSTAT = 'sound/items/handling/surgery/hemostat1.ogg',
@@ -56,9 +56,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to excise infected flesh from [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to excise infected flesh from [limb.owner]'s [limb.plaintext_zone] with [tool]."),
- span_notice("[surgeon] begins to excise infected flesh from [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to excise infected flesh from [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to excise infected flesh from [FORMAT_LIMB_OWNER(limb)] with [tool]."),
+ span_notice("[surgeon] begins to excise infected flesh from [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "The infection in your [limb.plaintext_zone] stings like hell! It feels like you're being stabbed!")
@@ -70,17 +70,17 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully excise some of the infected flesh from [limb.owner]'s [limb.plaintext_zone][get_progress(wound)]."),
- span_notice("[surgeon] successfully excises some of the infected flesh from [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] successfully excises some of the infected flesh from [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You successfully excise some of the infected flesh from [FORMAT_LIMB_OWNER(limb)][get_progress(wound)]."),
+ span_notice("[surgeon] successfully excises some of the infected flesh from [FORMAT_LIMB_OWNER(limb)] with [tool]!"),
+ span_notice("[surgeon] successfully excises some of the infected flesh from [FORMAT_LIMB_OWNER(limb)]!"),
)
/datum/surgery_operation/limb/debride/on_failure(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
display_results(
surgeon,
limb.owner,
- span_notice("You carve away some of the healthy flesh from [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] carves away some of the healthy flesh from [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] carves away some of the healthy flesh from [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You carve away some of the healthy flesh from [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] carves away some of the healthy flesh from [FORMAT_LIMB_OWNER(limb)] with [tool]!"),
+ span_notice("[surgeon] carves away some of the healthy flesh from [FORMAT_LIMB_OWNER(limb)]!"),
)
limb.receive_damage(rand(4, 8), wound_bonus = CANT_WOUND, sharpness = tool.get_sharpness(), damage_source = tool)
diff --git a/code/modules/surgery/operations/operation_dental.dm b/code/modules/surgery/operations/operation_dental.dm
index a572520a6ba..413711caa2c 100644
--- a/code/modules/surgery/operations/operation_dental.dm
+++ b/code/modules/surgery/operations/operation_dental.dm
@@ -1,11 +1,13 @@
/datum/surgery_operation/limb/add_dental_implant
name = "add dental implant"
desc = "Implant a pill into a patient's teeth."
+ operation_flags = OPERATION_NO_PATIENT_REQUIRED
implements = list(
/obj/item/reagent_containers/applicator/pill = 1,
)
time = 1.6 SECONDS
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED|SURGERY_BONE_DRILLED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_DRILLED
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/limb/add_dental_implant/all_required_strings()
. = list()
@@ -36,9 +38,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to wedge [tool] in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to wedge \the [tool] in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to wedge something in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to wedge [tool] in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to wedge \the [tool] in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to wedge something in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "Something's being jammed into your [limb.plaintext_zone]!")
@@ -54,20 +56,22 @@
display_results(
surgeon,
limb.owner,
- span_notice("You wedge [tool] into [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] wedges [tool] into [limb.owner]'s [limb.plaintext_zone]!"),
- span_notice("[surgeon] wedges something into [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You wedge [tool] into [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] wedges [tool] into [FORMAT_LIMB_OWNER(limb)]!"),
+ span_notice("[surgeon] wedges something into [FORMAT_LIMB_OWNER(limb)]!"),
)
/datum/surgery_operation/limb/remove_dental_implant
name = "remove dental implant"
desc = "Remove a dental implant from a patient's teeth."
+ operation_flags = OPERATION_NO_PATIENT_REQUIRED
implements = list(
TOOL_HEMOSTAT = 1,
IMPLEMENT_HAND = 1,
)
time = 3.2 SECONDS
- all_surgery_states_required = SURGERY_BONE_DRILLED|SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_BONE_DRILLED|SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/limb/remove_dental_implant/get_default_radial_image()
return image(/obj/item/reagent_containers/applicator/pill)
@@ -85,9 +89,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin looking in [limb.owner]'s mouth for dental implants..."),
- span_notice("[surgeon] begins to look in [limb.owner]'s mouth."),
- span_notice("[surgeon] begins to examine [limb.owner]'s teeth."),
+ span_notice("You begin looking in [limb.owner || limb]'s mouth for dental implants..."),
+ span_notice("[surgeon] begins to look in [limb.owner || limb]'s mouth."),
+ span_notice("[surgeon] begins to examine [limb.owner || limb]'s teeth."),
)
display_pain(limb.owner, "You feel fingers poke around at your teeth.")
@@ -99,9 +103,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You don't find any dental implants in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] doesn't find any dental implants in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] finishes examining [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You don't find any dental implants in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] doesn't find any dental implants in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] finishes examining [FORMAT_LIMB_OWNER(limb)]."),
)
return
@@ -114,9 +118,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You carefully remove [yoinked] from [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] carefully removes [yoinked] from [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] carefully removes something from [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You carefully remove [yoinked] from [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] carefully removes [yoinked] from [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] carefully removes something from [FORMAT_LIMB_OWNER(limb)]."),
)
// Teeth pill code
diff --git a/code/modules/surgery/operations/operation_filter.dm b/code/modules/surgery/operations/operation_filter.dm
index 539f43b8755..44fe4be1dfd 100644
--- a/code/modules/surgery/operations/operation_filter.dm
+++ b/code/modules/surgery/operations/operation_filter.dm
@@ -7,7 +7,8 @@
operation_flags = OPERATION_LOOPING
required_bodytype = ~BODYTYPE_ROBOTIC
success_sound = 'sound/machines/card_slide.ogg'
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/limb/filter_blood/all_required_strings()
. = list()
diff --git a/code/modules/surgery/operations/operation_generic.dm b/code/modules/surgery/operations/operation_generic.dm
index 762761ab7c6..c41a64e4b4b 100644
--- a/code/modules/surgery/operations/operation_generic.dm
+++ b/code/modules/surgery/operations/operation_generic.dm
@@ -19,7 +19,7 @@
time = 1.6 SECONDS
preop_sound = 'sound/items/handling/surgery/scalpel1.ogg'
success_sound = 'sound/items/handling/surgery/scalpel2.ogg'
- operation_flags = OPERATION_AFFECTS_MOOD
+ operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_NO_PATIENT_REQUIRED
any_surgery_states_blocked = ALL_SURGERY_SKIN_STATES
/// We can't cut mobs with this biostate
var/biostate_blacklist = BIO_CHITIN
@@ -46,9 +46,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to make an incision in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to make an incision in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to make an incision in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to make an incision in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to make an incision in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to make an incision in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a stabbing in your [limb.plaintext_zone].")
@@ -58,13 +58,19 @@
if(!limb.can_bleed())
return
- var/blood_name = limb.owner.get_bloodtype()?.get_blood_name() || "Blood"
+ var/blood_name = limb.owner?.get_bloodtype()?.get_blood_name()
+ if(!blood_name && length(limb.blood_dna_info))
+ var/datum/blood_type/blood_type = limb.blood_dna_info[limb.blood_dna_info[1]]
+ blood_name = blood_type?.get_blood_name()
+ if(!blood_name)
+ blood_name = "Blood"
+
display_results(
surgeon,
limb.owner,
- span_notice("[blood_name] pools around the incision in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[blood_name] pools around the incision in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[blood_name] pools around the incision in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("[blood_name] pools around the incision in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[blood_name] pools around the incision in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[blood_name] pools around the incision in [FORMAT_LIMB_OWNER(limb)]."),
)
/// Subtype for thick skinned creatures (Xenomorphs)
@@ -95,6 +101,7 @@
name = "retract skin"
desc = "Retract the patient's skin to access their internal organs. \
Causes \"skin open\" surgical state."
+ operation_flags = OPERATION_NO_PATIENT_REQUIRED
required_bodytype = ~BODYTYPE_ROBOTIC
replaced_by = /datum/surgery_operation/limb/retract_skin/abductor
implements = list(
@@ -102,6 +109,7 @@
TOOL_SCREWDRIVER = 2.25,
TOOL_WIRECUTTER = 2.85,
/obj/item/stack/rods = 2.85,
+ /obj/item/kitchen/fork = 2.85,
)
time = 2.4 SECONDS
preop_sound = 'sound/items/handling/surgery/retractor1.ogg'
@@ -115,9 +123,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to retract the skin in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to retract the skin in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to retract the skin in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to retract the skin in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to retract the skin in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to retract the skin in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a severe stinging pain spreading across your [limb.plaintext_zone] as the skin is pulled back.")
@@ -136,6 +144,7 @@
desc = "Mend the incision in the patient's skin, closing it up. \
Clears most surgical states."
required_bodytype = ~BODYTYPE_ROBOTIC
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP | OPERATION_NO_PATIENT_REQUIRED
replaced_by = /datum/surgery_operation/limb/close_skin/abductor
implements = list(
TOOL_CAUTERY = 1,
@@ -181,9 +190,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to mend the incision in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to mend the incision in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to mend the incision in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to mend the incision in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to mend the incision in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to mend the incision in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "Your [limb.plaintext_zone] is being [istype(tool, /obj/item/stack/medical/suture) ? "pinched" : "burned"]!")
@@ -222,9 +231,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to clamp bleeders in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to clamp bleeders in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to clamp bleeders in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to clamp bleeders in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to clamp bleeders in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to clamp bleeders in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a pinch as the bleeding in your [limb.plaintext_zone] is slowed.")
@@ -246,6 +255,7 @@
desc = "Unclamp blood vessels in the patient's body to allow blood flow again. \
Clears \"vessels clamped\" surgical state."
required_bodytype = ~BODYTYPE_ROBOTIC
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP
replaced_by = /datum/surgery_operation/limb/unclamp_bleeders/abductor
implements = list(
TOOL_HEMOSTAT = 1,
@@ -270,9 +280,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to unclamp bleeders in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to unclamp bleeders in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to unclamp bleeders in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to unclamp bleeders in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to unclamp bleeders in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to unclamp bleeders in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a pressure release as blood starts flowing in your [limb.plaintext_zone] again.")
@@ -310,7 +320,7 @@
/obj/item = 'sound/items/handling/surgery/scalpel1.ogg',
)
success_sound = 'sound/items/handling/surgery/organ2.ogg'
- operation_flags = OPERATION_AFFECTS_MOOD
+ operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_NO_PATIENT_REQUIRED
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_BONE_SAWED|SURGERY_BONE_DRILLED
@@ -328,9 +338,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to saw through the bone in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to saw through the bone in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to saw through the bone in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to saw through the bone in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to saw through the bone in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to saw through the bone in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a horrid ache spread through the inside of your [limb.plaintext_zone]!")
@@ -341,9 +351,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You saw [limb.owner]'s [limb.plaintext_zone] open."),
- span_notice("[surgeon] saws [limb.owner]'s [limb.plaintext_zone] open!"),
- span_notice("[surgeon] saws [limb.owner]'s [limb.plaintext_zone] open!"),
+ span_notice("You saw [FORMAT_LIMB_OWNER(limb)] open."),
+ span_notice("[surgeon] saws [FORMAT_LIMB_OWNER(limb)] open!"),
+ span_notice("[surgeon] saws [FORMAT_LIMB_OWNER(limb)] open!"),
)
display_pain(limb.owner, "It feels like something just broke in your [limb.plaintext_zone]!")
@@ -353,6 +363,7 @@
desc = "Repair a patient's cut or broken bones. \
Clears \"bone sawed\" and \"bone drilled\" surgical states."
required_bodytype = ~BODYTYPE_ROBOTIC
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP
implements = list(
/obj/item/stack/medical/bone_gel = 1,
/obj/item/stack/sticky_tape/surgical = 1,
@@ -382,9 +393,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to fix the bones in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to fix the bones in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to fix the bones in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to fix the bones in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to fix the bones in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to fix the bones in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a grinding sensation in your [limb.plaintext_zone] as the bones are set back in place.")
@@ -398,6 +409,7 @@
desc = "Drill through a patient's bones. \
Causes \"bone drilled\" surgical state."
required_bodytype = ~BODYTYPE_ROBOTIC
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP
implements = list(
TOOL_DRILL = 1,
/obj/item/screwdriver/power = 1.25,
@@ -426,9 +438,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to drill into the bone in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to drill into the bone in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to drill into the bone in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to drill into the bone in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to drill into the bone in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to drill into the bone in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a horrible piercing pain in your [limb.plaintext_zone]!")
@@ -438,9 +450,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You drill into [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] drills into [limb.owner]'s [limb.plaintext_zone]!"),
- span_notice("[surgeon] drills into [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You drill into [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] drills into [FORMAT_LIMB_OWNER(limb)]!"),
+ span_notice("[surgeon] drills into [FORMAT_LIMB_OWNER(limb)]!"),
)
/datum/surgery_operation/limb/incise_organs
@@ -448,6 +460,7 @@
desc = "Make an incision in patient's internal organ tissue to allow for manipulation or repair. \
Causes \"organs cut\" surgical state."
required_bodytype = ~BODYTYPE_ROBOTIC
+ operation_flags = OPERATION_PRIORITY_NEXT_STEP
replaced_by = /datum/surgery_operation/limb/incise_organs/abductor
implements = list(
TOOL_SCALPEL = 1,
@@ -477,9 +490,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to make an incision in the organs within [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to make an incision in the organs within [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to make an incision in the organs within [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to make an incision in the organs within [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to make an incision in the organs within [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to make an incision in the organs within [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a stabbing in your [limb.plaintext_zone].")
@@ -490,9 +503,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You make an incision in the organs within [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] makes an incision in the organs within [limb.owner]'s [limb.plaintext_zone]!"),
- span_notice("[surgeon] makes an incision in the organs within [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You make an incision in the organs within [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] makes an incision in the organs within [FORMAT_LIMB_OWNER(limb)]!"),
+ span_notice("[surgeon] makes an incision in the organs within [FORMAT_LIMB_OWNER(limb)]!"),
)
display_pain(limb.owner, "You feel a sharp pain from inside your [limb.plaintext_zone]!")
diff --git a/code/modules/surgery/operations/operation_generic_mechanic.dm b/code/modules/surgery/operations/operation_generic_mechanic.dm
index f8e4ec33b9a..bbcdfd55fd2 100644
--- a/code/modules/surgery/operations/operation_generic_mechanic.dm
+++ b/code/modules/surgery/operations/operation_generic_mechanic.dm
@@ -10,7 +10,7 @@
/obj/item/knife = 2,
/obj/item = 10, // i think this amounts to a 180% chance of failure (clamped to 99%)
)
- operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC
+ operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC | OPERATION_NO_PATIENT_REQUIRED
required_bodytype = BODYTYPE_ROBOTIC
time = 2.4 SECONDS
preop_sound = 'sound/items/tools/screwdriver.ogg'
@@ -31,9 +31,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to unscrew the shell of [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to unscrew the shell of [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to unscrew the shell of [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to unscrew the shell of [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to unscrew the shell of [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to unscrew the shell of [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel your [limb.plaintext_zone] grow numb as the shell is unscrewed.", TRUE)
@@ -51,7 +51,7 @@
IMPLEMENT_HAND = 1,
TOOL_CROWBAR = 1,
)
- operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC
+ operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC | OPERATION_NO_PATIENT_REQUIRED
time = 1 SECONDS
preop_sound = 'sound/items/tools/ratchet.ogg'
success_sound = 'sound/machines/airlock/doorclick.ogg'
@@ -64,9 +64,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to open the hatch holders in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to open the hatch holders in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to open the hatch holders in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to open the hatch holders in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to open the hatch holders in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to open the hatch holders in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "The last faint pricks of tactile sensation fade from your [limb.plaintext_zone] as the hatch is opened.", TRUE)
@@ -88,7 +88,7 @@
/obj/item/knife = 2,
/obj/item = 10,
)
- operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC
+ operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC | OPERATION_NO_PATIENT_REQUIRED
time = 2.4 SECONDS
preop_sound = 'sound/items/tools/screwdriver.ogg'
success_sound = 'sound/items/tools/screwdriver2.ogg'
@@ -111,9 +111,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to screw the shell of [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to screw the shell of [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to screw the shell of [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to screw the shell of [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to screw the shell of [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to screw the shell of [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel the faint pricks of sensation return as your [limb.plaintext_zone]'s shell is screwed in.", TRUE)
@@ -131,7 +131,7 @@
TOOL_MULTITOOL = 1,
TOOL_HEMOSTAT = 1.33,
)
- operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC
+ operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC | OPERATION_NO_PATIENT_REQUIRED
time = 2.4 SECONDS
preop_sound = 'sound/items/taperecorder/tape_flip.ogg'
success_sound = 'sound/items/taperecorder/taperecorder_close.ogg'
@@ -145,9 +145,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to prepare electronics in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to prepare electronics in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to prepare electronics in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to prepare electronics in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to prepare electronics in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to prepare electronics in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You can feel a faint buzz in your [limb.plaintext_zone] as the electronics reboot.", TRUE)
@@ -165,7 +165,7 @@
TOOL_WRENCH = 1,
TOOL_RETRACTOR = 1.33,
)
- operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC
+ operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC | OPERATION_NO_PATIENT_REQUIRED
time = 2.4 SECONDS
preop_sound = 'sound/items/tools/ratchet.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
@@ -178,9 +178,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to unwrench some bolts in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to unwrench some bolts in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to unwrench some bolts in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to unwrench some bolts in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to unwrench some bolts in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to unwrench some bolts in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a jostle in your [limb.plaintext_zone] as the bolts begin to loosen.", TRUE)
@@ -201,7 +201,7 @@
operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC
time = 2.4 SECONDS
preop_sound = 'sound/items/tools/ratchet.ogg'
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED | OPERATION_NO_PATIENT_REQUIRED
/datum/surgery_operation/limb/mechanic_wrench/state_check(obj/item/bodypart/limb)
return LIMB_HAS_BONES(limb)
@@ -216,9 +216,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to wrench some bolts in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to wrench some bolts in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] begins to wrench some bolts in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to wrench some bolts in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to wrench some bolts in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to wrench some bolts in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a jostle in your [limb.plaintext_zone] as the bolts begin to tighten.", TRUE)
diff --git a/code/modules/surgery/operations/operation_implant_removal.dm b/code/modules/surgery/operations/operation_implant_removal.dm
index 3b4dc93b632..48b5ab9c91e 100644
--- a/code/modules/surgery/operations/operation_implant_removal.dm
+++ b/code/modules/surgery/operations/operation_implant_removal.dm
@@ -10,7 +10,8 @@
)
time = 6.4 SECONDS
success_sound = 'sound/items/handling/surgery/hemostat1.ogg'
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/basic/implant_removal/get_default_radial_image()
return image('icons/obj/medical/syringe.dmi', "implantcase-b")
diff --git a/code/modules/surgery/operations/operation_lipo.dm b/code/modules/surgery/operations/operation_lipo.dm
index 05dff033868..b04e856057f 100644
--- a/code/modules/surgery/operations/operation_lipo.dm
+++ b/code/modules/surgery/operations/operation_lipo.dm
@@ -19,7 +19,8 @@
/obj/item = 'sound/items/handling/surgery/scalpel1.ogg',
)
success_sound = 'sound/items/handling/surgery/organ2.ogg'
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/limb/lipoplasty/get_any_tool()
return "Any sharp edged item"
diff --git a/code/modules/surgery/operations/operation_lobotomy.dm b/code/modules/surgery/operations/operation_lobotomy.dm
index 66d64e1cd4b..903f7e29958 100644
--- a/code/modules/surgery/operations/operation_lobotomy.dm
+++ b/code/modules/surgery/operations/operation_lobotomy.dm
@@ -3,7 +3,7 @@
rnd_name = "Lobotomy (Lobotomy)"
desc = "Repair most of a patient's brain traumas, with the risk of causing new permanent traumas."
rnd_desc = "An invasive surgical procedure which guarantees removal of almost all brain traumas, but might cause another permanent trauma in return."
- operation_flags = OPERATION_MORBID | OPERATION_AFFECTS_MOOD | OPERATION_LOCKED | OPERATION_NOTABLE
+ operation_flags = OPERATION_MORBID | OPERATION_AFFECTS_MOOD | OPERATION_LOCKED | OPERATION_NOTABLE | OPERATION_NO_PATIENT_REQUIRED
implements = list(
TOOL_SCALPEL = 1.15,
/obj/item/melee/energy/sword = 0.55,
@@ -16,7 +16,8 @@
preop_sound = 'sound/items/handling/surgery/scalpel1.ogg'
success_sound = 'sound/items/handling/surgery/scalpel2.ogg'
failure_sound = 'sound/items/handling/surgery/organ2.ogg'
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED|SURGERY_BONE_SAWED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/organ/lobotomy/get_any_tool()
return "Any sharp edged item"
@@ -29,9 +30,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You begin to perform a lobotomy on [organ.owner]'s brain..."),
- span_notice("[surgeon] begins to perform a lobotomy on [organ.owner]'s brain."),
- span_notice("[surgeon] begins to perform surgery on [organ.owner]'s brain."),
+ span_notice("You begin to perform a lobotomy on [FORMAT_ORGAN_OWNER(organ)]'s brain..."),
+ span_notice("[surgeon] begins to perform a lobotomy on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
+ span_notice("[surgeon] begins to perform surgery on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
)
display_pain(organ.owner, "Your head pounds with unimaginable pain!")
@@ -39,21 +40,26 @@
display_results(
surgeon,
organ.owner,
- span_notice("You successfully perform a lobotomy on [organ.owner]!"),
- span_notice("[surgeon] successfully lobotomizes [organ.owner]!"),
- span_notice("[surgeon] finishes performing surgery on [organ.owner]'s brain."),
+ span_notice("You successfully perform a lobotomy on [FORMAT_ORGAN_OWNER(organ)]!"),
+ span_notice("[surgeon] successfully lobotomizes [FORMAT_ORGAN_OWNER(organ)]!"),
+ span_notice("[surgeon] finishes performing surgery on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
)
display_pain(organ.owner, "Your head goes totally numb for a moment, the pain is overwhelming!")
organ.cure_all_traumas(TRAUMA_RESILIENCE_LOBOTOMY)
- organ.owner.mind?.remove_antag_datum(/datum/antagonist/brainwashed)
+ if (organ.owner)
+ organ.owner.mind?.remove_antag_datum(/datum/antagonist/brainwashed)
+ else if (organ.brainmob)
+ organ.brainmob.mind?.remove_antag_datum(/datum/antagonist/brainwashed)
+
if(!prob(75))
return
+
switch(rand(1, 3))//Now let's see what hopefully-not-important part of the brain we cut off
if(1)
organ.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_MAGIC)
if(2)
- if(HAS_TRAIT(organ.owner, TRAIT_SPECIAL_TRAUMA_BOOST) && prob(50))
+ if(HAS_TRAIT(organ, TRAIT_SPECIAL_TRAUMA_BOOST) && prob(50))
organ.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_MAGIC)
else
organ.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_MAGIC)
@@ -65,21 +71,21 @@
surgeon,
organ.owner,
span_warning("You remove the wrong part, causing more damage!"),
- span_notice("[surgeon] unsuccessfully attempts to lobotomize [organ.owner]!"),
- span_notice("[surgeon] completes the surgery on [organ.owner]'s brain."),
+ span_notice("[surgeon] unsuccessfully attempts to lobotomize [FORMAT_ORGAN_OWNER(organ)]!"),
+ span_notice("[surgeon] completes the surgery on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
)
display_pain(organ.owner, "The pain in your head only seems to get worse!")
organ.apply_organ_damage(80)
switch(rand(1, 3))
if(1)
- organ.owner.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_MAGIC)
+ organ.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_MAGIC)
if(2)
- if(HAS_TRAIT(organ.owner, TRAIT_SPECIAL_TRAUMA_BOOST) && prob(50))
- organ.owner.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_MAGIC)
+ if(HAS_TRAIT(organ, TRAIT_SPECIAL_TRAUMA_BOOST) && prob(50))
+ organ.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_MAGIC)
else
- organ.owner.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_MAGIC)
+ organ.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_MAGIC)
if(3)
- organ.owner.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_MAGIC)
+ organ.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_MAGIC)
/datum/surgery_operation/organ/lobotomy/mechanic
name = "execute neural defragging"
diff --git a/code/modules/surgery/operations/operation_organ_manip.dm b/code/modules/surgery/operations/operation_organ_manip.dm
index 2c72e659a19..fb2879b714d 100644
--- a/code/modules/surgery/operations/operation_organ_manip.dm
+++ b/code/modules/surgery/operations/operation_organ_manip.dm
@@ -4,7 +4,7 @@
/datum/surgery_operation/limb/organ_manipulation
name = "organ manipulation"
abstract_type = /datum/surgery_operation/limb/organ_manipulation
- operation_flags = OPERATION_MORBID | OPERATION_NOTABLE
+ operation_flags = OPERATION_MORBID | OPERATION_NOTABLE | OPERATION_NO_PATIENT_REQUIRED
required_bodytype = ~BODYTYPE_ROBOTIC
/// Radial slice datums for every organ type we can manipulate
VAR_PRIVATE/list/cached_organ_manipulation_options
@@ -109,7 +109,7 @@
option.image = get_generic_limb_radial_image(limb.body_zone)
option.image.overlays += add_radial_overlays(organ.type)
option.name = "remove [initial(organ.name)]"
- option.info = "Remove [initial(organ.name)] from the patient."
+ option.info = "Remove [initial(organ.name)] from the [limb.owner ? "patient" : "limb"]."
LAZYSET(cached_organ_manipulation_options, "[organ.type]_remove", option)
options[option] = list("[OPERATION_ACTION]" = "remove", "[OPERATION_REMOVED_ORGAN]" = organ)
@@ -123,7 +123,7 @@
option.image = get_generic_limb_radial_image(limb.body_zone)
option.image.overlays += add_radial_overlays(list(image('icons/hud/screen_gen.dmi', "arrow_large_still"), organ.type))
option.name = "insert [initial(organ.name)]"
- option.info = "insert [initial(organ.name)] into the patient."
+ option.info = "insert [initial(organ.name)] into the [limb.owner ? "patient" : "limb"]."
LAZYSET(cached_organ_manipulation_options, "[organ.type]_insert", option)
var/list/result = list()
@@ -155,9 +155,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to remove [organ.name] from [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to remove [organ.name] from [limb.owner]."),
- span_notice("[surgeon] begins to remove something from [limb.owner]."),
+ span_notice("You begin to remove [organ.name] from [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to remove [organ.name] from [limb.owner || limb]."),
+ span_notice("[surgeon] begins to remove something from [limb.owner || limb]."),
)
display_pain(limb.owner, "You feel a tugging sensation in your [limb.plaintext_zone]!")
if("insert")
@@ -165,9 +165,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to insert [tool.name] into [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to insert [tool.name] into [limb.owner]."),
- span_notice("[surgeon] begins to insert something into [limb.owner]."),
+ span_notice("You begin to insert [tool.name] into [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to insert [tool.name] into [limb.owner || limb]."),
+ span_notice("[surgeon] begins to insert something into [limb.owner || limb]."),
)
display_pain(limb.owner, "You can feel something being placed in your [limb.plaintext_zone]!")
@@ -186,27 +186,33 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully extract [organ.name] from [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully extracts [organ.name] from [limb.owner]'s [limb.plaintext_zone]!"),
- span_notice("[surgeon] successfully extracts something from [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You successfully extract [organ.name] from [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully extracts [organ.name] from [FORMAT_LIMB_OWNER(limb)]!"),
+ span_notice("[surgeon] successfully extracts something from [FORMAT_LIMB_OWNER(limb)]!"),
)
display_pain(limb.owner, "Your [limb.plaintext_zone] throbs with pain, you can't feel your [organ.name] anymore!")
- log_combat(surgeon, limb.owner, "surgically removed [organ.name] from")
- organ.Remove(limb.owner)
- organ.forceMove(limb.owner.drop_location())
+ log_combat(surgeon, limb.owner || limb, "surgically removed [organ.name] from")
+ if (limb.owner)
+ organ.Remove(limb.owner)
+ else
+ organ.bodypart_remove(limb)
+ organ.forceMove(limb.owner ? limb.owner.drop_location() : limb.drop_location())
organ.on_surgical_removal(surgeon, limb, tool)
/datum/surgery_operation/limb/organ_manipulation/proc/on_success_insert_organ(obj/item/bodypart/limb, mob/living/surgeon, obj/item/organ/organ)
surgeon.temporarilyRemoveItemFromInventory(organ, TRUE)
organ.pre_surgical_insertion(surgeon, limb, limb.body_zone)
- organ.Insert(limb.owner)
+ if (limb.owner)
+ organ.Insert(limb.owner)
+ else
+ organ.bodypart_insert(limb)
organ.on_surgical_insertion(surgeon, limb, organ)
display_results(
surgeon,
limb.owner,
- span_notice("You successfully insert [organ.name] into [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully inserts [organ.name] into [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully inserts something into [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You successfully insert [organ.name] into [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully inserts [organ.name] into [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully inserts something into [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "Your [limb.plaintext_zone] throbs with pain as your new [organ.name] comes to life!")
@@ -246,7 +252,8 @@
/datum/surgery_operation/limb/organ_manipulation/internal/abductor
name = "experimental organ manipulation"
operation_flags = parent_type::operation_flags | OPERATION_IGNORE_CLOTHES | OPERATION_LOCKED | OPERATION_NO_WIKI
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
bone_locked_organs = "the brain or any chest organs EXCLUDING the heart"
/datum/surgery_operation/limb/organ_manipulation/internal/abductor/organ_check(obj/item/bodypart/limb, obj/item/organ/organ)
@@ -257,7 +264,8 @@
name = "feature manipulation"
desc = "Manipulate features of the patient, such as a moth's wings or a lizard's tail."
replaced_by = /datum/surgery_operation/limb/organ_manipulation/external/abductor
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED|SURGERY_BONE_SAWED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/limb/organ_manipulation/external/organ_check(obj/item/bodypart/limb, obj/item/organ/organ)
return (organ.organ_flags & ORGAN_EXTERNAL)
@@ -277,6 +285,7 @@
/datum/surgery_operation/limb/organ_manipulation/external/abductor
name = "experimental feature manipulation"
operation_flags = parent_type::operation_flags | OPERATION_IGNORE_CLOTHES | OPERATION_LOCKED | OPERATION_NO_WIKI
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
#undef OPERATION_REMOVED_ORGAN
diff --git a/code/modules/surgery/operations/operation_organ_repair.dm b/code/modules/surgery/operations/operation_organ_repair.dm
index 2fa6a8d6862..b4444ba1c4a 100644
--- a/code/modules/surgery/operations/operation_organ_repair.dm
+++ b/code/modules/surgery/operations/operation_organ_repair.dm
@@ -4,7 +4,7 @@
name = "repair organ"
desc = "Repair a patient's damaged organ."
required_organ_flag = ORGAN_TYPE_FLAGS & ~ORGAN_ROBOTIC
- operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_NOTABLE
+ operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_NOTABLE | OPERATION_NO_PATIENT_REQUIRED
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_ORGANS_CUT|SURGERY_BONE_SAWED
/// What % damage do we heal the organ to on success
/// Note that 0% damage = 100% health
@@ -65,9 +65,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You begin to make an incision in [organ.owner]'s lungs..."),
- span_notice("[surgeon] begins to make an incision in [organ.owner]."),
- span_notice("[surgeon] begins to make an incision in [organ.owner]."),
+ span_notice("You begin to make an incision in [FORMAT_ORGAN_OWNER(organ)]'s lungs..."),
+ span_notice("[surgeon] begins to make an incision in [FORMAT_ORGAN_OWNER(organ)]."),
+ span_notice("[surgeon] begins to make an incision in [FORMAT_ORGAN_OWNER(organ)]."),
)
display_pain(organ.owner, "You feel a stabbing pain in your chest!")
@@ -76,18 +76,18 @@
display_results(
surgeon,
organ.owner,
- span_notice("You successfully excise [organ.owner]'s most damaged lobe."),
- span_notice("[surgeon] successfully excises [organ.owner]'s most damaged lobe."),
- span_notice("[surgeon] successfully excises [organ.owner]'s most damaged lobe."),
+ span_notice("You successfully excise [FORMAT_ORGAN_OWNER(organ)]'s most damaged lobe."),
+ span_notice("[surgeon] successfully excises [FORMAT_ORGAN_OWNER(organ)]'s most damaged lobe."),
+ span_notice("[surgeon] successfully excises [FORMAT_ORGAN_OWNER(organ)]'s most damaged lobe."),
)
/datum/surgery_operation/organ/repair/lobectomy/on_failure(obj/item/organ/organ, mob/living/surgeon, obj/item/tool, list/operation_args)
. = ..()
- organ.owner.losebreath += 4
+ organ.owner?.losebreath += 4
display_results(
surgeon,
organ.owner,
- span_warning("You screw up, failing to excise [organ.owner]'s damaged lobe!"),
+ span_warning("You screw up, failing to excise [FORMAT_ORGAN_OWNER(organ)]'s damaged lobe!"),
span_warning("[surgeon] screws up!"),
span_warning("[surgeon] screws up!"),
)
@@ -130,9 +130,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You begin to cut out a damaged piece of [organ.owner]'s liver..."),
- span_notice("[surgeon] begins to make an incision in [organ.owner]."),
- span_notice("[surgeon] begins to make an incision in [organ.owner]."),
+ span_notice("You begin to cut out a damaged piece of [FORMAT_ORGAN_OWNER(organ)]'s liver..."),
+ span_notice("[surgeon] begins to make an incision in [FORMAT_ORGAN_OWNER(organ)]."),
+ span_notice("[surgeon] begins to make an incision in [FORMAT_ORGAN_OWNER(organ)]."),
)
display_pain(organ.owner, "Your abdomen burns in horrific stabbing pain!")
@@ -141,9 +141,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You successfully remove the damaged part of [organ.owner]'s liver."),
- span_notice("[surgeon] successfully removes the damaged part of [organ.owner]'s liver."),
- span_notice("[surgeon] successfully removes the damaged part of [organ.owner]'s liver."),
+ span_notice("You successfully remove the damaged part of [FORMAT_ORGAN_OWNER(organ)]'s liver."),
+ span_notice("[surgeon] successfully removes the damaged part of [FORMAT_ORGAN_OWNER(organ)]'s liver."),
+ span_notice("[surgeon] successfully removes the damaged part of [FORMAT_ORGAN_OWNER(organ)]'s liver."),
)
display_pain(organ.owner, "The pain receeds slightly!")
@@ -152,9 +152,9 @@
display_results(
surgeon,
organ.owner,
- span_warning("You cut the wrong part of [organ.owner]'s liver!"),
- span_warning("[surgeon] cuts the wrong part of [organ.owner]'s liver!"),
- span_warning("[surgeon] cuts the wrong part of [organ.owner]'s liver!"),
+ span_warning("You cut the wrong part of [FORMAT_ORGAN_OWNER(organ)]'s liver!"),
+ span_warning("[surgeon] cuts the wrong part of [FORMAT_ORGAN_OWNER(organ)]'s liver!"),
+ span_warning("[surgeon] cuts the wrong part of [FORMAT_ORGAN_OWNER(organ)]'s liver!"),
)
display_pain(organ.owner, "The pain in your abdomen intensifies!")
@@ -193,9 +193,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You begin to graft a bypass onto [organ.owner]'s heart..."),
- span_notice("[surgeon] begins to graft a bypass onto [organ.owner]'s heart."),
- span_notice("[surgeon] begins to graft a bypass onto [organ.owner]'s heart."),
+ span_notice("You begin to graft a bypass onto [FORMAT_ORGAN_OWNER(organ)]'s heart..."),
+ span_notice("[surgeon] begins to graft a bypass onto [FORMAT_ORGAN_OWNER(organ)]'s heart."),
+ span_notice("[surgeon] begins to graft a bypass onto [FORMAT_ORGAN_OWNER(organ)]'s heart."),
)
display_pain(organ.owner, "The pain in your chest is unbearable! You can barely take it anymore!")
@@ -204,22 +204,22 @@
display_results(
surgeon,
organ.owner,
- span_notice("You successfully graft a bypass onto [organ.owner]'s heart."),
- span_notice("[surgeon] successfully grafts a bypass onto [organ.owner]'s heart."),
- span_notice("[surgeon] successfully grafts a bypass onto [organ.owner]'s heart."),
+ span_notice("You successfully graft a bypass onto [FORMAT_ORGAN_OWNER(organ)]'s heart."),
+ span_notice("[surgeon] successfully grafts a bypass onto [FORMAT_ORGAN_OWNER(organ)]'s heart."),
+ span_notice("[surgeon] successfully grafts a bypass onto [FORMAT_ORGAN_OWNER(organ)]'s heart."),
)
display_pain(organ.owner, "The pain in your chest throbs, but your heart feels better than ever!")
/datum/surgery_operation/organ/repair/coronary_bypass/on_failure(obj/item/organ/organ, mob/living/surgeon, obj/item/tool, list/operation_args)
. = ..()
- organ.bodypart_owner.adjustBleedStacks(30)
- var/blood_name = LOWER_TEXT(organ.owner.get_bloodtype()?.get_blood_name()) || "blood"
+ organ.bodypart_owner?.adjustBleedStacks(30)
+ var/blood_name = LOWER_TEXT(organ.owner?.get_bloodtype()?.get_blood_name()) || "blood"
display_results(
surgeon,
organ.owner,
span_warning("You screw up in attaching the graft, and it tears off, tearing part of the heart!"),
- span_warning("[surgeon] screws up, causing [blood_name] to spurt out of [organ.owner]'s chest profusely!"),
- span_warning("[surgeon] screws up, causing [blood_name] to spurt out of [organ.owner]'s chest profusely!"),
+ span_warning("[surgeon] screws up, causing [blood_name] to spurt out of [FORMAT_ORGAN_OWNER(organ)]'s chest profusely!"),
+ span_warning("[surgeon] screws up, causing [blood_name] to spurt out of [FORMAT_ORGAN_OWNER(organ)]'s chest profusely!"),
)
display_pain(organ.owner, "Your chest burns; you feel like you're going insane!")
@@ -268,9 +268,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You begin to cut out a damaged piece of [organ.owner]'s stomach..."),
- span_notice("[surgeon] begins to make an incision in [organ.owner]."),
- span_notice("[surgeon] begins to make an incision in [organ.owner]."),
+ span_notice("You begin to cut out a damaged piece of [FORMAT_ORGAN_OWNER(organ)]'s stomach..."),
+ span_notice("[surgeon] begins to make an incision in [FORMAT_ORGAN_OWNER(organ)]."),
+ span_notice("[surgeon] begins to make an incision in [FORMAT_ORGAN_OWNER(organ)]."),
)
display_pain(organ.owner, "You feel a horrible stab in your gut!")
@@ -279,9 +279,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You successfully remove the damaged part of [organ.owner]'s stomach."),
- span_notice("[surgeon] successfully removes the damaged part of [organ.owner]'s stomach."),
- span_notice("[surgeon] successfully removes the damaged part of [organ.owner]'s stomach."),
+ span_notice("You successfully remove the damaged part of [FORMAT_ORGAN_OWNER(organ)]'s stomach."),
+ span_notice("[surgeon] successfully removes the damaged part of [FORMAT_ORGAN_OWNER(organ)]'s stomach."),
+ span_notice("[surgeon] successfully removes the damaged part of [FORMAT_ORGAN_OWNER(organ)]'s stomach."),
)
display_pain(organ.owner, "The pain in your gut receeds slightly!")
@@ -290,9 +290,9 @@
display_results(
surgeon,
organ.owner,
- span_warning("You cut the wrong part of [organ.owner]'s stomach!"),
- span_warning("[surgeon] cuts the wrong part of [organ.owner]'s stomach!"),
- span_warning("[surgeon] cuts the wrong part of [organ.owner]'s stomach!"),
+ span_warning("You cut the wrong part of [FORMAT_ORGAN_OWNER(organ)]'s stomach!"),
+ span_warning("[surgeon] cuts the wrong part of [FORMAT_ORGAN_OWNER(organ)]'s stomach!"),
+ span_warning("[surgeon] cuts the wrong part of [FORMAT_ORGAN_OWNER(organ)]'s stomach!"),
)
display_pain(organ.owner, "The pain in your gut intensifies!")
@@ -326,7 +326,8 @@
time = 6.4 SECONDS
heal_to_percent = 0
repeatable = TRUE
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/organ/repair/ears/all_blocked_strings()
return ..() + list("if the limb has bones, they must be intact")
@@ -341,9 +342,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You begin to fix [organ.owner]'s ears..."),
- span_notice("[surgeon] begins to fix [organ.owner]'s ears."),
- span_notice("[surgeon] begins to perform surgery on [organ.owner]'s ears."),
+ span_notice("You begin to fix [FORMAT_ORGAN_OWNER(organ)]'s ears..."),
+ span_notice("[surgeon] begins to fix [FORMAT_ORGAN_OWNER(organ)]'s ears."),
+ span_notice("[surgeon] begins to perform surgery on [FORMAT_ORGAN_OWNER(organ)]'s ears."),
)
display_pain(organ.owner, "You feel a dizzying pain in your head!")
@@ -354,32 +355,33 @@
display_results(
surgeon,
organ.owner,
- span_notice("You successfully fix [organ.owner]'s ears."),
- span_notice("[surgeon] successfully fixes [organ.owner]'s ears."),
- span_notice("[surgeon] successfully fixes [organ.owner]'s ears."),
+ span_notice("You successfully fix [FORMAT_ORGAN_OWNER(organ)]'s ears."),
+ span_notice("[surgeon] successfully fixes [FORMAT_ORGAN_OWNER(organ)]'s ears."),
+ span_notice("[surgeon] successfully fixes [FORMAT_ORGAN_OWNER(organ)]'s ears."),
)
display_pain(organ.owner, "Your head swims, but it seems like you can feel your hearing coming back!")
/datum/surgery_operation/organ/repair/ears/on_failure(obj/item/organ/ears/organ, mob/living/surgeon, obj/item/tool, list/operation_args)
var/obj/item/organ/brain/brain = locate() in organ.bodypart_owner
- if(brain)
+ if(isnull(brain))
display_results(
surgeon,
organ.owner,
- span_warning("You accidentally stab [organ.owner] right in the brain!"),
- span_warning("[surgeon] accidentally stabs [organ.owner] right in the brain!"),
- span_warning("[surgeon] accidentally stabs [organ.owner] right in the brain!"),
- )
- display_pain(organ.owner, "You feel a visceral stabbing pain right through your head, into your brain!")
- organ.owner.adjust_organ_loss(ORGAN_SLOT_BRAIN, 70)
- else
- display_results(
- surgeon,
- organ.owner,
- span_warning("You accidentally stab [organ.owner] right in the brain! Or would have, if [organ.owner] had a brain."),
- span_warning("[surgeon] accidentally stabs [organ.owner] right in the brain! Or would have, if [organ.owner] had a brain."),
- span_warning("[surgeon] accidentally stabs [organ.owner] right in the brain!"),
+ span_warning("You accidentally stab [FORMAT_ORGAN_OWNER(organ)] right in the brain! Or would have, if [FORMAT_ORGAN_OWNER(organ)] had a brain."),
+ span_warning("[surgeon] accidentally stabs [FORMAT_ORGAN_OWNER(organ)] right in the brain! Or would have, if [FORMAT_ORGAN_OWNER(organ)] had a brain."),
+ span_warning("[surgeon] accidentally stabs [FORMAT_ORGAN_OWNER(organ)] right in the brain!"),
)
+ return
+
+ display_results(
+ surgeon,
+ organ.owner,
+ span_warning("You accidentally stab [FORMAT_ORGAN_OWNER(organ)] right in the brain!"),
+ span_warning("[surgeon] accidentally stabs [FORMAT_ORGAN_OWNER(organ)] right in the brain!"),
+ span_warning("[surgeon] accidentally stabs [FORMAT_ORGAN_OWNER(organ)] right in the brain!"),
+ )
+ display_pain(organ.owner, "You feel a visceral stabbing pain right through your head, into your brain!")
+ organ.apply_organ_damage(70)
/datum/surgery_operation/organ/repair/eyes
name = "eye surgery"
@@ -395,7 +397,8 @@
target_type = /obj/item/organ/eyes
heal_to_percent = 0
repeatable = TRUE
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/organ/repair/eyes/all_blocked_strings()
return ..() + list("if the limb has bones, they must be intact")
@@ -413,46 +416,46 @@
display_results(
surgeon,
organ.owner,
- span_notice("You begin to fix [organ.owner]'s eyes..."),
- span_notice("[surgeon] begins to fix [organ.owner]'s eyes."),
- span_notice("[surgeon] begins to perform surgery on [organ.owner]'s eyes."),
+ span_notice("You begin to fix [FORMAT_ORGAN_OWNER(organ)]'s eyes..."),
+ span_notice("[surgeon] begins to fix [FORMAT_ORGAN_OWNER(organ)]'s eyes."),
+ span_notice("[surgeon] begins to perform surgery on [FORMAT_ORGAN_OWNER(organ)]'s eyes."),
)
display_pain(organ.owner, "You feel a stabbing pain in your eyes!")
/datum/surgery_operation/organ/repair/eyes/on_success(obj/item/organ/organ, mob/living/surgeon, obj/item/tool, list/operation_args)
. = ..()
- organ.owner.remove_status_effect(/datum/status_effect/temporary_blindness)
- organ.owner.set_eye_blur_if_lower(70 SECONDS) //this will fix itself slowly.
+ organ.owner?.remove_status_effect(/datum/status_effect/temporary_blindness)
+ organ.owner?.set_eye_blur_if_lower(70 SECONDS) //this will fix itself slowly.
display_results(
surgeon,
organ.owner,
- span_notice("You successfully fix [organ.owner]'s eyes."),
- span_notice("[surgeon] successfully fixes [organ.owner]'s eyes."),
- span_notice("[surgeon] successfully fixes [organ.owner]'s eyes."),
+ span_notice("You successfully fix [FORMAT_ORGAN_OWNER(organ)]'s eyes."),
+ span_notice("[surgeon] successfully fixes [FORMAT_ORGAN_OWNER(organ)]'s eyes."),
+ span_notice("[surgeon] successfully fixes [FORMAT_ORGAN_OWNER(organ)]'s eyes."),
)
display_pain(organ.owner, "Your vision blurs, but it seems like you can see a little better now!")
/datum/surgery_operation/organ/repair/eyes/on_failure(obj/item/organ/organ, mob/living/surgeon, obj/item/tool, list/operation_args)
var/obj/item/organ/brain/brain = locate() in organ.bodypart_owner
- if(brain)
+ if(isnull(brain))
display_results(
surgeon,
organ.owner,
- span_warning("You accidentally stab [organ.owner] right in the brain!"),
- span_warning("[surgeon] accidentally stabs [organ.owner] right in the brain!"),
- span_warning("[surgeon] accidentally stabs [organ.owner] right in the brain!"),
+ span_warning("You accidentally stab [FORMAT_ORGAN_OWNER(organ)] right in the brain! Or would have, if [FORMAT_ORGAN_OWNER(organ)] had a brain."),
+ span_warning("[surgeon] accidentally stabs [FORMAT_ORGAN_OWNER(organ)] right in the brain! Or would have, if [FORMAT_ORGAN_OWNER(organ)] had a brain."),
+ span_warning("[surgeon] accidentally stabs [FORMAT_ORGAN_OWNER(organ)] right in the brain!"),
)
- display_pain(organ.owner, "You feel a visceral stabbing pain right through your head, into your brain!")
- organ.owner.adjust_organ_loss(ORGAN_SLOT_BRAIN, 70)
+ return
- else
- display_results(
- surgeon,
- organ.owner,
- span_warning("You accidentally stab [organ.owner] right in the brain! Or would have, if [organ.owner] had a brain."),
- span_warning("[surgeon] accidentally stabs [organ.owner] right in the brain! Or would have, if [organ.owner] had a brain."),
- span_warning("[surgeon] accidentally stabs [organ.owner] right in the brain!"),
- )
+ display_results(
+ surgeon,
+ organ.owner,
+ span_warning("You accidentally stab [FORMAT_ORGAN_OWNER(organ)] right in the brain!"),
+ span_warning("[surgeon] accidentally stabs [FORMAT_ORGAN_OWNER(organ)] right in the brain!"),
+ span_warning("[surgeon] accidentally stabs [FORMAT_ORGAN_OWNER(organ)] right in the brain!"),
+ )
+ display_pain(organ.owner, "You feel a visceral stabbing pain right through your head, into your brain!")
+ organ.apply_organ_damage(70)
/datum/surgery_operation/organ/repair/brain
name = "brain surgery"
@@ -472,7 +475,8 @@
heal_to_percent = 0.25
failure_damage_percent = 0.3
repeatable = TRUE
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/organ/repair/brain/state_check(obj/item/organ/brain/organ)
return TRUE // always available so you can intentionally fail it
@@ -481,9 +485,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You begin to fix [organ.owner]'s brain..."),
- span_notice("[surgeon] begins to fix [organ.owner]'s brain."),
- span_notice("[surgeon] begins to perform surgery on [organ.owner]'s brain."),
+ span_notice("You begin to fix [FORMAT_ORGAN_OWNER(organ)]'s brain..."),
+ span_notice("[surgeon] begins to fix [FORMAT_ORGAN_OWNER(organ)]'s brain."),
+ span_notice("[surgeon] begins to perform surgery on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
)
display_pain(organ.owner, "Your head pounds with unimaginable pain!")
@@ -492,15 +496,18 @@
display_results(
surgeon,
organ.owner,
- span_notice("You succeed in fixing [organ.owner]'s brain."),
- span_notice("[surgeon] successfully fixes [organ.owner]'s brain!"),
- span_notice("[surgeon] completes the surgery on [organ.owner]'s brain."),
+ span_notice("You succeed in fixing [FORMAT_ORGAN_OWNER(organ)]'s brain."),
+ span_notice("[surgeon] successfully fixes [FORMAT_ORGAN_OWNER(organ)]'s brain!"),
+ span_notice("[surgeon] completes the surgery on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
)
display_pain(organ.owner, "The pain in your head receeds, thinking becomes a bit easier!")
- organ.owner.mind?.remove_antag_datum(/datum/antagonist/brainwashed)
+ if (organ.owner)
+ organ.owner.mind?.remove_antag_datum(/datum/antagonist/brainwashed)
+ else if (organ.brainmob)
+ organ.brainmob.mind?.remove_antag_datum(/datum/antagonist/brainwashed)
organ.cure_all_traumas(TRAUMA_RESILIENCE_SURGERY)
if(organ.damage > organ.maxHealth * 0.1)
- to_chat(surgeon, "[organ.owner]'s brain looks like it could be fixed further.")
+ to_chat(surgeon, "[FORMAT_ORGAN_OWNER(organ)]'s brain looks like it could be fixed further.")
/datum/surgery_operation/organ/repair/brain/on_failure(obj/item/organ/brain/organ, mob/living/surgeon, obj/item/tool, list/operation_args)
. = ..()
@@ -509,7 +516,7 @@
organ.owner,
span_warning("You screw up, causing more damage!"),
span_warning("[surgeon] screws up, causing brain damage!"),
- span_notice("[surgeon] completes the surgery on [organ.owner]'s brain."),
+ span_notice("[surgeon] completes the surgery on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
)
display_pain(organ.owner, "Your head throbs with horrible pain; thinking hurts!")
organ.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_LOBOTOMY)
diff --git a/code/modules/surgery/operations/operation_pacify.dm b/code/modules/surgery/operations/operation_pacify.dm
index 04f6c6c4571..06fd8cff7e5 100644
--- a/code/modules/surgery/operations/operation_pacify.dm
+++ b/code/modules/surgery/operations/operation_pacify.dm
@@ -3,7 +3,7 @@
rnd_name = "Paxopsy (Pacification)"
desc = "Remove aggressive tendencies from a patient's brain."
rnd_desc = "A surgical procedure which permanently inhibits the aggression center of the brain, making the patient unwilling to cause direct harm."
- operation_flags = OPERATION_MORBID | OPERATION_LOCKED | OPERATION_NOTABLE
+ operation_flags = OPERATION_MORBID | OPERATION_LOCKED | OPERATION_NOTABLE | OPERATION_NO_PATIENT_REQUIRED
implements = list(
TOOL_HEMOSTAT = 1,
TOOL_SCREWDRIVER = 2.85,
@@ -15,7 +15,8 @@
failure_sound = 'sound/items/handling/surgery/organ2.ogg'
required_organ_flag = ORGAN_TYPE_FLAGS & ~ORGAN_ROBOTIC
target_type = /obj/item/organ/brain
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED|SURGERY_BONE_SAWED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/organ/pacify/get_default_radial_image()
return image(/atom/movable/screen/alert/status_effect/high::overlay_icon, /atom/movable/screen/alert/status_effect/high::overlay_state)
@@ -24,9 +25,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You begin to pacify [organ.owner]..."),
- span_notice("[surgeon] begins to fix [organ.owner]'s brain."),
- span_notice("[surgeon] begins to perform surgery on [organ.owner]'s brain."),
+ span_notice("You begin to pacify [FORMAT_ORGAN_OWNER(organ)]..."),
+ span_notice("[surgeon] begins to fix [FORMAT_ORGAN_OWNER(organ)]'s brain."),
+ span_notice("[surgeon] begins to perform surgery on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
)
display_pain(organ.owner, "Your head pounds with unimaginable pain!")
@@ -34,9 +35,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You succeed in pacifying [organ.owner]."),
- span_notice("[surgeon] successfully fixes [organ.owner]!"),
- span_notice("[surgeon] completes the surgery on [organ.owner]'s brain."),
+ span_notice("You succeed in pacifying [FORMAT_ORGAN_OWNER(organ)]."),
+ span_notice("[surgeon] successfully fixes [FORMAT_ORGAN_OWNER(organ)]!"),
+ span_notice("[surgeon] completes the surgery on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
)
display_pain(organ.owner, "Your head pounds... the concept of violence flashes in your head, and nearly makes you hurl!")
organ.gain_trauma(/datum/brain_trauma/severe/pacifism, TRAUMA_RESILIENCE_LOBOTOMY)
@@ -45,9 +46,9 @@
display_results(
surgeon,
organ.owner,
- span_notice("You screw up, rewiring [organ.owner]'s brain the wrong way around..."),
+ span_notice("You screw up, rewiring [FORMAT_ORGAN_OWNER(organ)]'s brain the wrong way around..."),
span_warning("[surgeon] screws up, causing brain damage!"),
- span_notice("[surgeon] completes the surgery on [organ.owner]'s brain."),
+ span_notice("[surgeon] completes the surgery on [FORMAT_ORGAN_OWNER(organ)]'s brain."),
)
display_pain(organ.owner, "Your head pounds, and it feels like it's getting worse!")
organ.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_LOBOTOMY)
diff --git a/code/modules/surgery/operations/operation_plastic_surgery.dm b/code/modules/surgery/operations/operation_plastic_surgery.dm
index c5ca9d364d5..2ef9db9d856 100644
--- a/code/modules/surgery/operations/operation_plastic_surgery.dm
+++ b/code/modules/surgery/operations/operation_plastic_surgery.dm
@@ -116,7 +116,7 @@
success_sound = 'sound/effects/blob/attackblob.ogg'
failure_sound = 'sound/effects/blob/blobattack.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
- any_surgery_states_blocked = SURGERY_PLASTIC_APPLIED
+ any_surgery_states_blocked = SURGERY_PLASTIC_APPLIED|SURGERY_VESSELS_UNCLAMPED
/datum/surgery_operation/limb/add_plastic/get_default_radial_image()
return image(/obj/item/stack/sheet/plastic)
diff --git a/code/modules/surgery/operations/operation_puncture.dm b/code/modules/surgery/operations/operation_puncture.dm
index eebf3bba0c6..a95c4b76899 100644
--- a/code/modules/surgery/operations/operation_puncture.dm
+++ b/code/modules/surgery/operations/operation_puncture.dm
@@ -8,7 +8,7 @@
)
time = 3 SECONDS
preop_sound = 'sound/items/handling/surgery/hemostat1.ogg'
- operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_PRIORITY_NEXT_STEP
+ operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_PRIORITY_NEXT_STEP | OPERATION_NO_PATIENT_REQUIRED
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_ORGANS_CUT
/datum/surgery_operation/limb/repair_puncture/get_default_radial_image()
@@ -27,9 +27,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to realign the torn blood vessels in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to realign the torn blood vessels in [limb.owner]'s [limb.plaintext_zone] with [tool]."),
- span_notice("[surgeon] begins to realign the torn blood vessels in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to realign the torn blood vessels in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to realign the torn blood vessels in [FORMAT_LIMB_OWNER(limb)] with [tool]."),
+ span_notice("[surgeon] begins to realign the torn blood vessels in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a horrible stabbing pain in your [limb.plaintext_zone]!")
@@ -42,9 +42,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully realign the last of the torn blood vessels in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully realigns the last of the torn blood vessels in [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] successfully realigns the last of the torn blood vessels in [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You successfully realign the last of the torn blood vessels in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully realigns the last of the torn blood vessels in [FORMAT_LIMB_OWNER(limb)] with [tool]!"),
+ span_notice("[surgeon] successfully realigns the last of the torn blood vessels in [FORMAT_LIMB_OWNER(limb)]!"),
)
return
@@ -52,18 +52,18 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully realign some of the blood vessels in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully realigns some of the blood vessels in [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] successfully realigns some of the blood vessels in [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You successfully realign some of the blood vessels in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully realigns some of the blood vessels in [FORMAT_LIMB_OWNER(limb)] with [tool]!"),
+ span_notice("[surgeon] successfully realigns some of the blood vessels in [FORMAT_LIMB_OWNER(limb)]!"),
)
/datum/surgery_operation/limb/repair_puncture/on_failure(obj/item/bodypart/limb, mob/living/surgeon, tool, list/operation_args)
display_results(
surgeon,
limb.owner,
- span_notice("You jerk apart some of the blood vessels in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] jerks apart some of the blood vessels in [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] jerks apart some of the blood vessels in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You jerk apart some of the blood vessels in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] jerks apart some of the blood vessels in [FORMAT_LIMB_OWNER(limb)] with [tool]!"),
+ span_notice("[surgeon] jerks apart some of the blood vessels in [FORMAT_LIMB_OWNER(limb)]."),
)
limb.receive_damage(rand(4, 8), wound_bonus = 10, sharpness = SHARP_EDGED, damage_source = tool)
@@ -79,7 +79,7 @@
)
time = 3.2 SECONDS
preop_sound = 'sound/items/handling/surgery/hemostat1.ogg'
- operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_PRIORITY_NEXT_STEP
+ operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_PRIORITY_NEXT_STEP | OPERATION_NO_PATIENT_REQUIRED
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_ORGANS_CUT
/datum/surgery_operation/limb/seal_veins/get_default_radial_image()
@@ -108,9 +108,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You begin to seal the realigned blood vessels in [limb.owner]'s [limb.plaintext_zone]..."),
- span_notice("[surgeon] begins to seal the realigned blood vessels in [limb.owner]'s [limb.plaintext_zone] with [tool]."),
- span_notice("[surgeon] begins to seal the realigned blood vessels in [limb.owner]'s [limb.plaintext_zone]."),
+ span_notice("You begin to seal the realigned blood vessels in [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to seal the realigned blood vessels in [FORMAT_LIMB_OWNER(limb)] with [tool]."),
+ span_notice("[surgeon] begins to seal the realigned blood vessels in [FORMAT_LIMB_OWNER(limb)]."),
)
display_pain(limb.owner, "You feel a burning sensation in your [limb.plaintext_zone]!")
@@ -122,9 +122,9 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully seal the last of the ruptured blood vessels in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully seals the last of the ruptured blood vessels in [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] successfully seals the last of the ruptured blood vessels in [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You successfully seal the last of the ruptured blood vessels in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully seals the last of the ruptured blood vessels in [FORMAT_LIMB_OWNER(limb)] with [tool]!"),
+ span_notice("[surgeon] successfully seals the last of the ruptured blood vessels in [FORMAT_LIMB_OWNER(limb)]!"),
)
return
@@ -132,7 +132,7 @@
display_results(
surgeon,
limb.owner,
- span_notice("You successfully seal some of the blood vessels in [limb.owner]'s [limb.plaintext_zone]."),
- span_notice("[surgeon] successfully seals some of the blood vessels in [limb.owner]'s [limb.plaintext_zone] with [tool]!"),
- span_notice("[surgeon] successfully seals some of the blood vessels in [limb.owner]'s [limb.plaintext_zone]!"),
+ span_notice("You successfully seal some of the blood vessels in [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] successfully seals some of the blood vessels in [FORMAT_LIMB_OWNER(limb)] with [tool]!"),
+ span_notice("[surgeon] successfully seals some of the blood vessels in [FORMAT_LIMB_OWNER(limb)]!"),
)
diff --git a/code/modules/surgery/operations/operation_replace_limb.dm b/code/modules/surgery/operations/operation_replace_limb.dm
index 47f15cf518e..0cb5ea14e21 100644
--- a/code/modules/surgery/operations/operation_replace_limb.dm
+++ b/code/modules/surgery/operations/operation_replace_limb.dm
@@ -50,6 +50,13 @@
return FALSE
return TRUE
+/datum/surgery_operation/limb/replace_limb/pre_preop(atom/movable/operating_on, mob/living/surgeon, obj/item/bodypart/tool, list/operation_args)
+ if(!length(tool.contents))
+ return TRUE
+ // Prevents quickly filling someone with high-tier organs by augmenting them with a pre-stuffed limb
+ to_chat(surgeon, span_warning("[tool] needs to be empty in order to be attached!"))
+ return FALSE
+
/datum/surgery_operation/limb/replace_limb/on_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/bodypart/tool, list/operation_args)
// purposefully doesn't use plaintext zone for more context on what is being replaced with what
display_results(
diff --git a/code/modules/surgery/operations/operation_wing_repair.dm b/code/modules/surgery/operations/operation_wing_repair.dm
index 365d330afaa..b1bb24ec843 100644
--- a/code/modules/surgery/operations/operation_wing_repair.dm
+++ b/code/modules/surgery/operations/operation_wing_repair.dm
@@ -11,7 +11,8 @@
operation_flags = OPERATION_LOCKED | OPERATION_NOTABLE
time = 20 SECONDS
target_type = /obj/item/organ/wings/moth
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ all_surgery_states_required = SURGERY_SKIN_OPEN
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
required_organ_flag = NONE
/datum/surgery_operation/organ/fix_wings/get_default_radial_image()
diff --git a/code/modules/surgery/operations/operation_zombie.dm b/code/modules/surgery/operations/operation_zombie.dm
index 8e81cd6491c..2bb50cea049 100644
--- a/code/modules/surgery/operations/operation_zombie.dm
+++ b/code/modules/surgery/operations/operation_zombie.dm
@@ -9,7 +9,8 @@
)
time = 5 SECONDS
operation_flags = OPERATION_MORBID | OPERATION_LOCKED | OPERATION_NOTABLE
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED|SURGERY_BONE_SAWED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED
+ any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
var/list/zombie_chems = list(
/datum/reagent/medicine/rezadone,
/datum/reagent/toxin/zombiepowder,
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm
index f3212a39bea..f18bd16cbcc 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm
@@ -405,7 +405,7 @@
. = ..()
UnregisterSignal(organ_owner, COMSIG_LIVING_OPERATING_ON)
-/obj/item/organ/cyberimp/brain/surgical_processor/proc/check_surgery(datum/source, mob/living/patient, list/operations)
+/obj/item/organ/cyberimp/brain/surgical_processor/proc/check_surgery(datum/source, atom/movable/operating_on, list/operations)
SIGNAL_HANDLER
if(organ_flags & (ORGAN_FAILING|ORGAN_EMP))
diff --git a/code/modules/surgery/organs/organ_movement.dm b/code/modules/surgery/organs/organ_movement.dm
index 34d033b70e1..13d7dcd8560 100644
--- a/code/modules/surgery/organs/organ_movement.dm
+++ b/code/modules/surgery/organs/organ_movement.dm
@@ -305,7 +305,7 @@
/obj/item/organ/proc/on_surgical_removal(mob/living/user, obj/item/bodypart/limb, obj/item/tool)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ORGAN_SURGICALLY_REMOVED, user, limb.owner, limb.body_zone, tool)
- RemoveElement(/datum/element/decal/blood, _color = limb.owner.get_bloodtype()?.get_color() || BLOOD_COLOR_RED)
+ RemoveElement(/datum/element/decal/blood, _color = get_organ_blood_color(limb) || BLOOD_COLOR_RED)
/**
* Proc that gets called when the organ is surgically inserted by someone. Seem familiar?
@@ -325,3 +325,13 @@
CRASH("[src]'s ([type]) swap_zone was called with invalid zone [target_zone]")
zone = target_zone
slot = valid_zones[zone]
+
+/obj/item/organ/proc/get_organ_blood_color(obj/item/bodypart/limb)
+ var/blood_color = owner?.get_bloodtype()?.get_color()
+ if (!limb)
+ return blood_color
+ if (!blood_color)
+ blood_color = limb.owner?.get_bloodtype()?.get_color()
+ if (!blood_color && length(limb.blood_dna_info))
+ blood_color = get_color_from_blood_list(limb.blood_dna_info)
+ return blood_color
diff --git a/code/modules/surgery/surgery_tools.dm b/code/modules/surgery/surgery_tools.dm
index b271d70a81b..cff887ac4b2 100644
--- a/code/modules/surgery/surgery_tools.dm
+++ b/code/modules/surgery/surgery_tools.dm
@@ -401,7 +401,7 @@
if(downloaded)
. += mutable_appearance(src.icon, "+downloaded")
-/obj/item/surgical_processor/proc/check_surgery(datum/source, mob/living/patient, list/operations)
+/obj/item/surgical_processor/proc/check_surgery(datum/source, atom/movable/operating_on, list/operations)
SIGNAL_HANDLER
operations |= loaded_surgeries
diff --git a/icons/mob/human/bodyparts.dmi b/icons/mob/human/bodyparts.dmi
index 78be880423c..7fd40bb0bf2 100644
Binary files a/icons/mob/human/bodyparts.dmi and b/icons/mob/human/bodyparts.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index c2cfdc21351..ee72bca2035 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1175,6 +1175,7 @@
#include "code\datums\components\food_storage.dm"
#include "code\datums\components\force_move.dm"
#include "code\datums\components\fov_handler.dm"
+#include "code\datums\components\free_operation.dm"
#include "code\datums\components\fullauto.dm"
#include "code\datums\components\gas_leaker.dm"
#include "code\datums\components\geiger_sound.dm"