Fixes closets not reusing images for clients (#92408)

## About The Pull Request
- Fixes #92214
- Fixes #92407

Whenever a mob with a client attached to it steps into a closet we
create 2 images(background & contents image) to display to that viewer

https://github.com/tgstation/tgstation/blob/4ee0793ecd23876994fa125e4e112a13a6f44455/code/game/objects/structures/crates_lockers/closet_see_inside.dm#L87

We are meant to reuse these images for multiple clients however this
proc does not check if we already made an image before. It just creates
a new one for every client(and we don't keep track of those previously
creates images) so we loose track of all sanity.

But now we make sure we make this image just once to reuse across
multiple clients

## Changelog
🆑
fix: You can no longer see parallel universes when 2 or more people gets
shoved into closets.
code: cleaned up closet see through code. It's documented & slightly
faster performance wise
/🆑

(cherry picked from commit c6dcc304ce)
This commit is contained in:
SyncIt21
2025-08-05 08:57:00 +05:30
committed by nevimer
parent 1c453daac2
commit d4ac4824f8
2 changed files with 45 additions and 56 deletions
@@ -1,41 +1,34 @@
/// Responsible for showing the insides of a closet to those inside it.
/datum/closet_see_inside
var/obj/structure/closet/closet
///Closet grayed out image so players can click on it to get out of closet
var/image/background_image
///Stuff inside closet image
var/image/contents_image
var/list/client/clients_looking_at_image = list()
var/door_alpha = 85
/datum/closet_see_inside/New(obj/structure/closet/closet)
src.closet = closet
RegisterSignal(closet, COMSIG_ATOM_ENTERED, PROC_REF(on_atom_entered))
RegisterSignal(closet, COMSIG_ATOM_EXITED, PROC_REF(on_atom_exited))
RegisterSignal(closet, COMSIG_CLOSET_PRE_OPEN, PROC_REF(on_closet_pre_open))
RegisterSignal(closet, COMSIG_CLOSET_POST_CLOSE, PROC_REF(on_closet_closed))
/datum/closet_see_inside/Destroy(force)
if (contents_image)
for (var/client/looking_at_image as anything in clients_looking_at_image)
looking_at_image.images -= background_image
looking_at_image.images -= contents_image
contents_image.vis_contents.Cut()
clients_looking_at_image.Cut()
on_closet_pre_open(src)
return ..()
/datum/closet_see_inside/proc/create_image()
contents_image = new
contents_image.loc = closet
contents_image.appearance_flags |= KEEP_TOGETHER
/**
* Creates the closet background & contents image to display for the client
*
* Arguments
* * obj/structure/closet/closet - the closet whose insides we are taking a snapshot of
*/
/datum/closet_see_inside/proc/create_image(obj/structure/closet/closet)
PRIVATE_PROC(TRUE)
if(contents_image)
return
///closet grayed out image
background_image = image(
icon = closet.icon,
icon_state = closet.base_icon_state == null ? initial(closet.icon_state) : closet.base_icon_state,
icon_state = isnull(closet.base_icon_state) ? initial(closet.icon_state) : closet.base_icon_state,
loc = closet,
layer = BELOW_OBJ_LAYER,
)
@@ -43,6 +36,10 @@
background_image.opacity = MOUSE_OPACITY_TRANSPARENT
background_image.override = TRUE
//all stuff inside the closet image
contents_image = new
contents_image.loc = closet
contents_image.appearance_flags |= KEEP_TOGETHER
contents_image.add_filter(
"mask",
1,
@@ -52,7 +49,6 @@
y = -3,
)
)
contents_image.add_filter(
"color",
2,
@@ -62,45 +58,39 @@
)
)
//door & contents to add to image
if (closet.enable_door_overlay)
var/obj/effect/overlay/door = new
door.icon = closet.icon
door.icon_state = "[closet.icon_door || background_image.icon_state]_door"
door.alpha = door_alpha
door.alpha = 85
door.layer = ABOVE_ALL_MOB_LAYER
door.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
contents_image.vis_contents += door
for (var/atom/movable/movable in closet)
contents_image.vis_contents += movable
/datum/closet_see_inside/proc/on_atom_entered(obj/structure/closet/source, atom/movable/arrived)
/datum/closet_see_inside/proc/on_closet_pre_open(obj/structure/closet/source, mob/user)
SIGNAL_HANDLER
if (contents_image)
contents_image.vis_contents += arrived
if(contents_image)
for(var/atom/movable/movable in source)
if(ismob(movable))
var/client/client = GET_CLIENT(astype(movable, /mob))
if (client)
client.images -= background_image
client.images -= contents_image
contents_image.vis_contents.Cut()
if (ismob(arrived))
var/mob/arrived_mob = arrived
var/client/client = GET_CLIENT(arrived_mob)
if (client)
create_image()
clients_looking_at_image += client
client.images += background_image
client.images += contents_image
/datum/closet_see_inside/proc/on_atom_exited(obj/structure/closet/source, atom/movable/exited)
/datum/closet_see_inside/proc/on_closet_closed(obj/structure/closet/source, mob/user)
SIGNAL_HANDLER
if (!contents_image)
return
contents_image.vis_contents -= exited
if (ismob(exited))
var/mob/exited_mob = exited
var/client/client = GET_CLIENT(exited_mob)
if (client)
clients_looking_at_image -= client
client.images -= background_image
client.images -= contents_image
for(var/atom/movable/movable in source)
if(ismob(movable))
var/client/client = GET_CLIENT(astype(movable, /mob))
if (client)
create_image(source)
client.images += background_image
client.images += contents_image
if(contents_image)
contents_image.vis_contents += movable
@@ -100,9 +100,8 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
var/x_shake_pixel_shift = 2
/// how many pixels the closet can shift on the y axes when shaking
var/y_shake_pixel_shift = 1
VAR_PRIVATE
datum/closet_see_inside/closet_see_inside
///Creates see through image for clients to see while inside closet
VAR_PRIVATE/datum/closet_see_inside/closet_see_inside
/datum/armor/structure_closet
melee = 20