Files
Bubberstation/code/datums/components/aquarium.dm
SkyratBot 5aeb64a542 [MIRROR] Fixes some cases which references are used in trait sources, potentially causing hard deletes [MDB IGNORE] (#14716)
* Fixes some cases which references are used in trait sources, potentially causing hard deletes (#67974)

About The Pull Request

Fixes some cases in which actual references were used in trait sources instead of keys (or ref() keys).

This can cause some rare and difficult to find hard deletes.

Trait sources should be a string key relating to the source of it, not an actual reference to what added it. References within trait sources are never handled in Destroy(), because it's not expected behavior, meaning it can cause hanging references.

So, I went through with a regex to find some cases and replaced them.
I used the following and just picked through the few by hand to find erroneous ones.
ADD_TRAIT\(.+, .+, [a-z]+\)
REMOVE_TRAIT_TRAIT\(.+, .+, [a-z]+\)
Why It's Good For The Game

Less hard deletes, probably.
Changelog

cl Melbert
code: Some traits which mistakenly were sourced from a hard reference are no longer.
/cl

* Fixes some cases which references are used in trait sources, potentially causing hard deletes

* wew

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
Co-authored-by: Gandalf <9026500+Gandalf2k15@users.noreply.github.com>
2022-07-04 01:10:42 +01:00

304 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)
RegisterSignal(parent, animation_update_signals, .proc/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/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)
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, silent)
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/on_removed)
RegisterSignal(current_aquarium, COMSIG_AQUARIUM_SURFACE_CHANGED, .proc/on_surface_changed)
RegisterSignal(current_aquarium, COMSIG_AQUARIUM_FLUID_CHANGED,.proc/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_PARENT_ATTACKBY, COMSIG_ATOM_EXITED))
remove_visual_from_aquarium()
current_aquarium = null