mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-21 20:20:33 +01:00
Co-authored-by: tralezab <spamqetuo2@gmail.com> Co-authored-by: Mothblocks <35135081+Jared-Fogle@users.noreply.github.com> Co-authored-by: Aleksej Komarov <stylemistake@gmail.com> Co-authored-by: Qustinnus <Floydje123@hotmail.com> Co-authored-by: coiax <yellowbounder@gmail.com> Co-authored-by: AnturK <AnturK@users.noreply.github.com> Co-authored-by: tralezab <spamqetuo2@gmail.com> Co-authored-by: Mothblocks <35135081+Jared-Fogle@users.noreply.github.com> Co-authored-by: Aleksej Komarov <stylemistake@gmail.com> Co-authored-by: Qustinnus <Floydje123@hotmail.com> Co-authored-by: coiax <yellowbounder@gmail.com>
This commit is contained in:
co-authored by
tralezab
Mothblocks
Aleksej Komarov
Qustinnus
coiax
AnturK
parent
8c3d2811c2
commit
3000d75ad5
@@ -0,0 +1,212 @@
|
||||
/// Allows movables to be inserted/displayed in aquariums.
|
||||
/datum/component/aquarium_content
|
||||
/// This is a datum that describes our in-aquarium functionality
|
||||
var/datum/aquarium_behaviour/properties
|
||||
|
||||
/// 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
|
||||
|
||||
/datum/component/aquarium_content/Initialize(property_type)
|
||||
if(!ismovable(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if(ispath(property_type, /datum/aquarium_behaviour))
|
||||
properties = new property_type
|
||||
else
|
||||
CRASH("Invalid property type provided for aquarium content component")
|
||||
properties.parent = src
|
||||
|
||||
ADD_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, src)
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/enter_aquarium)
|
||||
|
||||
/datum/component/aquarium_content/PreTransfer()
|
||||
. = ..()
|
||||
REMOVE_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, src)
|
||||
|
||||
/datum/component/aquarium_content/Destroy(force, silent)
|
||||
if(current_aquarium)
|
||||
remove_from_aquarium()
|
||||
QDEL_NULL(vc_obj)
|
||||
QDEL_NULL(properties)
|
||||
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)
|
||||
if(HAS_TRAIT(movable_parent.loc, TRAIT_FISH_SAFE_STORAGE))
|
||||
on_tank_stasis()
|
||||
|
||||
/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(properties.unique)
|
||||
for(var/atom/movable/fish_or_prop in aquarium)
|
||||
if(fish_or_prop == parent)
|
||||
continue
|
||||
var/datum/component/aquarium_content/other_content = fish_or_prop.GetComponent(/datum/component/aquarium_content)
|
||||
if(other_content && other_content.properties.type == properties.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)
|
||||
RegisterSignal(current_aquarium, COMSIG_PARENT_ATTACKBY, .proc/attack_reaction)
|
||||
properties.on_inserted()
|
||||
|
||||
//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()
|
||||
|
||||
//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() //our animation start point changed, gotta redo
|
||||
|
||||
/// Our aquarium is hit with stuff
|
||||
/datum/component/aquarium_content/proc/attack_reaction(datum/source, obj/item/thing, mob/user, params)
|
||||
SIGNAL_HANDLER
|
||||
if(istype(thing, /obj/item/fish_feed))
|
||||
properties.on_feeding(thing.reagents)
|
||||
return COMPONENT_NO_AFTERATTACK
|
||||
else
|
||||
//stirred effect
|
||||
generate_animation()
|
||||
|
||||
/datum/component/aquarium_content/proc/on_fluid_changed()
|
||||
SIGNAL_HANDLER
|
||||
properties.on_fluid_changed()
|
||||
|
||||
/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()
|
||||
if(!properties)
|
||||
CRASH("Generating visual without properties.")
|
||||
|
||||
var/obj/effect/visual = new
|
||||
properties.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
|
||||
|
||||
/// Actually animates the vc holder
|
||||
/datum/component/aquarium_content/proc/generate_animation()
|
||||
switch(properties.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(properties.sprite_width / 2)
|
||||
var/avg_height = round(properties.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 = properties.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(properties.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.
|
||||
|
||||
|
||||
/// Floating to top animation.
|
||||
/datum/component/aquarium_content/proc/float_animation()
|
||||
return
|
||||
|
||||
|
||||
/datum/component/aquarium_content/proc/set_vc_base_position()
|
||||
var/list/aq_properties = current_aquarium.get_surface_properties()
|
||||
if(properties.randomize_position)
|
||||
var/avg_width = round(properties.sprite_width / 2)
|
||||
var/avg_height = round(properties.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
|
||||
|
||||
if(base_layer)
|
||||
current_aquarium.free_layer(base_layer)
|
||||
base_layer = current_aquarium.request_layer(properties.layer_mode)
|
||||
vc_obj.layer = base_layer
|
||||
|
||||
/datum/component/aquarium_content/proc/on_removed(datum/source, atom/movable/mover)
|
||||
SIGNAL_HANDLER
|
||||
if(mover != parent)
|
||||
return
|
||||
remove_from_aquarium()
|
||||
|
||||
/datum/component/aquarium_content/proc/remove_from_aquarium()
|
||||
properties.before_removal()
|
||||
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
|
||||
//We do not stop processing properties here. We want fish to die outside of aquariums after first insert. We only stop processing in properties.death or destroy
|
||||
|
||||
/datum/component/aquarium_content/proc/on_tank_stasis()
|
||||
// Stop processing until inserted into aquarium again.
|
||||
STOP_PROCESSING(SSobj, properties)
|
||||
@@ -1076,3 +1076,13 @@
|
||||
/obj/item/grenade/gas_crystal/zauker_crystal = 1
|
||||
)
|
||||
category = CAT_MISC
|
||||
|
||||
/datum/crafting_recipe/aquarium
|
||||
name = "Aquarium"
|
||||
result = /obj/structure/aquarium
|
||||
time = 10 SECONDS
|
||||
reqs = list(/obj/item/stack/sheet/metal = 15,
|
||||
/obj/item/stack/sheet/glass = 10,
|
||||
/obj/item/aquarium_kit = 1
|
||||
)
|
||||
category = CAT_MISC
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/datum/component/storage/concrete/fish_case
|
||||
max_items = 1
|
||||
can_hold_trait = TRAIT_FISH_CASE_COMPATIBILE
|
||||
can_hold_description = "fish and aquarium equipment"
|
||||
|
||||
/datum/component/storage/concrete/fish_case/can_be_inserted(obj/item/I, stop_messages, mob/M)
|
||||
/// Activate deferred components if any.
|
||||
SEND_SIGNAL(I, COMSIG_AQUARIUM_BEFORE_INSERT_CHECK)
|
||||
. = ..()
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
var/list/can_hold //if this is set, only items, and their children, will fit
|
||||
var/list/cant_hold //if this is set, items, and their children, won't fit
|
||||
var/list/exception_hold //if set, these items will be the exception to the max size of object that can fit.
|
||||
var/list/exception_hold //if set, these items will be the exception to the max size of object that can fit.
|
||||
/// If set can only contain stuff with this single trait present.
|
||||
var/list/can_hold_trait
|
||||
|
||||
var/can_hold_description
|
||||
|
||||
@@ -630,7 +632,7 @@
|
||||
if(!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[host] cannot hold [I]!</span>")
|
||||
return FALSE
|
||||
if(is_type_in_typecache(I, cant_hold) || HAS_TRAIT(I, TRAIT_NO_STORAGE_INSERT)) //Items which this container can't hold.
|
||||
if(is_type_in_typecache(I, cant_hold) || HAS_TRAIT(I, TRAIT_NO_STORAGE_INSERT) || (can_hold_trait && !HAS_TRAIT(I, can_hold_trait))) //Items which this container can't hold.
|
||||
if(!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[host] cannot hold [I]!</span>")
|
||||
return FALSE
|
||||
|
||||
Reference in New Issue
Block a user