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

🆑
add: Aquariums can now be switched between three different modes
(Manual/Assisted/Stasis) to replace the old Safe Mode toggle.
/🆑
This commit is contained in:
Ghom
2025-04-29 18:33:34 -06:00
committed by Shadow-Quill
parent f2f32f1611
commit b296d71625
8 changed files with 233 additions and 44 deletions
+10
View File
@@ -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
+6
View File
@@ -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)
+147 -12
View File
@@ -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("<b>Alt-click</b> 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
+1 -1
View File
@@ -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])
+10 -5
View File
@@ -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)
+14 -14
View File
@@ -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
@@ -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)
+44 -11
View File
@@ -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 (
<Flex fill>
<Flex.Item grow>
<Section fill title="Temperature">
{!!lockedFluidTemp && <LockedSection />}
<Knob
mt={3}
size={1.5}
@@ -299,6 +311,7 @@ const Settings = (props) => {
</Flex.Item>
<Flex.Item ml={1} grow>
<Section fill title="Fluid">
{!!lockedFluidTemp && <LockedSection />}
<Flex direction="column" mb={1}>
{fluidTypes.map((f) => (
<Flex.Item className="candystripe" key={f}>
@@ -306,11 +319,10 @@ const Settings = (props) => {
textAlign="center"
fluid
color="transparent"
content={f}
selected={fluidType === f}
onClick={() => act('fluid', { fluid: f })}
>
{f}
</Button>
/>
</Flex.Item>
))}
</Flex>
@@ -320,15 +332,23 @@ const Settings = (props) => {
<Section fill title="Settings">
<Box mt={2}>
<LabeledList>
<LabeledList.Item label="Safe Mode">
<Button
textAlign="center"
width="75px"
tooltip="Prevent fish dying in hostile water and temperatures at the cost of features like growth and reproduction"
content={safe_mode ? 'Online' : 'Offline'}
selected={safe_mode}
onClick={() => act('safe_mode')}
<LabeledList.Item label="Aquarium Mode">
<Dropdown
width="80%"
selected={currentMode}
options={aquariumModes}
onSelected={(value) =>
act('change_mode', { new_mode: value })
}
/>
<Tooltip content={currentTooltip}>
<Icon
name="question-circle"
color="blue"
size={1.5}
m={0.5}
/>
</Tooltip>
</LabeledList.Item>
<LabeledList.Item label="Feeding Interval">
<NumberInput
@@ -355,3 +375,16 @@ const Settings = (props) => {
function dissectName(input: string): string {
return input.split(' ')[0].slice(0, 18);
}
const LockedSection = () => {
return (
<Dimmer>
<Stack align="baseline" vertical>
<Stack ml={-2}>
<Icon color="red" name="lock" size={3} />
</Stack>
<Stack.Item fontSize="20px">LOCKED</Stack.Item>
</Stack>
</Dimmer>
);
};