Files
Bubberstation/code/datums/eigenstate.dm
MrMelbert 8259c2fc1e 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.... 

<img width="1567" height="397" alt="image"
src="https://github.com/user-attachments/assets/969b6d32-5e85-42a1-8d3b-7769b6968b95"
/>

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

🆑 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.
/🆑
2026-01-04 08:47:34 +13:00

152 lines
6.0 KiB
Plaintext

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/closet_teleport_controller
///The list of objects that something is linked to indexed by UID
var/list/eigen_targets = list()
///UID to object reference
var/list/eigen_id = list()
///Unique id counter
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/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
var/already_linked = eigen_id[target]
if(!already_linked)
continue
if(length(eigen_targets[already_linked]) > 1) //Eigenstates are notorious for having cliques!
if(!subtle)
target.visible_message("[target] fizzes, it's already linked to something else!")
targets -= target
continue
if(!subtle)
target.visible_message("[target] fizzes, collapsing its unique wavefunction into the others!") //If we're in a eigenlink all on our own and are open to new friends
remove_eigen_entry(target) //clearup for new stuff
//Do we still have targets?
if(!length(targets))
return FALSE
var/atom/visible_atom = targets[1] //The object that'll handle the messages
if(length(targets) == 1)
if(!subtle)
visible_atom.visible_message("[targets[1]] fizzes, there's nothing it can link to!")
return FALSE
var/subtle_keyword = subtle ? "subtle" : ""
eigen_targets["[id_counter][subtle_keyword]"] = list() //Add to the master list
for(var/atom/target as anything in targets)
eigen_targets["[id_counter][subtle_keyword]"] += target
eigen_id[target] = "[id_counter][subtle_keyword]"
RegisterSignal(target, COMSIG_CLOSET_INSERT, PROC_REF(use_eigenlinked_atom))
RegisterSignal(target, COMSIG_QDELETING, PROC_REF(remove_eigen_entry))
if(!subtle)
RegisterSignal(target, COMSIG_ATOM_TOOL_ACT(TOOL_WELDER), PROC_REF(tool_interact))
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.
target.alpha = 200
do_sparks(3, FALSE, target)
visible_atom.visible_message("The items shimmer and fizzle, turning a shade of violet blue.")
id_counter++
return TRUE
///reverts everything back to start
/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)
eigen_targets = null
eigen_id = null
id_counter = 1
return ..()
///removes an object reference from the master list
/datum/closet_teleport_controller/proc/remove_eigen_entry(atom/entry)
SIGNAL_HANDLER
var/id = eigen_id[entry]
eigen_targets[id] -= entry
eigen_id -= entry
entry.remove_atom_colour(FIXED_COLOUR_PRIORITY, COLOR_PERIWINKLEE)
entry.alpha = 255
UnregisterSignal(entry, list(
COMSIG_QDELETING,
COMSIG_CLOSET_INSERT,
COMSIG_ATOM_TOOL_ACT(TOOL_WELDER),
))
REMOVE_TRAIT(entry, TRAIT_BANNED_FROM_CARGO_SHUTTLE, REF(src))
///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/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]
if(!id)
stack_trace("[object_sent_from] attempted to eigenlink to something that didn't have a valid id!")
return FALSE
var/subtle = findtext(id, "subtle")
var/list/items = eigen_targets[id]
var/index = (items.Find(object_sent_from))+1 //index + 1
if(!index)
stack_trace("[object_sent_from] attempted to eigenlink to something that didn't contain it!")
return FALSE
if(index > length(eigen_targets[id]))//If we're at the end of the list (or we're 1 length long)
index = 1
var/atom/eigen_target = eigen_targets[id][index]
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_QUANTUM))
thing_to_send.forceMove(eigen_target)
else
if(!subtle)
object_sent_from.balloon_alert(thing_to_send, "nothing happens!")
return FALSE
//Create ONE set of sparks for ALL times in iteration
if(!subtle && spark_time != world.time)
do_sparks(5, FALSE, eigen_target)
do_sparks(5, FALSE, object_sent_from)
spark_time = world.time
return COMPONENT_CLOSET_INSERT_INTERRUPT
///Prevents tool use on the 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