Files
Bubberstation/code/datums/components/seethrough.dm
SkyratBot ffac8f0df0 [MIRROR] Fixes critical plane masters improperly not being readded in show_to [MDB IGNORE] (#19060)
Fixes critical plane masters improperly not being readded in show_to (#72604)

## About The Pull Request

[Adds support for pulling z offset context from an atom's
plane](9f215c5316)

This is needed to fix paper bins, since the object we plane set there
isn't actually on a z level.
Useful elsewhere too!

[Fixes compiler errors that came from asserting that plane spokesmen had
a plane
var](b830002443)

[Ensures lighting backdrops ALWAYS exist for each lighting
plane.](0e931169f7)

They can't float becuase we can see more then one plane at once yaknow?

[Fixes parallax going to shit if a mob moved zs without having a
client](244b2b25ba)

Issue lies with how is_outside_bounds just blocked any plane readding
It's possible for a client to not be connected during z moves, so we
need to account for them rejoining in show_to, instead of just blocking
any of our edge cases.

Fixing this involved having parallax override blocks for show_plane and
anything with the right critical flags ensuring mobs have JUST the right
PMs and relays.
It's duped logic but I'm unsure of how else to handle it and frankly
this stuff is just kinda depressing.
Might refactor later

[show_to can be called twice successfully with no hide_from
call.](092581a5c0)

Ensures no runtimes off the registers from this

## Why It's Good For The Game

Fixes #72543
Fixes lighting looking batshit on multiz. None reported this I cry into
the night.

## Changelog
🆑
fix: Fixes parallax showing up ABOVE the game if you moved z levels
while disconnected
/🆑

---------

Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: Time-Green <timkoster1@hotmail.com>
2023-01-31 16:30:01 -05:00

165 lines
6.1 KiB
Plaintext

///A component that lets you turn an object invisible when you're standing on certain relative turfs to it, like behind a tree
/datum/component/seethrough
///List of lists that represent relative coordinates to the source atom
var/list/relative_turf_coords
///A list of turfs on which we make ourself transparent
var/list/watched_turfs
///Associate list, with client = trickery_image. Track which client is being tricked with which image
var/list/tricked_mobs = list()
///Which alpha do we animate towards?
var/target_alpha
///How long our fase in/out takes
var/animation_time
///After we somehow moved (because ss13 is godless and does not respect anything), how long do we need to stand still to feel safe to setup our "behind" area again
var/perimeter_reset_timer
///Does this object let clicks from players its transparent to pass through it
var/clickthrough
///see_through_map is a define pointing to a specific map. It's basically defining the area which is considered behind. See see_through_maps.dm for a list of maps
/datum/component/seethrough/Initialize(see_through_map = SEE_THROUGH_MAP_DEFAULT, target_alpha = 100, animation_time = 0.5 SECONDS, perimeter_reset_timer = 2 SECONDS, clickthrough = TRUE)
. = ..()
relative_turf_coords = GLOB.see_through_maps[see_through_map]
if(!isatom(parent) || !LAZYLEN(relative_turf_coords))
return COMPONENT_INCOMPATIBLE
relative_turf_coords = GLOB.see_through_maps[see_through_map]
src.relative_turf_coords = relative_turf_coords
src.target_alpha = target_alpha
src.animation_time = animation_time
src.perimeter_reset_timer = perimeter_reset_timer
src.clickthrough = clickthrough
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(dismantle_perimeter))
setup_perimeter(parent)
///Loop through a list with relative coordinate lists to mark those tiles and hide our parent when someone enters those tiles
/datum/component/seethrough/proc/setup_perimeter(atom/parent)
watched_turfs = list()
for(var/list/coordinates as anything in relative_turf_coords)
var/turf/target = TURF_FROM_COORDS_LIST(list(parent.x + coordinates[1], parent.y + coordinates[2], parent.z + coordinates[3]))
if(isnull(target))
continue
RegisterSignal(target, COMSIG_ATOM_ENTERED, PROC_REF(on_entered))
RegisterSignal(target, COMSIG_ATOM_EXITED, PROC_REF(on_exited))
watched_turfs.Add(target)
///Someone entered one of our tiles, so sent an override overlay and a cute animation to make us fade out a bit
/datum/component/seethrough/proc/on_entered(atom/source, atom/movable/entered)
SIGNAL_HANDLER
if(!ismob(entered))
return
var/mob/mob = entered
if(!mob.client)
RegisterSignal(mob, COMSIG_MOB_LOGIN, PROC_REF(trick_mob))
return
if(mob in tricked_mobs)
return
trick_mob(mob)
///Remove the screen object and make us appear solid to the client again
/datum/component/seethrough/proc/on_exited(atom/source, atom/movable/exited, direction)
SIGNAL_HANDLER
if(!ismob(exited))
return
var/mob/mob = exited
if(!mob.client)
UnregisterSignal(mob, COMSIG_MOB_LOGIN)
return
var/turf/moving_to = get_turf(exited)
if(moving_to in watched_turfs)
return
//Check if we're being 'tricked'
if(mob in tricked_mobs)
var/image/trickery_image = tricked_mobs[mob]
animate(trickery_image, alpha = 255, time = animation_time)
tricked_mobs.Remove(mob)
UnregisterSignal(mob, COMSIG_MOB_LOGOUT)
//after playing the fade-in animation, remove the screen obj
addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/component/seethrough,clear_image), trickery_image, mob.client), animation_time)
///Apply the trickery image and animation
/datum/component/seethrough/proc/trick_mob(mob/fool)
var/datum/hud/our_hud = fool.hud_used
for(var/atom/movable/screen/plane_master/seethrough in our_hud.get_true_plane_masters(SEETHROUGH_PLANE))
seethrough.unhide_plane(fool)
var/atom/atom_parent = parent
var/image/user_overlay = new(atom_parent)
user_overlay.loc = atom_parent
user_overlay.override = TRUE
if(clickthrough)
//Special plane so we can click through the overlay
SET_PLANE_EXPLICIT(user_overlay, SEETHROUGH_PLANE, atom_parent)
//These are inherited, but we already use the atom's loc so we end up at double the pixel offset
user_overlay.pixel_x = 0
user_overlay.pixel_y = 0
fool.client.images += user_overlay
animate(user_overlay, alpha = target_alpha, time = animation_time)
tricked_mobs[fool] = user_overlay
RegisterSignal(fool, COMSIG_MOB_LOGOUT, PROC_REF(on_client_disconnect))
///Unrout ourselves after we somehow moved, and start a timer so we can re-restablish our behind area after standing still for a bit
/datum/component/seethrough/proc/dismantle_perimeter()
SIGNAL_HANDLER
for(var/turf in watched_turfs)
UnregisterSignal(turf, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED))
watched_turfs = null
clear_all_images()
//Timer override, so if our atom keeps moving the timer is reset until they stop for X time
addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/component/seethrough,setup_perimeter), parent), perimeter_reset_timer, TIMER_OVERRIDE | TIMER_UNIQUE)
///Remove a screen image from a client
/datum/component/seethrough/proc/clear_image(image/removee, client/remove_from)
remove_from?.images -= removee //player could've logged out during the animation, so check just in case
/datum/component/seethrough/proc/clear_all_images()
for(var/mob/fool in tricked_mobs)
var/image/trickery_image = tricked_mobs[fool]
fool.client?.images -= trickery_image
UnregisterSignal(fool, COMSIG_MOB_LOGOUT)
var/datum/hud/our_hud = fool.hud_used
for(var/atom/movable/screen/plane_master/seethrough in our_hud.get_true_plane_masters(SEETHROUGH_PLANE))
seethrough.hide_plane(fool)
tricked_mobs.Cut()
///Image is removed when they log out because client gets deleted, so drop the mob reference
/datum/component/seethrough/proc/on_client_disconnect(mob/fool)
SIGNAL_HANDLER
tricked_mobs.Remove(fool)
UnregisterSignal(fool, COMSIG_MOB_LOGOUT)
RegisterSignal(fool, COMSIG_MOB_LOGIN, PROC_REF(trick_mob))
var/datum/hud/our_hud = fool.hud_used
for(var/atom/movable/screen/plane_master/seethrough in our_hud.get_true_plane_masters(SEETHROUGH_PLANE))
seethrough.hide_plane(fool)