From 67d0110828a97e2ebd534912e2ceb8d9cab62788 Mon Sep 17 00:00:00 2001 From: The Sharkening <95130227+StrangeWeirdKitten@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:57:52 -0600 Subject: [PATCH] MOD Remote Control module (#4328) ## About The Pull Request Adds a MOD remote module that can control a modsuit remotely. ## Why It's Good For The Game Eventually I want to transition Proteans to this system where you access all your abilities via the UI of your modsuit. But I think it'll be fun to allow these interactions with all mods and a framework to expand on it. ## Proof Of Testing image ![dreamseeker_mW39ThGpoy](https://github.com/user-attachments/assets/4af97916-517f-4a27-acac-e7f88f2f5e97) ## Changelog :cl: add: MODsuit Remote Module that allows you to remotely control other people's modsuits /:cl: --- .../designs/mechfabricator_designs.dm | 10 ++ .../code/lewd_clothing/kink_collars.dm | 4 +- .../code/lewd_machinery/lustwish.dm | 1 + .../species/proteans/protean_modsuit.dm | 2 +- .../code/modules/mod/modules_lustwish.dm | 88 +++++++++++++++++- .../modules/research/techweb/all_nodes.dm | 5 + .../mob/clothing/modsuit/mod_modules.dmi | Bin 973 -> 1342 bytes .../tgui/interfaces/MODsuitremote.tsx | 84 +++++++++++++++++ 8 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/MODsuitremote.tsx diff --git a/modular_skyrat/master_files/code/modules/research/designs/mechfabricator_designs.dm b/modular_skyrat/master_files/code/modules/research/designs/mechfabricator_designs.dm index 148526d93b8..84c4aa090bb 100644 --- a/modular_skyrat/master_files/code/modules/research/designs/mechfabricator_designs.dm +++ b/modular_skyrat/master_files/code/modules/research/designs/mechfabricator_designs.dm @@ -1,3 +1,13 @@ /datum/design/modlink_scryer // use the loaded modlink scryer object to have the frequency set to NT upon creation build_path = /obj/item/clothing/neck/link_scryer/loaded + +/datum/design/module/mod_remote + name = "MODsuit Remote Module" + id = "mod_remote_module" + build_path = /obj/item/mod/module/remote_control + materials = list( + /datum/material/iron = SHEET_MATERIAL_AMOUNT *3 , + /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.5, + ) + research_icon_state = "module_remote" diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kink_collars.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kink_collars.dm index 8314a3942a8..e9a8ad5d171 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kink_collars.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kink_collars.dm @@ -256,7 +256,7 @@ /obj/item/mind_controller/Destroy(force) collar?.remote = null collar = null - . = ..() + return ..() /obj/item/mind_controller/attack_self(mob/user) if(!collar) @@ -274,7 +274,7 @@ inhand_icon_state = null kink_collar = TRUE /// Reference to the mind control remote - var/obj/item/mind_controller/remote = null + var/obj/item/mind_controller/remote var/emoting = "Shivers." /obj/item/clothing/neck/mind_collar/Initialize(mapload) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm index a639fbfaa8c..763b4eedf74 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/lustwish.dm @@ -146,6 +146,7 @@ "products" = list( /obj/item/mod/construction/plating/lustwish = 5, /obj/item/mod/module/hypno_visor = 5, + /obj/item/mod/module/remote_control = 5, ) ), list( diff --git a/modular_zubbers/code/modules/customization/species/proteans/protean_modsuit.dm b/modular_zubbers/code/modules/customization/species/proteans/protean_modsuit.dm index 1624daab779..c2427daf239 100644 --- a/modular_zubbers/code/modules/customization/species/proteans/protean_modsuit.dm +++ b/modular_zubbers/code/modules/customization/species/proteans/protean_modsuit.dm @@ -175,7 +175,7 @@ var/datum/mod_theme/the_theme = GLOB.mod_themes[plates.theme] name = initial(name) - name = initial(desc) + desc = initial(desc) for(var/obj/item/part as anything in get_parts()) part.name = initial(name) diff --git a/modular_zubbers/code/modules/mod/modules_lustwish.dm b/modular_zubbers/code/modules/mod/modules_lustwish.dm index 6bf1d1e46e5..e1f1f03a61b 100644 --- a/modular_zubbers/code/modules/mod/modules_lustwish.dm +++ b/modular_zubbers/code/modules/mod/modules_lustwish.dm @@ -4,7 +4,7 @@ theme = /datum/mod_theme/lustwish /obj/item/mod/module/hypno_visor - name = "Hypnosis Module" + name = "hypnosis module" desc = "A module inserted into the visor of a suit in which commands can be processed. Use on self to set directives." icon = 'modular_zubbers/icons/mob/clothing/modsuit/mod_modules.dmi' icon_state = "module_hypno" @@ -47,3 +47,89 @@ . = ..() if(isnull(overlay_state_inactive)) overlay_state_inactive = initial(overlay_state_inactive) + +/datum/storage/pockets/small/remote_module + max_slots = 1 + +/datum/storage/pockets/small/remote_module/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound) + . = ..() + set_holdable(can_hold_list = list(/obj/item/remote_controller)) + +/obj/item/mod/module/remote_control + name = "modsuit remote module" + desc = "A module, once inserted, will allow anyone with its linked remote to control all functionality of the suit." + icon = 'modular_zubbers/icons/mob/clothing/modsuit/mod_modules.dmi' + icon_state = "module_remote" + module_type = MODULE_PASSIVE + complexity = 0 + idle_power_cost = DEFAULT_CHARGE_DRAIN * 0 + incompatible_modules = list(/obj/item/mod/module/remote_control) + required_slots = list(ITEM_SLOT_OCLOTHING) + overlay_state_inactive = "module_remote_overlay" + overlay_icon_file = 'modular_zubbers/icons/mob/clothing/modsuit/mod_modules.dmi' + +/obj/item/mod/module/remote_control/Initialize(mapload) + . = ..() + create_storage(storage_type = /datum/storage/pockets/small/remote_module) + var/obj/item/remote_controller/remote = new /obj/item/remote_controller() + remote.module = src + remote.forceMove(src) + +/obj/item/mod/module/remote_control/can_install(obj/item/mod/control/mod) + if(locate(/obj/item/remote_controller) in contents) + balloon_alert(usr, "remove remote from storage") + return FALSE + return TRUE + +/obj/item/mod/module/remote_control/attack_self(mob/user) + . = ..() + atom_storage?.open_storage(user) + +/obj/item/remote_controller + name = "modsuit remote" + desc = "A remote that allows control of a modsuit once its paired module is inserted." + icon = 'modular_zubbers/icons/mob/clothing/modsuit/mod_modules.dmi' + icon_state = "remote_item" + w_class = WEIGHT_CLASS_SMALL + var/obj/item/mod/module/remote_control/module + +/obj/item/remote_controller/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "MODsuitremote") + ui.open() + +/obj/item/remote_controller/ui_data(mob/user) + var/list/data = list() + data["linked_suit"] = !!module?.mod + data["wearer"] = !!module.mod?.wearer + data["erp_pref_check"] = module.mod?.wearer?.client?.prefs.read_preference(/datum/preference/toggle/erp/sex_toy) + if(module.mod) + data += module.mod.ui_data() + return data + +/obj/item/remote_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + switch(action) + if("emote") + module.mod.wearer.emote("me", 1, params["emote"], TRUE) + +/obj/item/remote_controller/proc/forced_emote(emoter, user) + +/obj/item/remote_controller/Initialize(mapload, module_init) + . = ..() + src.module = module_init + +/obj/item/remote_controller/ui_static_data(mob/user) + return module?.mod?.ui_static_data() + +/obj/item/remote_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + return module?.mod?.ui_act(action, params, ui, state) + +/obj/item/remote_controller/Destroy(force) + module = null + return ..() + diff --git a/modular_zubbers/code/modules/research/techweb/all_nodes.dm b/modular_zubbers/code/modules/research/techweb/all_nodes.dm index 03ab6f7b56b..dd2c2140275 100644 --- a/modular_zubbers/code/modules/research/techweb/all_nodes.dm +++ b/modular_zubbers/code/modules/research/techweb/all_nodes.dm @@ -146,6 +146,11 @@ design_ids += "mod_mind_transfer" . = ..() +// Modsuit tech +/datum/techweb_node/mod_equip/New() + . = ..() + design_ids += list("mod_remote_module") + /datum/techweb_node/nerd id = TECHWEB_NODE_NERD display_name = "Theoretical Physics" diff --git a/modular_zubbers/icons/mob/clothing/modsuit/mod_modules.dmi b/modular_zubbers/icons/mob/clothing/modsuit/mod_modules.dmi index e246c5915e813c583cc52f7aeae1ec0306c23a7f..083ee9914e0743362715df4d012c207fdfc3550f 100644 GIT binary patch delta 1287 zcmV+i1^D{S2fhj=iBL{Q4GJ0x0000DNk~Le0001>0001>2m=5B0K5^G9{>OVPLU-^ ze@VV|sOyhBcXx(jR#pH200DGTPE!Ct=GbNc006mqR9JLGWpiV4X>fFDZ*Bkpc$|%p z%L;=q5JlI?R|MU*rE0g`6s5SRzaRmpCXk0DW5vI3tb$$G2eY~JICF=I*`>Ia70YiW zeSq`Q8g^!^mM`_xXPu}=s+jM*Qv)RHr&=%pR(>zv71pV(-3-a|B z35{7%<*$!F0hBRuNT$u=!TiDpQ~d-gta3p@KuQzhUJNE zz=?}PO~R|ePb-ZZO0-_0=+HX53ja91^c~hiqiY)-N!A0ni{1e;J%%7=~dOhG7_nVSX;;oBzf-6>TUD1W%U(P<1Rn zBf>l7<1N&H${#5dm|exH#`DV*kakT#q97Cw5$U2*3&dv>Q7|8`c|m;P{(f)^=>`fB z=3pnW3lZQ*RA>MmX*~$a;QWta7=~dOhG7_nVV(nZeIR*pQ*W?zZO+#of9Z2;0A*LP z`h3H55hi?b7(N3Y0T?nqBz-I(UWEz?66qP$V;#T-mvle}UL_hXAjSQ{`ayv>zZH0q zWtv{}G=hd8^kzS37R-K-;mNPpM4$cgYhMleERgBIYKR{p;2#t$;rFs$zhuheq~jz8 z1ji|T#6e$*+vX?Nudi!Re=v0j5R8+c&#Ct0r#9>LCi@h576j(tNwC8ll;H&8KSg>o z<}Ldad>4oMyo)=0RH4-Nk7Fyun>BxP&$u{B^Z?akhqLuoLAl#Hc200_vx4I^&F8^M zOpeNWGrQD6sDqaR!!Xe^z$eo%PxW!{6v~o;OwI6-I-!*Cb)xmy*sNk zr^eNN_3n%}H|pvP!!QieLGG2*e}kUVDIb6g%}DqO3l$a+^nM{=$r^gW(_(iBei<{jI8|Q_@jvk6 zKjPLxhtToDD%iV=dfV3X{22BX~9h9JEY=a7x`Gn9}pK zk0C*`H;|w-ZU-A{+)g(4<$LDp?=|`?m~Vlfh2aON8fj(tf4Orv6N0Nj&j$&R&)I$Y zf~wYP_hq4HG~(m2beaM3IoFx^mG|>UurYo|@4FcKd@i1-p?of?0fNcSCdUqfA)gB! z1oAm^A$a@V-vqTt!?^|p&*zLY41(@=W)J$~cfNV=Q9^;_bE7>und=u0C#~GgMJdbY zIt^YzQ9k!XS-+xuuFDy&l#+aoqdSIS7>4hG7_nVHoD;@gG`?A$Edy4SxUt002ovPDHLkV1f|pSMLA- delta 915 zcmV;E18n@h3e5*4iBL{Q4GJ0x0000DNk~Le0001h0001h2m=5B0QBII`2YX_MUf>* ze^A`ET>t<80d!JMQvg8b*k%9#0HAtQSad{Xb7OL8aCB*JZU6vyoKseCa&`CgQ*iP1 zZ44Am1_Y&OF+`3gWX+ub00N3hL_t(|ob8)!Z`&{o zge5Aj?WV~(FU$Y`sgw2M1S?N;D|h>H4}zlb#p01t0#H_*BhPpd@g2Go@1;Xuf5$f* z_-6d5uOq*$RElHMA?B;SsbY^&LU556P5%SnWw4zXUhvP%`{iJu(!pbZj@d9%9G zH36Febr2~0{jR?FjU*1pZnuTcT_=PPLI@#*kSDC>#cXam^|gC%cc*rD+v>lq=0Ekd zUH!Sh-Z-nla^Q^(ggq8k1E@JQf3r&!=!w530k$C|8?f#+Hko!9Kqc5yfP?l2KoVjs z@Z;eA03n1BLI@$G1FJQY03J+lZ!pdm7!Pk{?dqBltn&k{?!5Nrjl0VMhMxTa)Ej5j zbRgXim^~gs%<~yXJ-)~L1Lo*y4fGkvr$~?iV+q-dY>xvp&FO$h@8~-%f9Ti$QFuZK zA%qY@2qB~kYY!tNuOUK@(~(zP$ud=YaW~=sqtUN@kNoRIpj?qU|^cd5gzbUmZ*nPBfZF5 z{^RTG2o01Bfq^_p`W}&Ge_)F*=W#qM2FmZevH>v1n*m*a7*KRy)~C3)%jHcq(4K;% z*D28ThXF+wx&DY;oqKz~{M7D$T7_Xidae-C>mt;2@yj)`L<7IxRRhyJ*YAPZ&5+V)cfDAZS9E3Rz;9MMHW%#t7DuDFmy2O_| zUw#7v!~MP{aKGmphhaKazb&`;iUz=^z(wP8{reqy { + const { data } = useBackend(); + const { ui_theme } = data; + return ( + + + + + + ); +}; + +const RemoteSection = (props) => { + const { act, data } = useBackend(); + const { erp_pref_check } = data; + const [emote, setEmote] = useState(''); + return ( +
+ + + act('emote', { emote: emote })} + /> + + +
+ ); +}; +const RemoteMODsuitContent = (props) => { + const { data } = useBackend(); + const { linked_suit, wearer } = data; + if (!linked_suit) { + return ( + +
+ +
+
+ Not installed +
+ ); + } + if (!wearer) { + return ( + +
+ +
+
+ MODsuit Not Worn +
+ ); + } + return ( + <> + + + + ); +};