Allows you to deploy, undeploy, activate & deactivate other people's MODsuits via strip menu (#95250)

## About The Pull Request

Allows deployment, undeployment, activation and deactivation of other
people's MODsuits via stripping buttons. Deployment and undeployment is
the same as if you were right clicking the button, i.e. undeployment if
you have any parts active, deployment otherwise.

Each action takes the MOD's `strip_delay` as its time to complete.

DNA lock modules' consequences do not trigger on anybody if the `user`
is not the `wearer`. They still don't deploy unless the acting agent is
the right dna.

## Why It's Good For The Game

Currently, there are two ways to get a MODsuit to activate or deploy on
another person without that person's involvement. An very rare PDA app
and sacrificing an AI. That's unfortunate, given as MODsuits are the
most used space protection. If you're forced to take off someone's
MODsuit in order to provide some sort of medical care, you are unable to
then put it back on them to prevent additional space-wrought damage. You
can't ~waste~ use a MODsuit to protect a incapacitated prisoner, or
force a springlocked MODsuit on a potential victim. It feels like a
missed avenue of interaction.

The PDA app is still powerful, for better reasons, as is the AI. Not to
imply anyone was using AIs for this.

## Changelog
🆑

add: You can now deploy, undeploy, activate & deactivate other people's
MODsuit via the strip menu

/🆑
This commit is contained in:
Leland Kemble
2026-03-06 09:18:38 +13:00
committed by GitHub
parent 58fbd80836
commit 9a16063ba1
5 changed files with 99 additions and 8 deletions
+9 -5
View File
@@ -50,11 +50,7 @@
balloon_alert(user, "currently [active ? "unsealing" : "sealing"]!")
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
return FALSE
var/deploy = TRUE
for(var/obj/item/part as anything in get_parts())
if(part.loc != src)
deploy = FALSE
break
var/deploy = check_retracted()
wearer.visible_message(span_notice("[wearer]'s [src] [deploy ? "deploys" : "retracts"] its parts with a mechanical hiss."),
span_notice("[src] [deploy ? "deploys" : "retracts"] its parts with a mechanical hiss."),
span_hear("You hear a mechanical hiss."))
@@ -325,4 +321,12 @@
for(var/obj/item/part as anything in get_parts())
deploy(null, part, instant = TRUE)
/// Checks if the suit is fully retracted, with no parts outside
/obj/item/mod/control/proc/check_retracted()
for(var/obj/item/part as anything in get_parts())
if(part.loc != src)
return FALSE
return TRUE
#undef MOD_ACTIVATION_STEP_FLAGS
+66
View File
@@ -100,8 +100,12 @@
locked = TRUE
new_core?.install(src)
update_speed()
RegisterSignal(src, COMSIG_ATOM_EXITED, PROC_REF(on_exit))
RegisterSignal(src, COMSIG_SPEED_POTION_APPLIED, PROC_REF(on_potion))
RegisterSignal(src, COMSIG_ITEM_GET_STRIPPABLE_ALT_ACTIONS, PROC_REF(get_strippable_alternate_actions))
RegisterSignal(src, COMSIG_ITEM_STRIPPABLE_ALT_ACTION, PROC_REF(do_strippable_action))
for(var/obj/item/mod/module/module as anything in theme.inbuilt_modules)
module = new module(src)
install(module)
@@ -402,6 +406,68 @@
icon_state = "[skin]-[base_icon_state][active ? "-sealed" : ""]"
return ..()
/obj/item/mod/control/proc/get_strippable_alternate_actions(obj/item/source, atom/owner, mob/user, list/alt_actions)
SIGNAL_HANDLER
if(active)
alt_actions += "deactivate_mod"
else
alt_actions += "activate_mod"
if(check_retracted())
alt_actions += "deploy"
else
alt_actions += "undeploy"
/obj/item/mod/control/proc/do_strippable_action(obj/item/source, atom/owner, mob/user, action_key)
SIGNAL_HANDLER
if(!isliving(user))
return NONE
switch(action_key)
if("deploy", "undeploy")
owner.visible_message(
span_warning("[user] tries to [action_key] [owner]'s [src]..."),
span_userdanger("[user] is trying to [action_key] your [src]!"),
blind_message = span_hear("You hear rustling."),
ignored_mobs = user,
)
INVOKE_ASYNC(src, PROC_REF(attempt_strip_deploy), owner, user, action_key)
return COMPONENT_ALT_ACTION_DONE
if("activate_mod", "deactivate_mod")
owner.visible_message(
span_warning("[user] tries to press [owner]'s [src]'s power button..."),
span_userdanger("[user] is trying to press your [src]'s power button!"),
blind_message = span_hear("You hear rustling."),
ignored_mobs = user,
)
INVOKE_ASYNC(src, PROC_REF(attempt_strip_activate), owner, user)
return COMPONENT_ALT_ACTION_DONE
else
return NONE
/obj/item/mod/control/proc/attempt_strip_deploy(atom/owner, mob/user, message)
if(!do_after(user, strip_delay, owner))
return
owner.visible_message(
span_warning("[user] [message]s [owner]'s [src]."),
span_userdanger("[user] [message]s your [src]!"),
ignored_mobs = user,
)
quick_deploy(user)
/obj/item/mod/control/proc/attempt_strip_activate(atom/owner, mob/user)
if(!do_after(user, strip_delay, owner))
return
owner.visible_message(
span_warning("[user] presses [owner]'s [src]'s power button."),
span_userdanger("[user] presses your [src]'s power button!"),
ignored_mobs = user,
)
toggle_activate(user)
/obj/item/mod/control/proc/get_parts(all = FALSE)
. = list()
for(var/key in mod_parts)
+1 -1
View File
@@ -779,7 +779,7 @@
/obj/item/mod/module/dna_lock/reinforced/on_mod_activation(datum/source, mob/user)
. = ..()
if(. != MOD_CANCEL_ACTIVATE || !isliving(user))
if(. != MOD_CANCEL_ACTIVATE || !isliving(user) || user != mod.wearer)
return
if(mod.ai_assistant == user)
to_chat(mod.ai_assistant, span_danger("<B>fATaL EERRoR</B>: 381200-*#00CODE <B>BLUE</B>\nAI INTErFERenCE DEtECted\nACTi0N DISrEGArdED"))
+4 -2
View File
@@ -39,10 +39,12 @@
/obj/item/mod/module/eradication_lock/proc/on_mod_activation(datum/source, mob/user)
SIGNAL_HANDLER
if(true_owner_ckey && user.ckey != true_owner_ckey)
if(!true_owner_ckey || user.ckey == true_owner_ckey)
return NONE
if(user == mod.wearer)
to_chat(mod.wearer, span_userdanger("\"MODsuit compromised by timeline inhabitant! Eradicating...\""))
new /obj/structure/chrono_field(user.loc, user)
return MOD_CANCEL_ACTIVATE
return MOD_CANCEL_ACTIVATE
///Signal fired when the modsuit tries removing a module.
/obj/item/mod/module/eradication_lock/proc/on_mod_removal(datum/source, mob/user)
@@ -88,6 +88,25 @@ const ALTERNATE_ACTIONS: Record<string, AlternateAction> = {
icon: 'ribbon',
text: 'Strip accessory',
},
deploy: {
icon: 'plus',
text: 'Deploy MOD',
},
undeploy: {
icon: 'minus',
text: 'Undeploy MOD',
},
activate_mod: {
icon: 'power-off',
text: 'Activate MOD',
},
deactivate_mod: {
icon: 'power-off',
text: 'Deactivate MOD',
},
};
const SLOTS: Record<