mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 12:41:09 +01:00
Refactoring aquariums into components (feat: portable fish tanks) (#87866)
## About The Pull Request I've been meaning to do this for some time. I need this for portable/handheld aquariums/fishtanks to be possible. I'll sprite and code them before I call this PR ready, however suggestions and code reviews are welcome in the meantime. Being a pretty heavy refactor, some things might break (we have more than a few unit tests so perhaps not) while others, coincidentally, might be fixed without me knowing. Anyway I'm sure this PR fixes aquarium beauty, which wasn't really working to begin with because the code was so fucking bad. Nothing really worth of a CL entry tho. TODO: - [x] handheld aquariums, craftable with a kit and little plastic or buyable from the fun vendor ig. - [x] an aquarium upgrade for handheld aquariums to bypass possible restrictions. - [x] update the beauty element to consider items, which shouldn't contribute to the area beauty when held or otherwise not on a turf. ## Why It's Good For The Game This should make handheld aquariums possible. ## Changelog 🆑 refactor: refactored aquariums heavily. Please report any fishy bug. add: Added portable/handheld fish tanks to the game. They can be crafted with an aquarium kit and 5 sheets of plastic. While portable, they cannot store fish that are too big or if there're too many already. This restriction can be removed by using the new "bluespace fish tank kit" techweb item. map: Replaced the lawyer's stationary pet aquarium with a fish tank, so you can carry McGill around. balance: Reduced the iron cost of stationary aquariums a little. /🆑
This commit is contained in:
@@ -17,15 +17,6 @@
|
||||
*/
|
||||
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
|
||||
|
||||
/**
|
||||
* The type to check for duplication
|
||||
*
|
||||
* `null` means exact match on `type` (default)
|
||||
*
|
||||
* Any other type means that and all subtypes
|
||||
*/
|
||||
var/dupe_type
|
||||
|
||||
/// The datum this components belongs to
|
||||
var/datum/parent
|
||||
|
||||
@@ -57,7 +48,7 @@
|
||||
qdel(src, TRUE, TRUE)
|
||||
return
|
||||
|
||||
_JoinParent(parent)
|
||||
_JoinParent()
|
||||
|
||||
/**
|
||||
* Called during component creation with the same arguments as in new excluding parent.
|
||||
@@ -217,7 +208,7 @@
|
||||
*
|
||||
* Use this to do any special cleanup you might need to do before being deregged from an object
|
||||
*/
|
||||
/datum/component/proc/PreTransfer()
|
||||
/datum/component/proc/PreTransfer(datum/new_parent)
|
||||
return
|
||||
|
||||
/**
|
||||
@@ -328,7 +319,6 @@
|
||||
CRASH("[component_type] attempted instantiation!")
|
||||
|
||||
var/dupe_mode = initial(component_type.dupe_mode)
|
||||
var/dupe_type = initial(component_type.dupe_type)
|
||||
var/uses_sources = (dupe_mode == COMPONENT_DUPE_SOURCES)
|
||||
if(uses_sources && !source)
|
||||
CRASH("Attempted to add a sourced component of type '[component_type]' to '[type]' without a source!")
|
||||
@@ -339,10 +329,7 @@
|
||||
|
||||
raw_args[1] = src
|
||||
if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE && dupe_mode != COMPONENT_DUPE_SOURCES)
|
||||
if(!dupe_type)
|
||||
old_component = GetExactComponent(component_type)
|
||||
else
|
||||
old_component = GetComponent(dupe_type)
|
||||
old_component = GetComponent(component_type)
|
||||
|
||||
if(old_component)
|
||||
switch(dupe_mode)
|
||||
@@ -434,11 +421,11 @@
|
||||
* Removes the component from parent, ends up with a null parent
|
||||
* Used as a helper proc by the component transfer proc, does not clean up the component like Destroy does
|
||||
*/
|
||||
/datum/component/proc/ClearFromParent()
|
||||
/datum/component/proc/ClearFromParent(datum/new_parent)
|
||||
if(!parent)
|
||||
return
|
||||
var/datum/old_parent = parent
|
||||
PreTransfer()
|
||||
PreTransfer(new_parent)
|
||||
_RemoveFromParent()
|
||||
parent = null
|
||||
SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
|
||||
@@ -455,16 +442,17 @@
|
||||
if(!target || target.parent == src)
|
||||
return
|
||||
if(target.parent)
|
||||
target.ClearFromParent()
|
||||
target.parent = src
|
||||
var/result = target.PostTransfer()
|
||||
target.ClearFromParent(src)
|
||||
var/result = target.PostTransfer(src)
|
||||
switch(result)
|
||||
if(COMPONENT_INCOMPATIBLE)
|
||||
var/c_type = target.type
|
||||
qdel(target)
|
||||
CRASH("Incompatible [c_type] transfer attempt to a [type]!")
|
||||
|
||||
if(target == AddComponent(target))
|
||||
AddComponent(target)
|
||||
if(!QDELETED(target))
|
||||
target.parent = src
|
||||
target._JoinParent()
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,564 @@
|
||||
///Defines that clamp the beauty of the aquarium, to prevent it from making most areas great or horrid all by itself.
|
||||
#define MIN_AQUARIUM_BEAUTY -3500
|
||||
#define MAX_AQUARIUM_BEAUTY 6000
|
||||
|
||||
/**
|
||||
* The component that manages the aquariums UI, fluid, temperature, the current fish inside the parent object, as well as beauty,
|
||||
* and a few other common aquarium features.
|
||||
*/
|
||||
/datum/component/aquarium
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
can_transfer = TRUE
|
||||
/// list of fishes inside the parent object, sorted by type - does not include things with aquarium visuals that are not fish
|
||||
var/list/tracked_fish_by_type
|
||||
|
||||
///The current type of fluid of the aquarium
|
||||
var/fluid_type = AQUARIUM_FLUID_FRESHWATER
|
||||
///The current temperature of the fluid of the aquarium
|
||||
var/fluid_temp = DEFAULT_AQUARIUM_TEMP
|
||||
|
||||
///A lazy list of key instances and assoc vals representing how much beauty they contribute to the aquarium
|
||||
var/list/beauty_by_content
|
||||
|
||||
///The default beauty of the aquarium when empty.
|
||||
var/default_beauty
|
||||
|
||||
///A list of layers that are currently being used for the various overlays of the aquarium (from aquarium_content comp)
|
||||
var/list/used_layers = list()
|
||||
|
||||
///The minimum pixel x of the area where vis overlays should be displayed
|
||||
var/aquarium_zone_min_px
|
||||
///The maximum pixel x of the area where vis overlays should be displayed
|
||||
var/aquarium_zone_max_px
|
||||
///The minimum pixel y of the area where vis overlays should be displayed
|
||||
var/aquarium_zone_min_py
|
||||
///The maximum pixel y of the area where vis overlays should be displayed
|
||||
var/aquarium_zone_max_py
|
||||
|
||||
///While the feed (reagent) storage is not empty, this is the interval which the fish are fed.
|
||||
var/feeding_interval = 3 MINUTES
|
||||
///The last time fishes were fed by the acquarium itsef.
|
||||
var/last_feeding
|
||||
|
||||
///The minimum fluid temperature that can be reached by this aquarium
|
||||
var/min_fluid_temp = MIN_AQUARIUM_TEMP
|
||||
///The maximum fluid temperature that can be reached by this aquarium
|
||||
var/max_fluid_temp = MAX_AQUARIUM_TEMP
|
||||
|
||||
///static list of available fluid types.
|
||||
var/static/list/fluid_types = list(
|
||||
AQUARIUM_FLUID_SALTWATER,
|
||||
AQUARIUM_FLUID_FRESHWATER,
|
||||
AQUARIUM_FLUID_SULPHWATEVER,
|
||||
AQUARIUM_FLUID_AIR,
|
||||
)
|
||||
|
||||
///The size of the reagents holder which will store fish feed.
|
||||
var/reagents_size
|
||||
|
||||
/datum/component/aquarium/Initialize(
|
||||
min_px,
|
||||
max_px,
|
||||
min_py,
|
||||
max_py,
|
||||
default_beauty = 0,
|
||||
reagents_size = 6,
|
||||
min_fluid_temp = MIN_AQUARIUM_TEMP,
|
||||
max_fluid_temp = MAX_AQUARIUM_TEMP,
|
||||
)
|
||||
|
||||
if(!ismovable(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.default_beauty = default_beauty
|
||||
src.reagents_size = reagents_size
|
||||
|
||||
aquarium_zone_min_px = min_px
|
||||
aquarium_zone_max_px = max_px
|
||||
aquarium_zone_min_py = min_py
|
||||
aquarium_zone_max_py = max_py
|
||||
|
||||
src.min_fluid_temp = min_fluid_temp
|
||||
src.max_fluid_temp = max_fluid_temp
|
||||
fluid_temp = clamp(fluid_temp, min_fluid_temp, max_fluid_temp)
|
||||
|
||||
/datum/component/aquarium/RegisterWithParent()
|
||||
if(default_beauty)
|
||||
update_aquarium_beauty(0)
|
||||
|
||||
RegisterSignals(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON), PROC_REF(on_entered))
|
||||
RegisterSignal(parent, COMSIG_ATOM_EXITED, PROC_REF(on_exited))
|
||||
|
||||
RegisterSignal(parent, COMSIG_AQUARIUM_GET_REPRODUCTION_CANDIDATES, PROC_REF(get_candidates))
|
||||
RegisterSignal(parent, COMSIG_AQUARIUM_CHECK_EVOLUTION_CONDITIONS, PROC_REF(check_evolution))
|
||||
|
||||
RegisterSignal(parent, COMSIG_AQUARIUM_SET_VISUAL, PROC_REF(set_visual))
|
||||
RegisterSignal(parent, COMSIG_AQUARIUM_REMOVE_VISUAL, PROC_REF(remove_visual))
|
||||
|
||||
var/atom/movable/movable = parent
|
||||
|
||||
ADD_KEEP_TOGETHER(movable, AQUARIUM_TRAIT) //render the fish on the same layer of the aquarium.
|
||||
|
||||
if(reagents_size > 0)
|
||||
RegisterSignal(movable.reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed))
|
||||
if(!movable.reagents)
|
||||
movable.create_reagents(reagents_size, SEALED_CONTAINER)
|
||||
else if(movable.reagents.total_volume)
|
||||
start_autofeed(movable.reagents)
|
||||
RegisterSignal(movable, COMSIG_PLUNGER_ACT, PROC_REF(on_plunger_act))
|
||||
|
||||
RegisterSignal(movable, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_interaction))
|
||||
RegisterSignal(movable, COMSIG_CLICK_ALT, PROC_REF(on_click_alt))
|
||||
RegisterSignal(movable, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
|
||||
|
||||
if(isitem(movable))
|
||||
RegisterSignal(movable, COMSIG_ITEM_ATTACK_SELF, PROC_REF(interact))
|
||||
RegisterSignals(movable, list(COMSIG_ATOM_ATTACK_ROBOT_SECONDARY, COMSIG_ATOM_ATTACK_HAND_SECONDARY), PROC_REF(on_secondary_attack_hand))
|
||||
else
|
||||
RegisterSignal(movable, COMSIG_ATOM_UI_INTERACT, PROC_REF(interact))
|
||||
|
||||
movable.AddElement(/datum/element/relay_attackers)
|
||||
|
||||
for(var/atom/movable/content as anything in movable.contents)
|
||||
if(content.flags_1 & INITIALIZED_1)
|
||||
on_entered(movable, content)
|
||||
|
||||
ADD_TRAIT(movable, TRAIT_IS_AQUARIUM, AQUARIUM_TRAIT)
|
||||
|
||||
/datum/component/aquarium/UnregisterFromParent()
|
||||
var/atom/movable/movable = parent
|
||||
UnregisterSignal(movable, list(
|
||||
COMSIG_ATOM_ENTERED,
|
||||
COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON,
|
||||
COMSIG_ATOM_EXITED,
|
||||
COMSIG_AQUARIUM_GET_REPRODUCTION_CANDIDATES,
|
||||
COMSIG_AQUARIUM_CHECK_EVOLUTION_CONDITIONS,
|
||||
COMSIG_AQUARIUM_SET_VISUAL,
|
||||
COMSIG_AQUARIUM_REMOVE_VISUAL,
|
||||
COMSIG_PLUNGER_ACT,
|
||||
COMSIG_ATOM_ITEM_INTERACTION,
|
||||
COMSIG_CLICK_ALT,
|
||||
COMSIG_ATOM_EXAMINE,
|
||||
COMSIG_ITEM_ATTACK_SELF,
|
||||
COMSIG_ATOM_ATTACK_ROBOT_SECONDARY,
|
||||
COMSIG_ATOM_ATTACK_HAND_SECONDARY,
|
||||
COMSIG_ATOM_UI_INTERACT,
|
||||
))
|
||||
if(movable.reagents)
|
||||
UnregisterSignal(movable, COMSIG_REAGENTS_NEW_REAGENT)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
beauty_by_content = null
|
||||
tracked_fish_by_type = null
|
||||
movable.remove_traits(list(TRAIT_IS_AQUARIUM, TRAIT_AQUARIUM_PANEL_OPEN, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH), AQUARIUM_TRAIT)
|
||||
REMOVE_KEEP_TOGETHER(movable, AQUARIUM_TRAIT)
|
||||
|
||||
/datum/component/aquarium/PreTransfer(atom/movable/new_parent)
|
||||
if(!istype(new_parent))
|
||||
return
|
||||
if(HAS_TRAIT(parent, TRAIT_AQUARIUM_PANEL_OPEN))
|
||||
ADD_TRAIT(new_parent, TRAIT_AQUARIUM_PANEL_OPEN, AQUARIUM_TRAIT)
|
||||
if(HAS_TRAIT_FROM(parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT))
|
||||
ADD_TRAIT(new_parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT)
|
||||
var/atom/movable/movable = parent
|
||||
for(var/atom/movable/moving as anything in movable.contents)
|
||||
if(HAS_TRAIT(moving, TRAIT_AQUARIUM_CONTENT))
|
||||
moving.forceMove(new_parent)
|
||||
if(reagents_size)
|
||||
if(!new_parent.reagents)
|
||||
new_parent.create_reagents(reagents_size, SEALED_CONTAINER)
|
||||
movable.reagents.trans_to(new_parent, movable.reagents.total_volume)
|
||||
|
||||
/datum/component/aquarium/PostTransfer(datum/new_parent)
|
||||
if(!ismovable(new_parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
/datum/component/aquarium/InheritComponent(datum/component/aquarium/new_comp, i_am_original)
|
||||
fluid_temp = clamp(new_comp.fluid_temp, min_fluid_temp, max_fluid_temp)
|
||||
set_fluid_type(new_comp.fluid_type)
|
||||
feeding_interval = new_comp.feeding_interval
|
||||
last_feeding = new_comp.last_feeding
|
||||
var/atom/movable/movable = parent
|
||||
movable.update_appearance()
|
||||
|
||||
/datum/component/aquarium/proc/on_click_alt(atom/movable/source, mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
var/closing = HAS_TRAIT(parent, TRAIT_AQUARIUM_PANEL_OPEN)
|
||||
if(closing)
|
||||
REMOVE_TRAIT(parent, TRAIT_AQUARIUM_PANEL_OPEN, AQUARIUM_TRAIT)
|
||||
source.reagents.flags &= ~(TRANSPARENT|REFILLABLE)
|
||||
SStgui.close_uis(src)
|
||||
else
|
||||
ADD_TRAIT(parent, TRAIT_AQUARIUM_PANEL_OPEN, AQUARIUM_TRAIT)
|
||||
source.reagents.flags |= TRANSPARENT|REFILLABLE
|
||||
|
||||
source.balloon_alert(user, "panel [closing ? "closed" : "open"]")
|
||||
source.update_appearance()
|
||||
return CLICK_ACTION_SUCCESS
|
||||
|
||||
///This proc handles feeding the aquarium and inserting aquarium content.
|
||||
/datum/component/aquarium/proc/on_item_interaction(atom/movable/source, mob/living/user, obj/item/item, modifiers)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(istype(item, /obj/item/reagent_containers/cup/fish_feed))
|
||||
if(source.reagents && HAS_TRAIT(source, TRAIT_AQUARIUM_PANEL_OPEN))
|
||||
return //don't block, we'll be transferring reagents to the feed storage.
|
||||
if(!item.reagents.total_volume)
|
||||
source.balloon_alert(user, "[item] is empty!")
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
var/list/fishes = get_fishes()
|
||||
if(!length(fishes))
|
||||
source.balloon_alert(user, "no fish to feed!")
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
for(var/obj/item/fish/fish as anything in fishes)
|
||||
fish.feed(item.reagents)
|
||||
source.balloon_alert(user, "fed the fish")
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
if(!HAS_TRAIT(item, TRAIT_AQUARIUM_CONTENT))
|
||||
return //proceed with normal interactions
|
||||
|
||||
var/broken = source.get_integrity_percentage() <= source.integrity_failure
|
||||
if(!can_insert(source, item, user))
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
if(broken)
|
||||
source.balloon_alert(user, "aquarium is broken!")
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
if(!user.transferItemToLoc(item, source))
|
||||
user.balloon_alert(user, "stuck to your hand!")
|
||||
return ITEM_INTERACT_BLOCKING
|
||||
source.balloon_alert(user, "added to aquarium")
|
||||
source.update_appearance()
|
||||
return ITEM_INTERACT_SUCCESS
|
||||
|
||||
///Called when the feed storage is no longer empty.
|
||||
/datum/component/aquarium/proc/start_autofeed(atom/movable/source, new_reagent, amount, reagtemp, data, no_react)
|
||||
SIGNAL_HANDLER
|
||||
START_PROCESSING(SSobj, src)
|
||||
UnregisterSignal(source.reagents, COMSIG_REAGENTS_NEW_REAGENT)
|
||||
|
||||
///Feed the fish at defined intervals until the feed storage is empty.
|
||||
/datum/component/aquarium/process(seconds_per_tick)
|
||||
var/atom/movable/movable = parent
|
||||
if(!movable.reagents?.total_volume)
|
||||
if(movable.reagents)
|
||||
RegisterSignal(movable.reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed))
|
||||
return PROCESS_KILL
|
||||
if(world.time < last_feeding + feeding_interval)
|
||||
return
|
||||
last_feeding = world.time
|
||||
var/list/fishes = get_fishes()
|
||||
for(var/obj/item/fish/fish as anything in fishes)
|
||||
fish.feed(movable.reagents)
|
||||
|
||||
/datum/component/aquarium/proc/on_plunger_act(atom/movable/source, obj/item/plunger/plunger, mob/living/user, reinforced)
|
||||
SIGNAL_HANDLER
|
||||
if(!HAS_TRAIT(source, TRAIT_AQUARIUM_PANEL_OPEN))
|
||||
return
|
||||
INVOKE_ASYNC(src, PROC_REF(do_plunging), source, user)
|
||||
return COMPONENT_NO_AFTERATTACK
|
||||
|
||||
/datum/component/aquarium/proc/do_plunging(atom/movable/source, mob/living/user)
|
||||
user.balloon_alert_to_viewers("plunging...")
|
||||
if(do_after(user, 3 SECONDS, target = source))
|
||||
user.balloon_alert_to_viewers("finished plunging")
|
||||
source.reagents.expose(get_turf(source), TOUCH) //splash on the floor
|
||||
source.reagents.clear_reagents()
|
||||
|
||||
/datum/component/aquarium/proc/on_examine(atom/movable/source, mob/user, list/examine_list)
|
||||
SIGNAL_HANDLER
|
||||
var/panel_open = HAS_TRAIT(source, TRAIT_AQUARIUM_PANEL_OPEN)
|
||||
examine_list += span_notice("<b>Alt-click</b> to [panel_open ? "close" : "open"] the control and feed panel.")
|
||||
if(panel_open && source.reagents.total_volume)
|
||||
examine_list += span_notice("You can use a plunger to empty the feed storage.")
|
||||
|
||||
///Check if an item can be inserted into the aquarium
|
||||
/datum/component/aquarium/proc/can_insert(atom/movable/source, obj/item/item, mob/living/user)
|
||||
var/return_value = SEND_SIGNAL(src, COMSIG_AQUARIUM_CAN_INSERT, item, user)
|
||||
if(return_value & COMSIG_CANNOT_INSERT_IN_AQUARIUM)
|
||||
return FALSE
|
||||
if(return_value & COMSIG_CAN_INSERT_IN_AQUARIUM)
|
||||
return TRUE
|
||||
|
||||
if(HAS_TRAIT(item, TRAIT_UNIQUE_AQUARIUM_CONTENT))
|
||||
for(var/atom/movable/content as anything in source)
|
||||
if(content == item)
|
||||
continue
|
||||
if(content.type == item.type)
|
||||
source.balloon_alert(user, "cannot add to aquarium!")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
///Handles aquarium content insertion
|
||||
/datum/component/aquarium/proc/on_entered(atom/movable/source, atom/movable/entered)
|
||||
SIGNAL_HANDLER
|
||||
get_content_beauty(entered)
|
||||
if(!isfish(entered))
|
||||
return
|
||||
var/obj/item/fish/fish = entered
|
||||
LAZYADDASSOCLIST(tracked_fish_by_type, entered.type, entered)
|
||||
if(fish.stable_population < length(tracked_fish_by_type[fish.type]))
|
||||
for(var/obj/item/fish/anyfin as anything in tracked_fish_by_type[entered.type])
|
||||
anyfin.fish_flags |= FISH_FLAG_OVERPOPULATED
|
||||
check_fluid_and_temperature(fish)
|
||||
RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_status_changed))
|
||||
|
||||
///update the beauty_by_content of a 'beauty_by_content' key and then recalculate the beauty.
|
||||
/datum/component/aquarium/proc/get_content_beauty(atom/movable/content)
|
||||
var/list/beauty_holder = list()
|
||||
SEND_SIGNAL(content, COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY, beauty_holder)
|
||||
var/beauty = beauty_holder[1]
|
||||
if(!beauty)
|
||||
return
|
||||
var/old_beauty = default_beauty
|
||||
for(var/key in beauty_by_content)
|
||||
old_beauty += beauty_by_content[key]
|
||||
LAZYSET(beauty_by_content, content, beauty)
|
||||
update_aquarium_beauty(old_beauty)
|
||||
|
||||
///Handles aquarium content removal.
|
||||
/datum/component/aquarium/proc/on_exited(atom/movable/source, atom/movable/gone)
|
||||
SIGNAL_HANDLER
|
||||
var/beauty = beauty_by_content?[gone]
|
||||
if(beauty)
|
||||
var/old_beauty = default_beauty
|
||||
for(var/key in beauty_by_content)
|
||||
old_beauty += beauty_by_content[key]
|
||||
LAZYREMOVE(beauty_by_content, gone)
|
||||
update_aquarium_beauty(old_beauty)
|
||||
if(!isfish(gone))
|
||||
return
|
||||
var/obj/item/fish/fish = gone
|
||||
if(fish.stable_population == length(tracked_fish_by_type[fish.type]))
|
||||
for(var/obj/item/fish/anyfin as anything in tracked_fish_by_type[fish.type])
|
||||
anyfin.fish_flags &= ~FISH_FLAG_OVERPOPULATED
|
||||
LAZYREMOVEASSOC(tracked_fish_by_type, fish.type, fish)
|
||||
fish.fish_flags &= ~(FISH_FLAG_SAFE_TEMPERATURE|FISH_FLAG_SAFE_FLUID)
|
||||
UnregisterSignal(gone, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_status_changed))
|
||||
|
||||
///Return a list of fish which our fishie can reproduce with (including itself if self-reproducing)
|
||||
/datum/component/aquarium/proc/get_candidates(atom/movable/source, obj/item/fish/fish, list/candidates)
|
||||
SIGNAL_HANDLER
|
||||
var/list/types_to_mate_with = tracked_fish_by_type
|
||||
if(!HAS_TRAIT(fish, TRAIT_FISH_CROSSBREEDER))
|
||||
var/list/types_to_check = list(fish.type)
|
||||
if(fish.compatible_types)
|
||||
types_to_check |= fish.compatible_types
|
||||
types_to_mate_with = types_to_mate_with & types_to_check
|
||||
|
||||
for(var/obj/item/fish/fish_type as anything in types_to_mate_with)
|
||||
var/list/type_fishes = types_to_mate_with[fish_type]
|
||||
if(length(type_fishes) >= initial(fish_type.stable_population))
|
||||
continue
|
||||
candidates += type_fishes
|
||||
|
||||
///Check if an offspring of two fish (or one if self-reproducing) can evolve.
|
||||
/datum/component/aquarium/proc/check_evolution(atom/movable/source, obj/item/fish/fish, obj/item/fish/mate, datum/fish_evolution/evolution)
|
||||
SIGNAL_HANDLER
|
||||
//chances are halved if only one parent has this evolution.
|
||||
var/real_probability = (mate && (evolution.type in mate.evolution_types)) ? evolution.probability : evolution.probability * 0.5
|
||||
if(HAS_TRAIT(fish, TRAIT_FISH_MUTAGENIC) || (mate && HAS_TRAIT(mate, TRAIT_FISH_MUTAGENIC)))
|
||||
real_probability *= 3
|
||||
if(!prob(real_probability))
|
||||
return NONE
|
||||
if(!ISINRANGE(fluid_temp, evolution.required_temperature_min, evolution.required_temperature_max))
|
||||
return NONE
|
||||
return COMPONENT_ALLOW_EVOLUTION
|
||||
|
||||
/**
|
||||
* Toggles a couple flags that determine if the fish is in safe waters so that we won't have to use signals or
|
||||
* access this comp in multiple places just to confirm that.
|
||||
*/
|
||||
/datum/component/aquarium/proc/check_fluid_and_temperature(obj/item/fish/fish)
|
||||
if(compatible_fluid_type(fish.required_fluid_type, fluid_type) || (fluid_type == AQUARIUM_FLUID_AIR && HAS_TRAIT(fish, TRAIT_FISH_AMPHIBIOUS)))
|
||||
fish.fish_flags |= FISH_FLAG_SAFE_FLUID
|
||||
else
|
||||
fish.fish_flags &= ~FISH_FLAG_SAFE_FLUID
|
||||
if(ISINRANGE(fluid_temp, fish.required_temperature_min, fish.required_temperature_max))
|
||||
fish.fish_flags |= FISH_FLAG_SAFE_TEMPERATURE
|
||||
else
|
||||
fish.fish_flags &= ~FISH_FLAG_SAFE_TEMPERATURE
|
||||
|
||||
///Fish beauty changes when they're dead, so we need to update the beauty of the aquarium too.
|
||||
/datum/component/aquarium/proc/on_fish_status_changed(obj/item/fish/fish)
|
||||
get_content_beauty(fish)
|
||||
|
||||
/datum/component/aquarium/proc/update_aquarium_beauty(old_beauty)
|
||||
if(QDELETED(parent))
|
||||
return
|
||||
old_beauty = clamp(old_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY)
|
||||
var/new_beauty = 0
|
||||
for(var/key in beauty_by_content)
|
||||
new_beauty += beauty_by_content[key]
|
||||
new_beauty = clamp(new_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY)
|
||||
if(new_beauty == old_beauty)
|
||||
return
|
||||
if(old_beauty)
|
||||
parent.RemoveElement(/datum/element/beauty, old_beauty)
|
||||
if(new_beauty)
|
||||
parent.AddElement(/datum/element/beauty, new_beauty)
|
||||
|
||||
///Remove a visual overlay from an aquarium_content comp
|
||||
/datum/component/aquarium/proc/remove_visual(atom/movable/source, obj/effect/aquarium/visual)
|
||||
SIGNAL_HANDLER
|
||||
source.vis_contents -= visual
|
||||
used_layers -= visual.layer
|
||||
|
||||
///set values for a visual overlay for an aquarium_content comp
|
||||
/datum/component/aquarium/proc/set_visual(atom/movable/source, obj/effect/aquarium/visual)
|
||||
SIGNAL_HANDLER
|
||||
used_layers -= visual.layer
|
||||
visual.layer = request_layer(visual.layer_mode)
|
||||
visual.aquarium_zone_min_px = aquarium_zone_min_px
|
||||
visual.aquarium_zone_max_px = aquarium_zone_max_px
|
||||
visual.aquarium_zone_min_py = aquarium_zone_min_py
|
||||
visual.aquarium_zone_max_py = aquarium_zone_max_py
|
||||
visual.fluid_type = fluid_type
|
||||
|
||||
/datum/component/aquarium/proc/request_layer(layer_type)
|
||||
var/atom/movable/movable = parent
|
||||
switch(layer_type)
|
||||
if(AQUARIUM_LAYER_MODE_BEHIND_GLASS)
|
||||
return movable.layer + AQUARIUM_BELOW_GLASS_LAYER
|
||||
if(AQUARIUM_LAYER_MODE_BOTTOM)
|
||||
return movable.layer + AQUARIUM_MIN_OFFSET
|
||||
if(AQUARIUM_LAYER_MODE_TOP)
|
||||
return movable.layer + AQUARIUM_MAX_OFFSET
|
||||
if(AQUARIUM_LAYER_MODE_AUTO)
|
||||
var/chosen_layer = AQUARIUM_MIN_OFFSET + AQUARIUM_LAYER_STEP
|
||||
while((chosen_layer in used_layers) && (chosen_layer <= AQUARIUM_MAX_OFFSET))
|
||||
chosen_layer += AQUARIUM_LAYER_STEP
|
||||
used_layers += chosen_layer
|
||||
return movable.layer + chosen_layer
|
||||
|
||||
/datum/component/aquarium/proc/get_fishes()
|
||||
var/list/fishes = list()
|
||||
for(var/key in tracked_fish_by_type)
|
||||
fishes += tracked_fish_by_type[key]
|
||||
return fishes
|
||||
|
||||
/datum/component/aquarium/proc/interact(atom/movable/source, mob/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(HAS_TRAIT(source, TRAIT_AQUARIUM_PANEL_OPEN))
|
||||
INVOKE_ASYNC(src, PROC_REF(ui_interact), user)
|
||||
else if(!isitem(source))
|
||||
INVOKE_ASYNC(src, PROC_REF(admire), user)
|
||||
|
||||
/datum/component/aquarium/proc/on_secondary_attack_hand(obj/item/source, mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
INVOKE_ASYNC(src, PROC_REF(admire), user)
|
||||
return COMPONENT_CANCEL_ATTACK_CHAIN
|
||||
|
||||
/datum/component/aquarium/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
var/atom/movable/movable = parent
|
||||
ui = new(user, src, "Aquarium", movable.name)
|
||||
ui.open()
|
||||
|
||||
/datum/component/aquarium/ui_data(atom/movable/source, mob/user)
|
||||
. = ..()
|
||||
.["fluidType"] = fluid_type
|
||||
.["temperature"] = fluid_temp
|
||||
.["allowBreeding"] = HAS_TRAIT_FROM(source, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT)
|
||||
.["fishData"] = list()
|
||||
.["feedingInterval"] = feeding_interval / (1 MINUTES)
|
||||
.["propData"] = list()
|
||||
for(var/atom/movable/item as anything in source.contents)
|
||||
if(isfish(item))
|
||||
var/obj/item/fish/fish = item
|
||||
.["fishData"] += list(list(
|
||||
"fish_ref" = REF(fish),
|
||||
"fish_name" = uppertext(fish.name),
|
||||
"fish_happiness" = fish.get_happiness_value(),
|
||||
"fish_icon" = fish::icon,
|
||||
"fish_icon_state" = fish::icon_state,
|
||||
"fish_health" = fish.health,
|
||||
))
|
||||
continue
|
||||
.["propData"] += list(list(
|
||||
"prop_ref" = REF(item),
|
||||
"prop_name" = item.name,
|
||||
"prop_icon" = item::icon,
|
||||
"prop_icon_state" = item::icon_state,
|
||||
))
|
||||
|
||||
/datum/component/aquarium/ui_static_data(mob/user)
|
||||
. = ..()
|
||||
//I guess these should depend on the fluid so lava critters can get high or stuff below water freezing point but let's keep it simple for now.
|
||||
.["minTemperature"] = min_fluid_temp
|
||||
.["maxTemperature"] = max_fluid_temp
|
||||
.["fluidTypes"] = fluid_types
|
||||
.["heartIcon"] = 'icons/effects/effects.dmi'
|
||||
|
||||
/datum/component/aquarium/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
var/mob/user = usr
|
||||
var/atom/movable/movable = parent
|
||||
switch(action)
|
||||
if("temperature")
|
||||
var/temperature = params["temperature"]
|
||||
if(isnum(temperature) && temperature != fluid_temp)
|
||||
fluid_temp = clamp(temperature, min_fluid_temp, max_fluid_temp)
|
||||
for(var/obj/item/fish/fish as anything in get_fishes())
|
||||
check_fluid_and_temperature(fish)
|
||||
. = TRUE
|
||||
if("fluid")
|
||||
if(params["fluid"] != fluid_type && (params["fluid"] in fluid_types))
|
||||
set_fluid_type(params["fluid"])
|
||||
. = TRUE
|
||||
if("allow_breeding")
|
||||
if(HAS_TRAIT(movable, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH))
|
||||
REMOVE_TRAIT(movable, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT)
|
||||
else
|
||||
ADD_TRAIT(movable, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT)
|
||||
. = TRUE
|
||||
if("feeding_interval")
|
||||
feeding_interval = params["feeding_interval"] MINUTES
|
||||
. = TRUE
|
||||
if("pet_fish")
|
||||
var/obj/item/fish/fish = locate(params["fish_reference"]) in movable.contents
|
||||
fish?.pet_fish(user)
|
||||
if("remove_item")
|
||||
var/atom/movable/item = locate(params["item_reference"]) in movable.contents
|
||||
item?.forceMove(movable.drop_location())
|
||||
to_chat(user, span_notice("You take out [item] from [movable]."))
|
||||
if("rename_fish")
|
||||
var/new_name = sanitize_name(params["chosen_name"])
|
||||
var/atom/movable/fish = locate(params["fish_reference"]) in movable.contents
|
||||
if(!fish || !new_name || new_name == fish.name)
|
||||
return
|
||||
fish.AddComponent(/datum/component/rename, new_name, fish.desc)
|
||||
|
||||
/datum/component/aquarium/proc/set_fluid_type(new_fluid_type)
|
||||
var/atom/movable/movable = parent
|
||||
fluid_type = new_fluid_type
|
||||
SEND_SIGNAL(movable, COMSIG_AQUARIUM_FLUID_CHANGED, fluid_type)
|
||||
for(var/obj/item/fish/fish as anything in get_fishes())
|
||||
check_fluid_and_temperature(fish)
|
||||
|
||||
/datum/component/aquarium/proc/admire(atom/movable/source, mob/living/user)
|
||||
source.balloon_alert(user, "admiring aquarium...")
|
||||
if(!do_after(user, 5 SECONDS, target = source))
|
||||
return
|
||||
var/alive_fish = 0
|
||||
var/dead_fish = 0
|
||||
for(var/obj/item/fish/fish as anything in get_fishes())
|
||||
if(fish.status == FISH_ALIVE)
|
||||
alive_fish++
|
||||
else
|
||||
dead_fish++
|
||||
|
||||
var/morb = HAS_MIND_TRAIT(user, TRAIT_MORBID)
|
||||
//Check if there are live fish - good mood
|
||||
//All fish dead - bad mood.
|
||||
//No fish - nothing.
|
||||
if(alive_fish > 0)
|
||||
user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_bad : /datum/mood_event/aquarium_positive)
|
||||
else if(dead_fish > 0)
|
||||
user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_good : /datum/mood_event/aquarium_negative)
|
||||
|
||||
#undef MIN_AQUARIUM_BEAUTY
|
||||
#undef MAX_AQUARIUM_BEAUTY
|
||||
@@ -1,18 +1,5 @@
|
||||
///Malus to the beauty value if the fish content is dead
|
||||
#define DEAD_FISH_BEAUTY -500
|
||||
///Prevents more impressive fishes from providing a positive beauty even when dead.
|
||||
#define MAX_DEAD_FISH_BEAUTY -200
|
||||
///Some fish are already so ugly, they can't get much worse when dead
|
||||
#define MIN_DEAD_FISH_BEAUTY -600
|
||||
|
||||
///Defines that clamp the beauty of the aquarium, to prevent it from making most areas great or horrid all by itself.
|
||||
#define MIN_AQUARIUM_BEAUTY -3500
|
||||
#define MAX_AQUARIUM_BEAUTY 6000
|
||||
|
||||
/// 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/aquarium/vc_obj
|
||||
@@ -32,161 +19,103 @@
|
||||
/// Does this behviour need additional processing in aquarium, will be added to SSobj processing on insertion
|
||||
var/processing = FALSE
|
||||
|
||||
/// Signals of the parent that will trigger animation update
|
||||
var/animation_update_signals
|
||||
/// Signals for the aquarium we're in that trigger an animation update
|
||||
var/list/animation_update_signals
|
||||
|
||||
/// The current beauty this component gives to the aquarium it's in
|
||||
var/beauty
|
||||
|
||||
/// The original value of the beauty this component had when initialized
|
||||
var/original_beauty
|
||||
|
||||
/datum/component/aquarium_content/Initialize(animation_update_signals, beauty)
|
||||
if(!ismovable(parent))
|
||||
/datum/component/aquarium_content/Initialize(animation_update_signals)
|
||||
if(!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.animation_update_signals = animation_update_signals
|
||||
src.beauty = original_beauty = beauty
|
||||
if(animation_update_signals)
|
||||
RegisterSignals(parent, animation_update_signals, PROC_REF(generate_animation))
|
||||
src.animation_update_signals = islist(animation_update_signals) ? animation_update_signals : list(animation_update_signals)
|
||||
|
||||
ADD_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, REF(src))
|
||||
RegisterSignal(parent, COMSIG_TRY_INSERTING_IN_AQUARIUM, PROC_REF(is_ready_to_insert))
|
||||
ADD_TRAIT(parent, TRAIT_AQUARIUM_CONTENT, REF(src))
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(enter_aquarium))
|
||||
|
||||
if(isfish(parent))
|
||||
RegisterSignal(parent, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_status_changed))
|
||||
|
||||
//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))
|
||||
if(movable_parent.loc && HAS_TRAIT(movable_parent.loc, TRAIT_IS_AQUARIUM))
|
||||
on_inserted(movable_parent.loc)
|
||||
|
||||
/datum/component/aquarium_content/proc/on_fish_status_changed(obj/item/fish/source)
|
||||
SIGNAL_HANDLER
|
||||
var/old_beauty = beauty
|
||||
beauty = original_beauty
|
||||
if(source.status == FISH_DEAD)
|
||||
beauty = clamp(beauty + DEAD_FISH_BEAUTY, MIN_DEAD_FISH_BEAUTY, MAX_DEAD_FISH_BEAUTY)
|
||||
if(current_aquarium)
|
||||
change_aquarium_beauty(beauty - old_beauty)
|
||||
generate_animation()
|
||||
|
||||
/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()
|
||||
var/atom/movable/movable = parent
|
||||
if(movable.loc && HAS_TRAIT(movable.loc, TRAIT_IS_AQUARIUM))
|
||||
remove_from_aquarium(movable.loc)
|
||||
QDEL_NULL(vc_obj)
|
||||
REMOVE_TRAIT(parent, TRAIT_AQUARIUM_CONTENT, REF(src))
|
||||
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))
|
||||
if(HAS_TRAIT(movable_parent.loc, TRAIT_IS_AQUARIUM))
|
||||
on_inserted(movable_parent.loc)
|
||||
|
||||
/datum/component/aquarium_content/proc/is_ready_to_insert(datum/source, obj/structure/aquarium/aquarium)
|
||||
SIGNAL_HANDLER
|
||||
if(HAS_TRAIT(parent, TRAIT_UNIQUE_AQUARIUM_CONTENT))
|
||||
for(var/atom/movable/content as anything in aquarium)
|
||||
if(content == parent)
|
||||
continue
|
||||
if(content.type == parent.type)
|
||||
return COMSIG_CANNOT_INSERT_IN_AQUARIUM
|
||||
return COMSIG_CAN_INSERT_IN_AQUARIUM
|
||||
|
||||
/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))
|
||||
/datum/component/aquarium_content/proc/on_inserted(atom/movable/aquarium)
|
||||
RegisterSignal(aquarium, COMSIG_ATOM_EXITED, PROC_REF(on_removed))
|
||||
RegisterSignal(aquarium, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_fluid_changed))
|
||||
RegisterSignals(aquarium, animation_update_signals, PROC_REF(animation_update_signal_proc))
|
||||
|
||||
if(processing)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
//If we don't have vc object yet build it
|
||||
if(!vc_obj)
|
||||
generate_base_vc()
|
||||
generate_base_vc(aquarium)
|
||||
|
||||
//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.vis_contents |= vc_obj
|
||||
|
||||
change_aquarium_beauty(beauty)
|
||||
|
||||
///Modifies the beauty of the aquarium when content is added or removed, or when fishes die or live again somehow.
|
||||
/datum/component/aquarium_content/proc/change_aquarium_beauty(change)
|
||||
if(QDELETED(current_aquarium) || !change)
|
||||
return
|
||||
var/old_clamped_beauty = clamp(current_aquarium.current_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY)
|
||||
current_aquarium.current_beauty += change
|
||||
var/new_clamped_beauty = clamp(current_aquarium.current_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY)
|
||||
if(new_clamped_beauty == old_clamped_beauty)
|
||||
return
|
||||
if(current_aquarium.current_beauty)
|
||||
current_aquarium.RemoveElement(/datum/element/beauty, current_aquarium.current_beauty)
|
||||
if(current_aquarium.current_beauty)
|
||||
current_aquarium.AddElement(/datum/element/beauty, current_aquarium.current_beauty)
|
||||
|
||||
/// Aquarium surface changed in some way, we need to recalculate base position and aninmation
|
||||
/datum/component/aquarium_content/proc/on_surface_changed()
|
||||
/datum/component/aquarium_content/proc/on_fluid_changed(datum/source, new_fluid_type)
|
||||
SIGNAL_HANDLER
|
||||
set_vc_base_position()
|
||||
generate_animation(reset = TRUE) //our animation start point changed, gotta redo
|
||||
vc_obj.fluid_type = new_fluid_type
|
||||
generate_animation()
|
||||
|
||||
/datum/component/aquarium_content/proc/on_fluid_changed()
|
||||
SIGNAL_HANDLER
|
||||
///Called when one of the signals in the 'animation_update_signals' is sent
|
||||
/datum/component/aquarium_content/proc/animation_update_signal_proc(datum/source)
|
||||
generate_animation()
|
||||
|
||||
///Sends a signal to the parent to get them to update the aquarium animation of the visual object
|
||||
/datum/component/aquarium_content/proc/generate_animation(reset=FALSE)
|
||||
if(!current_aquarium)
|
||||
return
|
||||
SEND_SIGNAL(parent, COMSIG_AQUARIUM_CONTENT_DO_ANIMATION, reset ? null : current_animation, current_aquarium, vc_obj)
|
||||
|
||||
/datum/component/aquarium_content/proc/remove_visual_from_aquarium()
|
||||
current_aquarium.vis_contents -= vc_obj
|
||||
if(vc_obj.layer)
|
||||
current_aquarium.free_layer(vc_obj.layer)
|
||||
/datum/component/aquarium_content/proc/generate_animation(reset = FALSE)
|
||||
var/atom/movable/movable = parent
|
||||
SEND_SIGNAL(movable, COMSIG_AQUARIUM_CONTENT_DO_ANIMATION, reset ? null : current_animation, vc_obj)
|
||||
|
||||
/// Generates common visual object, propeties that don't depend on aquarium surface
|
||||
/datum/component/aquarium_content/proc/generate_base_vc()
|
||||
/datum/component/aquarium_content/proc/generate_base_vc(atom/movable/aquarium)
|
||||
vc_obj = new
|
||||
vc_obj.vis_flags |= VIS_INHERIT_ID | VIS_INHERIT_PLANE //plane so it shows properly in containers on inventory ui for handheld cases
|
||||
SEND_SIGNAL(parent, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, vc_obj)
|
||||
SEND_SIGNAL(parent, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, vc_obj, aquarium)
|
||||
|
||||
/datum/component/aquarium_content/proc/set_vc_base_position()
|
||||
SEND_SIGNAL(parent, AQUARIUM_CONTENT_RANDOMIZE_POSITION, current_aquarium, vc_obj)
|
||||
if(vc_obj.layer)
|
||||
current_aquarium.free_layer(vc_obj.layer)
|
||||
vc_obj.layer = current_aquarium.request_layer(vc_obj.layer_mode)
|
||||
var/atom/movable/movable = parent
|
||||
SEND_SIGNAL(movable, COMSIG_AQUARIUM_CONTENT_RANDOMIZE_POSITION, movable.loc, vc_obj)
|
||||
SEND_SIGNAL(movable.loc, COMSIG_AQUARIUM_SET_VISUAL, vc_obj)
|
||||
|
||||
/datum/component/aquarium_content/proc/on_removed(obj/structure/aquarium/source, atom/movable/gone, direction)
|
||||
/datum/component/aquarium_content/proc/on_removed(atom/movable/aquarium, atom/movable/gone, direction)
|
||||
SIGNAL_HANDLER
|
||||
if(parent != gone)
|
||||
return
|
||||
remove_from_aquarium()
|
||||
remove_from_aquarium(aquarium)
|
||||
|
||||
/datum/component/aquarium_content/proc/remove_from_aquarium()
|
||||
change_aquarium_beauty(-beauty)
|
||||
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
|
||||
/datum/component/aquarium_content/proc/remove_from_aquarium(atom/movable/aquarium)
|
||||
UnregisterSignal(aquarium, list(COMSIG_AQUARIUM_FLUID_CHANGED, COMSIG_ATOM_EXITED) + animation_update_signals)
|
||||
SEND_SIGNAL(aquarium, COMSIG_AQUARIUM_REMOVE_VISUAL, vc_obj)
|
||||
|
||||
///The visual overlay of the aquarium content. It can hold a couple vars that we can modify through signal procs.
|
||||
///The visual overlay of the aquarium content. It can hold a few vars with values about the component of the aquarium it's in.
|
||||
/obj/effect/aquarium
|
||||
layer = 0 //set on set_vc_base_position
|
||||
/// How the visual will be layered
|
||||
var/layer_mode = AQUARIUM_LAYER_MODE_AUTO
|
||||
|
||||
#undef DEAD_FISH_BEAUTY
|
||||
#undef MIN_DEAD_FISH_BEAUTY
|
||||
#undef MAX_DEAD_FISH_BEAUTY
|
||||
#undef MIN_AQUARIUM_BEAUTY
|
||||
#undef MAX_AQUARIUM_BEAUTY
|
||||
///minimum pixel x, inherited from the aquarium
|
||||
var/aquarium_zone_min_px
|
||||
///maximum pixel x, inherited from the aquarium
|
||||
var/aquarium_zone_max_px
|
||||
///minimum pixel y, inherited from the aquarium
|
||||
var/aquarium_zone_min_py
|
||||
///maximum pixel y, inherited from the aquarium
|
||||
var/aquarium_zone_max_py
|
||||
///The current fluid type, inherited fom the aquarium
|
||||
var/fluid_type
|
||||
|
||||
@@ -150,6 +150,6 @@
|
||||
SIGNAL_HANDLER
|
||||
overmind?.assume_direct_control(replacement)
|
||||
|
||||
/datum/component/blob_minion/PostTransfer()
|
||||
if(!isliving(parent))
|
||||
/datum/component/blob_minion/PostTransfer(datum/new_parent)
|
||||
if(!isliving(new_parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
@@ -22,12 +22,22 @@
|
||||
result = /obj/structure/aquarium
|
||||
time = 10 SECONDS
|
||||
reqs = list(
|
||||
/obj/item/stack/sheet/iron = 15,
|
||||
/obj/item/stack/sheet/iron = 10,
|
||||
/obj/item/stack/sheet/glass = 10,
|
||||
/obj/item/aquarium_kit = 1,
|
||||
)
|
||||
category = CAT_FURNITURE
|
||||
|
||||
/datum/crafting_recipe/fish_tank
|
||||
name = "Portable Fish Tank"
|
||||
result = /obj/item/fish_tank
|
||||
time = 10 SECONDS
|
||||
reqs = list(
|
||||
/obj/item/stack/sheet/plastic = 5,
|
||||
/obj/item/aquarium_kit = 1,
|
||||
)
|
||||
category = CAT_FURNITURE
|
||||
|
||||
/datum/crafting_recipe/mirror
|
||||
name = "Mirror"
|
||||
result = /obj/item/wallframe/mirror
|
||||
|
||||
@@ -82,10 +82,10 @@
|
||||
))
|
||||
REMOVE_TRAIT(parent, TRAIT_CUSTOMIZABLE_REAGENT_HOLDER, REF(src))
|
||||
|
||||
/datum/component/customizable_reagent_holder/PostTransfer()
|
||||
if(!isatom(parent))
|
||||
/datum/component/customizable_reagent_holder/PostTransfer(datum/new_parent)
|
||||
if(!isatom(new_parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
var/atom/atom_parent = parent
|
||||
var/atom/atom_parent = new_parent
|
||||
if (!atom_parent.reagents)
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
/datum/component/itempicky/UnregisterFromParent()
|
||||
UnregisterSignal(parent, COMSIG_LIVING_TRY_PUT_IN_HAND)
|
||||
|
||||
/datum/component/itempicky/PostTransfer()
|
||||
if(!ismob(parent))
|
||||
/datum/component/itempicky/PostTransfer(datum/new_parent)
|
||||
if(!ismob(new_parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
/datum/component/itempicky/InheritComponent(datum/component/itempicky/friend, i_am_original, list/arguments)
|
||||
|
||||
@@ -51,6 +51,6 @@
|
||||
/datum/component/multiple_lives/InheritComponent(datum/component/multiple_lives/new_comp , lives_left)
|
||||
src.lives_left += new_comp ? new_comp.lives_left : lives_left
|
||||
|
||||
/datum/component/multiple_lives/PostTransfer()
|
||||
if(!isliving(parent))
|
||||
/datum/component/multiple_lives/PostTransfer(datum/new_parent)
|
||||
if(!isliving(new_parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
@@ -56,10 +56,10 @@
|
||||
orbiter_list += newcomp.orbiter_list
|
||||
newcomp.orbiter_list = null
|
||||
|
||||
/datum/component/orbiter/PostTransfer()
|
||||
if(!isatom(parent) || isarea(parent) || !get_turf(parent))
|
||||
/datum/component/orbiter/PostTransfer(datum/new_parent)
|
||||
if(!isatom(parent) || isarea(new_parent) || !get_turf(new_parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
move_react(parent)
|
||||
move_react(new_parent)
|
||||
|
||||
/datum/component/orbiter/proc/begin_orbit(atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
|
||||
if(orbiter.orbiting)
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
RegisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item))
|
||||
return ..()
|
||||
|
||||
/datum/component/simple_rotation/PostTransfer()
|
||||
/datum/component/simple_rotation/PostTransfer(datum/new_parent)
|
||||
//Because of the callbacks which we don't track cleanly we can't transfer this
|
||||
//item cleanly, better to let the new of the new item create a new rotation datum
|
||||
//instead (there's no real state worth transferring)
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
COMSIG_TRY_ALT_ACTION,
|
||||
))
|
||||
|
||||
/datum/component/shy/PostTransfer()
|
||||
/datum/component/shy/PostTransfer(datum/new_parent)
|
||||
if(!ismob(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
COMSIG_TRY_ALT_ACTION,
|
||||
))
|
||||
|
||||
/datum/component/shy_in_room/PostTransfer()
|
||||
/datum/component/shy_in_room/PostTransfer(datum/new_parent)
|
||||
if(!ismob(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
dropsound = _dropsound
|
||||
drop_memory = _drop_memory
|
||||
|
||||
/datum/component/spill/PostTransfer()
|
||||
/datum/component/spill/PostTransfer(datum/new_parent)
|
||||
if(!isitem(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
/datum/component/technointrovert/UnregisterFromParent()
|
||||
UnregisterSignal(parent, list(COMSIG_TRY_USE_MACHINE, COMSIG_TRY_WIRES_INTERACT))
|
||||
|
||||
/datum/component/technointrovert/PostTransfer()
|
||||
/datum/component/technointrovert/PostTransfer(datum/new_parent)
|
||||
if(!ismob(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
/datum/component/technoshy/UnregisterFromParent()
|
||||
UnregisterSignal(parent, list(COMSIG_TRY_USE_MACHINE, COMSIG_TRY_WIRES_INTERACT))
|
||||
|
||||
/datum/component/technoshy/PostTransfer()
|
||||
/datum/component/technoshy/PostTransfer(datum/new_parent)
|
||||
if(!ismob(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
|
||||
@@ -159,14 +159,14 @@
|
||||
for(var/i in time_left_list)
|
||||
. |= text2num(i)
|
||||
|
||||
/datum/component/wet_floor/PreTransfer()
|
||||
/datum/component/wet_floor/PreTransfer(datum/new_parent)
|
||||
var/turf/O = parent
|
||||
O.cut_overlay(current_overlay)
|
||||
//That turf is no longer slippery, we're out of here
|
||||
//Slippery components don't transfer due to callbacks
|
||||
qdel(O.GetComponent(/datum/component/slippery))
|
||||
|
||||
/datum/component/wet_floor/PostTransfer()
|
||||
/datum/component/wet_floor/PostTransfer(datum/new_parent)
|
||||
if(!isopenturf(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
var/turf/T = parent
|
||||
|
||||
Reference in New Issue
Block a user