From 7663b39cc8fb0429d979d17c95bc966b4a0a1997 Mon Sep 17 00:00:00 2001 From: Ghom <42542238+Ghommie@users.noreply.github.com> Date: Wed, 20 Nov 2024 21:45:49 +0100 Subject: [PATCH] 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 :cl: 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. /:cl: --- _maps/map_files/Birdshot/birdshot.dmm | 3 +- .../map_files/Deltastation/DeltaStation2.dmm | 3 +- .../map_files/IceBoxStation/IceBoxStation.dmm | 3 +- _maps/map_files/MetaStation/MetaStation.dmm | 3 +- _maps/map_files/NorthStar/north_star.dmm | 3 +- _maps/map_files/tramstation/tramstation.dmm | 3 +- .../signals_atom/signals_atom_movable.dm | 3 + code/__DEFINES/dcs/signals/signals_fish.dm | 19 +- code/__DEFINES/fish.dm | 33 +- code/__DEFINES/is_helpers.dm | 2 - code/__DEFINES/traits/declarations.dm | 20 +- code/__DEFINES/traits/sources.dm | 3 + code/_compile_options.dm | 2 +- code/_globalvars/bitfields.dm | 3 + code/_globalvars/traits/_traits.dm | 7 +- code/datums/components/_component.dm | 32 +- code/datums/components/aquarium.dm | 564 ++++++++++++++++++ code/datums/components/aquarium_content.dm | 165 ++--- code/datums/components/blob_minion.dm | 4 +- code/datums/components/crafting/furniture.dm | 12 +- .../components/customizable_reagent_holder.dm | 6 +- code/datums/components/itempicky.dm | 4 +- code/datums/components/multiple_lives.dm | 4 +- code/datums/components/orbiter.dm | 6 +- code/datums/components/rotation.dm | 2 +- code/datums/components/shy.dm | 2 +- code/datums/components/shy_in_room.dm | 2 +- code/datums/components/spill.dm | 2 +- code/datums/components/technointrovert.dm | 2 +- code/datums/components/technoshy.dm | 2 +- code/datums/components/wet_floor.dm | 4 +- code/datums/elements/beauty.dm | 73 ++- code/datums/storage/subtypes/backpack.dm | 10 + code/datums/storage/subtypes/duffel_bag.dm | 13 + code/datums/storage/subtypes/fish_case.dm | 2 +- code/game/objects/items/food/lizard.dm | 20 +- code/modules/fishing/aquarium/aquarium.dm | 533 ++++++----------- code/modules/fishing/aquarium/aquarium_kit.dm | 20 +- .../fishing/aquarium/aquarium_upgrades.dm | 47 +- .../modules/fishing/aquarium/fish_analyzer.dm | 23 +- code/modules/fishing/fish/_fish.dm | 182 +++--- code/modules/fishing/fish/fish_evolution.dm | 41 +- code/modules/fishing/fish/fish_traits.dm | 20 +- code/modules/fishing/fish/types/air_space.dm | 7 +- code/modules/fishing/fish/types/freshwater.dm | 3 +- code/modules/fishing/fish/types/mining.dm | 9 +- code/modules/research/designs/misc_designs.dm | 12 + .../modules/research/designs/power_designs.dm | 4 +- .../research/techweb/nodes/service_nodes.dm | 1 + code/modules/unit_tests/component_tests.dm | 7 +- code/modules/unit_tests/fish_unit_tests.dm | 36 +- code/modules/vending/games.dm | 1 + icons/obj/aquarium/supplies.dmi | Bin 1258 -> 923 bytes icons/obj/aquarium/tanks.dmi | Bin 4151 -> 13650 bytes icons/obj/fishing.dmi | Bin 31186 -> 32217 bytes icons/obj/food/lizard.dmi | Bin 32083 -> 31999 bytes tgstation.dme | 1 + .../UpdatePaths/Scripts/87866_fish_tanks.txt | 1 + 58 files changed, 1246 insertions(+), 743 deletions(-) create mode 100644 code/datums/components/aquarium.dm create mode 100644 tools/UpdatePaths/Scripts/87866_fish_tanks.txt diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index 77ec96a4fe8..f18d092fd15 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -50755,7 +50755,8 @@ "rqt" = ( /obj/machinery/airalarm/directional/north, /obj/effect/decal/cleanable/dirt, -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/iron/dark, /area/station/service/lawoffice) "rqw" = ( diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 13c2c019fdb..08d3aa51e9b 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -44890,7 +44890,8 @@ /area/station/hallway/primary/fore) "lhp" = ( /obj/structure/sign/poster/official/report_crimes/directional/south, -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/wood, /area/station/service/lawoffice) "lhC" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 3a895cc7b53..498bba9c2e8 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -7969,7 +7969,8 @@ /turf/open/floor/iron/dark, /area/station/service/hydroponics/garden) "cgB" = ( -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/wood, /area/station/service/lawoffice) "cgC" = ( diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 839edbd4327..1bc583699ef 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -59697,7 +59697,8 @@ name = "Lawyer Requests Console" }, /obj/machinery/newscaster/directional/west, -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/wood, /area/station/service/lawoffice) "uZP" = ( diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm index aaa34d20351..94d868d1227 100644 --- a/_maps/map_files/NorthStar/north_star.dmm +++ b/_maps/map_files/NorthStar/north_star.dmm @@ -65672,7 +65672,8 @@ /turf/open/floor/iron/white, /area/station/science/lobby) "qSD" = ( -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /turf/open/floor/wood/parquet, /area/station/service/lawoffice) "qSJ" = ( diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 0335fd1b427..0c1245591ec 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -18865,7 +18865,8 @@ /area/station/commons/vacant_room) "fEZ" = ( /obj/structure/noticeboard/directional/north, -/obj/structure/aquarium/lawyer, +/obj/item/fish_tank/lawyer, +/obj/structure/table/wood, /obj/structure/cable, /obj/machinery/power/apc/auto_name/directional/west, /turf/open/floor/wood, diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm index 36a2ca2c805..f0024072218 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm @@ -122,6 +122,9 @@ ///from base of /atom/movable/point_at: (atom/A, obj/effect/temp_visual/point/point) #define COMSIG_MOVABLE_POINTED "movable_pointed" +///From /datum/component/aquarium/get_content_beauty: (beauty_holder) +#define COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY "movable_ge_aquarium_beauty" + /// Sent to movables when they are being stolen by a spy: (mob/living/spy, datum/spy_bounty/bounty) #define COMSIG_MOVABLE_SPY_STEALING "movable_spy_stealing" /// Called when something is pushed by a living mob bumping it: (mob/living/pusher, push force) diff --git a/code/__DEFINES/dcs/signals/signals_fish.dm b/code/__DEFINES/dcs/signals/signals_fish.dm index b3147466592..08a38fc02ab 100644 --- a/code/__DEFINES/dcs/signals/signals_fish.dm +++ b/code/__DEFINES/dcs/signals/signals_fish.dm @@ -1,23 +1,32 @@ // Aquarium related signals -#define COMSIG_AQUARIUM_SURFACE_CHANGED "aquarium_surface_changed" + +///From /datum/component/aquarium/ui_act, when changing the fluid of the aquarium: (fluid_type) #define COMSIG_AQUARIUM_FLUID_CHANGED "aquarium_fluid_changed" -///Called on aquarium/attackby: (aquarium) -#define COMSIG_TRY_INSERTING_IN_AQUARIUM "item_try_inserting_in_aquarium" +///From /datum/component/aquarium/can_insert: (obj/item/item) +#define COMSIG_AQUARIUM_CAN_INSERT "aquarium_can_insert" ///The item will be inserted into the aquarium #define COMSIG_CAN_INSERT_IN_AQUARIUM (1<<0) ///The item won't be inserted into the aquarium, but will early return attackby anyway. #define COMSIG_CANNOT_INSERT_IN_AQUARIUM (1<<1) +///From /datum/component/aquarium_content/set_vc_base_position: (obj/effect/aquarium/visual) +#define COMSIG_AQUARIUM_SET_VISUAL "aquarium_set_visual" +///From /datum/component/aquarium_content/remove_from_aquarium: (obj/effect/aquarium/visual) +#define COMSIG_AQUARIUM_REMOVE_VISUAL "aquarium_remove_visual" +///From /obj/item/fish/try_to_reproduce: (fish, candidates) +#define COMSIG_AQUARIUM_GET_REPRODUCTION_CANDIDATES "aquarium_get_reproduction_candidates" +///From /datum/fish_evolution/check_conditions: (fish, mate, evolution) +#define COMSIG_AQUARIUM_CHECK_EVOLUTION_CONDITIONS "aquarium_check_evolution_conditions" + #define COMPONENT_ALLOW_EVOLUTION (1<<0) ///Updates the appearance of a newly generated aquarium content visual:(visual) #define COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE "aquarium_content_apply_appearance" ///Updates the base position of an aquarium content visual:(aquarium, visual) -#define AQUARIUM_CONTENT_RANDOMIZE_POSITION "aquarium_content_randomize_position" +#define COMSIG_AQUARIUM_CONTENT_RANDOMIZE_POSITION "aquarium_content_randomize_position" ///Updates the animation of an aquarium content visual:(aquarium, visual) #define COMSIG_AQUARIUM_CONTENT_DO_ANIMATION "aquarium_content_do_animation" // Fish signals #define COMSIG_FISH_STATUS_CHANGED "fish_status_changed" -#define COMSIG_FISH_STIRRED "fish_stirred" ///From /obj/item/fish/process: (seconds_per_tick) #define COMSIG_FISH_LIFE "fish_life" ///From /datum/fish_trait/eat_fish: (predator) diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm index 387d8b47d1b..7d4b5e0d547 100644 --- a/code/__DEFINES/fish.dm +++ b/code/__DEFINES/fish.dm @@ -98,10 +98,19 @@ #define AQUARIUM_ANIMATION_FISH_SWIM "fish" #define AQUARIUM_ANIMATION_FISH_DEAD "dead" -#define AQUARIUM_PROPERTIES_PX_MIN "px_min" -#define AQUARIUM_PROPERTIES_PX_MAX "px_max" -#define AQUARIUM_PROPERTIES_PY_MIN "py_min" -#define AQUARIUM_PROPERTIES_PY_MAX "py_max" +//standard layer defines for aquariums + +///The distance that should separate each layer of the aquarium +#define AQUARIUM_LAYER_STEP 0.01 +/// Aquarium content layer offsets +#define AQUARIUM_MIN_OFFSET 0.02 +#define AQUARIUM_MAX_OFFSET 1 +/// The layer of the glass overlay +#define AQUARIUM_GLASS_LAYER 0.02 +/// The layer of the aquarium pane borders +#define AQUARIUM_BORDERS_LAYER AQUARIUM_MAX_OFFSET + AQUARIUM_LAYER_STEP +/// Layer for stuff rendered below the glass overlay +#define AQUARIUM_BELOW_GLASS_LAYER 0.01 #define AQUARIUM_LAYER_MODE_BOTTOM "bottom" #define AQUARIUM_LAYER_MODE_TOP "top" @@ -141,6 +150,16 @@ ///Used to calculate how many bites a fish can take and therefore the amount of reagents it has. #define FISH_WEIGHT_BITE_DIVISOR (FISH_GRIND_RESULTS_WEIGHT_DIVISOR * FISH_WEIGHT_GRIND_TO_BITE_MULT) +///Set of operations that calculate the slowdown of fish based on weight +#define GET_FISH_SLOWDOWN(weighty) round(((weighty/FISH_WEIGHT_SLOWDOWN_DIVISOR)**FISH_WEIGHT_SLOWDOWN_EXPONENT)-1.3, 0.1) + +/** + * Gets a "rank" for fish weight to determine the force of the fish (or fish tank) + * basically, a gross estimate based on how weight generaly scales up (250, 500, 1000, 2000, 4000 etc...) + * for most fish + */ +#define GET_FISH_WEIGHT_RANK(weighty) max(round(1 + log(2, max(weighty/FISH_WEIGHT_FORCE_DIVISOR, 1)), 1), 1) + ///The breeding timeout for newly instantiated fish is multiplied by this. #define NEW_FISH_BREEDING_TIMEOUT_MULT 2 ///The last feeding timestamp of newly instantiated fish is multiplied by this: ergo, they spawn 50% hungry. @@ -158,6 +177,12 @@ #define FISH_FLAG_EXPERIMENT_SCANNABLE (1<<3) ///It lets us know that fish/update_size_and_weight() is currently running. #define FISH_FLAG_UPDATING_SIZE_AND_WEIGHT (1<<4) +///Flag added when the population of this fish type exceeeds the stable population inside the aquarium +#define FISH_FLAG_OVERPOPULATED (1<<5) +///Flag added when in an aquarium which temperature is within its safe limits +#define FISH_FLAG_SAFE_TEMPERATURE (1<<6) +///Flag added when in an aquarium with the right fluid type. +#define FISH_FLAG_SAFE_FLUID (1<<7) #define MIN_AQUARIUM_TEMP T0C diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 26baf699409..b7919353f1a 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -241,8 +241,6 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list( #define isstructure(A) (istype(A, /obj/structure)) -#define isaquarium(A) (istype(A, /obj/structure/aquarium)) - #define ismachinery(A) (istype(A, /obj/machinery)) #define istramwall(A) (istype(A, /obj/structure/tram)) diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 64e5031cd66..94064352b84 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -731,6 +731,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Mobs won't slip on a wet turf while it has this trait #define TRAIT_TURF_IGNORE_SLIPPERY "turf_ignore_slippery" +///failsafe for whether an item with the beauty element is influencing the beauty of the area of not. +#define TRAIT_BEAUTY_APPLIED "beauty_applied" + /// Mobs with this trait can't send the mining shuttle console when used outside the station itself #define TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION "forbid_mining_shuttle_console_outside_station" @@ -799,8 +802,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_ROD_ATTRACT_SHINY_LOVERS "rod_attract_shiny_lovers" /// This rod can be used to fish on lava #define TRAIT_ROD_LAVA_USABLE "rod_lava_usable" -/// Stuff that can go inside fish cases -#define TRAIT_FISH_CASE_COMPATIBILE "fish_case_compatibile" +/// Stuff that can go inside fish cases and aquariums +#define TRAIT_AQUARIUM_CONTENT "aquarium_content" /// If the item can be used as a bit. #define TRAIT_FISHING_BAIT "fishing_bait" /// This bait will kill any fish that doesn't have it on its favorite_bait list @@ -818,6 +821,19 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai * instead, it'll allow the fishing dud to be there unless there's at least one fish that likes the bait */ #define TRAIT_BAIT_ALLOW_FISHING_DUD "bait_dont_affect_fishing_dud" +/** + * This location has the aquarium component. Not much different than a GetComponent() + * disguised as an 'is_x' macro, but I don't have to hide anything here. + * I just don't want a confusing 'is_aquarium(A)' macro which people think it's interchangable with + * an 'istype(A, /obj/structure/aquarium)' when it's the component what truly matters. + */ +#define TRAIT_IS_AQUARIUM "is_aquarium" +/// A location (probably aquarium) that amplifies the zaps of electricity-generating fish. +#define TRAIT_BIOELECTRIC_GENERATOR "bioelectric_generator" +/// A location (likely aquarium) that doesn't allow fish to growth and reproduce +#define TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH "stop_fish_reproduction_and_growth" +/// This is an aquarium with an open panel +#define TRAIT_AQUARIUM_PANEL_OPEN "aquarium_panel_open" /// Plants that were mutated as a result of passive instability, not a mutation threshold. #define TRAIT_PLANT_WILDMUTATE "wildmutation" /// If you hit an APC with exposed internals with this item it will try to shock you diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 12d1c2e3b90..4deb3925141 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -309,3 +309,6 @@ /// Trait from an engraving #define ENGRAVED_TRAIT "engraved" + +/// From the aquarium component +#define AQUARIUM_TRAIT "aquarium" diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 9772ebff8d1..2bd9309406a 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -81,7 +81,7 @@ #endif // REFERENCE_TRACKING_STANDARD // If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between -// #define UNIT_TESTS +//#define UNIT_TESTS // If this is uncommented, will attempt to load and initialize prof.dll/libprof.so by default. // Even if it's not defined, you can pass "tracy" via -params in order to try to load it. diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 0f5f47b242a..689750ff9ce 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -571,6 +571,9 @@ DEFINE_BITFIELD(fish_flags, list( "FISH_DO_FLOP_ANIM" = FISH_DO_FLOP_ANIM, "FISH_FLAG_PETTED" = FISH_FLAG_PETTED, "FISH_FLAG_EXPERIMENT_SCANNABLE" = FISH_FLAG_EXPERIMENT_SCANNABLE, + "FISH_FLAG_UPDATING_SIZE_AND_WEIGHT" = FISH_FLAG_OVERPOPULATED, + "FISH_FLAG_SAFE_TEMPERATURE" = FISH_FLAG_SAFE_TEMPERATURE, + "FISH_FLAG_SAFE_FLUID" = FISH_FLAG_SAFE_FLUID, )) DEFINE_BITFIELD(bot_mode_flags, list( diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 9f71718a5f2..cd68ebac4ac 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -9,6 +9,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( /atom = list( "TRAIT_AI_PAUSED" = TRAIT_AI_PAUSED, "TRAIT_BANNED_FROM_CARGO_SHUTTLE" = TRAIT_BANNED_FROM_CARGO_SHUTTLE, + "TRAIT_BEAUTY_APPLIED" = TRAIT_BEAUTY_APPLIED, "TRAIT_BEING_SHOCKED" = TRAIT_BEING_SHOCKED, "TRAIT_CATCH_AND_RELEASE" = TRAIT_CATCH_AND_RELEASE, "TRAIT_COMMISSIONED" = TRAIT_COMMISSIONED, @@ -35,8 +36,10 @@ GLOBAL_LIST_INIT(traits_by_type, list( ), /atom/movable = list( "TRAIT_ACTIVE_STORAGE" = TRAIT_ACTIVE_STORAGE, + "TRAIT_AQUARIUM_PANEL_OPEN" = TRAIT_AQUARIUM_PANEL_OPEN, "TRAIT_AREA_SENSITIVE" = TRAIT_AREA_SENSITIVE, "TRAIT_ASHSTORM_IMMUNE" = TRAIT_ASHSTORM_IMMUNE, + "TRAIT_BIOELECTRIC_GENERATOR" = TRAIT_BIOELECTRIC_GENERATOR, "TRAIT_BLOCKING_EXPLOSIVES" = TRAIT_BLOCKING_EXPLOSIVES, "TRAIT_BOULDER_BREAKER" = TRAIT_BOULDER_BREAKER, "TRAIT_CASTABLE_LOC" = TRAIT_CASTABLE_LOC, @@ -44,13 +47,13 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_COMBAT_MODE_SKIP_INTERACTION" = TRAIT_COMBAT_MODE_SKIP_INTERACTION, "TRAIT_DEL_ON_SPACE_DUMP" = TRAIT_DEL_ON_SPACE_DUMP, "TRAIT_VALID_DNA_INFUSION" = TRAIT_VALID_DNA_INFUSION, - "TRAIT_FISH_CASE_COMPATIBILE" = TRAIT_FISH_CASE_COMPATIBILE, "TRAIT_FROZEN" = TRAIT_FROZEN, "TRAIT_HAS_LABEL" = TRAIT_HAS_LABEL, "TRAIT_HEARING_SENSITIVE" = TRAIT_HEARING_SENSITIVE, "TRAIT_HYPERSPACED" = TRAIT_HYPERSPACED, "TRAIT_IMMERSED" = TRAIT_IMMERSED, "TRAIT_IRRADIATED" = TRAIT_IRRADIATED, + "TRAIT_IS_AQUARIUM" = TRAIT_IS_AQUARIUM, "TRAIT_LAVA_IMMUNE" = TRAIT_LAVA_IMMUNE, "TRAIT_MOVE_FLOATING" = TRAIT_MOVE_FLOATING, "TRAIT_MOVE_FLYING" = TRAIT_MOVE_FLYING, @@ -72,6 +75,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SECLUDED_LOCATION" = TRAIT_SECLUDED_LOCATION, "TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE, "TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC, + "TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH" = TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, "TRAIT_TELEKINESIS_CONTROLLED" = TRAIT_TELEKINESIS_CONTROLLED, "TRAIT_UNDERFLOOR" = TRAIT_UNDERFLOOR, "TRAIT_UNIQUE_IMMERSE" = TRAIT_UNIQUE_IMMERSE, @@ -576,6 +580,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( ), /obj/item = list( "TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING, + "TRAIT_AQUARIUM_CONTENT" = TRAIT_AQUARIUM_CONTENT, "TRAIT_BAIT_ALLOW_FISHING_DUD" = TRAIT_BAIT_ALLOW_FISHING_DUD, "TRAIT_BAIT_UNCONSUMABLE" = TRAIT_BAIT_UNCONSUMABLE, "TRAIT_BAKEABLE" = TRAIT_BAKEABLE, diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index f78e2af6be1..85def333f4c 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -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() /** diff --git a/code/datums/components/aquarium.dm b/code/datums/components/aquarium.dm new file mode 100644 index 00000000000..3a07c2f1481 --- /dev/null +++ b/code/datums/components/aquarium.dm @@ -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("Alt-click 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 diff --git a/code/datums/components/aquarium_content.dm b/code/datums/components/aquarium_content.dm index 1307cb45fbb..4e6e25c86bc 100644 --- a/code/datums/components/aquarium_content.dm +++ b/code/datums/components/aquarium_content.dm @@ -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 diff --git a/code/datums/components/blob_minion.dm b/code/datums/components/blob_minion.dm index 78bff449317..8366a65b511 100644 --- a/code/datums/components/blob_minion.dm +++ b/code/datums/components/blob_minion.dm @@ -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 diff --git a/code/datums/components/crafting/furniture.dm b/code/datums/components/crafting/furniture.dm index 39f5a25a19f..c3f6557f89a 100644 --- a/code/datums/components/crafting/furniture.dm +++ b/code/datums/components/crafting/furniture.dm @@ -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 diff --git a/code/datums/components/customizable_reagent_holder.dm b/code/datums/components/customizable_reagent_holder.dm index fd1b8a2ac64..fda64d6c256 100644 --- a/code/datums/components/customizable_reagent_holder.dm +++ b/code/datums/components/customizable_reagent_holder.dm @@ -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 diff --git a/code/datums/components/itempicky.dm b/code/datums/components/itempicky.dm index bda8b1ae138..f8e4e2256d8 100644 --- a/code/datums/components/itempicky.dm +++ b/code/datums/components/itempicky.dm @@ -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) diff --git a/code/datums/components/multiple_lives.dm b/code/datums/components/multiple_lives.dm index ce2c4a5053f..f374b3e3775 100644 --- a/code/datums/components/multiple_lives.dm +++ b/code/datums/components/multiple_lives.dm @@ -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 diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm index 71f391e599a..74a59301e73 100644 --- a/code/datums/components/orbiter.dm +++ b/code/datums/components/orbiter.dm @@ -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) diff --git a/code/datums/components/rotation.dm b/code/datums/components/rotation.dm index 40df294af12..16d7bddec87 100644 --- a/code/datums/components/rotation.dm +++ b/code/datums/components/rotation.dm @@ -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) diff --git a/code/datums/components/shy.dm b/code/datums/components/shy.dm index a9b50a07efb..a6303b87a03 100644 --- a/code/datums/components/shy.dm +++ b/code/datums/components/shy.dm @@ -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 diff --git a/code/datums/components/shy_in_room.dm b/code/datums/components/shy_in_room.dm index 4e0c2421561..d4eeeb108d9 100644 --- a/code/datums/components/shy_in_room.dm +++ b/code/datums/components/shy_in_room.dm @@ -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 diff --git a/code/datums/components/spill.dm b/code/datums/components/spill.dm index 9112706046c..6c84299f689 100644 --- a/code/datums/components/spill.dm +++ b/code/datums/components/spill.dm @@ -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 diff --git a/code/datums/components/technointrovert.dm b/code/datums/components/technointrovert.dm index c6f51f80416..0797d24cb48 100644 --- a/code/datums/components/technointrovert.dm +++ b/code/datums/components/technointrovert.dm @@ -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 diff --git a/code/datums/components/technoshy.dm b/code/datums/components/technoshy.dm index e8d4441a287..168f6d61ab5 100644 --- a/code/datums/components/technoshy.dm +++ b/code/datums/components/technoshy.dm @@ -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 diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm index d1f5b0fb1b8..f362359f3cd 100644 --- a/code/datums/components/wet_floor.dm +++ b/code/datums/components/wet_floor.dm @@ -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 diff --git a/code/datums/elements/beauty.dm b/code/datums/elements/beauty.dm index ddd3a8f5eab..8bfa298c1a7 100644 --- a/code/datums/elements/beauty.dm +++ b/code/datums/elements/beauty.dm @@ -20,57 +20,76 @@ src.beauty = beauty - if(!beauty_counter[target] && ismovable(target)) + var/area/current_area = get_area(target) + var/beauty_active = TRUE + if(ismovable(target)) var/atom/movable/mov_target = target - mov_target.become_area_sensitive(BEAUTY_ELEMENT_TRAIT) - RegisterSignal(mov_target, COMSIG_ENTER_AREA, PROC_REF(enter_area)) - RegisterSignal(mov_target, COMSIG_EXIT_AREA, PROC_REF(exit_area)) + var/is_item = isitem(mov_target) + beauty_active = !is_item || isturf(mov_target.loc) + if(!beauty_counter[target]) + if(is_item) + RegisterSignal(mov_target, COMSIG_MOVABLE_MOVED, PROC_REF(on_item_moved)) + if(beauty_active) + mov_target.become_area_sensitive(BEAUTY_ELEMENT_TRAIT) + RegisterSignal(mov_target, COMSIG_ENTER_AREA, PROC_REF(enter_area)) + RegisterSignal(mov_target, COMSIG_EXIT_AREA, PROC_REF(exit_area)) beauty_counter[target]++ - var/area/current_area = get_area(target) - if(current_area && !current_area.outdoors) + if(current_area && !current_area.outdoors && beauty_active) current_area.totalbeauty += beauty current_area.update_beauty() /datum/element/beauty/proc/enter_area(datum/source, area/new_area) SIGNAL_HANDLER - if(new_area.outdoors) + if(new_area.outdoors || HAS_TRAIT(source, TRAIT_BEAUTY_APPLIED)) return new_area.totalbeauty += beauty * beauty_counter[source] new_area.update_beauty() + ADD_TRAIT(source, TRAIT_BEAUTY_APPLIED, INNATE_TRAIT) /datum/element/beauty/proc/exit_area(datum/source, area/old_area) SIGNAL_HANDLER - if(old_area.outdoors) + if(old_area.outdoors || !HAS_TRAIT(source, TRAIT_BEAUTY_APPLIED)) return old_area.totalbeauty -= beauty * beauty_counter[source] old_area.update_beauty() + REMOVE_TRAIT(source, TRAIT_BEAUTY_APPLIED, INNATE_TRAIT) -/datum/element/beauty/Detach(datum/source) +///Items only contribute to beauty while not inside other objects or mobs (e.g on the floor, on a table etc.). +/datum/element/beauty/proc/on_item_moved(obj/item/source, atom/old_loc, direction, forced) + SIGNAL_HANDLER + + var/is_old_turf = isturf(old_loc) + if(!is_old_turf && isturf(source.loc)) + source.become_area_sensitive(BEAUTY_ELEMENT_TRAIT) + RegisterSignal(source, COMSIG_ENTER_AREA, PROC_REF(enter_area), TRUE) + RegisterSignal(source, COMSIG_EXIT_AREA, PROC_REF(exit_area), TRUE) + enter_area(source, get_area(source.loc)) + else if(is_old_turf && !isturf(source.loc)) + source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT) + UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA)) + exit_area(source, get_area(old_loc)) + +/datum/element/beauty/Detach(atom/source) if(!beauty_counter[source]) return ..() - var/area/current_area = get_area(source) - if(QDELETED(source)) - . = ..() - UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA)) - if(current_area) - exit_area(source, current_area) - beauty_counter -= source - var/atom/movable/movable_source = source - if(istype(movable_source)) - movable_source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT) - else //lower the 'counter' down by one, update the area, and call parent if it's reached zero. + + var/area/current_area = (!isitem(source) || isturf(source.loc)) ? get_area(source) : null + if(!QDELETED(source))//lower the 'counter' down by one, update the area, and call parent if it's reached zero. beauty_counter[source]-- if(current_area && !current_area.outdoors) current_area.totalbeauty -= beauty current_area.update_beauty() - if(!beauty_counter[source]) - . = ..() - UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA)) - beauty_counter -= source - var/atom/movable/movable_source = source - if(istype(movable_source)) - movable_source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT) + if(beauty_counter[source]) + return + else if(current_area) + exit_area(source, current_area) + + UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA, COMSIG_MOVABLE_MOVED)) + beauty_counter -= source + var/atom/movable/movable_source = source + if(istype(movable_source)) + movable_source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT) diff --git a/code/datums/storage/subtypes/backpack.dm b/code/datums/storage/subtypes/backpack.dm index a6b3e1477dd..a2a1a13fcd4 100644 --- a/code/datums/storage/subtypes/backpack.dm +++ b/code/datums/storage/subtypes/backpack.dm @@ -2,6 +2,16 @@ max_total_storage = 21 max_slots = 21 +/datum/storage/backpack/New( + atom/parent, + max_slots, + max_specific_storage, + max_total_storage, +) + . = ..() + var/static/list/exception_cache = typecacheof(list(/obj/item/fish_tank)) + exception_hold = exception_cache + /datum/storage/backpack/santabag max_total_storage = 60 max_slots = 21 diff --git a/code/datums/storage/subtypes/duffel_bag.dm b/code/datums/storage/subtypes/duffel_bag.dm index cfa073da5e5..bb3a236d38b 100644 --- a/code/datums/storage/subtypes/duffel_bag.dm +++ b/code/datums/storage/subtypes/duffel_bag.dm @@ -2,6 +2,16 @@ max_total_storage = 30 max_slots = 21 +/datum/storage/duffel/New( + atom/parent, + max_slots, + max_specific_storage, + max_total_storage, +) + . = ..() + var/static/list/exception_cache = typecacheof(list(/obj/item/fish_tank)) + exception_hold = exception_cache + // Syndi bags get some FUN extras // You can fit any 2 bulky objects (assuming they're in the whitelist) // Should have traitorus stuff in here, not just useful big things @@ -60,6 +70,9 @@ /obj/item/storage/bag/money, // Heads! /obj/item/bodypart/head, + // Fish + /obj/item/fish, + /obj/item/fish_tank, ) // We keep the type list and the typecache list separate... diff --git a/code/datums/storage/subtypes/fish_case.dm b/code/datums/storage/subtypes/fish_case.dm index 47103e931b1..e38d55e0e1d 100644 --- a/code/datums/storage/subtypes/fish_case.dm +++ b/code/datums/storage/subtypes/fish_case.dm @@ -7,7 +7,7 @@ . = ..() if(!.) return . - if(!HAS_TRAIT(to_insert, TRAIT_FISH_CASE_COMPATIBILE)) + if(!HAS_TRAIT(to_insert, TRAIT_AQUARIUM_CONTENT)) if(messages && user) user.balloon_alert(user, "can't hold!") return FALSE diff --git a/code/game/objects/items/food/lizard.dm b/code/game/objects/items/food/lizard.dm index 74534592a5f..f8d8dbec8f5 100644 --- a/code/game/objects/items/food/lizard.dm +++ b/code/game/objects/items/food/lizard.dm @@ -142,8 +142,9 @@ . = ..() //Moonfish can lay eggs (unaffected by breeding, so think of them as unfertilizard) RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, PROC_REF(generate_aquarium_appearance)) - RegisterSignal(src, AQUARIUM_CONTENT_RANDOMIZE_POSITION, PROC_REF(randomize_aquarium_position)) - AddComponent(/datum/component/aquarium_content, beauty = 100) + RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_RANDOMIZE_POSITION, PROC_REF(randomize_aquarium_position)) + AddComponent(/datum/component/aquarium_content) + RegisterSignal(src, COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY, PROC_REF(get_aquarium_beauty)) /obj/item/food/moonfish_eggs/proc/generate_aquarium_appearance(datum/source, obj/effect/aquarium/visual) SIGNAL_HANDLER @@ -153,13 +154,18 @@ /obj/item/food/moonfish_eggs/proc/randomize_aquarium_position(datum/source, obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual) SIGNAL_HANDLER - var/list/aq_properties = current_aquarium.get_surface_properties() var/sprite_width = 5 - var/px_min = aq_properties[AQUARIUM_PROPERTIES_PX_MIN] - var/px_max = aq_properties[AQUARIUM_PROPERTIES_PX_MAX] - sprite_width + var/sprite_height = 4 + var/px_min = visual.aquarium_zone_min_px + var/px_max = visual.aquarium_zone_max_px - sprite_width + var/py_min = visual.aquarium_zone_min_py - sprite_height - visual.pixel_x = rand(px_min,px_max) - visual.pixel_y = rand(-1, 1) + visual.pixel_x = rand(px_min, px_max) + visual.pixel_y = py_min + rand(-1, 1) + +/obj/item/food/moonfish_eggs/proc/get_aquarium_beauty(datum/source, list/beauty_holder) + SIGNAL_HANDLER + beauty_holder += 100 //moonfish eggs are kinda eye candy /obj/item/food/moonfish_caviar name = "moonfish caviar paste" diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm index 7301f1a296e..18a54aebd2e 100644 --- a/code/modules/fishing/aquarium/aquarium.dm +++ b/code/modules/fishing/aquarium/aquarium.dm @@ -1,14 +1,3 @@ -#define AQUARIUM_LAYER_STEP 0.01 -/// Aquarium content layer offsets -#define AQUARIUM_MIN_OFFSET 0.02 -#define AQUARIUM_MAX_OFFSET 1 -/// The layer of the glass overlay -#define AQUARIUM_GLASS_LAYER 0.01 -/// The layer of the aquarium pane borders -#define AQUARIUM_BORDERS_LAYER AQUARIUM_MAX_OFFSET + AQUARIUM_LAYER_STEP -/// Layer for stuff rendered below the glass overlay -#define AQUARIUM_BELOW_GLASS_LAYER 0.01 - /obj/structure/aquarium name = "aquarium" desc = "A vivarium in which aquatic fauna and flora are usually kept and displayed." @@ -17,351 +6,75 @@ icon = 'icons/obj/aquarium/tanks.dmi' icon_state = "aquarium_map" + base_icon_state = "aquarium" integrity_failure = 0.3 - /// The icon state is used for mapping so mappers know what they're placing. This prefixes the real icon used in game. - /// For an example, "aquarium" gives the base sprite of "aquarium_base", the glass is "aquarium_glass_water", and so on. - var/icon_prefix = "aquarium" - - var/fluid_type = AQUARIUM_FLUID_FRESHWATER - var/fluid_temp = DEFAULT_AQUARIUM_TEMP - var/min_fluid_temp = MIN_AQUARIUM_TEMP - var/max_fluid_temp = MAX_AQUARIUM_TEMP - - ///While the feed 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 - - /// Can fish reproduce in this quarium. - var/reproduction_and_growth = TRUE - //This is the area where fish can swim var/aquarium_zone_min_px = 2 var/aquarium_zone_max_px = 31 var/aquarium_zone_min_py = 10 var/aquarium_zone_max_py = 28 - var/list/fluid_types = list(AQUARIUM_FLUID_SALTWATER, AQUARIUM_FLUID_FRESHWATER, AQUARIUM_FLUID_SULPHWATEVER, AQUARIUM_FLUID_AIR) + /// Default beauty of the aquarium, without anything inside it + var/default_beauty = 150 - var/panel_open = FALSE - - ///Current layers in use by aquarium contents - var/list/used_layers = list() - - /// /obj/item/fish in the aquarium, sorted by type - does not include things with aquarium visuals that are not fish - var/list/tracked_fish_by_type - - /// Var used to keep track of the current beauty of the aquarium, which can be throughfully changed by aquarium content. - var/current_beauty = 150 + ///Tracks the fluid type of our aquarium component. Used for the icon suffix of some overlays and splashing water when broken. + var/fluid_type = AQUARIUM_FLUID_FRESHWATER /obj/structure/aquarium/Initialize(mapload) . = ..() - update_appearance() - RegisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(track_if_fish)) - AddElement(/datum/element/relay_attackers) - RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked)) - create_reagents(6, SEALED_CONTAINER) - RegisterSignal(reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed)) + AddComponent(/datum/component/aquarium, aquarium_zone_min_px, aquarium_zone_max_px, aquarium_zone_min_py, aquarium_zone_max_py, default_beauty) AddComponent(/datum/component/plumbing/aquarium, start = anchored) - if(current_beauty) - AddElement(/datum/element/beauty, current_beauty) - ADD_KEEP_TOGETHER(src, INNATE_TRAIT) - -/obj/structure/aquarium/proc/track_if_fish(atom/source, atom/initialized) - SIGNAL_HANDLER - if(isfish(initialized)) - LAZYADDASSOCLIST(tracked_fish_by_type, initialized.type, initialized) - -/obj/structure/aquarium/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) - . = ..() - if(isfish(arrived)) - LAZYADDASSOCLIST(tracked_fish_by_type, arrived.type, arrived) - -/obj/structure/aquarium/Exited(atom/movable/gone, direction) - . = ..() - LAZYREMOVEASSOC(tracked_fish_by_type, gone.type, gone) - -/obj/structure/aquarium/proc/start_autofeed(datum/source, new_reagent, amount, reagtemp, data, no_react) - SIGNAL_HANDLER - START_PROCESSING(SSobj, src) - UnregisterSignal(reagents, COMSIG_REAGENTS_NEW_REAGENT) - -/obj/structure/aquarium/process(seconds_per_tick) - if(!reagents.total_volume) - RegisterSignal(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(reagents) - -/// Returns tracked_fish_by_type but flattened and without the items in the blacklist, also shuffled if shuffle is TRUE. -/obj/structure/aquarium/proc/get_fishes(shuffle = FALSE, blacklist) - . = list() - for(var/fish_type in tracked_fish_by_type) - . += tracked_fish_by_type[fish_type] - . -= blacklist - if(shuffle) - . = shuffle(.) - return . - -/obj/structure/aquarium/proc/request_layer(layer_type) - /** - * base aq layer - * min_offset = this value is returned on bottom layer mode - * min_offset + 0.1 fish1 - * min_offset + 0.2 fish2 - * ... these layers are returned for auto layer mode and tracked by used_layers - * min_offset + max_offset = this value is returned for top layer mode - * min_offset + max_offset + 1 = this is used for glass overlay - */ - //optional todo: hook up sending surface changed on aquarium changing layers - switch(layer_type) - if(AQUARIUM_LAYER_MODE_BEHIND_GLASS) - return layer + AQUARIUM_BELOW_GLASS_LAYER - if(AQUARIUM_LAYER_MODE_BOTTOM) - return layer + AQUARIUM_MIN_OFFSET - if(AQUARIUM_LAYER_MODE_TOP) - return 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 layer + chosen_layer - -/obj/structure/aquarium/proc/free_layer(value) - used_layers -= value - -/obj/structure/aquarium/proc/get_surface_properties() - . = list() - .[AQUARIUM_PROPERTIES_PX_MIN] = aquarium_zone_min_px - .[AQUARIUM_PROPERTIES_PX_MAX] = aquarium_zone_max_px - .[AQUARIUM_PROPERTIES_PY_MIN] = aquarium_zone_min_py - .[AQUARIUM_PROPERTIES_PY_MAX] = aquarium_zone_max_py + RegisterSignal(src, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_aquarium_liquid_changed)) + update_appearance() /obj/structure/aquarium/update_icon() . = ..() ///"aquarium_map" is used for mapping, so mappers can tell what it's. - icon_state = icon_prefix + "_base" + icon_state = base_icon_state + "_base" + +/obj/structure/aquarium/proc/on_aquarium_liquid_changed(datum/source, fluid_type) + SIGNAL_HANDLER + src.fluid_type = fluid_type + update_appearance() /obj/structure/aquarium/update_overlays() . = ..() - if(panel_open) - . += icon_prefix + "_panel" + if(HAS_TRAIT(src, TRAIT_AQUARIUM_PANEL_OPEN)) + . += base_icon_state + "_panel" + var/icon_suffix = fluid_type == AQUARIUM_FLUID_AIR ? "air" : "water" ///The glass overlay - var/suffix = fluid_type == AQUARIUM_FLUID_AIR ? "air" : "water" if(broken) - suffix += "_broken" - . += mutable_appearance(icon, icon_prefix + "_glass_cracks", layer = layer + AQUARIUM_BORDERS_LAYER) - . += mutable_appearance(icon, icon_prefix + "_glass_[suffix]", layer = layer + AQUARIUM_GLASS_LAYER) - . += mutable_appearance(icon, icon_prefix + "_borders", layer = layer + AQUARIUM_BORDERS_LAYER) - -/obj/structure/aquarium/examine(mob/user) - . = ..() - . += span_notice("Alt-click to [panel_open ? "close" : "open"] the control and feed panel.") - if(panel_open && reagents.total_volume) - . += span_notice("You can use a plunger to empty the feed storage.") - -/obj/structure/aquarium/click_alt(mob/living/user) - panel_open = !panel_open - balloon_alert(user, "panel [panel_open ? "open" : "closed"]") - if(panel_open) - reagents.flags |= TRANSPARENT|REFILLABLE - else - reagents.flags &= ~(TRANSPARENT|REFILLABLE) - update_appearance() - return CLICK_ACTION_SUCCESS + icon_suffix += "_broken" + . += mutable_appearance(icon, base_icon_state + "_glass_cracks", layer = layer + AQUARIUM_BORDERS_LAYER) + . += mutable_appearance(icon, base_icon_state + "_glass_[icon_suffix]", layer = layer + AQUARIUM_GLASS_LAYER) + . += mutable_appearance(icon, base_icon_state + "_borders", layer = layer + AQUARIUM_BORDERS_LAYER) /obj/structure/aquarium/wrench_act(mob/living/user, obj/item/tool) . = ..() default_unfasten_wrench(user, tool) return ITEM_INTERACT_SUCCESS -/obj/structure/aquarium/plunger_act(obj/item/plunger/P, mob/living/user, reinforced) - if(!panel_open) +/obj/structure/aquarium/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!istype(tool, /obj/item/stack/sheet/glass)) return - user.balloon_alert_to_viewers("plunging...") - if(do_after(user, 3 SECONDS, target = src)) - user.balloon_alert_to_viewers("finished plunging") - reagents.expose(get_turf(src), TOUCH) //splash on the floor - reagents.clear_reagents() - -/obj/structure/aquarium/attackby(obj/item/item, mob/living/user, params) - if(broken) - var/obj/item/stack/sheet/glass/glass = item - if(istype(glass)) - if(glass.get_amount() < 2) - balloon_alert(user, "it needs two sheets!") - return - balloon_alert(user, "fixing the aquarium...") - if(do_after(user, 2 SECONDS, target = src)) - glass.use(2) - broken = FALSE - atom_integrity = max_integrity - update_appearance() - return TRUE - else - var/insert_attempt = SEND_SIGNAL(item, COMSIG_TRY_INSERTING_IN_AQUARIUM, src) - switch(insert_attempt) - if(COMSIG_CAN_INSERT_IN_AQUARIUM) - if(!user.transferItemToLoc(item, src)) - user.balloon_alert(user, "stuck to your hand!") - return TRUE - balloon_alert(user, "added to aquarium") - update_appearance() - return TRUE - if(COMSIG_CANNOT_INSERT_IN_AQUARIUM) - balloon_alert(user, "cannot add to aquarium!") - return TRUE - - if(istype(item, /obj/item/reagent_containers/cup/fish_feed) && !panel_open) - if(!item.reagents.total_volume) - balloon_alert(user, "[item] is empty!") - return TRUE - var/list/fishes = get_fishes() - if(!length(fishes)) - balloon_alert(user, "no fish to feed!") - return TRUE - for(var/obj/item/fish/fish as anything in fishes) - fish.feed(item.reagents) - balloon_alert(user, "fed the fish") - return TRUE - if(istype(item, /obj/item/aquarium_upgrade)) - var/obj/item/aquarium_upgrade/upgrade = item - if(upgrade.upgrade_from_type != type) - balloon_alert(user, "wrong kind of aquarium!") - return - balloon_alert(user, "upgrading...") - if(!do_after(user, 5 SECONDS, src)) - return - var/obj/structure/aquarium/upgraded_aquarium = new upgrade.upgrade_to_type(loc) - for(var/atom/movable/moving in contents) - moving.forceMove(upgraded_aquarium) - balloon_alert(user, "upgraded") - qdel(upgrade) - qdel(src) - return - return ..() - -/obj/structure/aquarium/proc/on_attacked(datum/source, mob/attacker, attack_flags) - var/list/fishes = get_fishes() - //I wish this were an aquarium signal, but the aquarium_content component got in the way. - for(var/obj/item/fish/fish as anything in fishes) - SEND_SIGNAL(fish, COMSIG_FISH_STIRRED) - -/obj/structure/aquarium/interact(mob/user) - if(panel_open) - return ..() //call base ui_interact - else - admire(user) - -///Apply mood bonus depending on aquarium status -/obj/structure/aquarium/proc/admire(mob/living/user) - user.balloon_alert(user, "admiring aquarium...") - if(!do_after(user, 5 SECONDS, target = src)) - return - var/alive_fish = 0 - var/dead_fish = 0 - var/list/tracked_fish = get_fishes() - for(var/obj/item/fish/fish in tracked_fish) - 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) - // Could maybe scale power of this mood with number/types of fish - -/obj/structure/aquarium/ui_data(mob/user) - . = ..() - .["fluidType"] = fluid_type - .["temperature"] = fluid_temp - .["allowBreeding"] = reproduction_and_growth - .["fishData"] = list() - .["feedingInterval"] = feeding_interval / (1 MINUTES) - .["propData"] = list() - for(var/atom/movable/item in 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, - )) - -/obj/structure/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' - -/obj/structure/aquarium/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) - . = ..() - if(.) - return - var/mob/user = usr - switch(action) - if("temperature") - var/temperature = params["temperature"] - if(isnum(temperature)) - fluid_temp = clamp(temperature, min_fluid_temp, max_fluid_temp) - . = TRUE - if("fluid") - if(params["fluid"] in fluid_types) - fluid_type = params["fluid"] - SEND_SIGNAL(src, COMSIG_AQUARIUM_FLUID_CHANGED, fluid_type) - . = TRUE - if("allow_breeding") - reproduction_and_growth = !reproduction_and_growth - . = TRUE - if("feeding_interval") - feeding_interval = params["feeding_interval"] MINUTES - . = TRUE - if("pet_fish") - var/obj/item/fish/fish = locate(params["fish_reference"]) in contents - fish?.pet_fish(user) - if("remove_item") - var/atom/movable/item = locate(params["item_reference"]) in contents - item?.forceMove(drop_location()) - to_chat(user, span_notice("You take out [item] from [src].")) - if("rename_fish") - var/new_name = sanitize_name(params["chosen_name"]) - var/atom/movable/fish = locate(params["fish_reference"]) in contents - if(!fish || !new_name || new_name == fish.name) - return - fish.AddComponent(/datum/component/rename, new_name, fish.desc) - -/obj/structure/aquarium/ui_interact(mob/user, datum/tgui/ui) - . = ..() - ui = SStgui.try_update_ui(user, src, ui) - if(!ui) - ui = new(user, src, "Aquarium", name) - ui.open() + if(!broken) + balloon_alert(user, "aquarium not broken!") + return ITEM_INTERACT_BLOCKING + var/obj/item/stack/sheet/glass/glass = tool + if(glass.get_amount() < 2) + balloon_alert(user, "it needs two sheets!") + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "fixing the aquarium...") + if(!do_after(user, 2 SECONDS, target = src)) + return ITEM_INTERACT_BLOCKING + glass.use(2) + broken = FALSE + atom_integrity = max_integrity + update_appearance() + return ITEM_INTERACT_SUCCESS /obj/structure/aquarium/atom_break(damage_flag) . = ..() @@ -377,38 +90,14 @@ else possible_destinations_for_fish = list(droploc) playsound(src, 'sound/effects/glass/glassbr3.ogg', 100, TRUE) - for(var/atom/movable/fish in contents) - fish.forceMove(pick(possible_destinations_for_fish)) + for(var/atom/movable/content as anything in contents) + content.forceMove(pick(possible_destinations_for_fish)) if(fluid_type != AQUARIUM_FLUID_AIR) var/datum/reagents/reagent_splash = new() reagent_splash.add_reagent(/datum/reagent/water, 30) chem_splash(droploc, null, 3, list(reagent_splash)) update_appearance() -#undef AQUARIUM_LAYER_STEP -#undef AQUARIUM_MIN_OFFSET -#undef AQUARIUM_MAX_OFFSET -#undef AQUARIUM_GLASS_LAYER -#undef AQUARIUM_BORDERS_LAYER -#undef AQUARIUM_BELOW_GLASS_LAYER - -/obj/structure/aquarium/lawyer - anchored = TRUE - -/obj/structure/aquarium/lawyer/Initialize(mapload) - . = ..() - - new /obj/item/aquarium_prop/sand(src) - new /obj/item/aquarium_prop/seaweed(src) - - if(prob(85)) - new /obj/item/fish/goldfish/gill(src) - reagents.add_reagent(/datum/reagent/consumable/nutriment, 2) - else - new /obj/item/fish/goldfish/three_eyes/gill(src) - reagents.add_reagent(/datum/reagent/toxin/mutagen, 2) //three eyes goldfish feed on mutagen. - - /obj/structure/aquarium/prefilled anchored = TRUE @@ -424,3 +113,145 @@ //They'll be alive for about 30 minutes with this amount. reagents.add_reagent(/datum/reagent/consumable/nutriment, 3) + +/obj/item/fish_tank + name = "fish tank" + desc = "A more portable sort of aquarium to store various fishes in, unless they're too big or there're too many of them." + icon = 'icons/obj/aquarium/tanks.dmi' + icon_state = "fish_tank_map" + base_icon_state = "fish_tank" + force = 5 + throwforce = 5 + throw_range = 3 + w_class = WEIGHT_CLASS_BULKY + item_flags = SLOWS_WHILE_IN_HAND + + custom_price = PAYCHECK_CREW * 9 + + ///Tracks the fluid type of our aquarium component. Used for overlays + var/fluid_type = AQUARIUM_FLUID_FRESHWATER + + ///Fish which size exceed this value cannot be inserted + var/maximum_relative_size = 100 + ///Fish cannot be inserted if the sum of the size of all fish in this tank exceeds this value. + var/max_total_size = 220 + ///Tracks the sum of the size of all fish in this tank + var/current_summed_size = 0 + ///Tracks the sum of the weight of all fish in this tank + var/current_summed_weight = 0 + + var/slowdown_coeff = 1 + + ///The minimum fluid temperature of this fish tank + var/min_fluid_temp = MIN_AQUARIUM_TEMP + 12 + ///The maximum fluid temperature of this fish tank + var/max_fluid_temp = MAX_AQUARIUM_TEMP - 32 + ///The reagent capacity of this fish tank + var/reagent_size = 4 + +/obj/item/fish_tank/Initialize(mapload) + . = ..() + update_appearance() + AddComponent(\ + /datum/component/aquarium,\ + min_px = 6,\ + max_px = 26,\ + min_py = 6,\ + min_py = 24,\ + default_beauty = 100,\ + reagents_size = src.reagent_size,\ + min_fluid_temp = src.min_fluid_temp,\ + max_fluid_temp = src.max_fluid_temp,\ + ) + AddComponent(/datum/component/plumbing/aquarium, start = anchored) + RegisterSignal(src, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_aquarium_liquid_changed)) + RegisterSignal(src, COMSIG_AQUARIUM_CAN_INSERT, PROC_REF(can_insert)) + RegisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(on_new_fish)) + +/obj/item/fish_tank/update_icon() + . = ..() + ///"aquarium_map" is used for mapping, so mappers can tell what it's. + icon_state = base_icon_state + +/obj/item/fish_tank/proc/on_aquarium_liquid_changed(datum/source, fluid_type) + SIGNAL_HANDLER + src.fluid_type = fluid_type + update_appearance() + +/obj/item/fish_tank/update_overlays() + . = ..() + . += "[base_icon_state]_panel[HAS_TRAIT(src, TRAIT_AQUARIUM_PANEL_OPEN) ? "_open" : ""]" + . += mutable_appearance(icon, "[base_icon_state]_[fluid_type == AQUARIUM_FLUID_AIR ? "air" : "water"]", layer = layer + AQUARIUM_GLASS_LAYER) + . += mutable_appearance(icon, "[base_icon_state]_borders", layer = layer + AQUARIUM_BORDERS_LAYER) + +/obj/item/fish_tank/proc/can_insert(atom/movable/source, obj/item/item, mob/living/user) + SIGNAL_HANDLER + if(!isfish(item)) + return + var/obj/item/fish/fish = item + if(fish.size > maximum_relative_size) + balloon_alert(user, "fish is too big!") + return COMSIG_CANNOT_INSERT_IN_AQUARIUM + if(current_summed_size > max_total_size) + balloon_alert(user, "fish tank is full!") + return COMSIG_CANNOT_INSERT_IN_AQUARIUM + return COMSIG_CAN_INSERT_IN_AQUARIUM + +/obj/item/fish_tank/Entered(atom/movable/entered) + . = ..() + on_new_fish(src, entered) + +/obj/item/fish_tank/proc/on_new_fish(datum/source, atom/movable/movable) + SIGNAL_HANDLER + if(!isfish(movable)) + return + var/obj/item/fish/fish = movable + change_size_weight(fish.size, fish.weight) + RegisterSignal(fish, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, PROC_REF(on_fish_size_weight_updated)) + +/obj/item/fish_tank/proc/on_fish_size_weight_updated(obj/item/fish/source, new_size, new_weight) + SIGNAL_HANDLER + change_size_weight(new_size - source.size, new_weight - source.weight) + +/obj/item/fish_tank/Exited(atom/movable/gone) + if(isfish(gone)) + var/obj/item/fish/fish = gone + change_size_weight(-fish.size, -fish.weight) + UnregisterSignal(fish, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT) + return ..() + +/obj/item/fish_tank/proc/change_size_weight(size_change, weight_change) + current_summed_size += size_change + current_summed_weight += weight_change + if(current_summed_size > max_total_size) + ADD_TRAIT(src, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, INNATE_TRAIT) + else + REMOVE_TRAIT(src, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, INNATE_TRAIT) + if(HAS_TRAIT(src, TRAIT_SPEED_POTIONED) || current_summed_weight < FISH_WEIGHT_SLOWDOWN) + slowdown = 0 + drag_slowdown = 0 + else + slowdown = GET_FISH_SLOWDOWN(current_summed_weight) * slowdown_coeff + drag_slowdown = slowdown * 0.5 + if(ismob(loc)) + var/mob/mob = loc + mob.update_equipment_speed_mods() + + force = min(2 + (GET_FISH_WEIGHT_RANK(current_summed_weight) * 3), 21) + throwforce = force + +///The lawyer's own pet goldfish's fish tank. It used to be an aquarium, but now it can be held and carried around. +/obj/item/fish_tank/lawyer + +/obj/item/fish_tank/lawyer/Initialize(mapload) + . = ..() + + new /obj/item/aquarium_prop/sand(src) + new /obj/item/aquarium_prop/seaweed(src) + + if(prob(85)) + new /obj/item/fish/goldfish/gill(src) + reagents.add_reagent(/datum/reagent/consumable/nutriment, 3) + else + new /obj/item/fish/goldfish/three_eyes/gill(src) + reagents.add_reagent(/datum/reagent/toxin/mutagen, 3) //three eyes goldfish feed on mutagen. diff --git a/code/modules/fishing/aquarium/aquarium_kit.dm b/code/modules/fishing/aquarium/aquarium_kit.dm index 17dd518710d..f18f67b9313 100644 --- a/code/modules/fishing/aquarium/aquarium_kit.dm +++ b/code/modules/fishing/aquarium/aquarium_kit.dm @@ -117,20 +117,20 @@ /obj/item/aquarium_kit name = "DIY Aquarium Construction Kit" - desc = "Everything you need to build your own aquarium. Raw materials sold separately." + desc = "Everything you need to build your own aquarium or fish tank. Raw materials sold separately." icon = 'icons/obj/aquarium/supplies.dmi' icon_state = "construction_kit" w_class = WEIGHT_CLASS_TINY /obj/item/aquarium_kit/Initialize(mapload) . = ..() - var/static/list/recipes = list(/datum/crafting_recipe/aquarium) + var/static/list/recipes = list(/datum/crafting_recipe/aquarium, /datum/crafting_recipe/fish_tank) AddElement(/datum/element/slapcrafting, recipes) /obj/item/aquarium_prop name = "generic aquarium prop" desc = "very boring" - icon = 'icons/obj/aquarium/supplies.dmi' + icon = 'icons/obj/aquarium/tanks.dmi' w_class = WEIGHT_CLASS_TINY custom_materials = list(/datum/material/plastic = COIN_MATERIAL_AMOUNT) @@ -141,15 +141,21 @@ . = ..() //It's important that we register the signals before the component is attached. RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, PROC_REF(generate_aquarium_appearance)) - AddComponent(/datum/component/aquarium_content, beauty = beauty) + AddComponent(/datum/component/aquarium_content) ADD_TRAIT(src, TRAIT_UNIQUE_AQUARIUM_CONTENT, INNATE_TRAIT) -/obj/item/aquarium_prop/proc/generate_aquarium_appearance(datum/source, obj/effect/aquarium/visual) + RegisterSignal(src, COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY, PROC_REF(get_aquarium_beauty)) + +/obj/item/aquarium_prop/proc/generate_aquarium_appearance(datum/source, obj/effect/aquarium/visual, atom/movable/aquarium) SIGNAL_HANDLER - visual.icon = icon - visual.icon_state = icon_state + visual.icon = aquarium.icon + visual.icon_state = "[icon_state][isitem(aquarium) ? "_fish_tank" : ""]" visual.layer_mode = layer_mode +/obj/item/aquarium_prop/proc/get_aquarium_beauty(datum/source, list/beauty_holder) + SIGNAL_HANDLER + beauty_holder += beauty + /obj/item/aquarium_prop/rocks name = "decorative rocks" desc = "A bunch of tiny plastic rocks for decorating an aquarium. Surely you could have just used real pebbles?" diff --git a/code/modules/fishing/aquarium/aquarium_upgrades.dm b/code/modules/fishing/aquarium/aquarium_upgrades.dm index c73e6e9d230..042074500af 100644 --- a/code/modules/fishing/aquarium/aquarium_upgrades.dm +++ b/code/modules/fishing/aquarium/aquarium_upgrades.dm @@ -11,8 +11,25 @@ /// typepath of the new aquarium subtype created. var/upgrade_to_type = /obj/structure/aquarium +/obj/item/aquarium_upgrade/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!HAS_TRAIT(interacting_with, TRAIT_IS_AQUARIUM)) + return NONE + if(upgrade_from_type != interacting_with.type) + interacting_with.balloon_alert(user, "wrong kind of aquarium!") + return ITEM_INTERACT_BLOCKING + interacting_with.balloon_alert(user, "upgrading...") + if(!PERFORM_ALL_TESTS(aquarium_upgrade) && !do_after(user, 5 SECONDS, interacting_with)) + return ITEM_INTERACT_BLOCKING + var/atom/movable/upgraded_aquarium = new upgrade_to_type(interacting_with.drop_location()) + //This should transfer all the fish, reagents and settings from the aquarium component + interacting_with.TransferComponents(upgraded_aquarium) + upgraded_aquarium.balloon_alert(user, "upgraded") + qdel(src) + qdel(interacting_with) + return ITEM_INTERACT_SUCCESS + /obj/item/aquarium_upgrade/bioelec_gen - name = "Aquarium Bioelectricity Kit" + name = "aquarium bioelectricity kit" desc = "All the required components to allow an aquarium to harness energy bioelectric fish." icon_state = "bioelec_kit" upgrade_to_type = /obj/structure/aquarium/bioelec_gen @@ -22,9 +39,13 @@ desc = "An unconventional type of generator that boosts and harvests the energy produced by bioelectric fish." icon_state = "bioelec_map" - icon_prefix = "bioelec" + base_icon_state = "bioelec" - current_beauty = 0 + default_beauty = 0 + +/obj/structure/aquarium/bioelec_gen/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_BIOELECTRIC_GENERATOR, INNATE_TRAIT) /obj/structure/aquarium/bioelec_gen/zap_act(power, zap_flags) var/explosive = zap_flags & ZAP_MACHINE_EXPLOSIVE @@ -37,3 +58,23 @@ . += span_boldwarning("WARNING! WARNING! WARNING!") . += span_warning("The bioelectric potential of the fish inside is magnified to dangerous levels by the generator.") . += span_notice("Tesla coils are required to collect this magnified energy... and you'll want a grounding rod to protect yourself as well.") + +/obj/item/aquarium_upgrade/bluespace_tank + name = "bluespace fish tank kit" + desc = "The required components to upgrade your portable fish tank into bottomless, handheld aquarium." + icon_state = "bluespace_kit" + upgrade_from_type = /obj/item/fish_tank + upgrade_to_type = /obj/item/fish_tank/bluespace + +/obj/item/fish_tank/bluespace + name = "bluespace fish tank" + desc = "All the capacity of a bulky room aquarium, squeezed in a bag-sized rectangular cuboid." + icon_state = "fish_tank_bluespace_map" + base_icon_state = "fish_tank_bluespace" + w_class = WEIGHT_CLASS_NORMAL + maximum_relative_size = INFINITY + max_total_size = 2000 + slowdown_coeff = 0.15 + min_fluid_temp = MIN_AQUARIUM_TEMP + max_fluid_temp = MAX_AQUARIUM_TEMP + reagent_size = 6 diff --git a/code/modules/fishing/aquarium/fish_analyzer.dm b/code/modules/fishing/aquarium/fish_analyzer.dm index 617d8b600ba..3df28e326ac 100644 --- a/code/modules/fishing/aquarium/fish_analyzer.dm +++ b/code/modules/fishing/aquarium/fish_analyzer.dm @@ -59,18 +59,15 @@ . += emissive_appearance(icon, "fish_analyzer_emissive", src) /obj/item/fish_analyzer/interact_with_atom(atom/target, mob/living/user, list/modifiers) - if(!isfish(target) && !isaquarium(target)) + if(!isfish(target) && !HAS_TRAIT(target, TRAIT_IS_AQUARIUM)) return NONE if(!user.can_read(src) || user.is_blind()) return ITEM_INTERACT_BLOCKING - if(isfish(target) || istype(target, /obj/structure/aquarium)) - scanned_item = WEAKREF(target) - SEND_SIGNAL(src, COMSIG_FISH_ANALYZER_ANALYZE_STATUS, target, user) - ui_interact(user) - return ITEM_INTERACT_SUCCESS - - return NONE + scanned_item = WEAKREF(target) + SEND_SIGNAL(src, COMSIG_FISH_ANALYZER_ANALYZE_STATUS, target, user) + ui_interact(user) + return ITEM_INTERACT_SUCCESS /obj/item/fish_analyzer/ui_interact(mob/user, datum/tgui/ui) if(isnull(scanned_item?.resolve())) @@ -92,13 +89,13 @@ data["fish_scanned"] = TRUE return extract_fish_info(data, scanned_object) - var/obj/structure/aquarium/aquarium = scanned_object + var/atom/movable/aquarium = scanned_object for(var/obj/item/fish/fishie in aquarium) - extract_fish_info(data, fishie, aquarium) + extract_fish_info(data, fishie) return data -/obj/item/fish_analyzer/proc/extract_fish_info(list/data, obj/item/fish/fishie, obj/structure/aquarium/aquarium) +/obj/item/fish_analyzer/proc/extract_fish_info(list/data, obj/item/fish/fishie) var/list/fish_traits = list() var/list/fish_evolutions = list() @@ -129,12 +126,12 @@ "fish_min_temp" = fishie.required_temperature_min, "fish_max_temp" = fishie.required_temperature_max, "fish_hunger" = HAS_TRAIT(fishie, TRAIT_FISH_NO_HUNGER) ? 0 : 1 - fishie.get_hunger(), - "fish_fluid_compatible" = aquarium ? compatible_fluid_type(fishie.required_fluid_type, aquarium.fluid_type) : null, + "fish_fluid_compatible" = fishie.fish_flags & FISH_FLAG_SAFE_FLUID, "fish_fluid_type" = fishie.required_fluid_type, "fish_breed_timer" = round(max(fishie.breeding_wait - world.time, 0) / 10), "fish_traits" = fish_traits, "fish_evolutions" = fish_evolutions, - "fish_suitable_temp" = aquarium ? ISINRANGE(aquarium.fluid_temp, fishie.required_temperature_min, fishie.required_temperature_max) : null + "fish_suitable_temp" = fishie.fish_flags & FISH_FLAG_SAFE_TEMPERATURE, )) return data diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 033e6e60f2f..b43a2bf439a 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -176,10 +176,11 @@ base_icon_state = icon_state //It's important that we register the signals before the component is attached. RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_DO_ANIMATION, PROC_REF(update_aquarium_animation)) - RegisterSignal(src, AQUARIUM_CONTENT_RANDOMIZE_POSITION, PROC_REF(randomize_aquarium_position)) + RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_RANDOMIZE_POSITION, PROC_REF(randomize_aquarium_position)) RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, PROC_REF(update_aquarium_appearance)) - AddComponent(/datum/component/aquarium_content, list(COMSIG_FISH_STIRRED), beauty) + AddComponent(/datum/component/aquarium_content, list(COMSIG_ATOM_WAS_ATTACKED)) + RegisterSignal(src, COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY, PROC_REF(get_aquarium_beauty)) RegisterSignal(src, COMSIG_ATOM_ON_LAZARUS_INJECTOR, PROC_REF(use_lazarus)) if(fish_flags & FISH_DO_FLOP_ANIM) RegisterSignal(src, COMSIG_ATOM_TEMPORARY_ANIMATION_START, PROC_REF(on_temp_animation)) @@ -549,7 +550,7 @@ make_edible() if(weight >= FISH_WEIGHT_SLOWDOWN && !HAS_TRAIT(src, TRAIT_SPEED_POTIONED)) - slowdown = round(((weight/FISH_WEIGHT_SLOWDOWN_DIVISOR)**FISH_WEIGHT_SLOWDOWN_EXPONENT)-1.3, 0.1) + slowdown = GET_FISH_SLOWDOWN(weight) drag_slowdown = round(slowdown * 0.5, 1) else slowdown = 0 @@ -592,14 +593,6 @@ AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, amount, time, screentip_verb = "Cut") return amount //checked by a unit test -/** - * Weight, unlike size, is a bit more exponential, but the world isn't perfect, so isn't my code. - * Anyway, this returns a gross estimate of the "rank" of "category" for our fish weight, based on how - * weight generaly scales up (250, 500, 1000, 2000, 4000 etc...) - */ -/obj/item/fish/proc/get_weight_rank() - return max(round(1 + log(2, weight/FISH_WEIGHT_FORCE_DIVISOR), 1), 1) - ///Reset weapon-related variables of this items and recalculates those values based on the fish weight and size. /obj/item/fish/proc/update_fish_force() if(force >= 15 && hitsound == SFX_ALT_FISH_SLAP) @@ -619,7 +612,7 @@ bare_wound_bonus = initial(bare_wound_bonus) toolspeed = initial(toolspeed) - var/weight_rank = get_weight_rank() + var/weight_rank = GET_FISH_WEIGHT_RANK(weight) throw_range -= weight_rank get_force_rank() @@ -828,16 +821,13 @@ ///Proc that should be called when the fish is fed. By default, it grows the fish depending on various variables. /obj/item/fish/proc/sate_hunger() - if(isaquarium(loc)) - var/obj/structure/aquarium/aquarium = loc - if(!aquarium.reproduction_and_growth) - last_feeding = world.time - return + if(HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) + last_feeding = world.time + return var/hunger = get_hunger() if(hunger < 0.05) //don't bother growing for very small amounts. last_feeding = world.time return - last_feeding = world.time var/new_size = size var/new_weight = weight var/hunger_mult @@ -854,7 +844,7 @@ new_size += CEILING((maximum_size - size) * base_mult / (w_class * FISH_SIZE_WEIGHT_GROWTH_MALUS) * hunger_mult, 1) new_size = min(new_size, maximum_size) if(weight < maximum_weight) - new_weight += CEILING((maximum_weight - weight) * base_mult / (get_weight_rank() * FISH_SIZE_WEIGHT_GROWTH_MALUS) * hunger_mult, 1) + new_weight += CEILING((maximum_weight - weight) * base_mult / (GET_FISH_WEIGHT_RANK(weight) * FISH_SIZE_WEIGHT_GROWTH_MALUS) * hunger_mult, 1) new_weight = min(new_weight, maximum_weight) if(new_size != size || new_weight != weight) update_size_and_weight(new_size, new_weight) @@ -871,9 +861,8 @@ return // Do additional stuff - var/in_aquarium = isaquarium(loc) // Start flopping if outside of fish container - var/should_be_flopping = status == FISH_ALIVE && !HAS_TRAIT(src, TRAIT_FISH_STASIS) && !in_aquarium + var/should_be_flopping = status == FISH_ALIVE && !HAS_TRAIT(src, TRAIT_FISH_STASIS) && loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM) if(should_be_flopping) start_flopping() @@ -912,7 +901,7 @@ stop_flopping() if(!silent) var/message = span_notice(replacetext(death_text, "%SRC", "[src]")) - if(isaquarium(loc)) + if(loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) loc.visible_message(message) else visible_message(message) @@ -997,47 +986,44 @@ visual.icon_state = dedicated_in_aquarium_icon_state || "[initial(icon_state)]_small" visual.color = aquarium_vc_color -/obj/item/fish/proc/randomize_aquarium_position(datum/source, obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual) +/obj/item/fish/proc/randomize_aquarium_position(datum/source, atom/movable/current_aquarium, obj/effect/aquarium/visual) SIGNAL_HANDLER - var/list/aq_properties = current_aquarium.get_surface_properties() var/avg_width = round(sprite_width * 0.5) var/avg_height = round(sprite_height * 0.5) - 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_height - 16 + var/px_min = visual.aquarium_zone_min_px + avg_width - 16 + var/px_max = visual.aquarium_zone_max_px - avg_width - 16 + var/py_min = visual.aquarium_zone_min_py + avg_height - 16 + var/py_max = visual.aquarium_zone_max_py - avg_height - 16 visual.pixel_x = visual.base_pixel_x = rand(px_min,px_max) visual.pixel_y = visual.base_pixel_y = rand(py_min,py_max) -/obj/item/fish/proc/get_aquarium_animation() - var/obj/structure/aquarium/aquarium = loc - if(!istype(aquarium) || aquarium.fluid_type == AQUARIUM_FLUID_AIR || status == FISH_DEAD) - return AQUARIUM_ANIMATION_FISH_DEAD - else - return AQUARIUM_ANIMATION_FISH_SWIM - -/obj/item/fish/proc/update_aquarium_animation(datum/source, current_animation, obj/structure/current_aquarium, obj/effect/visual) +/obj/item/fish/proc/update_aquarium_animation(datum/source, current_animation, obj/effect/visual, fluid_type) SIGNAL_HANDLER - var/animation = get_aquarium_animation() + var/animation = get_aquarium_animation(fluid_type) if(animation == current_animation) return switch(animation) if(AQUARIUM_ANIMATION_FISH_SWIM) - swim_animation(current_aquarium, visual) + swim_animation(visual) if(AQUARIUM_ANIMATION_FISH_DEAD) - dead_animation(current_aquarium, visual) + dead_animation(visual) + +/obj/item/fish/proc/get_aquarium_animation(fluid_type) + if(fluid_type == AQUARIUM_FLUID_AIR || status == FISH_DEAD) + return AQUARIUM_ANIMATION_FISH_DEAD + else + return AQUARIUM_ANIMATION_FISH_SWIM /// Create looping random path animation, pixel offsets parameters include offsets already -/obj/item/fish/proc/swim_animation(obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual) +/obj/item/fish/proc/swim_animation(obj/effect/aquarium/visual) 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/px_min = visual.aquarium_zone_min_px + avg_width - 16 + var/px_max = visual.aquarium_zone_max_px - avg_width - 16 + var/py_min = visual.aquarium_zone_min_py + avg_height - 16 + var/py_max = visual.aquarium_zone_max_py - avg_width - 16 var/origin_x = visual.base_pixel_x var/origin_y = visual.base_pixel_y @@ -1062,22 +1048,36 @@ animate(transform = dir_mx, time = 0, loop = -1) animate(pixel_x = target_x, pixel_y = target_y, time = eyeballed_time, loop = -1) -/obj/item/fish/proc/dead_animation(obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual) +/obj/item/fish/proc/dead_animation(obj/effect/aquarium/visual) //Set base_pixel_y 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 + var/py_min = visual.aquarium_zone_min_py + avg_height - 16 visual.base_pixel_y = py_min animate(visual, pixel_y = py_min, time = 1) //flop to bottom and end current animation. +///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 + +/obj/item/fish/proc/get_aquarium_beauty(datum/source, list/beauty_holder) + SIGNAL_HANDLER + var/actual_beauty = beauty + if(status == FISH_DEAD) + actual_beauty = clamp(beauty + DEAD_FISH_BEAUTY, MIN_DEAD_FISH_BEAUTY, MAX_DEAD_FISH_BEAUTY) + + beauty_holder += actual_beauty + +#undef DEAD_FISH_BEAUTY +#undef MIN_DEAD_FISH_BEAUTY +#undef MAX_DEAD_FISH_BEAUTY + /// Checks if our current environment lets us live. /obj/item/fish/proc/proper_environment(temp_range_min = required_temperature_min, temp_range_max = required_temperature_max) - var/obj/structure/aquarium/aquarium = loc - if(istype(aquarium)) - if(!compatible_fluid_type(required_fluid_type, aquarium.fluid_type)) - if(aquarium.fluid_type != AQUARIUM_FLUID_AIR || !HAS_TRAIT(src, TRAIT_FISH_AMPHIBIOUS)) - return FALSE - if(!ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max)) + if(loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) + if(!(fish_flags & FISH_FLAG_SAFE_TEMPERATURE) || !(fish_flags & FISH_FLAG_SAFE_FLUID)) return FALSE return TRUE @@ -1131,54 +1131,48 @@ bites_amount -= amount generate_fish_reagents(amount) +/// Returns tracked_fish_by_type but flattened and without the items in the blacklist, also shuffled if shuffle is TRUE. +/obj/item/fish/proc/get_aquarium_fishes(shuffle = FALSE, blacklist) + . = list() + for(var/obj/item/fish/fish in loc) + . += fish + . -= blacklist + if(shuffle) + . = shuffle(.) + return . + /obj/item/fish/proc/ready_to_reproduce(being_targeted = FALSE) - var/obj/structure/aquarium/aquarium = loc - if(!istype(aquarium)) + if(!loc || !HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) return FALSE if(being_targeted && HAS_TRAIT(src, TRAIT_FISH_NO_MATING)) return FALSE - if(!being_targeted && length(aquarium.get_fishes()) >= AQUARIUM_MAX_BREEDING_POPULATION) + if(!being_targeted && length(get_aquarium_fishes()) >= AQUARIUM_MAX_BREEDING_POPULATION) return FALSE - return aquarium.reproduction_and_growth && health >= initial(health) * 0.8 && stable_population >= 1 && world.time >= breeding_wait + return !HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH) && health >= initial(health) * 0.8 && stable_population >= 1 && world.time >= breeding_wait /obj/item/fish/proc/try_to_reproduce() - var/obj/structure/aquarium/aquarium = loc - if(!istype(aquarium)) + if(!loc || !HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) return FALSE var/obj/item/fish/second_fish - /** - * Fishes with this trait cannot mate, but could still reproduce asexually, so don't early return. - * Also mating takes priority over that. - */ + ///Fishes with this trait cannot mate, but could still reproduce asexually, so don't early return. if(!HAS_TRAIT(src, TRAIT_FISH_NO_MATING)) var/list/available_fishes = list() - var/types_to_mate_with = aquarium.tracked_fish_by_type - if(!HAS_TRAIT(src, TRAIT_FISH_CROSSBREEDER)) - var/list/types_to_check = list(type) - if(compatible_types) - types_to_check |= 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 - available_fishes += type_fishes - - available_fishes -= src //no self-mating. + SEND_SIGNAL(loc, COMSIG_AQUARIUM_GET_REPRODUCTION_CANDIDATES, src, available_fishes) if(length(available_fishes)) - for(var/obj/item/fish/other_fish as anything in shuffle(available_fishes)) + //make sure we check if the fish can reproduce with itself last, since that should've lower priority + available_fishes = shuffle(available_fishes) - src + available_fishes += src + for(var/obj/item/fish/other_fish as anything in available_fishes) if(other_fish.ready_to_reproduce(TRUE)) second_fish = other_fish break - if(!second_fish) + if(!second_fish || second_fish == src) //check if the fish can self-reproduce in these cases. if(!HAS_TRAIT(src, TRAIT_FISH_SELF_REPRODUCE)) return FALSE - if(length(aquarium.tracked_fish_by_type[type]) >= stable_population) - return FALSE + second_fish = null //set it to null, since this will make the following operations a bit easier if(PERFORM_ALL_TESTS(fish_breeding) && second_fish && !length(evolution_types)) return create_offspring(second_fish.type, second_fish) @@ -1188,13 +1182,13 @@ var/list/possible_evolutions = list() for(var/evolution_type in evolution_types) var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type] - if(evolution.check_conditions(src, second_fish, aquarium)) + if(evolution.check_conditions(src, second_fish, loc)) possible_evolutions += evolution if(second_fish?.evolution_types) var/secondary_evolutions = (second_fish.evolution_types - evolution_types) for(var/evolution_type in secondary_evolutions) var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type] - if(evolution.check_conditions(second_fish, src, aquarium)) + if(evolution.check_conditions(second_fish, src, loc)) possible_evolutions += evolution var/list/types = spawn_types || list(type) @@ -1205,7 +1199,7 @@ var/list/second_fish_types = second_fish.spawn_types || list(second_fish.type) var/recessive = HAS_TRAIT(src, TRAIT_FISH_RECESSIVE) var/recessive_partner = HAS_TRAIT(second_fish, TRAIT_FISH_RECESSIVE) - if(length(aquarium.tracked_fish_by_type[type]) >= stable_population) + if(fish_flags & FISH_FLAG_OVERPOPULATED) if(recessive_partner && !recessive) return FALSE chosen_type = pick(second_fish_types) @@ -1335,7 +1329,7 @@ var/fish_zap_range = 1 var/fish_zap_power = 1 KILO JOULES // ~5 damage, just a little friendly "yeeeouch!" var/fish_zap_flags = ZAP_MOB_DAMAGE - if(istype(loc, /obj/structure/aquarium/bioelec_gen)) + if(HAS_TRAIT(loc, TRAIT_BIOELECTRIC_GENERATOR)) fish_zap_range = 5 fish_zap_power = GET_FISH_ELECTROGENESIS(src) if(HAS_TRAIT(src, TRAIT_FISH_ON_TESLIUM)) @@ -1378,13 +1372,13 @@ happiness_value++ if(get_hunger() < 0.5) happiness_value++ - var/obj/structure/aquarium/aquarium = loc - if(!istype(aquarium)) - return happiness_value - if(compatible_fluid_type(required_fluid_type, aquarium.fluid_type)) - happiness_value++ - if(ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max)) - happiness_value++ + if(loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) + if(fish_flags & FISH_FLAG_SAFE_FLUID) + happiness_value++ + if(fish_flags & FISH_FLAG_SAFE_TEMPERATURE) + happiness_value++ + else if(proper_environment()) + happiness_value += 2 if(bites_amount) // ouch happiness_value -= 2 if(health < initial(health) * 0.6) @@ -1396,7 +1390,7 @@ pet_fish(user) /obj/item/fish/proc/pet_fish(mob/living/user) - var/in_aquarium = isaquarium(loc) + var/in_aquarium = loc && HAS_TRAIT(loc, TRAIT_IS_AQUARIUM) if(status == FISH_DEAD) to_chat(user, span_warning("You try to pet [src], but [p_theyre()] motionless!")) return FALSE diff --git a/code/modules/fishing/fish/fish_evolution.dm b/code/modules/fishing/fish/fish_evolution.dm index a9261b091c9..8dd9bb0ffcd 100644 --- a/code/modules/fishing/fish/fish_evolution.dm +++ b/code/modules/fishing/fish/fish_evolution.dm @@ -43,20 +43,14 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) * Keep in mind the mate and aquarium arguments may be null if * the fish is self-reproducing or this evolution is a result of a fish_growth component */ -/datum/fish_evolution/proc/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/proc/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) SHOULD_CALL_PARENT(TRUE) - if(aquarium) - //chances are halved if only one parent has this evolution. - var/real_probability = (mate && (type in mate.evolution_types)) ? probability : probability/2 - if(HAS_TRAIT(source, TRAIT_FISH_MUTAGENIC) || (mate && HAS_TRAIT(mate, TRAIT_FISH_MUTAGENIC))) - real_probability *= 3 - if(!prob(real_probability)) - return FALSE - if(!ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max)) - return FALSE - else if(!source.proper_environment(required_temperature_min, required_temperature_max)) - return FALSE - return TRUE + //the fish don't reproduce outside of aquariums but can still grow there, so we just check if the temperature is right. + if(!aquarium) + return source.proper_environment(required_temperature_min, required_temperature_max) + if(SEND_SIGNAL(aquarium, COMSIG_AQUARIUM_CHECK_EVOLUTION_CONDITIONS, source, mate, src) & COMPONENT_ALLOW_EVOLUTION) + return TRUE + return FALSE ///This is called when the evolution is set as the result type of a fish_growth component /datum/fish_evolution/proc/growth_checks(obj/item/fish/source, seconds_per_tick, growth) @@ -66,20 +60,17 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) return COMPONENT_DONT_GROW if(source.get_hunger() >= 0.5) //too hungry to grow return COMPONENT_DONT_GROW - var/obj/structure/aquarium/aquarium = source.loc - if(istype(aquarium) && !aquarium.reproduction_and_growth) //the aquarium has breeding disabled + if(HAS_TRAIT(source.loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) //the aquarium has breeding disabled return COMPONENT_DONT_GROW - else - aquarium = null - if(!check_conditions(source, aquarium = aquarium)) + if(!check_conditions(source)) return COMPONENT_DONT_GROW ///Called by the fish analyzer right click function. Returns a text string used as tooltip. /datum/fish_evolution/proc/get_evolution_tooltip() . = "" if(required_temperature_min > 0 || required_temperature_max < INFINITY) - var/max_temp = required_temperature_max < INFINITY ? " and [required_temperature_max]" : "" - . = "An aquarium temperature between [required_temperature_min][max_temp] is required." + var/max_temp = required_temperature_max < INFINITY ? " to [required_temperature_max]" : "" + . = "An aquarium temperature of [required_temperature_min][max_temp] is required." if(conditions_note) . += " [conditions_note]" return . @@ -90,7 +81,7 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) new_traits = list(/datum/fish_trait/lubed) conditions_note = "The fish must be fed lube beforehand." -/datum/fish_evolution/lubefish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/lubefish/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) if(!HAS_TRAIT(source, TRAIT_FISH_FED_LUBE)) return FALSE return ..() @@ -109,7 +100,7 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) conditions_note = "The fish (and its mate) needs to be unusually big both in size and weight." show_result_on_wiki = FALSE -/datum/fish_evolution/mastodon/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/mastodon/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) if((source.size < 120 || source.weight < 3000) || (mate && (mate.size < 120 || mate.weight < 3000))) return FALSE return ..() @@ -137,7 +128,7 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) new_traits = list(/datum/fish_trait/predator, /datum/fish_trait/aggressive) conditions_note = "The fish needs to be unusually big and aggressive" -/datum/fish_evolution/chainsawfish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/chainsawfish/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) var/double_avg_size = /obj/item/fish/goldfish::average_size * 2 var/double_avg_weight = /obj/item/fish/goldfish::average_weight * 2 if(source.size >= double_avg_size && source.weight >= double_avg_weight && (/datum/fish_trait/aggressive in source.fish_traits)) @@ -149,7 +140,7 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) new_fish_type = /obj/item/fish/pike/armored conditions_note = "The fish needs to have the stinger trait" -/datum/fish_evolution/armored_pike/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/armored_pike/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) if(HAS_TRAIT(source, TRAIT_FISH_STINGER)) return ..() return FALSE @@ -165,7 +156,7 @@ GLOBAL_LIST_EMPTY(fishes_by_fish_evolution) conditions_note = "The final stage of fritterfish growth. It gotta be big!" show_result_on_wiki = FALSE -/datum/fish_evolution/nessiefish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium) +/datum/fish_evolution/nessiefish/check_conditions(obj/item/fish/source, obj/item/fish/mate, atom/movable/aquarium) if(source.size >= (/obj/item/fish/fryish/fritterish::average_size * 1.5) && source.size >= (/obj/item/fish/fryish/fritterish::average_weight * 1.5)) return ..() return FALSE diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm index dd0edb7ca3d..aa045c6308b 100644 --- a/code/modules/fishing/fish/fish_traits.dm +++ b/code/modules/fishing/fish/fish_traits.dm @@ -184,7 +184,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/nocturnal/proc/check_light(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(isturf(source.loc) || isaquarium(source)) + if(source.loc && (HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM) || isturf(source.loc))) var/turf/turf = get_turf(source) var/light_amount = turf.get_lumcount() if(light_amount > SHADOW_SPECIES_LIGHT_THRESHOLD) @@ -293,7 +293,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/emulsijack/proc/emulsify(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(!isaquarium(source.loc)) + if(!source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) return var/emulsified = FALSE for(var/obj/item/fish/victim in source.loc) @@ -332,7 +332,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/necrophage/proc/eat_dead_fishes(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(source.get_hunger() > 0.75 || !isaquarium(source.loc)) + if(source.get_hunger() > 0.75 || !source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) return for(var/obj/item/fish/victim in source.loc) if(victim.status != FISH_DEAD || victim == source || HAS_TRAIT(victim, TRAIT_YUCKY_FISH)) @@ -400,7 +400,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) return source.set_status(FISH_ALIVE) var/message = span_nicegreen("[source] twitches. It's alive!") - if(isaquarium(source.loc)) + if(source.loc && HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) source.loc.visible_message(message) else source.visible_message(message) @@ -425,10 +425,9 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/predator/proc/eat_fishes(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(source.get_hunger() > 0.75 || !isaquarium(source.loc)) + if(source.get_hunger() > 0.75 || !source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) return - var/obj/structure/aquarium/aquarium = source.loc - for(var/obj/item/fish/victim in aquarium.get_fishes(TRUE, source)) + for(var/obj/item/fish/victim as anything in source.get_aquarium_fishes(TRUE, source)) if(victim.size < source.size * 0.7) // It's a big fish eat small fish world continue if(victim.status != FISH_ALIVE || victim == source || HAS_TRAIT(victim, TRAIT_YUCKY_FISH) || SPT_PROB(80, seconds_per_tick)) @@ -531,13 +530,12 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits()) /datum/fish_trait/aggressive/proc/try_attack_fish(obj/item/fish/source, seconds_per_tick) SIGNAL_HANDLER - if(!isaquarium(source.loc) || !SPT_PROB(1, seconds_per_tick)) + if(!source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM) || !SPT_PROB(1, seconds_per_tick)) return - var/obj/structure/aquarium/aquarium = source.loc - for(var/obj/item/fish/victim in aquarium.get_fishes(TRUE, source)) + for(var/obj/item/fish/victim as anything in source.get_aquarium_fishes(TRUE, source)) if(victim.status != FISH_ALIVE) continue - aquarium.visible_message(span_warning("[source] violently [pick("whips", "bites", "attacks", "slams")] [victim]")) + source.loc.visible_message(span_warning("[source] violently [pick("whips", "bites", "attacks", "slams")] [victim]")) var/damage = round(rand(4, 20) * (source.size / victim.size)) //smaller fishes take extra damage. victim.adjust_health(victim.health - damage) return diff --git a/code/modules/fishing/fish/types/air_space.dm b/code/modules/fishing/fish/types/air_space.dm index c60fc7a36ef..3b053dc4c25 100644 --- a/code/modules/fishing/fish/types/air_space.dm +++ b/code/modules/fishing/fish/types/air_space.dm @@ -173,13 +173,12 @@ if(health <= initial(health) * 0.6 || hunger >= 0.6) //if too hurt or hungry, don't grow. return COMPONENT_DONT_GROW - if(!isaquarium(loc)) + if(!loc || !HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) return - var/obj/structure/aquarium/aquarium = loc - if(!aquarium.reproduction_and_growth) //the aquarium has breeding disabled + if(HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) //the aquarium has breeding disabled return COMPONENT_DONT_GROW - if(length(aquarium.get_fishes()) > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate. + if(length(get_aquarium_fishes()) > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate. return COMPONENT_DONT_GROW /obj/item/fish/baby_carp/proc/on_growth(datum/source, mob/living/basic/carp/result) diff --git a/code/modules/fishing/fish/types/freshwater.dm b/code/modules/fishing/fish/types/freshwater.dm index 660a406183c..16687bc6497 100644 --- a/code/modules/fishing/fish/types/freshwater.dm +++ b/code/modules/fishing/fish/types/freshwater.dm @@ -201,8 +201,7 @@ var/hunger = get_hunger() if(hunger >= 0.7) //too hungry to grow return COMPONENT_DONT_GROW - var/obj/structure/aquarium/aquarium = loc - if(istype(aquarium) && !aquarium.reproduction_and_growth) //the aquarium has breeding disabled + if(HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) //the aquarium has breeding disabled return COMPONENT_DONT_GROW /obj/item/fish/tadpole/proc/on_growth(datum/source, mob/living/basic/frog/result) diff --git a/code/modules/fishing/fish/types/mining.dm b/code/modules/fishing/fish/types/mining.dm index 53c1b89673c..3a1d6ff8310 100644 --- a/code/modules/fishing/fish/types/mining.dm +++ b/code/modules/fishing/fish/types/mining.dm @@ -78,15 +78,14 @@ if(hunger >= 0.4) //I'm hungry and angry anger += growth * 0.6 - if(!isaquarium(loc)) + if(!loc || !HAS_TRAIT(loc, TRAIT_IS_AQUARIUM)) return - var/obj/structure/aquarium/aquarium = loc - if(!aquarium.reproduction_and_growth) //the aquarium has breeding disabled + if(HAS_TRAIT(loc, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH)) //the aquarium has breeding disabled return COMPONENT_DONT_GROW - if(!locate(/obj/item/aquarium_prop) in aquarium) //the aquarium deco is quite barren + if(!locate(/obj/item/aquarium_prop) in loc) //the aquarium deco is quite barren anger += growth * 0.25 - var/fish_count = length(aquarium.get_fishes()) + var/fish_count = length(get_aquarium_fishes()) if(!ISINRANGE(fish_count, 3, AQUARIUM_MAX_BREEDING_POPULATION * 0.5)) //too lonely or overcrowded anger += growth * 0.3 if(fish_count > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate. diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index 3aaa99e5ae0..56764318f1b 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -1046,6 +1046,18 @@ ) departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE +/datum/design/bluespace_fish_tank + name = "Bluespace Fish Tank Kit" + desc = "The required components to upgrade the capacity of a fish tank." + id = "bluespace_fish_tank_kit" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 3, /datum/material/titanium = SMALL_MATERIAL_AMOUNT, /datum/material/bluespace = SMALL_MATERIAL_AMOUNT * 3) + build_path = /obj/item/aquarium_upgrade/bluespace_tank + category = list( + RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_SERVICE, + ) + departmental_flags = DEPARTMENT_BITFLAG_SERVICE | DEPARTMENT_BITFLAG_CARGO | DEPARTMENT_BITFLAG_SCIENCE + // Coffeemaker Stuff /datum/design/coffeepot diff --git a/code/modules/research/designs/power_designs.dm b/code/modules/research/designs/power_designs.dm index deb2256ff10..38edc5b5593 100644 --- a/code/modules/research/designs/power_designs.dm +++ b/code/modules/research/designs/power_designs.dm @@ -167,10 +167,12 @@ ) departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING -/datum/design/board/bioelec_gen +/datum/design/bioelec_gen name = "Aquarium Bioelectricity Kit" desc = "The required components to convert an aquarium into a bioelectricity generator." id = "bioelec_gen" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 5, /datum/material/gold = SMALL_MATERIAL_AMOUNT) build_path = /obj/item/aquarium_upgrade/bioelec_gen category = list( RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING, diff --git a/code/modules/research/techweb/nodes/service_nodes.dm b/code/modules/research/techweb/nodes/service_nodes.dm index a2f6b030071..824cd611b83 100644 --- a/code/modules/research/techweb/nodes/service_nodes.dm +++ b/code/modules/research/techweb/nodes/service_nodes.dm @@ -174,6 +174,7 @@ "auto_reel", "fish_analyzer", "bluespace_fish_case", + "bluespace_fish_tank_kit", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_2_POINTS) required_experiments = list(/datum/experiment/scanning/fish) diff --git a/code/modules/unit_tests/component_tests.dm b/code/modules/unit_tests/component_tests.dm index f609e73c4b7..1fae3982510 100644 --- a/code/modules/unit_tests/component_tests.dm +++ b/code/modules/unit_tests/component_tests.dm @@ -1,12 +1,7 @@ /datum/unit_test/component_duping/Run() var/list/bad_dms = list() - var/list/bad_dts = list() for(var/t in typesof(/datum/component)) var/datum/component/comp = t if(!isnum(initial(comp.dupe_mode))) bad_dms += t - var/dupe_type = initial(comp.dupe_type) - if(dupe_type && !ispath(dupe_type)) - bad_dts += t - TEST_ASSERT(!length(bad_dms) && !length(bad_dts), - "Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])") + TEST_ASSERT(!length(bad_dms), "Components with invalid dupe modes: ([bad_dms.Join(",")])") diff --git a/code/modules/unit_tests/fish_unit_tests.dm b/code/modules/unit_tests/fish_unit_tests.dm index 94337ae2cc2..43f06ad11e5 100644 --- a/code/modules/unit_tests/fish_unit_tests.dm +++ b/code/modules/unit_tests/fish_unit_tests.dm @@ -128,7 +128,6 @@ description = "It smells fishy." /obj/structure/aquarium/traits - reproduction_and_growth = TRUE var/obj/item/fish/testdummy/crossbreeder/crossbreeder var/obj/item/fish/testdummy/cloner/cloner var/obj/item/fish/testdummy/sterile/sterile @@ -155,7 +154,6 @@ fish_traits = list(/datum/fish_trait/no_mating) /obj/structure/aquarium/evolution - reproduction_and_growth = TRUE var/obj/item/fish/testdummy/evolve/evolve var/obj/item/fish/testdummy/evolve_two/evolve_two @@ -195,7 +193,7 @@ ///During the fish_growth unit test, we spawn a fish outside of the aquarium and check that this actually stops it from growing /datum/fish_evolution/dummy/two/growth_checks(obj/item/fish/source, seconds_per_tick, growth) . = ..() - if(!isaquarium(source.loc)) + if(!source.loc || !HAS_TRAIT(source.loc, TRAIT_IS_AQUARIUM)) return COMPONENT_DONT_GROW ///A test that checks that fishing portals can be linked and function as expected @@ -353,7 +351,6 @@ TEST_ASSERT(dummy_boogaloo, "The new fish type cannot be found inside the aquarium") /obj/structure/aquarium/crab - reproduction_and_growth = TRUE //needed for growing up ///Our test subject var/obj/item/fish/chasm_crab/instant_growth/crabbie @@ -489,5 +486,36 @@ for(var/obj/item/fish/fish as anything in box) fish.randomize_size_and_weight() +/datum/unit_test/aquarium_upgrade + +/datum/unit_test/aquarium_upgrade/Run() + var/mob/living/carbon/human/dummy/user = allocate(__IMPLIED_TYPE__) + var/obj/item/aquarium_upgrade/bioelec_gen/upgrade = allocate(__IMPLIED_TYPE__) + var/obj/structure/aquarium/aquarium = allocate(upgrade::upgrade_from_type) + + var/datum/component/aquarium/comp = aquarium.GetComponent(__IMPLIED_TYPE__) + TEST_ASSERT(comp, "[aquarium.type] doesn't have an aquarium component") + comp.set_fluid_type(AQUARIUM_FLUID_AIR) + comp.fluid_temp = MAX_AQUARIUM_TEMP + aquarium.add_traits(list(TRAIT_AQUARIUM_PANEL_OPEN, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH), AQUARIUM_TRAIT) + + var/type_to_check = upgrade::upgrade_to_type + var/turf/aquarium_loc = aquarium.loc + user.put_in_hands(upgrade) + upgrade.melee_attack_chain(user, aquarium) + TEST_ASSERT(QDELETED(aquarium), "Old [aquarium.type] was not deleted after upgrade") + + var/obj/structure/aquarium/upgraded_aquarium = locate(type_to_check) in aquarium_loc + TEST_ASSERT(upgraded_aquarium, "New [upgraded_aquarium.type] was not spawned after upgrade") + comp = upgraded_aquarium.GetComponent(/datum/component/aquarium) + TEST_ASSERT(comp, "New [upgraded_aquarium.type] doesn't have an aquarium component") + + TEST_ASSERT_EQUAL(comp.fluid_type, AQUARIUM_FLUID_AIR, "Inherited aquarium fluid type should be [AQUARIUM_FLUID_AIR]") + TEST_ASSERT_EQUAL(comp.fluid_temp, MAX_AQUARIUM_TEMP, "Inherited aquarium fluid temperature should be [MAX_AQUARIUM_TEMP]") + TEST_ASSERT(HAS_TRAIT(upgraded_aquarium, TRAIT_AQUARIUM_PANEL_OPEN), "The new aquarium should have its panel open") + TEST_ASSERT(HAS_TRAIT(upgraded_aquarium, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH), "The 'growth and reproduction' setting for this aquarium should be disabled") + + TEST_ASSERT(QDELETED(upgrade), "Aquarium upgrade wasn't deleted afterward") + #undef FISH_REAGENT_AMOUNT #undef TRAIT_FISH_TESTING diff --git a/code/modules/vending/games.dm b/code/modules/vending/games.dm index 27d2afa88e5..a5f2c84920d 100644 --- a/code/modules/vending/games.dm +++ b/code/modules/vending/games.dm @@ -57,6 +57,7 @@ /obj/item/reagent_containers/cup/fish_feed = 4, /obj/item/fish_analyzer = 2, /obj/item/fishing_rod/telescopic = 1, + /obj/item/fish_tank = 1, ), ), list( diff --git a/icons/obj/aquarium/supplies.dmi b/icons/obj/aquarium/supplies.dmi index 08f4923a0abddad89d60ec331d566002c16e8eae..73a1f5ffe345ab36dcf58bf0f2c4909bbe62ef56 100644 GIT binary patch delta 874 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 Fm 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%0AxO$(&+GVL+2iCreE#hjMCH0GrYUbo7 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 literal 31186 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 oIK!q0W4&f0q{pnNkj3Mf?4qY0?rI^{Qv*} diff --git a/icons/obj/food/lizard.dmi b/icons/obj/food/lizard.dmi index 9e60c814a2e5ad6cc95cc6190bcfac2ad6610407..44dc43e84de9da12963e4988cc09e423a659a0cd 100644 GIT binary patch literal 31999 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 literal 32083 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 diff --git a/tgstation.dme b/tgstation.dme index 12698d296b1..51f540d2c85 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1046,6 +1046,7 @@ #include "code\datums\components\amputating_limbs.dm" #include "code\datums\components\anti_magic.dm" #include "code\datums\components\appearance_on_aggro.dm" +#include "code\datums\components\aquarium.dm" #include "code\datums\components\aquarium_content.dm" #include "code\datums\components\area_based_godmode.dm" #include "code\datums\components\area_sound_manager.dm" diff --git a/tools/UpdatePaths/Scripts/87866_fish_tanks.txt b/tools/UpdatePaths/Scripts/87866_fish_tanks.txt new file mode 100644 index 00000000000..a0762acacf2 --- /dev/null +++ b/tools/UpdatePaths/Scripts/87866_fish_tanks.txt @@ -0,0 +1 @@ +/obj/structure/aquarium/lawyer : /obj/item/fish_tank/lawyer