Files
Bubberstation/code/datums/components/seethrough.dm
SkyratBot 68e962fa21 [MIRROR] Adds seethrough component [MDB IGNORE] (#16421)
* Adds seethrough component (#69642)

Adds a seethrough component!
Standing behind a big object with this component will make the object transparent:

https://youtu.be/nnyWMJakVtE

And no one else can see it:

And yes you can click through it thanks to the power of plane masters!

Standing behind a tree is a pretty big meme and people will have to either shift right click or bump into you to ever find you. This makes it so much better to implement big objects, since they no longer obscure the tiles behind them
It's also useful for existing big objects, like billboards and the likes

🆑
qol: You can now see through big trees when you stand behind them!
refactor: Adds a seethrough component to make it easier to add big stationairy objects without reducing visibility
/🆑

Info

This is done by sending an override overlay to the user that obscures the normal object and plays an animation.

It registers an ENTERED signal on specific turfs. Those tiles in which it hides stuff is defined as a list of list coordinates, for which I made a global list with some defines. It's really crappy and I'd appreciate some feedback on that

* Adds seethrough component

Co-authored-by: Time-Green <timkoster1@hotmail.com>
2022-09-25 14:56:36 -07:00

148 lines
5.3 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
///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)
. = ..()
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
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/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/on_entered)
RegisterSignal(target, COMSIG_ATOM_EXITED, .proc/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/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, /datum/component/seethrough/proc/clear_image, trickery_image, mob.client), animation_time)
///Apply the trickery image and animation
/datum/component/seethrough/proc/trick_mob(mob/fool)
var/image/user_overlay = new(parent)
user_overlay.loc = parent
user_overlay.override = TRUE
//Special plane so we can click through the overlay
user_overlay.plane = ABOVE_GAME_NO_MOUSE_PLANE
//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/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, /datum/component/seethrough/proc/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)
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/trick_mob)