mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-29 19:11:51 +00:00
* Micro-optimize qdel by only permitting one parameter (#80628) 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. * Micro-optimize qdel by only permitting one parameter --------- Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
306 lines
11 KiB
Plaintext
306 lines
11 KiB
Plaintext
/// Allows movables to be inserted/displayed in aquariums.
|
|
/datum/component/aquarium_content
|
|
/// Keeps track of our current aquarium.
|
|
var/obj/structure/aquarium/current_aquarium
|
|
|
|
//This is visual effect holder that will end up in aquarium's vis_contents
|
|
var/obj/effect/vc_obj
|
|
|
|
/// Base px offset of the visual object in current aquarium aka current base position
|
|
var/base_px = 0
|
|
/// Base px offset of the visual object in current aquarium aka current base position
|
|
var/base_py = 0
|
|
//Current layer for the visual object
|
|
var/base_layer
|
|
|
|
|
|
/**
|
|
* Fish sprite how to:
|
|
* Need to be centered on 16,16 in the dmi and facing left by default.
|
|
* sprite_height/sprite_width is the size it will have in aquarium and used to control animation boundaries.
|
|
* source_height/source_width is the size of the original icon (ideally only the non-empty parts)
|
|
*/
|
|
|
|
|
|
/// Icon used for in aquarium sprite
|
|
var/icon = 'icons/obj/aquarium.dmi'
|
|
/// If this is set this icon state will be used for the holder while icon_state will only be used for item/catalog. Transformation from source_width/height WON'T be applied.
|
|
var/icon_state
|
|
/// Applied to vc object only for use with greyscaled icons.
|
|
var/aquarium_vc_color
|
|
/// Transformation applied to the visual holder - used when scaled down sprites are used as in aquarium visual
|
|
var/matrix/base_transform
|
|
|
|
/// How the thing will be layered
|
|
var/layer_mode = AQUARIUM_LAYER_MODE_AUTO
|
|
|
|
/// If the starting position is randomised within bounds when inserted into aquarium.
|
|
var/randomize_position = FALSE
|
|
|
|
//Target sprite size for path/position calculations.
|
|
var/sprite_height = 3
|
|
var/sprite_width = 3
|
|
|
|
//This is the size of the source sprite. This will be used to calculate scale down factor.
|
|
var/source_width = 32
|
|
var/source_height = 32
|
|
|
|
/// Currently playing animation
|
|
var/current_animation
|
|
|
|
/// Does this behviour need additional processing in aquarium, will be added to SSobj processing on insertion
|
|
var/processing = FALSE
|
|
|
|
/// TODO: Change this into trait checked on aquarium insertion
|
|
var/unique = FALSE
|
|
|
|
/// Proc used to retrieve current animation state from the parent, optional
|
|
var/animation_getter
|
|
|
|
/// Signals of the parent that will trigger animation update
|
|
var/animation_update_signals
|
|
|
|
|
|
/datum/component/aquarium_content/Initialize(animation_getter, animation_update_signals)
|
|
if(!ismovable(parent))
|
|
return COMPONENT_INCOMPATIBLE
|
|
|
|
src.animation_getter = animation_getter
|
|
src.animation_update_signals = animation_update_signals
|
|
if(animation_update_signals)
|
|
RegisterSignals(parent, animation_update_signals, PROC_REF(generate_animation))
|
|
|
|
if(istype(parent,/obj/item/fish))
|
|
InitializeFromFish()
|
|
else if(istype(parent,/obj/item/aquarium_prop))
|
|
InitializeFromProp()
|
|
else
|
|
InitializeOther()
|
|
|
|
ADD_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, REF(src))
|
|
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(enter_aquarium))
|
|
|
|
//If component is added to something already in aquarium at the time initialize it properly.
|
|
var/atom/movable/movable_parent = parent
|
|
if(istype(movable_parent.loc, /obj/structure/aquarium))
|
|
on_inserted(movable_parent.loc)
|
|
|
|
/// Sets visuals properties for fish
|
|
/datum/component/aquarium_content/proc/InitializeFromFish()
|
|
var/obj/item/fish/fish = parent
|
|
|
|
icon = fish.icon
|
|
sprite_height = fish.sprite_height
|
|
sprite_width = fish.sprite_width
|
|
aquarium_vc_color = fish.aquarium_vc_color
|
|
|
|
if(fish.dedicated_in_aquarium_icon_state)
|
|
if(fish.dedicated_in_aquarium_icon)
|
|
icon = fish.dedicated_in_aquarium_icon
|
|
icon_state = fish.dedicated_in_aquarium_icon_state
|
|
base_transform = matrix()
|
|
else
|
|
icon_state = fish.icon_state
|
|
var/matrix/matrix = matrix()
|
|
var/x_scale = fish.sprite_width / fish.source_width
|
|
var/y_scale = fish.sprite_height / fish.source_height
|
|
matrix.Scale(x_scale, y_scale)
|
|
base_transform = matrix
|
|
|
|
randomize_position = TRUE
|
|
|
|
/// Sets visuals properties for fish
|
|
/datum/component/aquarium_content/proc/InitializeFromProp()
|
|
var/obj/item/aquarium_prop/prop = parent
|
|
|
|
icon = prop.icon
|
|
icon_state = prop.icon_state
|
|
layer_mode = prop.layer_mode
|
|
sprite_height = 32
|
|
sprite_width = 32
|
|
base_transform = matrix()
|
|
|
|
unique = TRUE
|
|
|
|
/// Mostly for admin abuse
|
|
/datum/component/aquarium_content/proc/InitializeOther()
|
|
sprite_width = 8
|
|
sprite_height = 8
|
|
|
|
var/matrix/matrix = matrix()
|
|
var/x_scale = sprite_width / 32
|
|
var/y_scale = sprite_height / 32
|
|
matrix.Scale(x_scale, y_scale)
|
|
base_transform = matrix
|
|
|
|
|
|
/datum/component/aquarium_content/PreTransfer()
|
|
. = ..()
|
|
REMOVE_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, REF(src))
|
|
|
|
/datum/component/aquarium_content/Destroy(force)
|
|
if(current_aquarium)
|
|
remove_from_aquarium()
|
|
QDEL_NULL(vc_obj)
|
|
return ..()
|
|
|
|
/datum/component/aquarium_content/proc/enter_aquarium(datum/source, OldLoc, Dir, Forced)
|
|
SIGNAL_HANDLER
|
|
var/atom/movable/movable_parent = parent
|
|
if(istype(movable_parent.loc, /obj/structure/aquarium))
|
|
on_inserted(movable_parent.loc)
|
|
|
|
/datum/component/aquarium_content/proc/is_ready_to_insert(obj/structure/aquarium/aquarium)
|
|
//This is kinda awful but we're unaware of other fish
|
|
if(unique)
|
|
for(var/atom/movable/fish_or_prop in aquarium)
|
|
if(fish_or_prop == parent)
|
|
continue
|
|
if(fish_or_prop.type == parent.type)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/datum/component/aquarium_content/proc/on_inserted(atom/aquarium)
|
|
current_aquarium = aquarium
|
|
RegisterSignal(current_aquarium, COMSIG_ATOM_EXITED, PROC_REF(on_removed))
|
|
RegisterSignal(current_aquarium, COMSIG_AQUARIUM_SURFACE_CHANGED, PROC_REF(on_surface_changed))
|
|
RegisterSignal(current_aquarium, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_fluid_changed))
|
|
|
|
if(processing)
|
|
START_PROCESSING(SSobj, src)
|
|
|
|
//If we don't have vc object yet build it
|
|
if(!vc_obj)
|
|
vc_obj = generate_base_vc()
|
|
|
|
//Set default position and layer
|
|
set_vc_base_position()
|
|
generate_animation(reset = TRUE)
|
|
|
|
//Finally add it to to objects vis_contents
|
|
current_aquarium.vis_contents |= vc_obj
|
|
|
|
/// Aquarium surface changed in some way, we need to recalculate base position and aninmation
|
|
/datum/component/aquarium_content/proc/on_surface_changed()
|
|
SIGNAL_HANDLER
|
|
set_vc_base_position()
|
|
generate_animation(reset = TRUE) //our animation start point changed, gotta redo
|
|
|
|
/datum/component/aquarium_content/proc/on_fluid_changed()
|
|
SIGNAL_HANDLER
|
|
generate_animation()
|
|
|
|
/datum/component/aquarium_content/proc/remove_visual_from_aquarium()
|
|
current_aquarium.vis_contents -= vc_obj
|
|
if(base_layer)
|
|
current_aquarium.free_layer(base_layer)
|
|
|
|
/// Generates common visual object, propeties that don't depend on aquarium surface
|
|
/datum/component/aquarium_content/proc/generate_base_vc()
|
|
var/obj/effect/visual = new
|
|
apply_appearance(visual)
|
|
visual.vis_flags |= VIS_INHERIT_ID | VIS_INHERIT_PLANE //plane so it shows properly in containers on inventory ui for handheld cases
|
|
return visual
|
|
|
|
/// Applies icon,color and base scaling to our visual holder
|
|
/datum/component/aquarium_content/proc/apply_appearance(obj/effect/holder)
|
|
holder.icon = icon
|
|
holder.icon_state = icon_state
|
|
holder.transform = matrix(base_transform)
|
|
if(aquarium_vc_color)
|
|
holder.color = aquarium_vc_color
|
|
|
|
|
|
/// Actually animates the vc holder
|
|
/datum/component/aquarium_content/proc/generate_animation(reset=FALSE)
|
|
if(!current_aquarium)
|
|
return
|
|
var/next_animation = animation_getter ? call(parent,animation_getter)() : null
|
|
if(current_animation == next_animation && !reset)
|
|
return
|
|
current_animation = next_animation
|
|
switch(current_animation)
|
|
if(AQUARIUM_ANIMATION_FISH_SWIM)
|
|
swim_animation()
|
|
return
|
|
if(AQUARIUM_ANIMATION_FISH_DEAD)
|
|
dead_animation()
|
|
return
|
|
|
|
|
|
/// Create looping random path animation, pixel offsets parameters include offsets already
|
|
/datum/component/aquarium_content/proc/swim_animation()
|
|
var/avg_width = round(sprite_width / 2)
|
|
var/avg_height = round(sprite_height / 2)
|
|
|
|
var/list/aq_properties = current_aquarium.get_surface_properties()
|
|
var/px_min = aq_properties[AQUARIUM_PROPERTIES_PX_MIN] + avg_width - 16
|
|
var/px_max = aq_properties[AQUARIUM_PROPERTIES_PX_MAX] - avg_width - 16
|
|
var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16
|
|
var/py_max = aq_properties[AQUARIUM_PROPERTIES_PY_MAX] - avg_width - 16
|
|
|
|
var/origin_x = base_px
|
|
var/origin_y = base_py
|
|
var/prev_x = origin_x
|
|
var/prev_y = origin_y
|
|
animate(vc_obj, pixel_x = origin_x, time = 0, loop = -1) //Just to start the animation
|
|
var/move_number = rand(3, 5) //maybe unhardcode this
|
|
for(var/i in 1 to move_number)
|
|
//If it's last movement, move back to start otherwise move to some random point
|
|
var/target_x = i == move_number ? origin_x : rand(px_min,px_max) //could do with enforcing minimal delta for prettier zigzags
|
|
var/target_y = i == move_number ? origin_y : rand(py_min,py_max)
|
|
var/dx = prev_x - target_x
|
|
var/dy = prev_y - target_y
|
|
prev_x = target_x
|
|
prev_y = target_y
|
|
var/dist = abs(dx) + abs(dy)
|
|
var/eyeballed_time = dist * 2 //2ds per px
|
|
//Face the direction we're going
|
|
var/matrix/dir_mx = matrix(base_transform)
|
|
if(dx <= 0) //assuming default sprite is facing left here
|
|
dir_mx.Scale(-1, 1)
|
|
animate(transform = dir_mx, time = 0, loop = -1)
|
|
animate(pixel_x = target_x, pixel_y = target_y, time = eyeballed_time, loop = -1)
|
|
|
|
/datum/component/aquarium_content/proc/dead_animation()
|
|
//Set base_py to lowest possible value
|
|
var/avg_height = round(sprite_height / 2)
|
|
var/list/aq_properties = current_aquarium.get_surface_properties()
|
|
var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16
|
|
base_py = py_min
|
|
animate(vc_obj, pixel_y = py_min, time = 1) //flop to bottom and end current animation.
|
|
|
|
/datum/component/aquarium_content/proc/set_vc_base_position()
|
|
if(randomize_position)
|
|
randomize_base_position()
|
|
if(base_layer)
|
|
current_aquarium.free_layer(base_layer)
|
|
base_layer = current_aquarium.request_layer(layer_mode)
|
|
vc_obj.layer = base_layer
|
|
|
|
/datum/component/aquarium_content/proc/randomize_base_position()
|
|
var/list/aq_properties = current_aquarium.get_surface_properties()
|
|
var/avg_width = round(sprite_width / 2)
|
|
var/avg_height = round(sprite_height / 2)
|
|
var/px_min = aq_properties[AQUARIUM_PROPERTIES_PX_MIN] + avg_width - 16
|
|
var/px_max = aq_properties[AQUARIUM_PROPERTIES_PX_MAX] - avg_width - 16
|
|
var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16
|
|
var/py_max = aq_properties[AQUARIUM_PROPERTIES_PY_MAX] - avg_width - 16
|
|
|
|
base_px = rand(px_min,px_max)
|
|
base_py = rand(py_min,py_max)
|
|
|
|
vc_obj.pixel_x = base_px
|
|
vc_obj.pixel_y = base_py
|
|
|
|
/datum/component/aquarium_content/proc/on_removed(datum/source, atom/movable/gone, direction)
|
|
SIGNAL_HANDLER
|
|
if(parent != gone)
|
|
return
|
|
remove_from_aquarium()
|
|
|
|
/datum/component/aquarium_content/proc/remove_from_aquarium()
|
|
UnregisterSignal(current_aquarium, list(COMSIG_AQUARIUM_SURFACE_CHANGED, COMSIG_AQUARIUM_FLUID_CHANGED, COMSIG_ATOM_ATTACKBY, COMSIG_ATOM_EXITED))
|
|
remove_visual_from_aquarium()
|
|
current_aquarium = null
|