mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 16:47:13 +01:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user