Skip to content

Commit

Permalink
Refactoring aquariums into components (feat: portable fish tanks) (tg…
Browse files Browse the repository at this point in the history
…station#87866)

## About The Pull Request
I've been meaning to do this for some time. I need this for
portable/handheld aquariums/fishtanks to be possible. I'll sprite and
code them before I call this PR ready, however suggestions and code
reviews are welcome in the meantime.

Being a pretty heavy refactor, some things might break (we have more
than a few unit tests so perhaps not) while others, coincidentally,
might be fixed without me knowing. Anyway I'm sure this PR fixes
aquarium beauty, which wasn't really working to begin with because the
code was so fucking bad. Nothing really worth of a CL entry tho.


TODO:
- [x] handheld aquariums, craftable with a kit and little plastic or
buyable from the fun vendor ig.
- [x] an aquarium upgrade for handheld aquariums to bypass possible
restrictions.
- [x] update the beauty element to consider items, which shouldn't
contribute to the area beauty when held or otherwise not on a turf.

## Why It's Good For The Game
This should make handheld aquariums possible.

## Changelog

:cl:
refactor: refactored aquariums heavily. Please report any fishy bug.
add: Added portable/handheld fish tanks to the game. They can be crafted
with an aquarium kit and 5 sheets of plastic. While portable, they
cannot store fish that are too big or if there're too many already. This
restriction can be removed by using the new "bluespace fish tank kit"
techweb item.
map: Replaced the lawyer's stationary pet aquarium with a fish tank, so
you can carry McGill around.
balance: Reduced the iron cost of stationary aquariums a little.
/:cl:
  • Loading branch information
Ghommie authored Nov 20, 2024
1 parent 941b117 commit 7663b39
Showing 58 changed files with 1,245 additions and 742 deletions.
3 changes: 2 additions & 1 deletion _maps/map_files/Birdshot/birdshot.dmm
Original file line number Diff line number Diff line change
@@ -50755,7 +50755,8 @@
"rqt" = (
/obj/machinery/airalarm/directional/north,
/obj/effect/decal/cleanable/dirt,
/obj/structure/aquarium/lawyer,
/obj/item/fish_tank/lawyer,
/obj/structure/table/wood,
/turf/open/floor/iron/dark,
/area/station/service/lawoffice)
"rqw" = (
3 changes: 2 additions & 1 deletion _maps/map_files/Deltastation/DeltaStation2.dmm
Original file line number Diff line number Diff line change
@@ -44890,7 +44890,8 @@
/area/station/hallway/primary/fore)
"lhp" = (
/obj/structure/sign/poster/official/report_crimes/directional/south,
/obj/structure/aquarium/lawyer,
/obj/item/fish_tank/lawyer,
/obj/structure/table/wood,
/turf/open/floor/wood,
/area/station/service/lawoffice)
"lhC" = (
3 changes: 2 additions & 1 deletion _maps/map_files/IceBoxStation/IceBoxStation.dmm
Original file line number Diff line number Diff line change
@@ -7969,7 +7969,8 @@
/turf/open/floor/iron/dark,
/area/station/service/hydroponics/garden)
"cgB" = (
/obj/structure/aquarium/lawyer,
/obj/item/fish_tank/lawyer,
/obj/structure/table/wood,
/turf/open/floor/wood,
/area/station/service/lawoffice)
"cgC" = (
3 changes: 2 additions & 1 deletion _maps/map_files/MetaStation/MetaStation.dmm
Original file line number Diff line number Diff line change
@@ -59697,7 +59697,8 @@
name = "Lawyer Requests Console"
},
/obj/machinery/newscaster/directional/west,
/obj/structure/aquarium/lawyer,
/obj/item/fish_tank/lawyer,
/obj/structure/table/wood,
/turf/open/floor/wood,
/area/station/service/lawoffice)
"uZP" = (
3 changes: 2 additions & 1 deletion _maps/map_files/NorthStar/north_star.dmm
Original file line number Diff line number Diff line change
@@ -65672,7 +65672,8 @@
/turf/open/floor/iron/white,
/area/station/science/lobby)
"qSD" = (
/obj/structure/aquarium/lawyer,
/obj/item/fish_tank/lawyer,
/obj/structure/table/wood,
/turf/open/floor/wood/parquet,
/area/station/service/lawoffice)
"qSJ" = (
3 changes: 2 additions & 1 deletion _maps/map_files/tramstation/tramstation.dmm
Original file line number Diff line number Diff line change
@@ -18865,7 +18865,8 @@
/area/station/commons/vacant_room)
"fEZ" = (
/obj/structure/noticeboard/directional/north,
/obj/structure/aquarium/lawyer,
/obj/item/fish_tank/lawyer,
/obj/structure/table/wood,
/obj/structure/cable,
/obj/machinery/power/apc/auto_name/directional/west,
/turf/open/floor/wood,
Original file line number Diff line number Diff line change
@@ -122,6 +122,9 @@
///from base of /atom/movable/point_at: (atom/A, obj/effect/temp_visual/point/point)
#define COMSIG_MOVABLE_POINTED "movable_pointed"

///From /datum/component/aquarium/get_content_beauty: (beauty_holder)
#define COMSIG_MOVABLE_GET_AQUARIUM_BEAUTY "movable_ge_aquarium_beauty"

/// Sent to movables when they are being stolen by a spy: (mob/living/spy, datum/spy_bounty/bounty)
#define COMSIG_MOVABLE_SPY_STEALING "movable_spy_stealing"
/// Called when something is pushed by a living mob bumping it: (mob/living/pusher, push force)
19 changes: 14 additions & 5 deletions code/__DEFINES/dcs/signals/signals_fish.dm
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
// Aquarium related signals
#define COMSIG_AQUARIUM_SURFACE_CHANGED "aquarium_surface_changed"

///From /datum/component/aquarium/ui_act, when changing the fluid of the aquarium: (fluid_type)
#define COMSIG_AQUARIUM_FLUID_CHANGED "aquarium_fluid_changed"
///Called on aquarium/attackby: (aquarium)
#define COMSIG_TRY_INSERTING_IN_AQUARIUM "item_try_inserting_in_aquarium"
///From /datum/component/aquarium/can_insert: (obj/item/item)
#define COMSIG_AQUARIUM_CAN_INSERT "aquarium_can_insert"
///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)
///From /datum/component/aquarium_content/set_vc_base_position: (obj/effect/aquarium/visual)
#define COMSIG_AQUARIUM_SET_VISUAL "aquarium_set_visual"
///From /datum/component/aquarium_content/remove_from_aquarium: (obj/effect/aquarium/visual)
#define COMSIG_AQUARIUM_REMOVE_VISUAL "aquarium_remove_visual"
///From /obj/item/fish/try_to_reproduce: (fish, candidates)
#define COMSIG_AQUARIUM_GET_REPRODUCTION_CANDIDATES "aquarium_get_reproduction_candidates"
///From /datum/fish_evolution/check_conditions: (fish, mate, evolution)
#define COMSIG_AQUARIUM_CHECK_EVOLUTION_CONDITIONS "aquarium_check_evolution_conditions"
#define COMPONENT_ALLOW_EVOLUTION (1<<0)

///Updates the appearance of a newly generated aquarium content visual:(visual)
#define COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE "aquarium_content_apply_appearance"
///Updates the base position of an aquarium content visual:(aquarium, visual)
#define AQUARIUM_CONTENT_RANDOMIZE_POSITION "aquarium_content_randomize_position"
#define COMSIG_AQUARIUM_CONTENT_RANDOMIZE_POSITION "aquarium_content_randomize_position"
///Updates the animation of an aquarium content visual:(aquarium, visual)
#define COMSIG_AQUARIUM_CONTENT_DO_ANIMATION "aquarium_content_do_animation"

// Fish signals
#define COMSIG_FISH_STATUS_CHANGED "fish_status_changed"
#define COMSIG_FISH_STIRRED "fish_stirred"
///From /obj/item/fish/process: (seconds_per_tick)
#define COMSIG_FISH_LIFE "fish_life"
///From /datum/fish_trait/eat_fish: (predator)
33 changes: 29 additions & 4 deletions code/__DEFINES/fish.dm
Original file line number Diff line number Diff line change
@@ -98,10 +98,19 @@
#define AQUARIUM_ANIMATION_FISH_SWIM "fish"
#define AQUARIUM_ANIMATION_FISH_DEAD "dead"

#define AQUARIUM_PROPERTIES_PX_MIN "px_min"
#define AQUARIUM_PROPERTIES_PX_MAX "px_max"
#define AQUARIUM_PROPERTIES_PY_MIN "py_min"
#define AQUARIUM_PROPERTIES_PY_MAX "py_max"
//standard layer defines for aquariums

///The distance that should separate each layer of the aquarium
#define AQUARIUM_LAYER_STEP 0.01
/// Aquarium content layer offsets
#define AQUARIUM_MIN_OFFSET 0.02
#define AQUARIUM_MAX_OFFSET 1
/// The layer of the glass overlay
#define AQUARIUM_GLASS_LAYER 0.02
/// The layer of the aquarium pane borders
#define AQUARIUM_BORDERS_LAYER AQUARIUM_MAX_OFFSET + AQUARIUM_LAYER_STEP
/// Layer for stuff rendered below the glass overlay
#define AQUARIUM_BELOW_GLASS_LAYER 0.01

#define AQUARIUM_LAYER_MODE_BOTTOM "bottom"
#define AQUARIUM_LAYER_MODE_TOP "top"
@@ -141,6 +150,16 @@
///Used to calculate how many bites a fish can take and therefore the amount of reagents it has.
#define FISH_WEIGHT_BITE_DIVISOR (FISH_GRIND_RESULTS_WEIGHT_DIVISOR * FISH_WEIGHT_GRIND_TO_BITE_MULT)

///Set of operations that calculate the slowdown of fish based on weight
#define GET_FISH_SLOWDOWN(weighty) round(((weighty/FISH_WEIGHT_SLOWDOWN_DIVISOR)**FISH_WEIGHT_SLOWDOWN_EXPONENT)-1.3, 0.1)

/**
* Gets a "rank" for fish weight to determine the force of the fish (or fish tank)
* basically, a gross estimate based on how weight generaly scales up (250, 500, 1000, 2000, 4000 etc...)
* for most fish
*/
#define GET_FISH_WEIGHT_RANK(weighty) max(round(1 + log(2, max(weighty/FISH_WEIGHT_FORCE_DIVISOR, 1)), 1), 1)

///The breeding timeout for newly instantiated fish is multiplied by this.
#define NEW_FISH_BREEDING_TIMEOUT_MULT 2
///The last feeding timestamp of newly instantiated fish is multiplied by this: ergo, they spawn 50% hungry.
@@ -158,6 +177,12 @@
#define FISH_FLAG_EXPERIMENT_SCANNABLE (1<<3)
///It lets us know that fish/update_size_and_weight() is currently running.
#define FISH_FLAG_UPDATING_SIZE_AND_WEIGHT (1<<4)
///Flag added when the population of this fish type exceeeds the stable population inside the aquarium
#define FISH_FLAG_OVERPOPULATED (1<<5)
///Flag added when in an aquarium which temperature is within its safe limits
#define FISH_FLAG_SAFE_TEMPERATURE (1<<6)
///Flag added when in an aquarium with the right fluid type.
#define FISH_FLAG_SAFE_FLUID (1<<7)


#define MIN_AQUARIUM_TEMP T0C
2 changes: 0 additions & 2 deletions code/__DEFINES/is_helpers.dm
Original file line number Diff line number Diff line change
@@ -241,8 +241,6 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(

#define isstructure(A) (istype(A, /obj/structure))

#define isaquarium(A) (istype(A, /obj/structure/aquarium))

#define ismachinery(A) (istype(A, /obj/machinery))

#define istramwall(A) (istype(A, /obj/structure/tram))
20 changes: 18 additions & 2 deletions code/__DEFINES/traits/declarations.dm
Original file line number Diff line number Diff line change
@@ -731,6 +731,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
///Mobs won't slip on a wet turf while it has this trait
#define TRAIT_TURF_IGNORE_SLIPPERY "turf_ignore_slippery"

///failsafe for whether an item with the beauty element is influencing the beauty of the area of not.
#define TRAIT_BEAUTY_APPLIED "beauty_applied"

/// Mobs with this trait can't send the mining shuttle console when used outside the station itself
#define TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION "forbid_mining_shuttle_console_outside_station"

@@ -799,8 +802,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_ROD_ATTRACT_SHINY_LOVERS "rod_attract_shiny_lovers"
/// This rod can be used to fish on lava
#define TRAIT_ROD_LAVA_USABLE "rod_lava_usable"
/// Stuff that can go inside fish cases
#define TRAIT_FISH_CASE_COMPATIBILE "fish_case_compatibile"
/// Stuff that can go inside fish cases and aquariums
#define TRAIT_AQUARIUM_CONTENT "aquarium_content"
/// If the item can be used as a bit.
#define TRAIT_FISHING_BAIT "fishing_bait"
/// This bait will kill any fish that doesn't have it on its favorite_bait list
@@ -818,6 +821,19 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
* instead, it'll allow the fishing dud to be there unless there's at least one fish that likes the bait
*/
#define TRAIT_BAIT_ALLOW_FISHING_DUD "bait_dont_affect_fishing_dud"
/**
* This location has the aquarium component. Not much different than a GetComponent()
* disguised as an 'is_x' macro, but I don't have to hide anything here.
* I just don't want a confusing 'is_aquarium(A)' macro which people think it's interchangable with
* an 'istype(A, /obj/structure/aquarium)' when it's the component what truly matters.
*/
#define TRAIT_IS_AQUARIUM "is_aquarium"
/// A location (probably aquarium) that amplifies the zaps of electricity-generating fish.
#define TRAIT_BIOELECTRIC_GENERATOR "bioelectric_generator"
/// A location (likely aquarium) that doesn't allow fish to growth and reproduce
#define TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH "stop_fish_reproduction_and_growth"
/// This is an aquarium with an open panel
#define TRAIT_AQUARIUM_PANEL_OPEN "aquarium_panel_open"
/// Plants that were mutated as a result of passive instability, not a mutation threshold.
#define TRAIT_PLANT_WILDMUTATE "wildmutation"
/// If you hit an APC with exposed internals with this item it will try to shock you
3 changes: 3 additions & 0 deletions code/__DEFINES/traits/sources.dm
Original file line number Diff line number Diff line change
@@ -309,3 +309,6 @@

/// Trait from an engraving
#define ENGRAVED_TRAIT "engraved"

/// From the aquarium component
#define AQUARIUM_TRAIT "aquarium"
2 changes: 1 addition & 1 deletion code/_compile_options.dm
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@
#endif // REFERENCE_TRACKING_STANDARD

// If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between
// #define UNIT_TESTS
//#define UNIT_TESTS

// If this is uncommented, will attempt to load and initialize prof.dll/libprof.so by default.
// Even if it's not defined, you can pass "tracy" via -params in order to try to load it.
3 changes: 3 additions & 0 deletions code/_globalvars/bitfields.dm
Original file line number Diff line number Diff line change
@@ -571,6 +571,9 @@ DEFINE_BITFIELD(fish_flags, list(
"FISH_DO_FLOP_ANIM" = FISH_DO_FLOP_ANIM,
"FISH_FLAG_PETTED" = FISH_FLAG_PETTED,
"FISH_FLAG_EXPERIMENT_SCANNABLE" = FISH_FLAG_EXPERIMENT_SCANNABLE,
"FISH_FLAG_UPDATING_SIZE_AND_WEIGHT" = FISH_FLAG_OVERPOPULATED,
"FISH_FLAG_SAFE_TEMPERATURE" = FISH_FLAG_SAFE_TEMPERATURE,
"FISH_FLAG_SAFE_FLUID" = FISH_FLAG_SAFE_FLUID,
))

DEFINE_BITFIELD(bot_mode_flags, list(
7 changes: 6 additions & 1 deletion code/_globalvars/traits/_traits.dm
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
/atom = list(
"TRAIT_AI_PAUSED" = TRAIT_AI_PAUSED,
"TRAIT_BANNED_FROM_CARGO_SHUTTLE" = TRAIT_BANNED_FROM_CARGO_SHUTTLE,
"TRAIT_BEAUTY_APPLIED" = TRAIT_BEAUTY_APPLIED,
"TRAIT_BEING_SHOCKED" = TRAIT_BEING_SHOCKED,
"TRAIT_CATCH_AND_RELEASE" = TRAIT_CATCH_AND_RELEASE,
"TRAIT_COMMISSIONED" = TRAIT_COMMISSIONED,
@@ -35,22 +36,24 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/atom/movable = list(
"TRAIT_ACTIVE_STORAGE" = TRAIT_ACTIVE_STORAGE,
"TRAIT_AQUARIUM_PANEL_OPEN" = TRAIT_AQUARIUM_PANEL_OPEN,
"TRAIT_AREA_SENSITIVE" = TRAIT_AREA_SENSITIVE,
"TRAIT_ASHSTORM_IMMUNE" = TRAIT_ASHSTORM_IMMUNE,
"TRAIT_BIOELECTRIC_GENERATOR" = TRAIT_BIOELECTRIC_GENERATOR,
"TRAIT_BLOCKING_EXPLOSIVES" = TRAIT_BLOCKING_EXPLOSIVES,
"TRAIT_BOULDER_BREAKER" = TRAIT_BOULDER_BREAKER,
"TRAIT_CASTABLE_LOC" = TRAIT_CASTABLE_LOC,
"TRAIT_CHASM_STOPPER" = TRAIT_CHASM_STOPPER,
"TRAIT_COMBAT_MODE_SKIP_INTERACTION" = TRAIT_COMBAT_MODE_SKIP_INTERACTION,
"TRAIT_DEL_ON_SPACE_DUMP" = TRAIT_DEL_ON_SPACE_DUMP,
"TRAIT_VALID_DNA_INFUSION" = TRAIT_VALID_DNA_INFUSION,
"TRAIT_FISH_CASE_COMPATIBILE" = TRAIT_FISH_CASE_COMPATIBILE,
"TRAIT_FROZEN" = TRAIT_FROZEN,
"TRAIT_HAS_LABEL" = TRAIT_HAS_LABEL,
"TRAIT_HEARING_SENSITIVE" = TRAIT_HEARING_SENSITIVE,
"TRAIT_HYPERSPACED" = TRAIT_HYPERSPACED,
"TRAIT_IMMERSED" = TRAIT_IMMERSED,
"TRAIT_IRRADIATED" = TRAIT_IRRADIATED,
"TRAIT_IS_AQUARIUM" = TRAIT_IS_AQUARIUM,
"TRAIT_LAVA_IMMUNE" = TRAIT_LAVA_IMMUNE,
"TRAIT_MOVE_FLOATING" = TRAIT_MOVE_FLOATING,
"TRAIT_MOVE_FLYING" = TRAIT_MOVE_FLYING,
@@ -72,6 +75,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SECLUDED_LOCATION" = TRAIT_SECLUDED_LOCATION,
"TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE,
"TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC,
"TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH" = TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH,
"TRAIT_TELEKINESIS_CONTROLLED" = TRAIT_TELEKINESIS_CONTROLLED,
"TRAIT_UNDERFLOOR" = TRAIT_UNDERFLOOR,
"TRAIT_UNIQUE_IMMERSE" = TRAIT_UNIQUE_IMMERSE,
@@ -576,6 +580,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/obj/item = list(
"TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING,
"TRAIT_AQUARIUM_CONTENT" = TRAIT_AQUARIUM_CONTENT,
"TRAIT_BAIT_ALLOW_FISHING_DUD" = TRAIT_BAIT_ALLOW_FISHING_DUD,
"TRAIT_BAIT_UNCONSUMABLE" = TRAIT_BAIT_UNCONSUMABLE,
"TRAIT_BAKEABLE" = TRAIT_BAKEABLE,
32 changes: 10 additions & 22 deletions code/datums/components/_component.dm
Original file line number Diff line number Diff line change
@@ -17,15 +17,6 @@
*/
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER

/**
* The type to check for duplication
*
* `null` means exact match on `type` (default)
*
* Any other type means that and all subtypes
*/
var/dupe_type

/// The datum this components belongs to
var/datum/parent

@@ -57,7 +48,7 @@
qdel(src, TRUE, TRUE)
return

_JoinParent(parent)
_JoinParent()

/**
* Called during component creation with the same arguments as in new excluding parent.
@@ -217,7 +208,7 @@
*
* Use this to do any special cleanup you might need to do before being deregged from an object
*/
/datum/component/proc/PreTransfer()
/datum/component/proc/PreTransfer(datum/new_parent)
return

/**
@@ -328,7 +319,6 @@
CRASH("[component_type] attempted instantiation!")

var/dupe_mode = initial(component_type.dupe_mode)
var/dupe_type = initial(component_type.dupe_type)
var/uses_sources = (dupe_mode == COMPONENT_DUPE_SOURCES)
if(uses_sources && !source)
CRASH("Attempted to add a sourced component of type '[component_type]' to '[type]' without a source!")
@@ -339,10 +329,7 @@

raw_args[1] = src
if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE && dupe_mode != COMPONENT_DUPE_SOURCES)
if(!dupe_type)
old_component = GetExactComponent(component_type)
else
old_component = GetComponent(dupe_type)
old_component = GetComponent(component_type)

if(old_component)
switch(dupe_mode)
@@ -434,11 +421,11 @@
* Removes the component from parent, ends up with a null parent
* Used as a helper proc by the component transfer proc, does not clean up the component like Destroy does
*/
/datum/component/proc/ClearFromParent()
/datum/component/proc/ClearFromParent(datum/new_parent)
if(!parent)
return
var/datum/old_parent = parent
PreTransfer()
PreTransfer(new_parent)
_RemoveFromParent()
parent = null
SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
@@ -455,16 +442,17 @@
if(!target || target.parent == src)
return
if(target.parent)
target.ClearFromParent()
target.parent = src
var/result = target.PostTransfer()
target.ClearFromParent(src)
var/result = target.PostTransfer(src)
switch(result)
if(COMPONENT_INCOMPATIBLE)
var/c_type = target.type
qdel(target)
CRASH("Incompatible [c_type] transfer attempt to a [type]!")

if(target == AddComponent(target))
AddComponent(target)
if(!QDELETED(target))
target.parent = src
target._JoinParent()

/**
Loading

0 comments on commit 7663b39

Please sign in to comment.