From 9f686f5cc63aa1ecbce689dfc82dc6969061e8fb Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:44:36 +0300 Subject: [PATCH] new aquarium ui and few new additions (#85627) ## About The Pull Request gives the aquarium a new ui: ![image](https://github.com/user-attachments/assets/e00b16dd-e457-4742-95c3-c68dfeac9bc5) u can now also pet and nickname ur fish through this interface. petting them will have them do a small dance and increase their happiness ![image](https://github.com/user-attachments/assets/150528f0-befc-47ea-8dbc-01052bfb702d) the hearts indicate how happy the fish is with the tank's living conditions and if theyve been petted recently. ## Why It's Good For The Game gives aquariums a better UI making it easier to use ## Changelog :cl: qol: gives aquariums a new easier to use UI /:cl: --- code/__DEFINES/dcs/signals/signals_fish.dm | 2 + code/datums/components/aquarium_content.dm | 8 +- code/modules/fishing/aquarium/aquarium.dm | 52 ++- code/modules/fishing/fish/_fish.dm | 33 ++ tgui/packages/tgui/interfaces/Aquarium.tsx | 409 ++++++++++++++++----- 5 files changed, 405 insertions(+), 99 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_fish.dm b/code/__DEFINES/dcs/signals/signals_fish.dm index 7980aa7ca1e..8af3b8dfca7 100644 --- a/code/__DEFINES/dcs/signals/signals_fish.dm +++ b/code/__DEFINES/dcs/signals/signals_fish.dm @@ -17,6 +17,8 @@ #define COMSIG_FISH_EATEN_BY_OTHER_FISH "fish_eaten_by_other_fish" ///From /obj/item/fish/feed: (fed_reagents, fed_reagent_type) #define COMSIG_FISH_FED "fish_on_fed" +///from /obj/item/fish/pet_fish +#define COMSIG_FISH_PETTED "fish_petted" ///From /obj/item/fish/update_size_and_weight: (new_size, new_weight) #define COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT "fish_update_size_and_weight" ///From /obj/item/fish/update_fish_force: (weight_rank, bonus_malus) diff --git a/code/datums/components/aquarium_content.dm b/code/datums/components/aquarium_content.dm index 21c6c75ca16..feb3275ba6c 100644 --- a/code/datums/components/aquarium_content.dm +++ b/code/datums/components/aquarium_content.dm @@ -24,7 +24,6 @@ //Current layer for the visual object var/base_layer - /** * Fish sprite how to: * Need to be centered on 16,16 in the dmi and facing left by default. @@ -97,6 +96,7 @@ ADD_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, REF(src)) RegisterSignal(parent, COMSIG_TRY_INSERTING_IN_AQUARIUM, PROC_REF(is_ready_to_insert)) RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(enter_aquarium)) + RegisterSignal(parent, COMSIG_FISH_PETTED, PROC_REF(on_fish_petted)) //If component is added to something already in aquarium at the time initialize it properly. var/atom/movable/movable_parent = parent @@ -274,7 +274,6 @@ dead_animation() return - /// Create looping random path animation, pixel offsets parameters include offsets already /datum/component/aquarium_content/proc/swim_animation() var/avg_width = round(sprite_width / 2) @@ -325,6 +324,11 @@ base_layer = current_aquarium.request_layer(layer_mode) vc_obj.layer = base_layer +/datum/component/aquarium_content/proc/on_fish_petted() + SIGNAL_HANDLER + + new /obj/effect/temp_visual/heart(get_turf(parent)) + /datum/component/aquarium_content/proc/randomize_base_position() var/list/aq_properties = current_aquarium.get_surface_properties() var/avg_width = round(sprite_width / 2) diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm index d4ea9230568..4dab79791ca 100644 --- a/code/modules/fishing/aquarium/aquarium.dm +++ b/code/modules/fishing/aquarium/aquarium.dm @@ -307,14 +307,30 @@ /obj/structure/aquarium/ui_data(mob/user) . = ..() - .["fluid_type"] = fluid_type + .["fluidType"] = fluid_type .["temperature"] = fluid_temp - .["allow_breeding"] = allow_breeding - .["feeding_interval"] = feeding_interval / (1 MINUTES) - var/list/content_data = list() - for(var/atom/movable/fish in contents) - content_data += list(list("name"=fish.name,"ref"=ref(fish))) - .["contents"] = content_data + .["allowBreeding"] = allow_breeding + .["FishData"] = list() + .["feedingInterval"] = feeding_interval / (1 MINUTES) + .["PropData"] = list() + for(var/atom/movable/item in contents) + if(isfish(item)) + var/obj/item/fish/fish = item + .["fishData"] += list(list( + "fish_ref" = REF(fish), + "fish_name" = fish.name, + "fish_happiness" = fish.get_happiness_value(), + "fish_icon" = fish::icon, + "fish_icon_state" = fish::icon_state, + "fish_health" = fish.health, + )) + continue + .["propData"] += list(list( + "prop_ref" = REF(item), + "prop_name" = item.name, + "prop_icon" = item::icon, + "prop_icon_state" = item::icon_state, + )) /obj/structure/aquarium/ui_static_data(mob/user) . = ..() @@ -322,6 +338,7 @@ .["minTemperature"] = min_fluid_temp .["maxTemperature"] = max_fluid_temp .["fluidTypes"] = fluid_types + .["heart_icon"] = 'icons/effects/effects.dmi' /obj/structure/aquarium/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() @@ -345,14 +362,19 @@ if("feeding_interval") feeding_interval = params["feeding_interval"] MINUTES . = TRUE - if("remove") - var/atom/movable/inside = locate(params["ref"]) in contents - if(inside) - if(isitem(inside)) - user.put_in_hands(inside) - else - inside.forceMove(get_turf(src)) - to_chat(user,span_notice("You take out [inside] from [src].")) + if("pet_fish") + var/obj/item/fish/fish = locate(params["fish_reference"]) in contents + fish?.pet_fish(user) + if("remove_item") + var/atom/movable/item = locate(params["item_reference"]) in contents + item?.forceMove(drop_location()) + to_chat(user, span_notice("You take out [item] from [src].")) + if("rename_fish") + var/new_name = sanitize_name(params["chosen_name"]) + if(!new_name) + return + var/atom/movable/fish = locate(params["fish_reference"]) in contents + fish.name = new_name /obj/structure/aquarium/ui_interact(mob/user, datum/tgui/ui) . = ..() diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm index 7b112783457..1ff70599071 100644 --- a/code/modules/fishing/fish/_fish.dm +++ b/code/modules/fishing/fish/_fish.dm @@ -1,3 +1,9 @@ +#define FISH_SAD 0 +#define FISH_NEUTRAL 1 +#define FISH_SATISFIED 2 +#define FISH_HAPPY 3 +#define FISH_VERY_HAPPY 4 + // Fish path used for autogenerated fish /obj/item/fish name = "generic looking aquarium fish" @@ -147,6 +153,8 @@ /// The beauty this fish provides to the aquarium it's inserted in. var/beauty = FISH_BEAUTY_GENERIC + ///have we recently pet this fish + var/recently_petted = FALSE /obj/item/fish/Initialize(mapload, apply_qualities = TRUE) . = ..() @@ -755,6 +763,31 @@ if(HAS_TRAIT(src, TRAIT_FISH_FROM_CASE)) //Avoid printing money by simply ordering fish and sending it back. calculated_price *= 0.05 return round(calculated_price) +/obj/item/fish/proc/get_happiness_value() + var/happiness_value = 0 + if(recently_petted) + happiness_value++ + if(HAS_TRAIT(src, TRAIT_FISH_NO_HUNGER) || min((world.time - last_feeding) / feeding_frequency, 1) < 0.5) + happiness_value++ + var/obj/structure/aquarium/aquarium = loc + if(!istype(aquarium)) + return happiness_value + if(compatible_fluid_type(required_fluid_type, aquarium.fluid_type)) + happiness_value++ + if(ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max)) + happiness_value++ + return happiness_value + +/obj/item/fish/proc/pet_fish(mob/living/user) + if(recently_petted) + to_chat(user, span_warning("[src] runs away from your finger as you dip it into the water!")) + return + if(electrogenesis_power > 15 MEGA JOULES) + user.electrocute_act(5, src) //was it all worth it? + recently_petted = TRUE + SEND_SIGNAL(src, COMSIG_FISH_PETTED) + to_chat(user, span_notice("[src] dances around!")) + addtimer(VARSET_CALLBACK(src, recently_petted, FALSE), 30 SECONDS) /// Returns random fish, using random_case_rarity probabilities. /proc/random_fish_type(required_fluid) diff --git a/tgui/packages/tgui/interfaces/Aquarium.tsx b/tgui/packages/tgui/interfaces/Aquarium.tsx index 6f29920fc0b..90030afbfb4 100644 --- a/tgui/packages/tgui/interfaces/Aquarium.tsx +++ b/tgui/packages/tgui/interfaces/Aquarium.tsx @@ -1,10 +1,15 @@ +import { capitalizeFirst } from 'common/string'; import { + Box, Button, + DmIcon, Flex, + Icon, Knob, - LabeledControls, + LabeledList, NumberInput, Section, + Stack, } from 'tgui-core/components'; import { BooleanLike } from 'tgui-core/react'; @@ -13,98 +18,338 @@ import { Window } from '../layouts'; type Data = { temperature: number; - fluid_type: string; + fluidType: string; minTemperature: number; maxTemperature: number; fluidTypes: string[]; - contents: { ref: string; name: string }[]; - allow_breeding: BooleanLike; - feeding_interval: number; + fishData: FishData[]; + propData: PropData[]; + allowBreeding: BooleanLike; + feedingInterval: number; + heartIcon: string; + heartIconState: string; + heartEmptyIconState: string; +}; + +type FishData = { + fish_ref: string; + fish_name: string; + fish_happiness: number; + fish_icon: string; + fish_icon_state: string; + fish_health: number; +}; + +type PropData = { + prop_ref: string; + prop_name: string; + prop_icon: string; + prop_icon_state: string; }; export const Aquarium = (props) => { const { act, data } = useBackend(); - const { - temperature, - fluid_type, - minTemperature, - maxTemperature, - fluidTypes, - contents, - allow_breeding, - feeding_interval, - } = data; + const { fishData } = data; return ( - + -
- - - - act('temperature', { - temperature: value, - }) - } - /> - - - - {fluidTypes.map((f) => ( - -
-
- {contents.map((movable) => ( -
+ + + + + + + +
+ + {fishData.map((fish) => ( + + + + ))} + +
+
+ + + +
+
+
); }; + +const FishInfo = (props) => { + const { act, data } = useBackend(); + const { fish } = props; + + return ( + + + + + + + + + + {fish.fish_name.toUpperCase()} + + 0 ? -4 : 1}> + {(fish.fish_health > 0 && ( + + )) || } + + + + + + + + + { + act('rename_fish', { + fish_reference: fish.fish_ref, + chosen_name: value, + }); + }} + style={{ + padding: '3px', + borderRadius: '1em', + background: '#151326', + }} + > + + + + + Rename + + + + + + + ); +}; + +const PropTypes = (props) => { + const { act, data } = useBackend(); + const { propData } = data; + + return ( +
+ + {propData.map((prop) => ( + + + + ))} + +
+ ); +}; + +const CalculateHappiness = (props) => { + const { data } = useBackend(); + const { heartIcon } = data; + const { happiness } = props; + + return ( + + {Array.from({ length: 5 }, (_, index) => ( + = index ? 'full_heart' : 'empty_heart'} + height="48px" + width="48px" + /> + ))} + + ); +}; + +const Settings = (props) => { + const { act, data } = useBackend(); + const { + temperature, + minTemperature, + maxTemperature, + fluidTypes, + fluidType, + allowBreeding, + feedingInterval, + } = data; + + return ( + + +
+ + act('temperature', { + temperature: value, + }) + } + /> +
+
+ +
+ + {fluidTypes.map((f) => ( + + + + ))} + +
+
+ +
+ + + +
+
+
+ ); +}; +function dissectName(input: string): string { + return input.split(' ')[0].slice(0, 18); +}