Fish update just dropped. New fish, aquarium type, two traits and a research node. (#83508)

## About The Pull Request

### New fish

#### anxious zipzap
An incredibly anxiety-ridden and electroreceptive fish. Worried about
the walls of its tank closing in constantly. Both literally and as a
general metaphorical unease about life's direction.

It produces electricity, and HATES being with any other fish. One other
zipzap is allowed at most.


![image](https://github.com/tgstation/tgstation/assets/40974010/6a1a31f3-1c84-4b5e-b8ca-652c5007813a)

#### monocloning jumpercable
A surprisingly useful if nasty looking creation from the syndicate fish
labs. Drop one in a tank, and watch it self-feed and multiply. Generates
more and more power as a growing swarm!

A far better option for power generation than the zipzap, but it's a
syndicate fish. Get an emag or get very lucky!


![image](https://github.com/tgstation/tgstation/assets/40974010/7f4aaf32-919c-40d9-890f-72ea9f91ac3c)

Working spacebase example


![image](https://github.com/tgstation/tgstation/assets/40974010/8eeaaf18-c0ab-48a5-8ada-e0eb7f118dd9)


### Aquarium type

Bioelectricity Generator! It boosts the power of electroreceptive fish
to very dangerous levels! But, uh, you put a tesla coil, and voila! Free
power! ...Free power if you keep the fish fed! More fish, more power!


![image](https://github.com/tgstation/tgstation/assets/40974010/e6abcd5b-93df-4ffc-ac6a-78585fd4a6c6)

You can convert an aquarium into a bioelectricity generator by
researching the new node, "Marine Utility", from bio processing. Then
you build the biogen upgrade kit and apply it to an already existing
aquarium.

### Fish Traits

#### Electrogenesis

Trait turns the fish into a shock weapon while it's alive, and every
time it feeds, it releases a weak tesla bolt to anyone close to the
tank.

#### Anxiety

anxious fish die when any other fish are in the tank. Please keep them
alone :[

## Why It's Good For The Game

Fish are great, but fish don't have anything to give back to the
station, right? Well, there's some small stuff, but I wanted to add a
bit of functionality towards being able to benefit from having fish.

The anxiety trait with the zipzap gives spessmen a bit of a challenge.
You can settle for one fish generating power per tank and do a
multi-tank generation setup, or you can do some crossbreeding to make a
fish WITH electroreceptive but WITHOUT anxiety. Neat!

## Changelog
🆑
add: New fish, the anxious zipzap
add: New syndicate fish, the monocloning jumpercable
add: New aquarium, the bioelectricity generator
fix: Mixotrophic fish now properly lack food requirements
/🆑
This commit is contained in:
tralezab
2024-05-31 15:26:03 +12:00
committed by GitHub
parent 9476c4158a
commit af33a5fb06
19 changed files with 222 additions and 20 deletions
+5
View File
@@ -147,3 +147,8 @@
///Fluff. The name of the aquarium company shown in the fish catalog
#define AQUARIUM_COMPANY "Aquatech Ltd."
/// how long between electrogenesis zaps
#define ELECTROGENESIS_DURATION 40 SECONDS
/// a random range the electrogenesis cooldown varies by
#define ELECTROGENESIS_VARIANCE (rand(-10 SECONDS, 10 SECONDS))
+2
View File
@@ -909,6 +909,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_FISH_NO_HUNGER "fish_no_hunger"
///It comes from a fish case. Relevant for bounties so far.
#define TRAIT_FISH_FROM_CASE "fish_from_case"
///Fish will also occasionally fire weak tesla zaps
#define TRAIT_FISH_ELECTROGENESIS "fish_electrogenesis"
/// Trait given to angelic constructs to let them purge cult runes
#define TRAIT_ANGELIC "angelic"
+1
View File
@@ -561,6 +561,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FISH_NO_MATING" = TRAIT_FISH_NO_MATING,
"TRAIT_FISH_SELF_REPRODUCE" = TRAIT_FISH_SELF_REPRODUCE,
"TRAIT_FISH_TOXIN_IMMUNE" = TRAIT_FISH_TOXIN_IMMUNE,
"TRAIT_FISH_ELECTROGENESIS" = TRAIT_FISH_ELECTROGENESIS,
"TRAIT_RESIST_EMULSIFY" = TRAIT_RESIST_EMULSIFY,
"TRAIT_YUCKY_FISH" = TRAIT_YUCKY_FISH,
),
+2 -2
View File
@@ -23,7 +23,7 @@
/// Icon used for in aquarium sprite
var/icon = 'icons/obj/aquarium.dmi'
var/icon = 'icons/obj/aquarium/fish.dmi'
/// If this is set this icon state will be used for the holder while icon_state will only be used for item/catalog. Transformation from source_width/height WON'T be applied.
var/icon_state
/// Applied to vc object only for use with greyscaled icons.
@@ -61,7 +61,7 @@
var/animation_update_signals
/datum/component/aquarium_content/Initialize(animation_getter, animation_update_signals)
/datum/component/aquarium_content/Initialize(icon, animation_getter, animation_update_signals)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
+26 -7
View File
@@ -11,15 +11,19 @@
/obj/structure/aquarium
name = "aquarium"
desc = "A vivivarium in which aquatic fuana and flora are usually kept and displayed."
desc = "A vivarium in which aquatic fauna and flora are usually kept and displayed."
density = TRUE
anchored = TRUE
icon = 'icons/obj/aquarium.dmi'
icon = 'icons/obj/aquarium/tanks.dmi'
icon_state = "aquarium_map"
integrity_failure = 0.3
/// The icon state is used for mapping so mappers know what they're placing. This prefixes the real icon used in game.
/// For an example, "aquarium" gives the base sprite of "aquarium_base", the glass is "aquarium_glass_water", and so on.
var/icon_prefix = "aquarium"
var/fluid_type = AQUARIUM_FLUID_FRESHWATER
var/fluid_temp = DEFAULT_AQUARIUM_TEMP
var/min_fluid_temp = MIN_AQUARIUM_TEMP
@@ -138,20 +142,20 @@
/obj/structure/aquarium/update_icon()
. = ..()
///"aquarium_map" is used for mapping, so mappers can tell what it's.
icon_state = "aquarium_base"
icon_state = icon_prefix + "_base"
/obj/structure/aquarium/update_overlays()
. = ..()
if(panel_open)
. += "panel"
. += icon_prefix + "_panel"
///The glass overlay
var/suffix = fluid_type == AQUARIUM_FLUID_AIR ? "air" : "water"
if(broken)
suffix += "_broken"
. += mutable_appearance(icon, "aquarium_glass_cracks", layer = layer + AQUARIUM_BORDERS_LAYER)
. += mutable_appearance(icon, "aquarium_glass_[suffix]", layer = layer + AQUARIUM_GLASS_LAYER)
. += mutable_appearance(icon, "aquarium_borders", layer = layer + AQUARIUM_BORDERS_LAYER)
. += mutable_appearance(icon, icon_prefix + "_glass_cracks", layer = layer + AQUARIUM_BORDERS_LAYER)
. += mutable_appearance(icon, icon_prefix + "_glass_[suffix]", layer = layer + AQUARIUM_GLASS_LAYER)
. += mutable_appearance(icon, icon_prefix + "_borders", layer = layer + AQUARIUM_BORDERS_LAYER)
/obj/structure/aquarium/examine(mob/user)
. = ..()
@@ -212,6 +216,21 @@
fish.feed(item.reagents)
balloon_alert(user, "fed the fish")
return TRUE
if(istype(item, /obj/item/aquarium_upgrade))
var/obj/item/aquarium_upgrade/upgrade = item
if(upgrade.upgrade_from_type != type)
balloon_alert(user, "wrong kind of aquarium!")
return
balloon_alert(user, "upgrading...")
if(!do_after(user, 5 SECONDS, src))
return
var/obj/structure/aquarium/upgraded_aquarium = new upgrade.upgrade_to_type(loc)
for(var/atom/movable/moving in contents)
moving.forceMove(upgraded_aquarium)
balloon_alert(user, "upgraded")
qdel(upgrade)
qdel(src)
return
return ..()
/obj/structure/aquarium/proc/on_attacked(datum/source, mob/attacker, attack_flags)
@@ -2,7 +2,7 @@
/obj/item/fish_feed
name = "fish feed can"
desc = "A refillable can that dispenses nutritious fish feed."
icon = 'icons/obj/aquarium.dmi'
icon = 'icons/obj/aquarium/supplies.dmi'
icon_state = "fish_feed"
w_class = WEIGHT_CLASS_TINY
@@ -58,7 +58,7 @@
name = "ominous fish case"
/obj/item/storage/fish_case/syndicate/get_fish_type()
return pick(/obj/item/fish/donkfish, /obj/item/fish/emulsijack)
return pick(/obj/item/fish/donkfish, /obj/item/fish/emulsijack, /obj/item/fish/jumpercable)
/obj/item/storage/fish_case/tiziran
name = "imported fish case"
@@ -76,6 +76,7 @@
/obj/item/fish/boned = 1,
/obj/item/fish/clownfish/lube = 3,
/obj/item/fish/emulsijack = 1,
/obj/item/fish/jumpercable = 1,
/obj/item/fish/sludgefish/purple = 1,
/obj/item/fish/pufferfish = 3,
/obj/item/fish/slimefish = 2,
@@ -93,7 +94,7 @@
/obj/item/aquarium_kit
name = "DIY Aquarium Construction Kit"
desc = "Everything you need to build your own aquarium. Raw materials sold separately."
icon = 'icons/obj/aquarium.dmi'
icon = 'icons/obj/aquarium/supplies.dmi'
icon_state = "construction_kit"
w_class = WEIGHT_CLASS_TINY
@@ -104,14 +105,14 @@
/obj/item/aquarium_prop
name = "generic aquarium prop"
desc = "very boring"
icon = 'icons/obj/aquarium.dmi'
icon = 'icons/obj/aquarium/supplies.dmi'
w_class = WEIGHT_CLASS_TINY
var/layer_mode = AQUARIUM_LAYER_MODE_BOTTOM
/obj/item/aquarium_prop/Initialize(mapload)
. = ..()
AddComponent(/datum/component/aquarium_content)
AddComponent(/datum/component/aquarium_content, icon)
/obj/item/aquarium_prop/rocks
name = "rocks"
@@ -0,0 +1,37 @@
/// Aquarium upgrades, can be applied to a basic aquarium to upgrade it into an advanced subtype.
/obj/item/aquarium_upgrade
name = "Aquarium Upgrade"
desc = "An upgrade."
icon = 'icons/obj/aquarium/supplies.dmi'
icon_state = "construction_kit"
/// What kind of aquarium can accept this upgrade. Strict type check, no subtypes.
var/upgrade_from_type = /obj/structure/aquarium
/// typepath of the new aquarium subtype created.
var/upgrade_to_type = /obj/structure/aquarium
/obj/item/aquarium_upgrade/bioelec_gen
name = "Aquarium Bioelectricity Kit"
desc = "All the required components to allow an aquarium to harness energy bioelectric fish."
icon_state = "bioelec_kit"
upgrade_to_type = /obj/structure/aquarium/bioelec_gen
/obj/structure/aquarium/bioelec_gen
name = "bioelectricity generator"
desc = "An unconventional type of generator that boosts and harvests the energy produced by bioelectric fish."
icon_state = "bioelec_map"
icon_prefix = "bioelec"
/obj/structure/aquarium/bioelec_gen/zap_act(power, zap_flags)
var/explosive = zap_flags & ZAP_MACHINE_EXPLOSIVE
if(!explosive)
return //immune to all other shocks to make sure power can be generated without breaking the generator itself
return ..()
/obj/structure/aquarium/bioelec_gen/examine(mob/user)
. = ..()
. += span_boldwarning("WARNING! WARNING! WARNING!")
. += span_warning("The bioelectric potential of the fish inside is magnified to dangerous levels by the generator.")
. += span_notice("Tesla coils are required to collect this magnified energy... and you'll want a grounding rod to protect yourself as well.")
+22 -2
View File
@@ -2,7 +2,7 @@
/obj/item/fish
name = "generic looking aquarium fish"
desc = "very bland"
icon = 'icons/obj/aquarium.dmi'
icon = 'icons/obj/aquarium/fish.dmi'
icon_state = "bugfish"
lefthand_file = 'icons/mob/inhands/fish_lefthand.dmi'
righthand_file = 'icons/mob/inhands/fish_righthand.dmi'
@@ -138,10 +138,14 @@
/// If this fish type counts towards the Fish Species Scanning experiments
var/experisci_scannable = TRUE
/// cooldown on creating tesla zaps
COOLDOWN_DECLARE(electrogenesis_cooldown)
/// power of the tesla zap created by the fish in a bioelectric generator
var/electrogenesis_power = 10 MEGA JOULES
/obj/item/fish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
AddComponent(/datum/component/aquarium_content, 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_STATUS_CHANGED,COMSIG_FISH_STIRRED))
RegisterSignal(src, COMSIG_ATOM_ON_LAZARUS_INJECTOR, PROC_REF(use_lazarus))
if(do_flop_animation)
@@ -351,6 +355,9 @@
if(ready_to_reproduce())
try_to_reproduce()
if(HAS_TRAIT(src, TRAIT_FISH_ELECTROGENESIS) && COOLDOWN_FINISHED(src, electrogenesis_cooldown))
try_electrogenesis()
SEND_SIGNAL(src, COMSIG_FISH_LIFE, seconds_per_tick)
/obj/item/fish/proc/set_status(new_status)
@@ -605,6 +612,19 @@
if(flopping)
flop_animation()
/obj/item/fish/proc/try_electrogenesis()
if(status == FISH_DEAD || is_hungry())
return
COOLDOWN_START(src, electrogenesis_cooldown, ELECTROGENESIS_DURATION + ELECTROGENESIS_VARIANCE)
var/fish_zap_range = 1
var/fish_zap_power = 1 KILO JOULES // ~5 damage, just a little friendly "yeeeouch!"
var/fish_zap_flags = ZAP_MOB_DAMAGE
if(istype(loc, /obj/structure/aquarium/bioelec_gen))
fish_zap_range = 5
fish_zap_power = electrogenesis_power
fish_zap_flags |= (ZAP_GENERATES_POWER | ZAP_MOB_STUN)
tesla_zap(source = get_turf(src), zap_range = fish_zap_range, power = fish_zap_power, cutoff = 1 MEGA JOULES, zap_flags = fish_zap_flags)
/// Returns random fish, using random_case_rarity probabilities.
/proc/random_fish_type(required_fluid)
var/static/probability_table
+52 -2
View File
@@ -313,7 +313,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
name = "Aggressive"
inheritability = 80
diff_traits_inheritability = 40
catalog_description = "This fish is agressively territorial, and may attack fish that come close to it."
catalog_description = "This fish is aggressively territorial, and may attack fish that come close to it."
/datum/fish_trait/aggressive/apply_to_fish(obj/item/fish/fish)
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(try_attack_fish))
@@ -364,7 +364,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
catalog_description = "This fish is capable of substaining itself by producing its own sources of energy (food)."
incompatible_traits = list(/datum/fish_trait/predator, /datum/fish_trait/necrophage)
/datum/fish_trait/antigrav/apply_to_fish(obj/item/fish/fish)
/datum/fish_trait/mixotroph/apply_to_fish(obj/item/fish/fish)
ADD_TRAIT(fish, TRAIT_FISH_NO_HUNGER, FISH_TRAIT_DATUM)
/datum/fish_trait/antigrav
@@ -379,3 +379,53 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
/datum/fish_trait/antigrav/apply_to_fish(obj/item/fish/fish)
fish.AddElement(/datum/element/forced_gravity, NEGATIVE_GRAVITY)
///Anxiety means the fish will die if in a location with more than 3 fish (including itself)
///This is just barely enough to crossbreed out of anxiety, but it severely limits the potential of
/datum/fish_trait/anxiety
name = "Anxiety"
inheritability = 100
diff_traits_inheritability = 70
catalog_description = "This fish tends to die of stress when forced to be around too many other fish."
/datum/fish_trait/anxiety/apply_to_fish(obj/item/fish/fish)
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(on_fish_life))
///signal sent when the anxiety fish is fed, killing it if sharing contents with too many fish.
/datum/fish_trait/anxiety/proc/on_fish_life(obj/item/fish/fish, seconds_per_tick)
SIGNAL_HANDLER
var/fish_tolerance = 3
if(!fish.loc || fish.status == FISH_DEAD)
return
for(var/obj/item/other_fish in fish.loc.contents)
if(fish_tolerance <= 0)
fish.loc.visible_message(span_warning("[fish] seems to freak out for a moment, then it stops moving..."))
fish.set_status(FISH_DEAD)
return
fish_tolerance -= 1
/datum/fish_trait/electrogenesis
name = "Electrogenesis"
inheritability = 60
diff_traits_inheritability = 30
catalog_description = "This fish is electroreceptive, and will generate electric fields. Can be harnessed inside a bioelectric generator."
/datum/fish_trait/electrogenesis/apply_to_fish(obj/item/fish/fish)
ADD_TRAIT(fish, TRAIT_FISH_ELECTROGENESIS, FISH_TRAIT_DATUM)
RegisterSignal(fish, COMSIG_ITEM_ATTACK, PROC_REF(on_item_attack))
/datum/fish_trait/electrogenesis/proc/on_item_attack(obj/item/fish/fish, mob/living/target, mob/living/user)
SIGNAL_HANDLER
if(fish.status == FISH_ALIVE)
fish.force = 16
fish.damtype = BURN
fish.attack_verb_continuous = list("shocks", "zaps")
fish.attack_verb_simple = list("shock", "zap")
fish.hitsound = 'sound/effects/sparks4.ogg'
else
fish.force = fish::force
fish.damtype = fish::damtype
fish.attack_verb_continuous = fish::attack_verb_continuous
fish.attack_verb_simple = fish::attack_verb_simple
fish.hitsound = fish::hitsound
+45 -2
View File
@@ -295,6 +295,26 @@
required_temperature_min = MIN_AQUARIUM_TEMP+5
required_temperature_max = MIN_AQUARIUM_TEMP+40
/obj/item/fish/jumpercable
name = "monocloning jumpercable"
desc = "A surprisingly useful if nasty looking creation from the syndicate fish labs. Drop one in a tank, and \
watch it self-feed and multiply. Generates more and more power as a growing swarm!"
icon_state = "jumpercable"
dedicated_in_aquarium_icon_state = "jumpercable_small"
sprite_width = 17
sprite_height = 5
stable_population = 12
average_size = 110
average_weight = 10000
random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+30
fish_traits = list(
/datum/fish_trait/parthenogenesis,
/datum/fish_trait/mixotroph,
/datum/fish_trait/electrogenesis,
)
/obj/item/fish/ratfish
name = "ratfish"
desc = "A rat exposed to the murky waters of maintenance too long. Any higher power, if it revealed itself, would state that the ratfish's continued existence is extremely unwelcome."
@@ -400,9 +420,9 @@
/obj/item/fish/mastodon
name = "unmarine mastodon"
desc = "A monster of exposed muscles and innards, wrapped in a fish-like skeleton. You don't remember ever seeing it on the catalog."
icon = 'icons/obj/aquarium_wide.dmi'
icon = 'icons/obj/aquarium/wide.dmi'
icon_state = "mastodon"
dedicated_in_aquarium_icon = 'icons/obj/aquarium.dmi'
dedicated_in_aquarium_icon = 'icons/obj/aquarium/fish.dmi'
dedicated_in_aquarium_icon_state = "mastodon_small"
base_pixel_x = -16
pixel_x = -16
@@ -612,3 +632,26 @@
return FALSE
target.apply_status_effect(/datum/status_effect/ice_block_talisman, freeze_timer SECONDS)
return FALSE
/obj/item/fish/zipzap
name = "anxious zipzap"
desc = "A fish overflowing with crippling anxiety and electric potential. Worried about the walls of its tank closing in constantly. Both literally and as a general metaphorical unease about life's direction."
icon_state = "zipzap"
icon_state_dead = "zipzap_dead"
sprite_width = 8
sprite_height = 8
stable_population = 3
average_size = 30
average_weight = 500
random_case_rarity = FISH_RARITY_VERY_RARE
favorite_bait = list(/obj/item/food/bait/doughball)
required_temperature_min = MIN_AQUARIUM_TEMP+18
required_temperature_max = MIN_AQUARIUM_TEMP+26
fish_traits = list(
/datum/fish_trait/no_mating,
/datum/fish_trait/wary,
/datum/fish_trait/anxiety,
/datum/fish_trait/electrogenesis,
)
//anxiety naturally limits the amount of zipzaps per tank, so they are stronger alone
electrogenesis_power = 20 MEGA JOULES
@@ -97,6 +97,7 @@
FISHING_DUD = 5,
/obj/item/fish/donkfish = 5,
/obj/item/fish/emulsijack = 5,
/obj/item/fish/jumpercable = 5,
)
catalog_description = "Syndicate dimension (Fishing portal generator)"
radial_name = "Syndicate"
@@ -101,6 +101,16 @@
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING
/datum/design/board/bioelec_gen
name = "Aquarium Bioelectricity Kit"
desc = "The required components to convert an aquarium into a bioelectricity generator."
id = "bioelec_gen"
build_path = /obj/item/aquarium_upgrade/bioelec_gen
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_ENGINEERING,
)
departmental_flags = DEPARTMENT_BITFLAG_ENGINEERING
/datum/design/turbine_part_compressor
name = "Turbine Compressor"
desc = "The basic tier of a compressor blade."
@@ -508,6 +508,18 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000)
discount_experiments = list(/datum/experiment/scanning/random/cytology = 3000) //Big discount to reinforce doing it.
/datum/techweb_node/marine_util
id = "marine_util"
display_name = "Marine Utility"
description = "Fish are nice to look at and all, but they can be put to use."
prereq_ids = list("bio_process")
design_ids = list(
"bioelec_gen",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000)
// only available if you've done the first fishing experiment (thus unlocking fishing tech), but not a strict requirement to get the tech
discount_experiments = list(/datum/experiment/scanning/fish/second = 3000)
/////////////////////////Advanced Surgery/////////////////////////
/datum/techweb_node/imp_wt_surgery
Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Before

Width:  |  Height:  |  Size: 570 B

After

Width:  |  Height:  |  Size: 570 B

+1
View File
@@ -4061,6 +4061,7 @@
#include "code\modules\fishing\fishing_rod.dm"
#include "code\modules\fishing\aquarium\aquarium.dm"
#include "code\modules\fishing\aquarium\aquarium_kit.dm"
#include "code\modules\fishing\aquarium\aquarium_upgrades.dm"
#include "code\modules\fishing\aquarium\fish_analyzer.dm"
#include "code\modules\fishing\fish\_fish.dm"
#include "code\modules\fishing\fish\chasm_detritus.dm"