From 15c707de3be7cdde4cddd4db0c49c40d49af950f Mon Sep 17 00:00:00 2001
From: "Bridget (Yaki)" <53800426+YakiAttaki@users.noreply.github.com>
Date: Fri, 13 Feb 2026 03:30:14 +1100
Subject: [PATCH] [Bounty] Ports and reworks the Cybernetic limb mounts quirk
from Nova (#5136)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## About The Pull Request
Picking up the work from
https://github.com/Bubberstation/Bubberstation/pull/4672 that has since
gone stale and unadded to the game. PR description will be the same as
previously.
Partial Port of https://github.com/NovaSector/NovaSector/pull/465
reworked to use right clicking on self similar the ghouls rather than a
spell.
Coded as a bounty for Dinklemyr
## Why It's Good For The Game
Similar to Nova's explanation, suitable for a lot of character ideas
who's mechanical limbs can be popped off.
## Proof Of Testing
Screenshots/Videos
Unfortunately I don't have the images from the initial PR, but I can
confirm this works as intended.
## Changelog
🆑SpaceCatSS13, YakiAttaki
add: The cybernetic limb mount quirk has been added, synths and people
with synthetic limbs can now take this quirk to remove their limbs at
their discretion.
/:cl:
---------
Co-authored-by: Alexis
---
.../~~bubber_defines/traits/declarations.dm | 3 +
code/_globalvars/traits/_traits.dm | 1 +
code/_globalvars/traits/admin_tooling.dm | 1 +
.../mob/living/carbon/human/_species.dm | 2 +-
code/modules/surgery/bodyparts/_bodyparts.dm | 6 +-
.../positive_quirks/robot_limb_detach.dm | 94 +++++++++++++++++++
tgstation.dme | 1 +
7 files changed, 106 insertions(+), 2 deletions(-)
create mode 100644 modular_zubbers/code/modules/quirks/positive_quirks/robot_limb_detach.dm
diff --git a/code/__DEFINES/~~bubber_defines/traits/declarations.dm b/code/__DEFINES/~~bubber_defines/traits/declarations.dm
index 594783f8609..36cda32d942 100644
--- a/code/__DEFINES/~~bubber_defines/traits/declarations.dm
+++ b/code/__DEFINES/~~bubber_defines/traits/declarations.dm
@@ -61,3 +61,6 @@
// For Bloodsuckers/Hemophages that vomit blood
#define TRAIT_STOMACH_BLOOD_VOMIT "stomach_blood_vomit"
+
+/// The trait that determines if someone has the robotic limb reattachment quirk.
+#define TRAIT_ROBOTIC_LIMBATTACHMENT "trait_robotic_limbattachment"
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index c761d27c881..eacc5d89589 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -901,6 +901,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_REVIVES_BY_HEALING" = TRAIT_REVIVES_BY_HEALING,
"TRAIT_RIGGER" = TRAIT_RIGGER,
"TRAIT_ROBOTIC_DNA_ORGANS" = TRAIT_ROBOTIC_DNA_ORGANS,
+ "TRAIT_ROBOTIC_LIMBATTACHMENT" = TRAIT_ROBOTIC_LIMBATTACHMENT, //BUBBER addition for robotic limb reattachment quirk.
"TRAIT_ROPEBUNNY" = TRAIT_ROPEBUNNY,
"TRAIT_RSD_COMPATIBLE" = TRAIT_RSD_COMPATIBLE,
"TRAIT_SACRIFICED" = TRAIT_SACRIFICED ,
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index b890cbcb217..56fe4c74110 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -478,6 +478,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_REVIVES_BY_HEALING" = TRAIT_REVIVES_BY_HEALING,
"TRAIT_RIGGER" = TRAIT_RIGGER,
"TRAIT_ROBOTIC_DNA_ORGANS" = TRAIT_ROBOTIC_DNA_ORGANS,
+ "TRAIT_ROBOTIC_LIMBATTACHMENT" = TRAIT_ROBOTIC_LIMBATTACHMENT, //BUBBER ADDITION - Trait that lets you install robotic limbs only.
"TRAIT_ROPEBUNNY" = TRAIT_ROPEBUNNY,
"TRAIT_RSD_COMPATIBLE" = TRAIT_RSD_COMPATIBLE,
"TRAIT_SACRIFICED" = TRAIT_SACRIFICED ,
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index d117311a731..50c8faf870d 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -1039,7 +1039,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
to_chat(owner, span_warning("You attempt to touch [target]!"))
return
- SEND_SIGNAL(owner, COMSIG_MOB_ATTACK_HAND, owner, target, attacker_style)
+ SEND_SIGNAL(owner, COMSIG_MOB_ATTACK_HAND, owner, target, attacker_style, modifiers) //BUBBER EDIT - Original line SEND_SIGNAL(owner, COMSIG_MOB_ATTACK_HAND, owner, target, attacker_style)
if(LAZYACCESS(modifiers, RIGHT_CLICK))
disarm(owner, target, attacker_style)
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index bf41921041c..2457963025b 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -423,7 +423,11 @@
if(ishuman(victim))
var/mob/living/carbon/human/human_victim = victim
- if(HAS_TRAIT(victim, TRAIT_LIMBATTACHMENT) || HAS_TRAIT(src, TRAIT_EASY_ATTACH))
+ if(HAS_TRAIT(victim, TRAIT_LIMBATTACHMENT) || HAS_TRAIT(src, TRAIT_EASY_ATTACH) || (HAS_TRAIT(victim, TRAIT_ROBOTIC_LIMBATTACHMENT) && bodytype & BODYTYPE_ROBOTIC)) // BUBBER EDIT CHANGE - ORIGINAL: if(HAS_TRAIT(victim, TRAIT_LIMBATTACHMENT) || HAS_TRAIT(src, TRAIT_EASY_ATTACH))
+ // BUBBER EDIT ADDITION START - robot_limb_detach_quirk
+ if (!HAS_TRAIT(victim, TRAIT_LIMBATTACHMENT) && !HAS_TRAIT(victim, TRAIT_EASY_ATTACH) && !(bodytype & BODYTYPE_ROBOTIC) && !(bodytype & BODYTYPE_KINETIC) && !(bodytype & BODYTYPE_PEG)) //if we're trying to attach something that's not synthetic, end out
+ return
+ // BUBBER EDIT ADDITION END
if(!human_victim.get_bodypart(body_zone))
user.temporarilyRemoveItemFromInventory(src, TRUE)
if(!try_attach_limb(victim))
diff --git a/modular_zubbers/code/modules/quirks/positive_quirks/robot_limb_detach.dm b/modular_zubbers/code/modules/quirks/positive_quirks/robot_limb_detach.dm
new file mode 100644
index 00000000000..56dc1e87b9d
--- /dev/null
+++ b/modular_zubbers/code/modules/quirks/positive_quirks/robot_limb_detach.dm
@@ -0,0 +1,94 @@
+/datum/quirk/robot_limb_detach
+ name = "Cybernetic Limb Mounts"
+ desc = "You are able to detach and reattach any installed robotic limbs with very little effort, as long as they're in good condition. Right Click yourself while targeting a limb to remove it."
+ gain_text = span_notice("Internal sensors report limb disengagement protocols are ready and waiting.")
+ lose_text = span_notice("ERROR: LIMB DISENGAGEMENT PROTOCOLS OFFLINE.")
+ medical_record_text = "Patient bears quick-attach and release limb joint cybernetics."
+ value = 4
+ mob_trait = TRAIT_ROBOTIC_LIMBATTACHMENT
+ icon = FA_ICON_HANDSHAKE_SIMPLE_SLASH
+ quirk_flags = QUIRK_HUMAN_ONLY
+
+/datum/quirk/robot_limb_detach/add(client/client_source)
+ quirk_holder.AddElement(/datum/element/robot_self_amputation)
+
+/datum/quirk/robot_limb_detach/remove()
+ quirk_holder.RemoveElement(/datum/element/robot_self_amputation)
+
+/// Give to a human to allow them to self amputate their mechanical limbs
+/datum/element/robot_self_amputation
+
+/datum/element/robot_self_amputation/Attach(datum/target)
+ . = ..()
+
+ if(!iscarbon(target))
+ return ELEMENT_INCOMPATIBLE
+
+ RegisterSignal(target, COMSIG_MOB_ATTACK_HAND, PROC_REF(pre_self_amputate))
+
+
+/datum/element/robot_self_amputation/Detach(datum/source, ...)
+ UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND)
+ . = ..()
+
+/datum/element/robot_self_amputation/proc/pre_self_amputate(mob/living/carbon/amputee, mob/amputee_again, mob/living/target, datum/martial_art/attacker_style, modifiers)
+ SIGNAL_HANDLER
+
+ if(target != amputee)
+ return
+
+ if(!LAZYACCESS(modifiers, RIGHT_CLICK))
+ return
+
+ var/obj/item/bodypart/targeted_limb = amputee.get_bodypart(check_zone(amputee.zone_selected))
+
+ if(!targeted_limb)
+ return
+
+ // Chest removal Fail
+ if (targeted_limb.body_zone == BODY_ZONE_CHEST)
+ return
+
+ // Head removal fail for people who can't live without one
+ if (targeted_limb.body_zone == BODY_ZONE_HEAD && !issynthetic(amputee))
+ return
+
+ // Organic Limb Fail
+ if (IS_ORGANIC_LIMB(targeted_limb))
+ return
+
+ if(HAS_TRAIT(amputee, TRAIT_NODISMEMBER))
+ to_chat(amputee, span_warning("ERROR: LIMB DISENGAGEMENT PROTOCOLS OFFLINE. Seek out a maintenance technician."))
+ return
+
+ if (amputee.handcuffed)
+ to_chat(amputee, span_alert("You can't get a good enough grip with your hands bound."))
+ return
+
+ if (length(targeted_limb.wounds) >= 1)
+ amputee.balloon_alert(amputee, "can't detach wounded limbs!")
+ playsound(amputee, 'sound/machines/buzz/buzz-sigh.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ return
+
+ INVOKE_ASYNC(src, PROC_REF(self_amputate), amputee, targeted_limb)
+
+/datum/element/robot_self_amputation/proc/self_amputate(mob/living/carbon/amputee, obj/item/bodypart/targeted_limb)
+ amputee.balloon_alert(amputee, "detaching limb...")
+ playsound(amputee, 'sound/items/tools/rped.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ amputee.visible_message(span_notice("[amputee] shuffles [amputee.p_their()] [targeted_limb.name] forward, actuators hissing and whirring as [amputee.p_they()] disengage[amputee.p_s()] the limb from its mount..."))
+
+ if(!do_after(amputee, 10 SECONDS))
+ amputee.balloon_alert(amputee, "interrupted!")
+ return
+ if(amputee.handcuffed) //Prevents removing your arms if you get handcuffed part way through
+ return
+
+ playsound(amputee, 'sound/machines/buzz/buzz-sigh.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ amputee.visible_message(span_notice("With a gentle twist, [amputee] finally prises [amputee.p_their()] [targeted_limb.name] free from its socket."))
+ targeted_limb.drop_limb()
+ amputee.put_in_hands(targeted_limb)
+ amputee.balloon_alert(amputee, "limb detached!")
+ if(prob(5))
+ playsound(amputee, 'sound/items/champagne_pop.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ else
+ playsound(amputee, 'sound/items/deconstruct.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
diff --git a/tgstation.dme b/tgstation.dme
index 8ad108610df..db88569d435 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -9699,6 +9699,7 @@
#include "modular_zubbers\code\modules\public_logging\public_logging.dm"
#include "modular_zubbers\code\modules\quirks\neutral_quirks\body_temp.dm"
#include "modular_zubbers\code\modules\quirks\neutral_quirks\unique_blood_color.dm"
+#include "modular_zubbers\code\modules\quirks\positive_quirks\robot_limb_detach.dm"
#include "modular_zubbers\code\modules\quirks\positive_quirks\telepathy.dm"
#include "modular_zubbers\code\modules\quote_of_the_round\config.dm"
#include "modular_zubbers\code\modules\quote_of_the_round\debug_verbs.dm"