mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-10 08:54:15 +00:00
## About The Pull Request FOV as it is currently implemented is incompatible* with wallening. I'm doin wallening, so we gotta redo things here. The issue is the masking of mobs. Wallening relies on sidemap (layering based off physical position), which only works on things on the same plane (because planes are basically sheets we render down onto) So rather then masking mobs, let's reuse the masking idea from old fov, and use it to cut out a bit of the game render plane, and blur/over-saturate the bit that's masked out. My hope is this makes things visible in light, but not as much in darkness, alongside making more vivid shit more easily seen (just like real life) Here's some videos, what follows after is the commits I care about (since I had to rip a bunch of planes to nothing, so the files changed tab might be a bit of a mess) Oh also I had to remove the darkness pref since the darkness is doing a lot of the heavy lifting now. I'm sorry. Edit: NEW FOV SPRITES! Thanks dongle your aviator glasses will guide us to a better future. https://github.com/tgstation/tgstation/assets/58055496/afa9eeb8-8b7b-4364-b0c0-7ac8070b5609 https://github.com/tgstation/tgstation/assets/58055496/0eff040c-8bf1-47e4-a4f3-dac56fb2ccc8 ## Commits I Care About [Implements something like fov, but without the planes as layers hell](a604c7b1c8) Rather then masking out mobs standing behind us, we use a combo color matrix and blur filter to make the stuff covered by fov harder to see. We achive this by splitting the game plane into two, masking both by fov (one normally and one inversely), and then applying effects to one of the two. I want to make the fov fullscreens more gradient, but as an effect this is a good start [Removes WALL_PLANE_UPPER by adding a WALL_PLANE overlay to material walls (init cost comes here)](2548933739) @Mothblocks see this. comment in commit explains further but uh, we need to draw material walls to the light mask plane so things actually can be seen on them, but we can't do that and also have them be big, so they get an overlay. Sorry, slight init time bump, about 0.5 seconds. I can kill it with wallening. [Moves SEETHROUGH_PLANE above ABOVE_GAME_PLANE](beec4c00e0) I don't think it actually wants to draw here @Time-Green I think this was you so pinging for opinion [Resprites FOV masks to be clean (and more consistent)](f02ad13696) [f02ad13](f02ad13696) This is 100% donglesplonge's work, he's spent a week or so going back and forth with me sharpening these to a mirror shine, real chill ## Why It's Good For The Game Walls are closing in ## Changelog 🆑 LemonInTheDark, Donglesplonge image: Redoes fov "mask" sprites. They're clean, have a very pleasant dithering effect, and look real fuckin good! del: Changed FOV, it no longer hides mobs, instead it blurs the hidden area, and makes it a bit darker/oversaturated /🆑 ###### * It's technically possible if we start using render targets to create 2 sets of sources but that's insane and we aren't doing it
414 lines
19 KiB
Plaintext
414 lines
19 KiB
Plaintext
/**
|
|
* A visual element that makes movables entering the attached turfs look immersed into that turf.
|
|
*
|
|
* Abandon all hope, ye who read forth, for this immerse works on mind-numbing workarounds,
|
|
*/
|
|
/datum/element/immerse
|
|
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY | ELEMENT_BESPOKE
|
|
argument_hash_start_idx = 2
|
|
///An association list of turfs that have this element attached and their affected contents.
|
|
var/list/attached_turfs_and_movables = list()
|
|
|
|
/**
|
|
* A list of movables that shouldn't be affected by the element, either because it'd look bad
|
|
* or barely perceptible.
|
|
*/
|
|
var/static/list/movables_to_ignore
|
|
///A list of icons generated from a target and a mask, later used as appearances for the overlays.
|
|
var/static/list/generated_immerse_icons = list()
|
|
///A list of instances of /atom/movable/immerse_overlay then used as visual overlays for the immersed movables.
|
|
var/list/generated_visual_overlays = list()
|
|
///An association list of movables as key and overlays as assoc.
|
|
var/list/immersed_movables
|
|
|
|
var/icon
|
|
var/icon_state
|
|
var/mask_icon
|
|
var/color
|
|
var/alpha
|
|
|
|
/datum/element/immerse/Attach(turf/target, icon, icon_state, mask_icon, color = "#777777", alpha = 180)
|
|
. = ..()
|
|
if(!isturf(target) || !icon || !icon_state || !mask_icon)
|
|
return ELEMENT_INCOMPATIBLE
|
|
|
|
if(!movables_to_ignore)
|
|
movables_to_ignore = typecacheof(list(
|
|
/obj/effect,
|
|
/mob/dead,
|
|
/obj/projectile,
|
|
))
|
|
|
|
movables_to_ignore += GLOB.WALLITEMS_INTERIOR
|
|
movables_to_ignore += GLOB.WALLITEMS_EXTERIOR
|
|
|
|
src.icon = icon
|
|
src.icon_state = icon_state
|
|
src.color = color
|
|
src.alpha = alpha
|
|
src.mask_icon = mask_icon
|
|
|
|
/**
|
|
* Hello, you may be wondering why we're blending icons and not simply
|
|
* overlaying one mutable appearance with the blend multiply on another.
|
|
* Well, the latter option doesn't work as neatly when added
|
|
* to an atom with the KEEP_TOGETHER appearance flag, with the mask icon also
|
|
* showing on said atom, while we don't want it to.
|
|
*
|
|
* Also using KEEP_APART isn't an option, because unless it's drawn as one with
|
|
* its visual loation, the whole plane the atom belongs to will count as part of the
|
|
* mask of the final visual overlay since that's how the BLEND_INSET_OVERLAY blend mode works here.
|
|
* In layman terms, with KEEP_APART on, if a flying monkey gets nears an immersed
|
|
* human, the visual overlay will appear on the flying monkey even if it shouldn't.
|
|
*/
|
|
var/icon/immerse_icon = generated_immerse_icons["[icon]-[icon_state]-[mask_icon]"]
|
|
if(!immerse_icon)
|
|
immerse_icon = icon(icon, icon_state)
|
|
var/icon/sub_mask = icon('icons/effects/effects.dmi', mask_icon)
|
|
immerse_icon.Blend(sub_mask, ICON_MULTIPLY)
|
|
immerse_icon = fcopy_rsc(immerse_icon)
|
|
generated_immerse_icons["[icon]-[icon_state]-[mask_icon]"] = immerse_icon
|
|
|
|
RegisterSignal(target, SIGNAL_ADDTRAIT(TRAIT_IMMERSE_STOPPED), PROC_REF(stop_immersion))
|
|
RegisterSignal(target, SIGNAL_REMOVETRAIT(TRAIT_IMMERSE_STOPPED), PROC_REF(start_immersion))
|
|
|
|
if(!HAS_TRAIT(target, TRAIT_IMMERSE_STOPPED))
|
|
start_immersion(target)
|
|
|
|
/datum/element/immerse/Detach(turf/source)
|
|
UnregisterSignal(source, list(SIGNAL_ADDTRAIT(TRAIT_IMMERSE_STOPPED), SIGNAL_REMOVETRAIT(TRAIT_IMMERSE_STOPPED)))
|
|
if(!HAS_TRAIT(source, TRAIT_IMMERSE_STOPPED))
|
|
stop_immersion(source)
|
|
return ..()
|
|
|
|
///Makes the element start affecting the turf and its contents. Called on Attach() or when TRAIT_IMMERSE_STOPPED is removed.
|
|
/datum/element/immerse/proc/start_immersion(turf/source)
|
|
SIGNAL_HANDLER
|
|
RegisterSignals(source, list(COMSIG_ATOM_ABSTRACT_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON), PROC_REF(on_init_or_entered))
|
|
RegisterSignal(source, COMSIG_ATOM_ABSTRACT_EXITED, PROC_REF(on_atom_exited))
|
|
attached_turfs_and_movables += source
|
|
for(var/atom/movable/movable as anything in source)
|
|
if(!(movable.flags_1 & INITIALIZED_1))
|
|
continue
|
|
on_init_or_entered(source, movable)
|
|
|
|
///Stops the element from affecting on the turf and its contents. Called on Detach() or when TRAIT_IMMERSE_STOPPED is added.
|
|
/datum/element/immerse/proc/stop_immersion(turf/source)
|
|
SIGNAL_HANDLER
|
|
UnregisterSignal(source, list(COMSIG_ATOM_ABSTRACT_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, COMSIG_ATOM_ABSTRACT_EXITED))
|
|
for(var/atom/movable/movable as anything in attached_turfs_and_movables[source])
|
|
remove_from_element(source, movable)
|
|
attached_turfs_and_movables -= source
|
|
|
|
/**
|
|
* If the movable is within the right layers and planes, not in the list of movable types to ignore,
|
|
* or already affected by the element for that matter, Signals will be registered and,
|
|
* unless the movable (or whatever it's buckled to) is flying, it'll appear as if immersed in that water.
|
|
*/
|
|
/datum/element/immerse/proc/on_init_or_entered(turf/source, atom/movable/movable)
|
|
SIGNAL_HANDLER
|
|
if(HAS_TRAIT(movable, TRAIT_IMMERSED))
|
|
return
|
|
if(movable.layer >= ABOVE_ALL_MOB_LAYER || !ISINRANGE(movable.plane, MUTATE_PLANE(FLOOR_PLANE, source), MUTATE_PLANE(GAME_PLANE, source)))
|
|
return
|
|
if(is_type_in_typecache(movable, movables_to_ignore))
|
|
return
|
|
|
|
var/atom/movable/buckled
|
|
if(isliving(movable))
|
|
var/mob/living/living_mob = movable
|
|
RegisterSignal(living_mob, COMSIG_LIVING_SET_BUCKLED, PROC_REF(on_set_buckled))
|
|
buckled = living_mob.buckled
|
|
|
|
try_immerse(movable, buckled)
|
|
RegisterSignal(movable, COMSIG_QDELETING, PROC_REF(on_movable_qdel))
|
|
LAZYADD(attached_turfs_and_movables[source], movable)
|
|
ADD_TRAIT(movable, TRAIT_IMMERSED, ELEMENT_TRAIT(src))
|
|
|
|
/datum/element/immerse/proc/on_movable_qdel(atom/movable/source)
|
|
SIGNAL_HANDLER
|
|
remove_from_element(source.loc, source)
|
|
|
|
/**
|
|
* The main proc, which adds a visual overlay to the movable that has entered the turf to make it look immersed.
|
|
* It's kind of iffy but basically, we want the overlay to cover as much area as needed to
|
|
* avoid the movable's icon from spilling horizontally or below.
|
|
* Also, while these visual overlays are mainly cached movables, for certain movables, such as living mobs,
|
|
* we want them to have their own unique vis overlay with additional signals registered.
|
|
* This allows the vis overlay to look more or less unchanged while its owner is spinning or resting
|
|
* without otherwise affecting other movables with identical overlays.
|
|
*/
|
|
/datum/element/immerse/proc/add_immerse_overlay(atom/movable/movable)
|
|
var/list/icon_dimensions = get_icon_dimensions(movable.icon)
|
|
var/width = icon_dimensions["width"] || world.icon_size
|
|
var/height = icon_dimensions["height"] || world.icon_size
|
|
|
|
var/is_below_water = movable.layer < WATER_LEVEL_LAYER ? "underwater-" : ""
|
|
|
|
var/atom/movable/immerse_overlay/vis_overlay = generated_visual_overlays["[is_below_water][width]x[height]"]
|
|
|
|
if(!vis_overlay) //create the overlay if not already done.
|
|
vis_overlay = generate_vis_overlay(width, height, is_below_water)
|
|
|
|
|
|
ADD_KEEP_TOGETHER(movable, ELEMENT_TRAIT(src))
|
|
|
|
/**
|
|
* Let's give an unique immerse visual only to those movables that would
|
|
* benefit from this the most, for the sake of a smidge of lightweightness.
|
|
*/
|
|
if(HAS_TRAIT(movable, TRAIT_UNIQUE_IMMERSE))
|
|
var/atom/movable/immerse_overlay/original_vis_overlay = vis_overlay
|
|
vis_overlay = new(null)
|
|
vis_overlay.appearance = original_vis_overlay
|
|
vis_overlay.extra_width = original_vis_overlay.extra_width
|
|
vis_overlay.extra_height = original_vis_overlay.extra_height
|
|
vis_overlay.overlay_appearance = original_vis_overlay.overlay_appearance
|
|
SEND_SIGNAL(movable, COMSIG_MOVABLE_EDIT_UNIQUE_IMMERSE_OVERLAY, vis_overlay)
|
|
RegisterSignal(movable, COMSIG_ATOM_SPIN_ANIMATION, PROC_REF(on_spin_animation))
|
|
RegisterSignal(movable, COMSIG_LIVING_POST_UPDATE_TRANSFORM, PROC_REF(on_update_transform))
|
|
|
|
movable.vis_contents |= vis_overlay
|
|
|
|
LAZYSET(immersed_movables, movable, vis_overlay)
|
|
|
|
///Initializes and caches a new visual overlay given parameters such as width, height and whether it should appear fully underwater.
|
|
/datum/element/immerse/proc/generate_vis_overlay(width, height, is_below_water)
|
|
|
|
var/atom/movable/immerse_overlay/vis_overlay = new(null, src)
|
|
|
|
/**
|
|
* vis contents spin around the center of the icon of their vis locs
|
|
* but since we want the appearance to stay where it should be,
|
|
* we have to counteract this one.
|
|
*/
|
|
var/extra_width = (width - world.icon_size) * 0.5
|
|
var/extra_height = (height - world.icon_size) * 0.5
|
|
var/mutable_appearance/overlay_appearance = new()
|
|
var/icon/immerse_icon = generated_immerse_icons["[icon]-[icon_state]-[mask_icon]"]
|
|
var/last_i = width/world.icon_size
|
|
for(var/i in -1 to last_i)
|
|
var/mutable_appearance/underwater = mutable_appearance(icon, icon_state)
|
|
underwater.pixel_x = world.icon_size * i - extra_width
|
|
underwater.pixel_y = -world.icon_size - extra_height
|
|
overlay_appearance.overlays += underwater
|
|
|
|
var/mutable_appearance/water_level = is_below_water ? underwater : mutable_appearance(immerse_icon)
|
|
water_level.pixel_x = world.icon_size * i - extra_width
|
|
water_level.pixel_y = -extra_height
|
|
overlay_appearance.overlays += water_level
|
|
|
|
|
|
vis_overlay.color = color
|
|
vis_overlay.alpha = alpha
|
|
vis_overlay.overlays = list(overlay_appearance)
|
|
|
|
vis_overlay.extra_width = extra_width
|
|
vis_overlay.extra_height = extra_height
|
|
vis_overlay.overlay_appearance = overlay_appearance
|
|
|
|
generated_visual_overlays["[is_below_water][width]x[height]"] = vis_overlay
|
|
return vis_overlay
|
|
|
|
///This proc removes the vis_overlay, the keep together trait and some signals from the movable.
|
|
/datum/element/immerse/proc/remove_immerse_overlay(atom/movable/movable)
|
|
var/atom/movable/immerse_overlay/vis_overlay = LAZYACCESS(immersed_movables, movable)
|
|
if(!vis_overlay)
|
|
return
|
|
movable.vis_contents -= vis_overlay
|
|
LAZYREMOVE(immersed_movables, movable)
|
|
if(HAS_TRAIT(movable, TRAIT_UNIQUE_IMMERSE))
|
|
UnregisterSignal(movable, list(COMSIG_ATOM_SPIN_ANIMATION, COMSIG_LIVING_POST_UPDATE_TRANSFORM))
|
|
qdel(vis_overlay)
|
|
REMOVE_KEEP_TOGETHER(movable, ELEMENT_TRAIT(src))
|
|
|
|
/**
|
|
* Called by init_or_entered() and on_set_buckled().
|
|
* This applies the overlay if neither the movable or whatever is buckled to (exclusive to living mobs) are flying
|
|
* as well as movetype signals when the movable isn't buckled.
|
|
*/
|
|
/datum/element/immerse/proc/try_immerse(atom/movable/movable, atom/movable/buckled)
|
|
var/atom/movable/to_check = buckled || movable
|
|
if(!(to_check.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && !movable.throwing)
|
|
add_immerse_overlay(movable)
|
|
if(!buckled)
|
|
RegisterSignal(movable, COMSIG_MOVETYPE_FLAG_ENABLED, PROC_REF(on_move_flag_enabled))
|
|
RegisterSignal(movable, COMSIG_MOVETYPE_FLAG_DISABLED, PROC_REF(on_move_flag_disabled))
|
|
RegisterSignal(movable, COMSIG_MOVABLE_POST_THROW, PROC_REF(on_throw))
|
|
RegisterSignal(movable, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(on_throw_landed))
|
|
|
|
/**
|
|
* Called by on_set_buckled() and remove_from_element().
|
|
* This removes the filter and signals from the movable unless it doesn't have them.
|
|
*/
|
|
/datum/element/immerse/proc/try_unimmerse(atom/movable/movable, atom/movable/buckled)
|
|
var/atom/movable/to_check = buckled || movable
|
|
if(!(to_check.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) && !movable.throwing)
|
|
remove_immerse_overlay(movable)
|
|
if(!buckled)
|
|
UnregisterSignal(movable, list(COMSIG_MOVETYPE_FLAG_ENABLED, COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_MOVABLE_POST_THROW, COMSIG_MOVABLE_THROW_LANDED))
|
|
|
|
/datum/element/immerse/proc/on_set_buckled(mob/living/source, atom/movable/new_buckled)
|
|
SIGNAL_HANDLER
|
|
try_unimmerse(source, source.buckled)
|
|
try_immerse(source, new_buckled)
|
|
|
|
///Removes the overlay from mob and bucklees is flying.
|
|
/datum/element/immerse/proc/on_move_flag_enabled(atom/movable/source, flag, old_movement_type)
|
|
SIGNAL_HANDLER
|
|
if(!(flag & MOVETYPES_NOT_TOUCHING_GROUND) || (old_movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || source.throwing)
|
|
return
|
|
remove_immerse_overlay(source)
|
|
for(var/mob/living/buckled_mob as anything in source.buckled_mobs)
|
|
remove_immerse_overlay(buckled_mob)
|
|
|
|
///Works just like on_move_flag_enabled, except it only has to check that movable isn't flying
|
|
/datum/element/immerse/proc/on_throw(atom/movable/source)
|
|
SIGNAL_HANDLER
|
|
if(source.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
|
|
return
|
|
remove_immerse_overlay(source)
|
|
for(var/mob/living/buckled_mob as anything in source.buckled_mobs)
|
|
remove_immerse_overlay(buckled_mob)
|
|
|
|
///Readds the overlay to the mob and bucklees if no longer flying.
|
|
/datum/element/immerse/proc/on_move_flag_disabled(atom/movable/source, flag, old_movement_type)
|
|
SIGNAL_HANDLER
|
|
if(!(flag & MOVETYPES_NOT_TOUCHING_GROUND) || (source.movement_type & MOVETYPES_NOT_TOUCHING_GROUND) || source.throwing)
|
|
return
|
|
add_immerse_overlay(source)
|
|
for(var/mob/living/buckled_mob as anything in source.buckled_mobs)
|
|
add_immerse_overlay(buckled_mob)
|
|
|
|
///Works just like on_move_flag_disabled, except it only has to check that movable isn't flying
|
|
/datum/element/immerse/proc/on_throw_landed(atom/movable/source)
|
|
SIGNAL_HANDLER
|
|
if(source.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
|
|
return
|
|
add_immerse_overlay(source)
|
|
for(var/mob/living/buckled_mob as anything in source.buckled_mobs)
|
|
add_immerse_overlay(buckled_mob)
|
|
|
|
/**
|
|
* Called when a movable exits the turf. If its new location is not in the list of turfs with this element,
|
|
* Remove the movable from the element.
|
|
*/
|
|
/datum/element/immerse/proc/on_atom_exited(turf/source, atom/movable/exited, direction)
|
|
SIGNAL_HANDLER
|
|
if(!(exited.loc in attached_turfs_and_movables))
|
|
remove_from_element(source, exited)
|
|
else
|
|
LAZYREMOVE(attached_turfs_and_movables[source], exited)
|
|
LAZYADD(attached_turfs_and_movables[exited.loc], exited)
|
|
|
|
///Remove any signal, overlay, trait given to the movable and reference to it within the element.
|
|
/datum/element/immerse/proc/remove_from_element(turf/source, atom/movable/movable)
|
|
var/atom/movable/buckled
|
|
if(isliving(movable))
|
|
var/mob/living/living_mob = movable
|
|
buckled = living_mob.buckled
|
|
try_unimmerse(movable, buckled)
|
|
|
|
UnregisterSignal(movable, list(COMSIG_LIVING_SET_BUCKLED, COMSIG_QDELETING))
|
|
REMOVE_TRAIT(movable, TRAIT_IMMERSED, ELEMENT_TRAIT(src))
|
|
LAZYREMOVE(attached_turfs_and_movables[source], movable)
|
|
|
|
/// A band-aid to keep the (unique) visual overlay from scaling and rotating along with its owner. I'm sorry.
|
|
/datum/element/immerse/proc/on_update_transform(mob/living/source, resize, new_lying_angle, is_opposite_angle)
|
|
SIGNAL_HANDLER
|
|
var/matrix/new_transform = matrix()
|
|
new_transform.Scale(1/source.current_size)
|
|
new_transform.Turn(-new_lying_angle)
|
|
|
|
var/atom/movable/immerse_overlay/vis_overlay = immersed_movables[source]
|
|
if(is_opposite_angle)
|
|
vis_overlay.transform = new_transform
|
|
vis_overlay.adjust_living_overlay_offset(source)
|
|
return
|
|
|
|
/**
|
|
* Here, we temporarily switch from the offset of the mutable appearance to one for movable used as visual overlay.
|
|
* Why? While visual overlays can be animated, their fixed point stays at the center of the icon of the atom
|
|
* they're attached to and not theirs, which can make manipulating the transform var a pain, but because
|
|
* we cannot do that with normal overlay or filters (reliably), we have to bend a knee and try to compensate it.
|
|
*/
|
|
vis_overlay.overlays = list(vis_overlay.overlay_appearance)
|
|
|
|
/// Oh, yeah, didn't I mention turning a visual overlay affects its pixel x/y/w/z too? Yeah, it sucks.
|
|
var/new_x = vis_overlay.extra_width
|
|
var/new_y = vis_overlay.extra_height
|
|
var/old_div = source.current_size/resize
|
|
var/offset_lying = source.rotate_on_lying ? PIXEL_Y_OFFSET_LYING : source.get_pixel_y_offset_standing(source.current_size/resize)
|
|
switch(source.lying_prev)
|
|
if(270)
|
|
vis_overlay.pixel_x += -offset_lying / old_div
|
|
if(90)
|
|
vis_overlay.pixel_x += offset_lying / old_div
|
|
if(0)
|
|
vis_overlay.pixel_y += -source.get_pixel_y_offset_standing(source.current_size/resize) / old_div
|
|
|
|
switch(new_lying_angle)
|
|
if(270)
|
|
new_x += -source.body_position_pixel_y_offset / source.current_size
|
|
if(90)
|
|
new_x += source.body_position_pixel_y_offset / source.current_size
|
|
if(0)
|
|
new_y += -source.body_position_pixel_y_offset / source.current_size
|
|
|
|
animate(vis_overlay, transform = new_transform, pixel_x = new_x, pixel_y = new_y, time = UPDATE_TRANSFORM_ANIMATION_TIME, easing = (EASE_IN|EASE_OUT))
|
|
addtimer(CALLBACK(vis_overlay, TYPE_PROC_REF(/atom/movable/immerse_overlay, adjust_living_overlay_offset), source), UPDATE_TRANSFORM_ANIMATION_TIME)
|
|
|
|
///Spin the overlay in the opposite direction so it doesn't look like it's spinning at all.
|
|
/datum/element/immerse/proc/on_spin_animation(atom/source, speed, loops, segments, segment)
|
|
SIGNAL_HANDLER
|
|
var/atom/movable/immerse_overlay/vis_overlay = immersed_movables[source]
|
|
vis_overlay.do_spin_animation(speed, loops, segments, -segment)
|
|
|
|
///We need to make sure to remove hard refs from the element when deleted.
|
|
/datum/element/immerse/proc/clear_overlay_refs(atom/movable/immerse_overlay/source)
|
|
//Assume that every vis loc is also in the immersed_movables list
|
|
for(var/atom/movable/vis_loc as anything in source.vis_locs)
|
|
remove_from_element(vis_loc.loc, vis_loc)
|
|
LAZYREMOVE(generated_visual_overlays, source)
|
|
source.overlay_appearance = null
|
|
|
|
///The not-quite-perfect movable used by the immerse element for its nefarious deeds.
|
|
/atom/movable/immerse_overlay
|
|
appearance_flags = RESET_TRANSFORM|RESET_COLOR|RESET_ALPHA|KEEP_TOGETHER
|
|
vis_flags = VIS_INHERIT_PLANE|VIS_INHERIT_ID
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
blend_mode = BLEND_INSET_OVERLAY
|
|
layer = WATER_VISUAL_OVERLAY_LAYER
|
|
plane = FLOAT_PLANE
|
|
/**
|
|
* The actual overlay used to make the mob look like it's half-covered in water.
|
|
*
|
|
* For visual overlays, pixel y/x/w/z are amplified by the a, b, d, e variables
|
|
* of the transform matrix of the movable they're attached to.
|
|
* For example, if a mob is twice its normal size (a = 2, e = 2),
|
|
* offsetting the movable used as visual overlay by 4 pixels to the right will result
|
|
* in the visual overlay moving 8 pixels to the right.
|
|
*
|
|
* This however, doesn't extend to the overlays of our visual overlay. which is why there's
|
|
* a mutable appearance variable that we use for those pixel offsets that really shouldn't be affected
|
|
* by the transform of our vis loc(s) in the first place.
|
|
*/
|
|
var/mutable_appearance/overlay_appearance
|
|
///The base pixel x offset of this movable
|
|
var/extra_width = 0
|
|
///The base pixel y offset of this movable
|
|
var/extra_height = 0
|
|
|
|
/atom/movable/immerse_overlay/Initialize(mapload, datum/element/immerse/element)
|
|
. = ..()
|
|
verbs.Cut() //"Cargo cultttttt" or something. Either way, they're better off without verbs.
|
|
element?.RegisterSignal(src, COMSIG_QDELETING, TYPE_PROC_REF(/datum/element/immerse, clear_overlay_refs))
|
|
|
|
///Called by COMSIG_MOVABLE_EDIT_UNIQUE_IMMERSE_OVERLAY for living mobs and a few procs from the immerse element.
|
|
/atom/movable/immerse_overlay/proc/adjust_living_overlay_offset(mob/living/source)
|
|
pixel_x = extra_width
|
|
pixel_y = extra_height
|
|
overlay_appearance.pixel_y = -source.body_position_pixel_y_offset
|
|
overlays = list(overlay_appearance)
|
|
overlay_appearance.pixel_y = 0
|