From b296d716259ef6ed95cb953ee94ab7ba3659b235 Mon Sep 17 00:00:00 2001 From: Ghom <42542238+Ghommie@users.noreply.github.com> Date: Mon, 28 Apr 2025 15:09:44 +0000 Subject: [PATCH] Aquariums have now three different modes (Manual/Assisted/Stasis) (#90605) ## About The Pull Request This PR replaces the toggleable safe mode with Manual, Assisted and Stasis. Manual and Stasis mode works much like the old safe mode being off and on respectively. The real addition here is the new default assisted mode, which automatically sets fluid and temperature based on what combination of these two settings would keep the most fish alive. It can also temporarily enable stasis if over half of the fish population is at risk of dying. The main difference with stasis is that it doesn't necessarily prevent fish death if it's a minority of the general population, rather just make aquarium easier for players to manage. Also added a line about fluid and temp when examining aquariums. Still have to test it, definitely a WIP. ## Why It's Good For The Game I want to make aquariums a bit easier and more accessible, since the benefits are fairly meager for the efforts needed to maintain one, excluding the power generator. I'll probably look into fish traits and fish reproduction next, who knows... ## Changelog :cl: add: Aquariums can now be switched between three different modes (Manual/Assisted/Stasis) to replace the old Safe Mode toggle. /:cl: --- code/__DEFINES/fish.dm | 10 ++ code/__HELPERS/cmp.dm | 6 + code/datums/components/aquarium.dm | 159 ++++++++++++++++-- code/modules/cargo/bounties/assistant.dm | 2 +- code/modules/fishing/aquarium/aquarium.dm | 15 +- code/modules/fishing/fish/_fish.dm | 28 +-- .../spaceruin_code/hauntedtradingpost.dm | 2 +- tgui/packages/tgui/interfaces/Aquarium.tsx | 55 ++++-- 8 files changed, 233 insertions(+), 44 deletions(-) diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm index 3824c3f1ab7..8125013f88e 100644 --- a/code/__DEFINES/fish.dm +++ b/code/__DEFINES/fish.dm @@ -186,6 +186,9 @@ ///The last feeding timestamp of newly instantiated fish is multiplied by this: ergo, they spawn 50% hungry. #define NEW_FISH_LAST_FEEDING_MULT 0.33 +///If get_hunger is above this value, the fish is considered to be starving and will slowly lose health because of it +#define FISH_STARVING_THRESHOLD 1 + //IF YOU ADD ANY NEW FLAG, ADD IT TO THE RESPECTIVE BITFIELD in _globalvars/bitfields.dm TOO! ///This fish is shown in the catalog and on the wiki (this only matters as an initial, compile-time value) @@ -227,6 +230,13 @@ ///Fluff. The name of the aquarium company shown in the fish catalog #define AQUARIUM_COMPANY "Aquatech Ltd." +///aquarium mode where you've to manage food, fluid type and temperature to keep fish alive +#define AQUARIUM_MODE_MANUAL "Manual" +///Aquarium mode where the code tries to determine what fluid type and temperature to use. Default. +#define AQUARIUM_MODE_AUTO "Assisted" +///Prevents fish from dying because of temp/fluid settings and starvation but stops growth. +#define AQUARIUM_MODE_SAFE "Stasis" + /// how long between electrogenesis zaps #define ELECTROGENESIS_DURATION 40 SECONDS /// a random range the electrogenesis cooldown varies by diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index ead1b5cbff5..66de92f17fd 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -24,6 +24,12 @@ b = REF(b) return sorttext("[a]", "[b]") +/proc/cmp_list_len_asc(list/a, list/b) + return length(a) - length(b) + +/proc/cmp_list_len_dsc(list/a, list/b) + return length(b) - length(a) + /proc/cmp_name_asc(atom/a, atom/b) return sorttext(b.name, a.name) diff --git a/code/datums/components/aquarium.dm b/code/datums/components/aquarium.dm index e417b5f54fc..576852a471e 100644 --- a/code/datums/components/aquarium.dm +++ b/code/datums/components/aquarium.dm @@ -52,10 +52,19 @@ AQUARIUM_FLUID_SULPHWATEVER, AQUARIUM_FLUID_AIR, ) + ///static list of available aquarium modes + var/static/list/aquarium_modes = list( + AQUARIUM_MODE_MANUAL = "Take full control of fluid and temperature settings. There's no stasis option here.", + AQUARIUM_MODE_AUTO = "Let an internal processor regulate aquarium settings based on its fish population. Can temporarily fall back to stasis.", + AQUARIUM_MODE_SAFE = "Prevent fish from dying from wrong fluid/temperature settings and hunger, but also stops growth and reproduction", + ) ///The size of the reagents holder which will store fish feed. var/reagents_size + ///The current aquarium mode, one of either AQUARIUM_MODE_MANUAL, AQUARIUM_MODE_AUTO or AQUARIUM_MODE_SAFE. + var/current_mode + /datum/component/aquarium/Initialize( min_px, max_px, @@ -65,6 +74,7 @@ reagents_size = 6, min_fluid_temp = MIN_AQUARIUM_TEMP, max_fluid_temp = MAX_AQUARIUM_TEMP, + init_mode = AQUARIUM_MODE_AUTO, ) if(!ismovable(parent)) @@ -82,6 +92,8 @@ src.max_fluid_temp = max_fluid_temp fluid_temp = clamp(fluid_temp, min_fluid_temp, max_fluid_temp) + set_aquarium_mode(init_mode) + /datum/component/aquarium/RegisterWithParent() if(default_beauty) update_aquarium_beauty(0) @@ -169,9 +181,12 @@ 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 + var/old_mode = current_mode //Make sure we don't keep updating temp/fluid on AQUARIUM_MODE_AUTO for this. + current_mode = NONE for(var/atom/movable/moving as anything in movable.contents) if(HAS_TRAIT(moving, TRAIT_AQUARIUM_CONTENT)) moving.forceMove(new_parent) + current_mode = old_mode if(reagents_size) if(!new_parent.reagents) new_parent.create_reagents(reagents_size, SEALED_CONTAINER) @@ -220,8 +235,7 @@ 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) + feed_fishes(item, fishes) source.balloon_alert(user, "fed the fish") return ITEM_INTERACT_SUCCESS @@ -261,9 +275,13 @@ if(world.time < last_feeding + feeding_interval) return last_feeding = world.time - var/list/fishes = get_fishes() + feed_fishes(movable) + +/datum/component/aquarium/proc/feed_fishes(atom/movable/movable, list/fishes = get_fishes()) for(var/obj/item/fish/fish as anything in fishes) fish.feed(movable.reagents) + if(current_mode == AQUARIUM_MODE_AUTO && is_fish_population_safe()) + REMOVE_TRAIT(parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) /datum/component/aquarium/proc/on_plunger_act(atom/movable/source, obj/item/plunger/plunger, mob/living/user, reinforced) SIGNAL_HANDLER @@ -282,8 +300,9 @@ /datum/component/aquarium/proc/on_examine(atom/movable/source, mob/user, list/examine_list) SIGNAL_HANDLER + examine_list += span_notice("Its temperature and fluid are currently set to [EXAMINE_HINT("[fluid_temp] K")] and [EXAMINE_HINT(fluid_type)].") 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.") + examine_list += span_notice("[EXAMINE_HINT("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.") @@ -315,7 +334,11 @@ 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) + + if(current_mode == AQUARIUM_MODE_AUTO && fish.status != FISH_DEAD) + get_optimal_aquarium_settings() + else + 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. @@ -350,6 +373,8 @@ 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)) + if(current_mode == AQUARIUM_MODE_AUTO && fish.status != FISH_DEAD) + get_optimal_aquarium_settings() ///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) @@ -385,7 +410,7 @@ * 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))) + if((fluid_type in GLOB.fish_compatible_fluid_types[fish.required_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 @@ -397,6 +422,8 @@ ///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) + if(current_mode == AQUARIUM_MODE_AUTO) + get_optimal_aquarium_settings() /datum/component/aquarium/proc/update_aquarium_beauty(old_beauty) if(QDELETED(parent)) @@ -452,6 +479,99 @@ fishes += tracked_fish_by_type[key] return fishes +/datum/component/aquarium/proc/is_fish_population_safe() + var/list/fishes = get_fishes() + var/alive = 0 + var/not_safe = 0 + for(var/obj/item/fish/fishie as anything in fishes) + if(fishie.status == FISH_DEAD) + continue + if(!fishie.proper_environment() || fishie.get_hunger() > FISH_STARVING_THRESHOLD * 0.9) + not_safe++ + alive++ + return not_safe <= (alive / 2) + +/** + * Called if the mode is AQUARIUM_MODE_AUTO whenever a fish is added, removed, dies or resurrected. + * + * This tries to find what the best combination of fluid and temperature. is to ensure + * the survival of the highest number of fishes. It also can and will activate stasis if over half of + * the fish population is at risk of dying. It doesn't care if the fish is big or small, useful + * or not. All fish are equal before the impartial eye of AQUARIUM_MODE_AUTO. + */ +/datum/component/aquarium/proc/get_optimal_aquarium_settings() + if(!length(tracked_fish_by_type)) + REMOVE_TRAIT(parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + return + + var/list/can_survive_with = list() + var/list/checked_types = list() + var/list/sorted_tracked = sortTim(tracked_fish_by_type.Copy(), GLOBAL_PROC_REF(cmp_list_len_dsc), associative = TRUE) + var/list/fish_alive_by_type = list() + for(var/obj/item/fish/fish_type as anything in sorted_tracked) + var/our_min_temp = fish_type::required_temperature_min + var/our_max_temp = fish_type::required_temperature_max + var/our_fluid_type = fish_type::required_fluid_type + + checked_types += fish_type + LAZYINITLIST(can_survive_with[fish_type]) + + var/fish_amount = 0 + for(var/obj/item/fish/fish_instance as anything in tracked_fish_by_type[fish_type]) + if(fish_instance.status == FISH_ALIVE) + fish_amount++ + + if(!fish_amount) //all fishes of this type are dead, skip it. + continue + + fish_alive_by_type[fish_type] = fish_amount + + //Check if there's an overlap with the temp/fluid requirements of the other types. + for(var/obj/item/fish/enemy_type as anything in (tracked_fish_by_type - checked_types)) + var/enem_min_temp = initial(enemy_type.required_temperature_min) + var/enem_max_temp = initial(enemy_type.required_temperature_max) + if(our_min_temp > enem_max_temp || our_max_temp < enem_min_temp) + continue + var/enem_fluid_type = initial(enemy_type.required_fluid_type) + if(!length(GLOB.fish_compatible_fluid_types[our_fluid_type] & GLOB.fish_compatible_fluid_types[enem_fluid_type])) + continue + can_survive_with[fish_type] |= enemy_type + LAZYOR(can_survive_with[enemy_type], fish_type) + + var/list/highest_val_list = list(tracked_fish_by_type[1]) //In case there's only one type of fish in here... + var/highest_val = -1 + for(var/fish_type as anything in can_survive_with) + var/list/compatible_types = can_survive_with[fish_type] + var/fish_amount = fish_alive_by_type[fish_type] + for(var/obj/item/fish/fish_instance as anything in tracked_fish_by_type[fish_type]) + if(fish_instance.status == FISH_ALIVE) + fish_amount++ + for(var/ally_type in compatible_types) + var/val = fish_amount + fish_alive_by_type[ally_type] + var/list/common_allies = compatible_types & can_survive_with[ally_type] + for(var/third_type in common_allies) + val += fish_alive_by_type[third_type] + if(val <= highest_val) + continue + highest_val_list = list(fish_type, ally_type) + common_allies + highest_val = val + + var/min_temp = MIN_AQUARIUM_TEMP + var/max_temp = MAX_AQUARIUM_TEMP + var/list/possible_fluids = fluid_types.Copy() + for(var/obj/item/fish/chosen_type as anything in highest_val_list) + possible_fluids &= GLOB.fish_compatible_fluid_types[chosen_type::required_fluid_type] + min_temp = max(min_temp, chosen_type::required_temperature_min) + max_temp = min(max_temp, chosen_type::required_temperature_max) + + fluid_temp = clamp(min_temp + round((max_temp - min_temp) / 2), min_fluid_temp, max_fluid_temp) + set_fluid_type(possible_fluids[1]) + + if(!is_fish_population_safe()) + ADD_TRAIT(parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + else + REMOVE_TRAIT(parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + /datum/component/aquarium/proc/interact(atom/movable/source, mob/user) SIGNAL_HANDLER if(HAS_TRAIT(source, TRAIT_AQUARIUM_PANEL_OPEN)) @@ -482,7 +602,9 @@ var/atom/movable/aquarium = parent .["fluidType"] = fluid_type .["temperature"] = fluid_temp - .["safe_mode"] = HAS_TRAIT_FROM(aquarium, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + .["lockedFluidTemp"] = current_mode == AQUARIUM_MODE_AUTO + .["currentMode"] = current_mode + .["currentTooltip"] = aquarium_modes[current_mode] .["fishData"] = list() .["feedingInterval"] = feeding_interval / (1 MINUTES) .["propData"] = list() @@ -512,6 +634,10 @@ .["maxTemperature"] = max_fluid_temp .["fluidTypes"] = fluid_types .["heartIcon"] = 'icons/effects/effects.dmi' + var/list/modes_no_assoc = list() //the UI dropdown won't work with assoc lists here + for(var/mode in aquarium_modes) + modes_no_assoc += mode + .["aquariumModes"] = modes_no_assoc /datum/component/aquarium/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() @@ -531,11 +657,9 @@ if(params["fluid"] != fluid_type && (params["fluid"] in fluid_types)) set_fluid_type(params["fluid"]) . = TRUE - if("safe_mode") - if(HAS_TRAIT_FROM(movable, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT)) - REMOVE_TRAIT(movable, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) - else - ADD_TRAIT(movable, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + if("change_mode") + var/new_mode = params["new_mode"] + set_aquarium_mode(new_mode) . = TRUE if("feeding_interval") feeding_interval = params["feeding_interval"] MINUTES @@ -554,6 +678,17 @@ return fish.AddComponent(/datum/component/rename, new_name, fish.desc) +/datum/component/aquarium/proc/set_aquarium_mode(new_mode) + if(new_mode == current_mode) + return + REMOVE_TRAIT(parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + current_mode = new_mode + switch(current_mode) + if(AQUARIUM_MODE_SAFE) + ADD_TRAIT(parent, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) + if(AQUARIUM_MODE_AUTO) + get_optimal_aquarium_settings() + /datum/component/aquarium/proc/set_fluid_type(new_fluid_type) var/atom/movable/movable = parent fluid_type = new_fluid_type diff --git a/code/modules/cargo/bounties/assistant.dm b/code/modules/cargo/bounties/assistant.dm index ff48ad6df01..5046627f014 100644 --- a/code/modules/cargo/bounties/assistant.dm +++ b/code/modules/cargo/bounties/assistant.dm @@ -280,4 +280,4 @@ description = "We need [LOWER_TEXT(fluid_type)] fish to populate our aquariums with. Fishes that are dead or bought from cargo will only be paid half as much." /datum/bounty/item/assistant/fish/fluid/can_ship_fish(obj/item/fish/fishie) - return compatible_fluid_type(fishie.required_fluid_type, fluid_type) + return (fluid_type in GLOB.fish_compatible_fluid_types[fishie.required_fluid_type]) diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm index 2b1e190a9b1..e5402acc84e 100644 --- a/code/modules/fishing/aquarium/aquarium.dm +++ b/code/modules/fishing/aquarium/aquarium.dm @@ -22,9 +22,12 @@ ///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 + ///The initial mode for the aquarium component + var/init_mode = AQUARIUM_MODE_AUTO + /obj/structure/aquarium/Initialize(mapload) . = ..() - AddComponent(/datum/component/aquarium, aquarium_zone_min_pw, aquarium_zone_max_pw, aquarium_zone_min_pz, aquarium_zone_max_pz, default_beauty) + AddComponent(/datum/component/aquarium, aquarium_zone_min_pw, aquarium_zone_max_pw, aquarium_zone_min_pz, aquarium_zone_max_pz, default_beauty, init_mode = init_mode) AddComponent(/datum/component/plumbing/aquarium, start = anchored) RegisterSignal(src, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_aquarium_liquid_changed)) update_appearance() @@ -100,12 +103,11 @@ /obj/structure/aquarium/prefilled anchored = TRUE + init_mode = AQUARIUM_MODE_SAFE /obj/structure/aquarium/prefilled/Initialize(mapload) . = ..() - ADD_TRAIT(src, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) //start with safe mode on - new /obj/item/aquarium_prop/sand(src) new /obj/item/aquarium_prop/seaweed(src) @@ -151,6 +153,9 @@ ///The reagent capacity of this fish tank var/reagent_size = 4 + ///The initial mode for the aquarium component + var/init_mode = AQUARIUM_MODE_AUTO + /obj/item/fish_tank/Initialize(mapload) . = ..() update_appearance() @@ -164,6 +169,7 @@ reagents_size = src.reagent_size,\ min_fluid_temp = src.min_fluid_temp,\ max_fluid_temp = src.max_fluid_temp,\ + init_mode = init_mode,\ ) AddComponent(/datum/component/plumbing/aquarium, start = anchored) RegisterSignal(src, COMSIG_AQUARIUM_FLUID_CHANGED, PROC_REF(on_aquarium_liquid_changed)) @@ -244,12 +250,11 @@ ///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 + init_mode = AQUARIUM_MODE_SAFE /obj/item/fish_tank/lawyer/Initialize(mapload) . = ..() - ADD_TRAIT(src, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) //start with safe mode on - new /obj/item/aquarium_prop/sand(src) new /obj/item/aquarium_prop/seaweed(src) diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 009af3c5414..fe0a60b7879 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -6,6 +6,15 @@ #define FISH_SUBMERGING_THRESHOLD 100 SECONDS #define STARVING_FISH_SUBMERGING_THRESHOLD 20 SECONDS +GLOBAL_LIST_INIT(fish_compatible_fluid_types, list( + AQUARIUM_FLUID_ANY_WATER = list(AQUARIUM_FLUID_SALTWATER, AQUARIUM_FLUID_FRESHWATER, AQUARIUM_FLUID_SULPHWATEVER), + AQUARIUM_FLUID_ANADROMOUS = list(AQUARIUM_FLUID_SALTWATER, AQUARIUM_FLUID_FRESHWATER), + AQUARIUM_FLUID_SALTWATER = list(AQUARIUM_FLUID_SALTWATER), + AQUARIUM_FLUID_FRESHWATER = list(AQUARIUM_FLUID_FRESHWATER), + AQUARIUM_FLUID_SULPHWATEVER = list(AQUARIUM_FLUID_SULPHWATEVER), + AQUARIUM_FLUID_AIR = list(AQUARIUM_FLUID_AIR), +)) + // Fish path used for autogenerated fish /obj/item/fish name = "fish" @@ -863,14 +872,14 @@ check_flopping() ///Returns the value for hunger ranging from 0 to the cap (by default 1) -/obj/item/fish/proc/get_hunger(cap = 1) +/obj/item/fish/proc/get_hunger(cap = FISH_STARVING_THRESHOLD) . = clamp((world.time - last_feeding) / feeding_frequency, 0, cap) if(HAS_TRAIT(src, TRAIT_FISH_NO_HUNGER)) - return min(., 0.2) + return min(., FISH_STARVING_THRESHOLD * 0.2) /obj/item/fish/proc/get_starvation_mult() - var/hunger = get_hunger(cap = 2) - return hunger >= 1 ? hunger : 0 + var/hunger = get_hunger(cap = FISH_STARVING_THRESHOLD * 2) + return hunger >= FISH_STARVING_THRESHOLD ? hunger : 0 ///Feed the fishes with the contents of the fish feed /obj/item/fish/proc/feed(datum/reagents/fed_reagents) @@ -1568,21 +1577,12 @@ continue if(required_fluid) var/init_fish_fluid_type = initial(fish.required_fluid_type) - if(!compatible_fluid_type(init_fish_fluid_type, required_fluid)) + if(!(required_fluid in GLOB.fish_compatible_fluid_types[init_fish_fluid_type])) continue chance_table[fish] = initial(fish.random_case_rarity) probability_table[argkey] = chance_table return pick_weight(probability_table[argkey]) -/proc/compatible_fluid_type(fish_fluid_type, fluid_type) - switch(fish_fluid_type) - if(AQUARIUM_FLUID_ANY_WATER) - return fluid_type != AQUARIUM_FLUID_AIR - if(AQUARIUM_FLUID_ANADROMOUS) - return fluid_type == AQUARIUM_FLUID_SALTWATER || fluid_type == AQUARIUM_FLUID_FRESHWATER - else - return fish_fluid_type == fluid_type - #undef GET_FISH_ELECTROGENESIS #undef FISH_SAD #undef FISH_VERY_HAPPY diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm index 30cb60cc243..07c60c4f380 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm @@ -85,10 +85,10 @@ /obj/structure/aquarium/donkfish name = "office aquarium" desc = "A home for captive fish. This one has 'DONK CO' engraved on the glass." + init_mode = AQUARIUM_MODE_SAFE /obj/structure/aquarium/donkfish/Initialize(mapload) . = ..() - ADD_TRAIT(src, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH, AQUARIUM_TRAIT) new /obj/item/aquarium_prop/rocks(src) new /obj/item/aquarium_prop/seaweed(src) new /obj/item/fish/donkfish(src) diff --git a/tgui/packages/tgui/interfaces/Aquarium.tsx b/tgui/packages/tgui/interfaces/Aquarium.tsx index 2c1a8c1f549..b3b34a9d52b 100644 --- a/tgui/packages/tgui/interfaces/Aquarium.tsx +++ b/tgui/packages/tgui/interfaces/Aquarium.tsx @@ -1,7 +1,9 @@ import { Box, Button, + Dimmer, DmIcon, + Dropdown, Flex, Icon, Knob, @@ -9,6 +11,7 @@ import { NumberInput, Section, Stack, + Tooltip, } from 'tgui-core/components'; import { BooleanLike } from 'tgui-core/react'; import { capitalizeFirst } from 'tgui-core/string'; @@ -19,6 +22,7 @@ import { Window } from '../layouts'; type Data = { temperature: number; fluidType: string; + lockedFluidTemp: BooleanLike; minTemperature: number; maxTemperature: number; fluidTypes: string[]; @@ -29,6 +33,9 @@ type Data = { heartIcon: string; heartIconState: string; heartEmptyIconState: string; + currentMode: string; + currentTooltip: string; + aquariumModes: string[]; }; type FishData = { @@ -273,12 +280,17 @@ const Settings = (props) => { fluidType, safe_mode, feedingInterval, + lockedFluidTemp, + currentMode, + currentTooltip, + aquariumModes, } = data; return (
+ {!!lockedFluidTemp && } {
+ {!!lockedFluidTemp && } {fluidTypes.map((f) => ( @@ -306,11 +319,10 @@ const Settings = (props) => { textAlign="center" fluid color="transparent" + content={f} selected={fluidType === f} onClick={() => act('fluid', { fluid: f })} - > - {f} - + /> ))} @@ -320,15 +332,23 @@ const Settings = (props) => {
- -