mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-12 02:32:10 +00:00
Productionizes #80615. The core optimization is this: ```patch - var/hint = to_delete.Destroy(arglist(args.Copy(2))) // Let our friend know they're about to get fucked up. + var/hint = to_delete.Destroy(force) // Let our friend know they're about to get fucked up. ``` We avoid a heap allocation in the form of copying the args over to a new list. A/B testing shows this results in 33% better overtime, and in a real round shaving off a full second of self time and 0.4 seconds of overtime--both of these would be doubled in the event this is merged as the new proc was only being run 50% of the time.
133 lines
5.8 KiB
Plaintext
133 lines
5.8 KiB
Plaintext
/**
|
|
* A simple-ish component that reflects the icons of movables on the parent like a mirror.
|
|
* Sadly, there's no easy way to make the SOUTH dir reflection flip the visual so that you can see
|
|
* the back NORTH dir of a target while it's facing SOUTH beside adding the VIS_INHERIT_DIR flag
|
|
* to the target movable, which I'm not doing to spare eventual issues with other vis overlays in the future.
|
|
*/
|
|
/datum/component/reflection
|
|
/**
|
|
* The direction from which the component gets its visual overlays.
|
|
* The visuals are also flipped horizontally or vertically based on it.
|
|
*/
|
|
var/reflected_dir
|
|
/// the movable which the reflected movables are attached to, in turn added to the vis contents of the parent.
|
|
var/obj/effect/abstract/reflection_holder
|
|
/// A lazy assoc list that keeps track of which movables are being reflected and the associated reflections.
|
|
var/list/reflected_movables
|
|
/// A callback used check to know which movables should be reflected and which not.
|
|
var/datum/callback/can_reflect
|
|
///the base matrix used by reflections
|
|
var/matrix/reflection_matrix
|
|
///the filter data added to reflection holder.
|
|
var/list/reflection_filter
|
|
///the transparency channel value of the reflection holder.
|
|
var/alpha
|
|
///A list of signals that when sent to the parent, will force the comp to recalculate the reflected movables.
|
|
var/list/update_signals
|
|
|
|
/datum/component/reflection/Initialize(reflected_dir = NORTH, list/reflection_filter, matrix/reflection_matrix, datum/callback/can_reflect, alpha = 150, list/update_signals)
|
|
if(!ismovable(parent))
|
|
return COMPONENT_INCOMPATIBLE
|
|
|
|
var/static/list/connections = list(
|
|
COMSIG_ATOM_ENTERED = PROC_REF(on_movable_entered_or_initialized),
|
|
COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON = PROC_REF(on_movable_entered_or_initialized),
|
|
COMSIG_ATOM_EXITED = PROC_REF(on_movable_exited)
|
|
)
|
|
AddComponent(/datum/component/connect_range, parent, connections, 1, works_in_containers = FALSE)
|
|
src.reflected_dir = reflected_dir
|
|
src.reflection_matrix = reflection_matrix
|
|
src.reflection_filter = reflection_filter
|
|
src.can_reflect = can_reflect
|
|
reflection_holder = new(parent)
|
|
reflection_holder.alpha = alpha
|
|
reflection_holder.appearance_flags = KEEP_TOGETHER
|
|
reflection_holder.vis_flags = VIS_INHERIT_ID
|
|
reflection_holder.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
if(reflection_filter)
|
|
reflection_holder.add_filter("reflection", 1, reflection_filter)
|
|
|
|
var/atom/movable/mov_parent = parent
|
|
mov_parent.vis_contents += reflection_holder
|
|
set_reflection(new_dir = mov_parent.dir)
|
|
|
|
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_dir_change))
|
|
var/list/reflect_update_signals = list(COMSIG_MOVABLE_MOVED) + update_signals
|
|
RegisterSignals(parent, reflect_update_signals, PROC_REF(get_reflection_targets))
|
|
|
|
/datum/component/reflection/Destroy(force)
|
|
QDEL_LIST_ASSOC_VAL(reflected_movables)
|
|
QDEL_NULL(reflection_holder)
|
|
can_reflect = null
|
|
return ..()
|
|
|
|
///Called when the parent changes its direction.
|
|
/datum/component/reflection/proc/on_dir_change(atom/movable/source, old_dir, new_dir)
|
|
SIGNAL_HANDLER
|
|
set_reflection(old_dir, new_dir)
|
|
|
|
///Turns the allowed reflected direction alongside the parent's dir. then calls get_reflection_targets.
|
|
/datum/component/reflection/proc/set_reflection(old_dir = SOUTH, new_dir = SOUTH)
|
|
if(old_dir == new_dir)
|
|
return
|
|
|
|
reflected_dir = turn(reflected_dir, dir2angle(new_dir) - dir2angle(old_dir))
|
|
get_reflection_targets()
|
|
|
|
///Unsets the old reflected movables and sets it with new ones.
|
|
/datum/component/reflection/proc/get_reflection_targets(atom/movable/source)
|
|
SIGNAL_HANDLER
|
|
QDEL_LIST_ASSOC_VAL(reflected_movables)
|
|
for(var/atom/movable/target in view(1, source))
|
|
if(check_can_reflect(target, FALSE))
|
|
set_reflected(target)
|
|
|
|
///Checks if the target movable can be reflected or not.
|
|
/datum/component/reflection/proc/check_can_reflect(atom/movable/target, check_view = TRUE)
|
|
if(target == parent || (check_view && !(target in view(1, parent))))
|
|
return FALSE
|
|
var/atom/movable/mov_parent = parent
|
|
if(target.loc != mov_parent.loc && get_dir(mov_parent, target) != reflected_dir)
|
|
return FALSE
|
|
if(can_reflect && !can_reflect.Invoke(target))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
///Called when a movable enters a turf within the connected range
|
|
/datum/component/reflection/proc/on_movable_entered_or_initialized(atom/movable/source, atom/movable/arrived)
|
|
SIGNAL_HANDLER
|
|
if(LAZYACCESS(reflected_movables, arrived) || !check_can_reflect(arrived))
|
|
return
|
|
set_reflected(arrived)
|
|
|
|
///Called when a movable exits a turf within the connected range
|
|
/datum/component/reflection/proc/on_movable_exited(atom/movable/source, atom/movable/gone)
|
|
SIGNAL_HANDLER
|
|
var/atom/movable/reflection = LAZYACCESS(reflected_movables, gone)
|
|
if(!reflection || check_can_reflect(gone))
|
|
return
|
|
qdel(reflection)
|
|
LAZYREMOVE(reflected_movables, gone)
|
|
|
|
///Sets up a visual overlay of the target movables, which is added to the parent's vis contents.
|
|
/datum/component/reflection/proc/set_reflected(atom/movable/target)
|
|
SIGNAL_HANDLER
|
|
/**
|
|
* If the loc is null, only a black (or grey depending on alpha) silhouette of the target will be rendered
|
|
* Just putting this information here in case you want something like that in the future.
|
|
*/
|
|
var/obj/effect/abstract/reflection = new(parent)
|
|
reflection.vis_flags = VIS_INHERIT_ID
|
|
if(!target.render_target)
|
|
target.render_target = REF(target)
|
|
reflection.render_source = target.render_target
|
|
///The filter is added to the reflection holder; the matrix is not, otherwise that'd go affecting the filter.
|
|
if(reflection_matrix)
|
|
reflection.transform = reflection_matrix
|
|
if(reflected_dir == NORTH)
|
|
reflection.transform = reflection.transform.Scale(1, -1)
|
|
else if(reflected_dir != SOUTH)
|
|
reflection.transform = reflection.transform.Scale(-1, 1)
|
|
LAZYSET(reflected_movables, target, reflection)
|
|
reflection_holder.vis_contents += reflection
|