From 8259c2fc1e8e16d08c2209118098e33d18c7ca64 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Sat, 3 Jan 2026 13:47:34 -0600 Subject: [PATCH] Eigenstasium can no longer create Eigenlockers, but adds rare maint Eigenlockers as a replacement (#94683) ## About The Pull Request 1. Eigenstasium can no longer be splashed onto lockers to link them 2. Maintenance lockers can rarely be linked to another locker on the station - any locker, including head of staff lockers. This has a 0.1% chance, which amounts to a ~5% chance to spawn on a map with 50 lockers. 3. Teleporting via an eigenlocker no longer opens up the opposite side, allowing you to jumpscare people ## Why It's Good For The Game 1a. Eigenstasium's lockers are both significantly easier to produce and significantly easier to use than our "intended" method of spot-to-spot teleportation, the Quantum Pad. There is an argument to be made in that the Quantum Pad needs to be brought up to this level, and I may do that, because honestly, it's a valid point. But in the meanwhile, it doesn't really make sense that Chemistry can make better teleportation... than the teleportation department. 1b. Eigenstasium wears too many hats. It really is a wonder chem. It intrudes on other mechanics of other jobs and frankly doesn't need the extra sauce, it's already interesting enough. If your wikibox has a *collapsible*, it might be doing too much.... image 2. However, teleporting lockers are funny. We have the station trait, but it announces itself and affects (potentially) every closet, which ruins some of the mystique. So I wanted to add that effect as something you can stumble upon in the wild accidentally - Imagine hopping into a locker in maint to dodge sec and you end up in the Captain's office. 3. Builds off of 2. Finding a locker that teleports you into the middle of security so you can jump out and surprise them the next time they show up sounds really funny. ## Changelog :cl: Melbert del: Eigenstasium can no longer create Eigenlockers add: Rarely, maintenance lockers will spawn as Eigenlockers, potentially linked to any other locker on the station - yes, even the Captain's. add: Teleporting via an Eigenlocker doesn't auto-open the destination locker, allowing you to hide in them. /:cl: --- code/__DEFINES/dcs/signals/signals_object.dm | 4 -- code/__DEFINES/movement.dm | 2 - code/datums/eigenstate.dm | 49 ++++++++++++++----- code/datums/station_traits/neutral_traits.dm | 5 +- .../structures/crates_lockers/closets.dm | 16 +++++- .../chemistry/reagents/unique/eigenstasium.dm | 12 ----- 6 files changed, 53 insertions(+), 35 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 487872df053..c0793a7a835 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -260,10 +260,6 @@ ///a deliver_first element closet was successfully delivered #define COMSIG_CLOSET_DELIVERED "crate_delivered" -///Eigenstasium -///From base of [/datum/controller/subsystem/eigenstates/proc/use_eigenlinked_atom]: (var/target) -#define COMSIG_EIGENSTATE_ACTIVATE "eigenstate_activate" - // /obj signals for economy ///called when the payment component tries to charge an account. #define COMSIG_OBJ_ATTEMPT_CHARGE "obj_attempt_simple_charge" diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm index 28d5be84c52..35084b51aca 100644 --- a/code/__DEFINES/movement.dm +++ b/code/__DEFINES/movement.dm @@ -125,8 +125,6 @@ GLOBAL_VAR_INIT(glide_size_multiplier, 1.0) #define TELEPORT_CHANNEL_MAGIC "magic" /// Cult teleportation, does whatever it wants (unless there's holiness) #define TELEPORT_CHANNEL_CULT "cult" -/// Eigenstate teleportation, can do most things (that aren't in a teleport-prevented zone) -#define TELEPORT_CHANNEL_EIGENSTATE "eigenstate" /// Anything else #define TELEPORT_CHANNEL_FREE "free" diff --git a/code/datums/eigenstate.dm b/code/datums/eigenstate.dm index 8b113285b4b..390d79bde95 100644 --- a/code/datums/eigenstate.dm +++ b/code/datums/eigenstate.dm @@ -1,7 +1,7 @@ -GLOBAL_DATUM_INIT(eigenstate_manager, /datum/eigenstate_manager, new) +GLOBAL_DATUM_INIT(closet_teleport_controller, /datum/closet_teleport_controller, new) ///A singleton used to teleport people to a linked web of itterative entries. If one entry is deleted, the 2 around it will forge a link instead. -/datum/eigenstate_manager +/datum/closet_teleport_controller ///The list of objects that something is linked to indexed by UID var/list/eigen_targets = list() ///UID to object reference @@ -10,9 +10,11 @@ GLOBAL_DATUM_INIT(eigenstate_manager, /datum/eigenstate_manager, new) var/id_counter = 1 ///Limit the number of sparks created when teleporting multiple atoms to 1 var/spark_time = 0 + /// Weakref to an admin spawned eigenlinked closet - the next one spawned will link to it + var/datum/weakref/admin_link ///Creates a new link of targets unique to their own id -/datum/eigenstate_manager/proc/create_new_link(targets, subtle = TRUE) +/datum/closet_teleport_controller/proc/create_new_link(targets, subtle = TRUE) if(length(targets) <= 1) return FALSE for(var/atom/target as anything in targets) //Clear out any connected @@ -45,7 +47,6 @@ GLOBAL_DATUM_INIT(eigenstate_manager, /datum/eigenstate_manager, new) RegisterSignal(target, COMSIG_QDELETING, PROC_REF(remove_eigen_entry)) if(!subtle) RegisterSignal(target, COMSIG_ATOM_TOOL_ACT(TOOL_WELDER), PROC_REF(tool_interact)) - target.RegisterSignal(target, COMSIG_EIGENSTATE_ACTIVATE, TYPE_PROC_REF(/obj/structure/closet,bust_open)) ADD_TRAIT(target, TRAIT_BANNED_FROM_CARGO_SHUTTLE, REF(src)) if(!subtle) target.add_atom_colour(COLOR_PERIWINKLEE, FIXED_COLOUR_PRIORITY) //Tint the locker slightly. @@ -57,7 +58,7 @@ GLOBAL_DATUM_INIT(eigenstate_manager, /datum/eigenstate_manager, new) return TRUE ///reverts everything back to start -/datum/eigenstate_manager/eigenstates/Destroy() +/datum/closet_teleport_controller/eigenstates/Destroy() for(var/index in 1 to id_counter) for(var/entry in eigen_targets["[index]"]) remove_eigen_entry(entry) @@ -67,7 +68,7 @@ GLOBAL_DATUM_INIT(eigenstate_manager, /datum/eigenstate_manager, new) return ..() ///removes an object reference from the master list -/datum/eigenstate_manager/proc/remove_eigen_entry(atom/entry) +/datum/closet_teleport_controller/proc/remove_eigen_entry(atom/entry) SIGNAL_HANDLER var/id = eigen_id[entry] eigen_targets[id] -= entry @@ -80,14 +81,13 @@ GLOBAL_DATUM_INIT(eigenstate_manager, /datum/eigenstate_manager, new) COMSIG_ATOM_TOOL_ACT(TOOL_WELDER), )) REMOVE_TRAIT(entry, TRAIT_BANNED_FROM_CARGO_SHUTTLE, REF(src)) - entry.UnregisterSignal(entry, COMSIG_EIGENSTATE_ACTIVATE) //This is a signal on the object itself so we have to call it from that ///Remove the current entry if we're empty for(var/targets in eigen_targets) if(!length(eigen_targets[targets])) eigen_targets -= targets ///Finds the object within the master list, then sends the thing to the object's location -/datum/eigenstate_manager/proc/use_eigenlinked_atom(atom/object_sent_from, atom/movable/thing_to_send) +/datum/closet_teleport_controller/proc/use_eigenlinked_atom(atom/object_sent_from, atom/movable/thing_to_send) SIGNAL_HANDLER var/id = eigen_id[object_sent_from] @@ -106,8 +106,8 @@ GLOBAL_DATUM_INIT(eigenstate_manager, /datum/eigenstate_manager, new) if(!eigen_target) stack_trace("No eigen target set for the eigenstate component!") return FALSE - if(check_teleport_valid(thing_to_send, eigen_target, TELEPORT_CHANNEL_EIGENSTATE)) - thing_to_send.forceMove(get_turf(eigen_target)) + if(check_teleport_valid(thing_to_send, eigen_target, TELEPORT_CHANNEL_QUANTUM)) + thing_to_send.forceMove(eigen_target) else if(!subtle) object_sent_from.balloon_alert(thing_to_send, "nothing happens!") @@ -117,12 +117,35 @@ GLOBAL_DATUM_INIT(eigenstate_manager, /datum/eigenstate_manager, new) do_sparks(5, FALSE, eigen_target) do_sparks(5, FALSE, object_sent_from) spark_time = world.time - //Calls a special proc for the atom if needed (closets use bust_open()) - SEND_SIGNAL(eigen_target, COMSIG_EIGENSTATE_ACTIVATE) return COMPONENT_CLOSET_INSERT_INTERRUPT ///Prevents tool use on the item -/datum/eigenstate_manager/proc/tool_interact(atom/source, mob/user, obj/item/item) +/datum/closet_teleport_controller/proc/tool_interact(atom/source, mob/user, obj/item/item) SIGNAL_HANDLER to_chat(user, span_notice("The unstable nature of [source] makes it impossible to use [item] on [source.p_them()]!")) return ITEM_INTERACT_BLOCKING + +// For testing purposes, primarily +// Spawns a closet that will auto-link to the next one spawned +/obj/structure/closet/eigenlinked + /// Whether or not this closet subtle links (no messages/effects) + var/subtle = FALSE + +/obj/structure/closet/eigenlinked/Initialize(mapload) + . = ..() + if(mapload) + // i'm making no effort to ensure these link properly for mappers. + // add a mapping helper which uses editable ids if you want that + log_mapping("Don't map in [type], it likely won't link properly as it is intended for debug. \ + If you want this on your map you will need to add a mapping helper.") + return INITIALIZE_HINT_QDEL + + var/obj/structure/closet/other = GLOB.closet_teleport_controller.admin_link?.resolve() + if(isnull(other)) + GLOB.closet_teleport_controller.admin_link = WEAKREF(src) + return + + GLOB.closet_teleport_controller.create_new_link(list(src, other), subtle = src.subtle) + +/obj/structure/closet/eigenlinked/stealth + subtle = TRUE diff --git a/code/datums/station_traits/neutral_traits.dm b/code/datums/station_traits/neutral_traits.dm index 1cfed1c5b49..16feace65c4 100644 --- a/code/datums/station_traits/neutral_traits.dm +++ b/code/datums/station_traits/neutral_traits.dm @@ -377,7 +377,8 @@ trait_type = STATION_TRAIT_NEUTRAL show_in_report = TRUE weight = 1 - report_message = "We've reports of high amount of trace eigenstasium on your station. Ensure that your closets are working correctly." + report_message = "We've reports of loose bluespace streams affecting your station's lockers and closets. \ + You might lose some of your belongings... or gain some new ones!" /datum/station_trait/linked_closets/on_round_start() . = ..() @@ -392,7 +393,7 @@ var/list/targets = list() for(var/how_many in 1 to rand(2,3)) targets += pick_n_take(roundstart_closets) - GLOB.eigenstate_manager.create_new_link(targets) + GLOB.closet_teleport_controller.create_new_link(targets) #define PRO_SKUB "pro-skub" diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index ff5e41f2c36..793a2afa3c3 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -176,8 +176,20 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) update_appearance() /obj/structure/closet/LateInitialize() - if(!opened && is_maploaded) - take_contents() + if(is_maploaded) + // very rare chance that a maintenance closet gets linked to another closet at roundstart + // 0.1% chance -> metastation has ~36 maintenance closets -> P(links>=1) = 3.54% per round -> about once every 30 rounds + if(prob(0.1) && istype(get_area(src), /area/station/maintenance)) + var/list/targets = GLOB.roundstart_station_closets.Copy() - src + var/list/picked = list(src) + for(var/i in 1 to pick(1, 2)) + picked += pick_n_take(targets) + GLOB.closet_teleport_controller.create_new_link(picked, subtle = TRUE) + if(!opened) + open(force = TRUE, special_effects = FALSE) + + if(!opened) + take_contents() if(sealed) var/datum/gas_mixture/external_air = loc.return_air() diff --git a/code/modules/reagents/chemistry/reagents/unique/eigenstasium.dm b/code/modules/reagents/chemistry/reagents/unique/eigenstasium.dm index 8caeb53906e..1865e75b205 100644 --- a/code/modules/reagents/chemistry/reagents/unique/eigenstasium.dm +++ b/code/modules/reagents/chemistry/reagents/unique/eigenstasium.dm @@ -107,15 +107,3 @@ do_sparks(5, FALSE, living_mob) //FOR ADDICTION-LIKE EFFECTS, SEE datum/status_effect/eigenstasium - -///Lets you link lockers together -/datum/reagent/eigenstate/expose_turf(turf/exposed_turf, reac_volume) - . = ..() - if(creation_purity < 0.8) - return - var/list/lockers = list() - for(var/obj/structure/closet/closet in exposed_turf.contents) - lockers += closet - if(!lockers.len) - return - GLOB.eigenstate_manager.create_new_link(lockers, subtle = FALSE)