[MIRROR] Aquariums now influence area beauty based on their contents. (#28703)

* Aquariums now influence area beauty based on their contents. (#84677)

## About The Pull Request
Aquariums now have a beauty element based on their contents. Props give
a fixed beauty value, while for fish it depends on whether they're alive
or not. Some fishes have a better score than others. A few, like the
ratfish, even have negative beauty.

Mobs with the morbid trait now get a positive moodlet when admiring an
aquarium full of dead fish, and a negative one when fish are alive.

Also, this brings a few improvements to bits of aquarium code. Namely
the removal of a GetComponent call and a few balloon alert calls.

## Why It's Good For The Game
Aquariums can be quite decorative at times.

## Changelog

🆑
add: Aquariums now influence the beauty score of an area based on their
contents.
add: Morbid people now get a positive moodlet when admiring an aquarium
full of dead fish, and a negative one when fish are alive.
/🆑

* Aquariums now influence area beauty based on their contents.

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
SkyratBot
2024-07-08 14:49:34 +05:30
committed by GitHub
co-authored by Ghom
parent 555857b062
commit 5afa528134
12 changed files with 186 additions and 39 deletions
@@ -1,6 +1,12 @@
// Aquarium related signals
#define COMSIG_AQUARIUM_SURFACE_CHANGED "aquarium_surface_changed"
#define COMSIG_AQUARIUM_FLUID_CHANGED "aquarium_fluid_changed"
///Called on aquarium/attackby: (aquarium)
#define COMSIG_TRY_INSERTING_IN_AQUARIUM "item_try_inserting_in_aquarium"
///The item will be inserted into the aquarium
#define COMSIG_CAN_INSERT_IN_AQUARIUM (1<<0)
///The item won't be inserted into the aquarium, but will early return attackby anyway.
#define COMSIG_CANNOT_INSERT_IN_AQUARIUM (1<<1)
// Fish signals
#define COMSIG_FISH_STATUS_CHANGED "fish_status_changed"
+9
View File
@@ -152,3 +152,12 @@
#define ELECTROGENESIS_DURATION 40 SECONDS
/// a random range the electrogenesis cooldown varies by
#define ELECTROGENESIS_VARIANCE (rand(-10 SECONDS, 10 SECONDS))
#define FISH_BEAUTY_DISGUSTING -500
#define FISH_BEAUTY_UGLY -300
#define FISH_BEAUTY_BAD -200
#define FISH_BEAUTY_NULL 0
#define FISH_BEAUTY_GENERIC 250
#define FISH_BEAUTY_GOOD 450
#define FISH_BEAUTY_GREAT 600
#define FISH_BEAUTY_EXCELLENT 700
+59 -5
View File
@@ -1,3 +1,14 @@
///Malus to the beauty value if the fish content is dead
#define DEAD_FISH_BEAUTY -500
///Prevents more impressive fishes from providing a positive beauty even when dead.
#define MAX_DEAD_FISH_BEAUTY -200
///Some fish are already so ugly, they can't get much worse when dead
#define MIN_DEAD_FISH_BEAUTY -600
///Defines that clamp the beauty of the aquarium, to prevent it from making most areas great or horrid all by itself.
#define MIN_AQUARIUM_BEAUTY -3500
#define MAX_AQUARIUM_BEAUTY 6000
/// Allows movables to be inserted/displayed in aquariums.
/datum/component/aquarium_content
/// Keeps track of our current aquarium.
@@ -60,13 +71,19 @@
/// Signals of the parent that will trigger animation update
var/animation_update_signals
/// The current beauty this component gives to the aquarium it's in
var/beauty
/datum/component/aquarium_content/Initialize(icon, animation_getter, animation_update_signals)
/// The original value of the beauty this component had when initialized
var/original_beauty
/datum/component/aquarium_content/Initialize(icon, animation_getter, animation_update_signals, beauty)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
src.animation_getter = animation_getter
src.animation_update_signals = animation_update_signals
src.beauty = original_beauty = beauty
if(animation_update_signals)
RegisterSignals(parent, animation_update_signals, PROC_REF(generate_animation))
@@ -78,6 +95,7 @@
InitializeOther()
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))
//If component is added to something already in aquarium at the time initialize it properly.
@@ -109,6 +127,18 @@
randomize_position = TRUE
RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_status_changed))
/datum/component/aquarium_content/proc/on_fish_status_changed(obj/item/fish/source)
SIGNAL_HANDLER
var/old_beauty = beauty
beauty = original_beauty
if(source.status == FISH_DEAD)
beauty = clamp(beauty + DEAD_FISH_BEAUTY, MIN_DEAD_FISH_BEAUTY, MAX_DEAD_FISH_BEAUTY)
if(current_aquarium)
change_aquarium_beauty(beauty - old_beauty)
generate_animation()
/// Sets visuals properties for fish
/datum/component/aquarium_content/proc/InitializeFromProp()
var/obj/item/aquarium_prop/prop = parent
@@ -150,15 +180,16 @@
if(istype(movable_parent.loc, /obj/structure/aquarium))
on_inserted(movable_parent.loc)
/datum/component/aquarium_content/proc/is_ready_to_insert(obj/structure/aquarium/aquarium)
/datum/component/aquarium_content/proc/is_ready_to_insert(datum/source, obj/structure/aquarium/aquarium)
SIGNAL_HANDLER
//This is kinda awful but we're unaware of other fish
if(unique)
for(var/atom/movable/fish_or_prop in aquarium)
if(fish_or_prop == parent)
continue
if(fish_or_prop.type == parent.type)
return FALSE
return TRUE
return COMSIG_CANNOT_INSERT_IN_AQUARIUM
return COMSIG_CAN_INSERT_IN_AQUARIUM
/datum/component/aquarium_content/proc/on_inserted(atom/aquarium)
current_aquarium = aquarium
@@ -180,6 +211,22 @@
//Finally add it to to objects vis_contents
current_aquarium.vis_contents |= vc_obj
change_aquarium_beauty(beauty)
///Modifies the beauty of the aquarium when content is added or removed, or when fishes die or live again somehow.
/datum/component/aquarium_content/proc/change_aquarium_beauty(change)
if(QDELETED(current_aquarium) || !change)
return
var/old_clamped_beauty = clamp(current_aquarium.current_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY)
current_aquarium.current_beauty += change
var/new_clamped_beauty = clamp(current_aquarium.current_beauty, MIN_AQUARIUM_BEAUTY, MAX_AQUARIUM_BEAUTY)
if(new_clamped_beauty == old_clamped_beauty)
return
if(current_aquarium.current_beauty)
current_aquarium.RemoveElement(/datum/element/beauty, current_aquarium.current_beauty)
if(current_aquarium.current_beauty)
current_aquarium.AddElement(/datum/element/beauty, current_aquarium.current_beauty)
/// Aquarium surface changed in some way, we need to recalculate base position and aninmation
/datum/component/aquarium_content/proc/on_surface_changed()
SIGNAL_HANDLER
@@ -293,13 +340,20 @@
vc_obj.pixel_x = base_px
vc_obj.pixel_y = base_py
/datum/component/aquarium_content/proc/on_removed(datum/source, atom/movable/gone, direction)
/datum/component/aquarium_content/proc/on_removed(obj/structure/aquarium/source, atom/movable/gone, direction)
SIGNAL_HANDLER
if(parent != gone)
return
remove_from_aquarium()
/datum/component/aquarium_content/proc/remove_from_aquarium()
change_aquarium_beauty(-beauty)
UnregisterSignal(current_aquarium, list(COMSIG_AQUARIUM_SURFACE_CHANGED, COMSIG_AQUARIUM_FLUID_CHANGED, COMSIG_ATOM_ATTACKBY, COMSIG_ATOM_EXITED))
remove_visual_from_aquarium()
current_aquarium = null
#undef DEAD_FISH_BEAUTY
#undef MIN_DEAD_FISH_BEAUTY
#undef MAX_DEAD_FISH_BEAUTY
#undef MIN_AQUARIUM_BEAUTY
#undef MAX_AQUARIUM_BEAUTY
+9
View File
@@ -37,6 +37,11 @@ Intended to push a creepy, mad scientist/doctor vibe, or someone who is downrigh
mood_change = 3
timeout = 6 MINUTES
/datum/mood_event/morbid_aquarium_good
description = "Eh eh, all the fish are sleeping..."
mood_change = 3
timeout = 90 SECONDS
// Negative Events - We helped someone stay alive.
/datum/mood_event/morbid_tend_wounds
@@ -49,3 +54,7 @@ Intended to push a creepy, mad scientist/doctor vibe, or someone who is downrigh
mood_change = -6
timeout = 2 MINUTES
/datum/mood_event/morbid_aquarium_bad
description = "Watching fish in an aquarium is lousy."
mood_change = -3
timeout = 90 SECONDS
+40 -24
View File
@@ -53,6 +53,9 @@
/// /obj/item/fish in the aquarium, sorted by type - does not include things with aquarium visuals that are not fish
var/list/tracked_fish_by_type
/// Var used to keep track of the current beauty of the aquarium, which can be throughfully changed by aquarium content.
var/current_beauty = 150
/obj/structure/aquarium/Initialize(mapload)
. = ..()
update_appearance()
@@ -62,6 +65,8 @@
create_reagents(6, SEALED_CONTAINER)
RegisterSignal(reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed))
AddComponent(/datum/component/plumbing/aquarium)
if(current_beauty)
AddElement(/datum/element/beauty, current_beauty)
ADD_KEEP_TOGETHER(src, INNATE_TRAIT)
/obj/structure/aquarium/proc/track_if_fish(atom/source, atom/initialized)
@@ -192,9 +197,9 @@
var/obj/item/stack/sheet/glass/glass = item
if(istype(glass))
if(glass.get_amount() < 2)
to_chat(user, span_warning("You need two glass sheets to fix the case!"))
balloon_alert(user, "it needs two sheets!")
return
to_chat(user, span_notice("You start fixing [src]..."))
balloon_alert(user, "fixing the aquarium...")
if(do_after(user, 2 SECONDS, target = src))
glass.use(2)
broken = FALSE
@@ -202,10 +207,18 @@
update_appearance()
return TRUE
else
var/datum/component/aquarium_content/content_component = item.GetComponent(/datum/component/aquarium_content)
if(content_component && content_component.is_ready_to_insert(src) && user.transferItemToLoc(item, src))
update_appearance()
return TRUE
var/insert_attempt = SEND_SIGNAL(item, COMSIG_TRY_INSERTING_IN_AQUARIUM, src)
switch(insert_attempt)
if(COMSIG_CAN_INSERT_IN_AQUARIUM)
if(!user.transferItemToLoc(item, src))
user.balloon_alert(user, "stuck to your hand!")
return TRUE
balloon_alert(user, "added to aquarium")
update_appearance()
return TRUE
if(COMSIG_CANNOT_INSERT_IN_AQUARIUM)
balloon_alert(user, "cannot add to aquarium!")
return TRUE
if(istype(item, /obj/item/fish_feed) && !panel_open)
if(!item.reagents.total_volume)
@@ -270,24 +283,27 @@
///Apply mood bonus depending on aquarium status
/obj/structure/aquarium/proc/admire(mob/living/user)
to_chat(user,span_notice("You take a moment to watch [src]."))
if(do_after(user, 5 SECONDS, target = src))
var/alive_fish = 0
var/dead_fish = 0
var/list/tracked_fish = get_fishes()
for(var/obj/item/fish/fish in tracked_fish)
if(fish.status == FISH_ALIVE)
alive_fish++
else
dead_fish++
//Check if there are live fish - good mood
//All fish dead - bad mood.
//No fish - nothing.
if(alive_fish > 0)
user.add_mood_event("aquarium", /datum/mood_event/aquarium_positive)
else if(dead_fish > 0)
user.add_mood_event("aquarium", /datum/mood_event/aquarium_negative)
// Could maybe scale power of this mood with number/types of fish
user.balloon_alert(user, "admiring aquarium...")
if(!do_after(user, 5 SECONDS, target = src))
return
var/alive_fish = 0
var/dead_fish = 0
var/list/tracked_fish = get_fishes()
for(var/obj/item/fish/fish in tracked_fish)
if(fish.status == FISH_ALIVE)
alive_fish++
else
dead_fish++
var/morb = HAS_TRAIT(user, TRAIT_MORBID)
//Check if there are live fish - good mood
//All fish dead - bad mood.
//No fish - nothing.
if(alive_fish > 0)
user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_bad : /datum/mood_event/aquarium_positive)
else if(dead_fish > 0)
user.add_mood_event("aquarium", morb ? /datum/mood_event/morbid_aquarium_good : /datum/mood_event/aquarium_negative)
// Could maybe scale power of this mood with number/types of fish
/obj/structure/aquarium/ui_data(mob/user)
. = ..()
@@ -110,10 +110,11 @@
w_class = WEIGHT_CLASS_TINY
var/layer_mode = AQUARIUM_LAYER_MODE_BOTTOM
var/beauty = 150
/obj/item/aquarium_prop/Initialize(mapload)
. = ..()
AddComponent(/datum/component/aquarium_content, icon)
AddComponent(/datum/component/aquarium_content, icon, beauty = beauty)
/obj/item/aquarium_prop/rocks
name = "rocks"
@@ -24,6 +24,8 @@
icon_state = "bioelec_map"
icon_prefix = "bioelec"
current_beauty = 0
/obj/structure/aquarium/bioelec_gen/zap_act(power, zap_flags)
var/explosive = zap_flags & ZAP_MACHINE_EXPLOSIVE
if(!explosive)
+4 -1
View File
@@ -143,9 +143,12 @@
/// power of the tesla zap created by the fish in a bioelectric generator
var/electrogenesis_power = 10 MEGA JOULES
/// The beauty this fish provides to the aquarium it's inserted in.
var/beauty = FISH_BEAUTY_GENERIC
/obj/item/fish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
AddComponent(/datum/component/aquarium_content, icon, PROC_REF(get_aquarium_animation), list(COMSIG_FISH_STATUS_CHANGED,COMSIG_FISH_STIRRED))
AddComponent(/datum/component/aquarium_content, icon, PROC_REF(get_aquarium_animation), list(COMSIG_FISH_STIRRED), beauty)
RegisterSignal(src, COMSIG_ATOM_ON_LAZARUS_INJECTOR, PROC_REF(use_lazarus))
if(do_flop_animation)
+25 -8
View File
@@ -19,6 +19,8 @@
desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case."
stable_population = 1
random_case_rarity = FISH_RARITY_NOPE
show_in_catalog = FALSE
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/angelfish
name = "angelfish"
@@ -77,6 +79,7 @@
)
required_temperature_min = MIN_AQUARIUM_TEMP+12
required_temperature_max = MIN_AQUARIUM_TEMP+30
beauty = FISH_BEAUTY_GOOD
// Saltwater fish below
@@ -107,6 +110,7 @@
evolution_types = null
compatible_types = list(/obj/item/fish/clownfish)
food = /datum/reagent/lube
beauty = FISH_BEAUTY_GREAT
/obj/item/fish/cardinal
name = "cardinalfish"
@@ -163,8 +167,9 @@
stable_population = 3
required_temperature_min = MIN_AQUARIUM_TEMP+23
required_temperature_max = MIN_AQUARIUM_TEMP+28
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/toxic)
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/lanternfish
name = "lanternfish"
@@ -182,6 +187,7 @@
fish_traits = list(/datum/fish_trait/nocturnal)
required_temperature_min = MIN_AQUARIUM_TEMP+2 //My source is that the water at a depth 6600 feet is pretty darn cold.
required_temperature_max = MIN_AQUARIUM_TEMP+18
beauty = FISH_BEAUTY_NULL
//Tiziran Fish
/obj/item/fish/dwarf_moonfish
@@ -195,6 +201,7 @@
average_weight = 2000
required_temperature_min = MIN_AQUARIUM_TEMP+20
required_temperature_max = MIN_AQUARIUM_TEMP+30
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/gunner_jellyfish
name = "gunner jellyfish"
@@ -205,6 +212,7 @@
fillet_type = /obj/item/food/fishmeat/gunner_jellyfish
required_temperature_min = MIN_AQUARIUM_TEMP+24
required_temperature_max = MIN_AQUARIUM_TEMP+32
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/needlefish
name = "needlefish"
@@ -261,6 +269,7 @@
)
evolution_types = list(/datum/fish_evolution/ice_chrab)
compatible_types = list(/obj/item/fish/chasm_crab/ice)
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/chasm_crab/ice
name = "arctic chrab"
@@ -271,13 +280,7 @@
required_temperature_max = MIN_AQUARIUM_TEMP+15
evolution_types = list(/datum/fish_evolution/chasm_chrab)
compatible_types = list(/obj/item/fish/chasm_crab)
/obj/item/storage/box/fish_debug
name = "box full of fish"
/obj/item/storage/box/fish_debug/PopulateContents()
for(var/fish_type in subtypesof(/obj/item/fish))
new fish_type(src)
beauty = FISH_BEAUTY_GREAT
/obj/item/fish/donkfish
name = "donk co. company patent donkfish"
@@ -290,6 +293,7 @@
fish_traits = list(/datum/fish_trait/yucky)
required_temperature_min = MIN_AQUARIUM_TEMP+15
required_temperature_max = MIN_AQUARIUM_TEMP+28
beauty = FISH_BEAUTY_EXCELLENT
/obj/item/fish/emulsijack
name = "toxic emulsijack"
@@ -301,6 +305,7 @@
fish_traits = list(/datum/fish_trait/emulsijack)
required_temperature_min = MIN_AQUARIUM_TEMP+5
required_temperature_max = MIN_AQUARIUM_TEMP+40
beauty = FISH_BEAUTY_BAD
/obj/item/fish/jumpercable
name = "monocloning jumpercable"
@@ -322,6 +327,7 @@
/datum/fish_trait/mixotroph,
/datum/fish_trait/electrogenesis,
)
beauty = FISH_BEAUTY_UGLY
/obj/item/fish/ratfish
name = "ratfish"
@@ -341,6 +347,7 @@
"Value" = DAIRY
)
)
beauty = FISH_BEAUTY_DISGUSTING
/obj/item/fish/ratfish/Initialize(mapload)
. = ..()
@@ -364,6 +371,7 @@
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+40
evolution_types = list(/datum/fish_evolution/purple_sludgefish)
beauty = FISH_BEAUTY_NULL
/obj/item/fish/sludgefish/purple
name = "purple sludgefish"
@@ -401,6 +409,7 @@
),
)
required_temperature_min = MIN_AQUARIUM_TEMP+20
beauty = FISH_BEAUTY_GREAT
/obj/item/fish/boned
name = "unmarine bonemass"
@@ -423,6 +432,7 @@
average_weight = 2000
death_text = "%SRC stops moving." //It's dead... or is it?
evolution_types = list(/datum/fish_evolution/mastodon)
beauty = FISH_BEAUTY_UGLY
/obj/item/fish/mastodon
name = "unmarine mastodon"
@@ -451,6 +461,7 @@
average_weight = 5000
death_text = "%SRC stops moving."
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/revival, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
beauty = FISH_BEAUTY_BAD
/obj/item/fish/holo
name = "holographic goldfish"
@@ -501,6 +512,7 @@
sprite_height = 8
average_size = 60
average_weight = 1000
beauty = FISH_BEAUTY_GOOD
/obj/item/fish/holo/angel
name = "holographic angelfish"
@@ -524,6 +536,7 @@
icon_state = "checkered" //it's a meta joke, buddy.
dedicated_in_aquarium_icon_state = "checkered_small"
sprite_width = 4
beauty = FISH_BEAUTY_NULL
/obj/item/fish/holo/halffish
name = "holographic half-fish"
@@ -533,6 +546,7 @@
sprite_height = 4
sprite_width = 10
average_size = 50
beauty = FISH_BEAUTY_UGLY
/obj/item/fish/starfish
name = "cosmostarfish"
@@ -554,6 +568,7 @@
grind_results = list(/datum/reagent/bluespace = 10, /datum/reagent/consumable/liquidgibs = 5)
fillet_type = null
fish_traits = list(/datum/fish_trait/antigrav, /datum/fish_trait/mixotroph)
beauty = FISH_BEAUTY_GREAT
/obj/item/fish/starfish/Initialize(mapload)
. = ..()
@@ -589,6 +604,7 @@
)
hitsound = null
throwforce = 5
beauty = FISH_BEAUTY_GOOD
///maximum bonus damage when winded up
var/maximum_bonus = 25
@@ -662,3 +678,4 @@
)
//anxiety naturally limits the amount of zipzaps per tank, so they are stronger alone
electrogenesis_power = 20 MEGA JOULES
beauty = FISH_BEAUTY_GOOD
+19
View File
@@ -37,6 +37,25 @@
else
fish_data["feed"] = "[AQUARIUM_COMPANY] Fish Feed"
fish_data["fishing_tips"] = build_fishing_tips(fish)
var/beauty_score = initial(fish.beauty)
switch(beauty_score)
if(-INFINITY to FISH_BEAUTY_DISGUSTING)
beauty_score = "OH HELL NAW!"
if(FISH_BEAUTY_DISGUSTING to FISH_BEAUTY_UGLY)
beauty_score = "☆☆☆☆☆"
if(FISH_BEAUTY_UGLY to FISH_BEAUTY_BAD)
beauty_score = "★☆☆☆☆"
if(FISH_BEAUTY_BAD to FISH_BEAUTY_NULL)
beauty_score = "★★☆☆☆"
if(FISH_BEAUTY_NULL to FISH_BEAUTY_GENERIC)
beauty_score = "★★★☆☆"
if(FISH_BEAUTY_GENERIC to FISH_BEAUTY_GOOD)
beauty_score = "★★★★☆"
if(FISH_BEAUTY_GOOD to FISH_BEAUTY_GREAT)
beauty_score = "★★★★★"
if(FISH_BEAUTY_GREAT to INFINITY)
beauty_score = "★★★★★★"
fish_data["beauty"] = beauty_score
fish_info += list(fish_data)
// TODO: Custom entries for unusual stuff
@@ -304,5 +304,12 @@
new /obj/item/fishing_line/reinforced(src)
new /obj/item/fishing_line/cloaked(src)
/obj/item/storage/box/fish_debug
name = "box full of fish"
/obj/item/storage/box/fish_debug/PopulateContents()
for(var/fish_type in subtypesof(/obj/item/fish))
new fish_type(src)
#undef MAGNET_HOOK_BONUS_MULTIPLIER
#undef RESCUE_HOOK_FISH_MULTIPLIER
@@ -27,6 +27,7 @@ type FishInfo = {
weight: string;
size: string;
icon: string;
beauty: string;
};
type FishCatalogData = {
@@ -93,6 +94,9 @@ export const FishCatalog = (props) => {
<LabeledList.Item label="Average weight">
{currentFish.weight} g
</LabeledList.Item>
<LabeledList.Item label="Aquarium Beauty Score">
{currentFish.beauty}
</LabeledList.Item>
<LabeledList.Item label="Fishing and Aquarium tips">
<LabeledList>
<LabeledList.Item label="Fishing locations">