mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-04 05:51:54 +00:00
## About The Pull Request For a moment, I had forgot about saying I'd take a look into it, but it seems the new fix to hard dels is causing some runtimes with empty weakrefs. Beside, WEAKREF() doesn't work well with qdeleting atoms (so you'd have to access the weak_reference var directly). How immersion works is quite confusing even for me who coded it, trying to work around some of the hefty limitations of the engine truly blows. I could even ask MrMelbert to make a proc-chain chart for it. But yeah, long story short, I only have a bare idea where the uncleared refs would be. I suspect it could be `immersed_movables`. It's totally possible since the proc can early return in a few cases, thus skipping the ref removal, hence the title. ## Why It's Good For The Game I didn't like the PR that implemented weakref usage into the element, but I let it pass because "hard dels = bad". However, the runtimes aren't that much more pleasant either. ## Changelog N/A
412 lines
19 KiB
Plaintext
412 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(isnull(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)
|
|
LAZYREMOVE(immersed_movables, movable)
|
|
REMOVE_KEEP_TOGETHER(movable, ELEMENT_TRAIT(src))
|
|
movable.vis_contents -= vis_overlay
|
|
if(HAS_TRAIT(movable, TRAIT_UNIQUE_IMMERSE))
|
|
UnregisterSignal(movable, list(COMSIG_ATOM_SPIN_ANIMATION, COMSIG_LIVING_POST_UPDATE_TRANSFORM, COMSIG_QDELETING))
|
|
if(!QDELETED(vis_overlay))
|
|
qdel(vis_overlay)
|
|
/**
|
|
* 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)
|
|
|
|
LAZYREMOVE(attached_turfs_and_movables[source], movable)
|
|
UnregisterSignal(movable, list(COMSIG_LIVING_SET_BUCKLED, COMSIG_QDELETING))
|
|
REMOVE_TRAIT(movable, TRAIT_IMMERSED, ELEMENT_TRAIT(src))
|
|
|
|
/// 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
|