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) => {
- -