Aquariums are now potential fishing spots. (#88243)

## About The Pull Request
You can now fish from aquariums if you wish to. This includes some
backend changes to make it possible for the fish table from
get_fish_table() to contain instances, and all that it entails up to
spawn_reward(), which is a requirement for the gimmick to respect the
various traits and other variables of the already instantiated fish
rather than read from cached properties.

## Why It's Good For The Game
The fish progress score/index had only little nasty flaw that has been
nagging me since day one: Not all fish species can be caught. Skipping
McGill, which is a peculiar case that for cheevo purposes should be
considered a standard goldfish, there is the one, unsignificant yet rare
purple sludgefish which can only be gotten as a rare evolution of the
generic sludgefish. Talk about petty, but this may be a long-term nit I
prefer to handle right now.

Also why not? The 'unmarine mastodon' is near impossible to get unless
you somehow find a oil well which is locked behind a specific ruin.

## Changelog

🆑
fix: Aquariums are now potential fishing spots.
/🆑
This commit is contained in:
Ghom
2024-11-29 04:33:18 +01:00
committed by GitHub
parent ccc2533380
commit 1dff5f6de3
19 changed files with 221 additions and 130 deletions
+2 -6
View File
@@ -187,7 +187,6 @@
///Flag added when in an aquarium with the right fluid type.
#define FISH_FLAG_SAFE_FLUID (1<<7)
#define MIN_AQUARIUM_TEMP T0C
#define MAX_AQUARIUM_TEMP (T0C + 100)
#define DEFAULT_AQUARIUM_TEMP (T0C + 24)
@@ -260,11 +259,8 @@
#define FISH_SOURCE_FLAG_EXPLOSIVE_MALUS (1<<0)
/// The fish source is not elegible for random rewards from bluespace fishing rods
#define FISH_SOURCE_FLAG_NO_BLUESPACE_ROD (1<<1)
/**
* If present, fish that can be caught from this source won't be included in the 'fish caught' score, unless
* present in other sources without this flag as well.
*/
#define FISH_SOURCE_FLAG_SKIP_CATCHABLES (1<<2)
/// When examined by someone with enough fishing skill, this will also display fish that doesn't have FISH_FLAG_SHOW_IN_CATALOG
#define FISH_SOURCE_FLAG_IGNORE_HIDDEN_ON_CATALOG (1<<2)
/**
* A macro to ensure the wikimedia filenames of fish icons are unique, especially since there're a couple fish that have
+3
View File
@@ -30,6 +30,9 @@
/proc/cmp_name_dsc(atom/a, atom/b)
return sorttext(a.name, b.name)
/proc/cmp_init_name_asc(atom/a, atom/b)
return sorttext(initial(b.name), initial(a.name))
/proc/cmp_records_asc(datum/record/a, datum/record/b)
return sorttext(b.name, a.name)
@@ -19,10 +19,13 @@ PROCESSING_SUBSYSTEM_DEF(fishing)
cached_fish_icons = list()
cached_unknown_fish_icons = list()
fish_properties = list()
catchable_fish = list()
var/icon/questionmark = icon('icons/effects/random_spawners.dmi', "questionmark")
var/list/mark_dimension = get_icon_dimensions(questionmark)
for(var/obj/item/fish/fish_type as anything in subtypesof(/obj/item/fish))
var/list/spawned_fish = list()
var/list/fish_subtypes = sortTim(subtypesof(/obj/item/fish), GLOBAL_PROC_REF(cmp_init_name_asc))
for(var/obj/item/fish/fish_type as anything in fish_subtypes)
var/list/fish_dimensions = get_icon_dimensions(fish_type::icon)
var/icon/fish_icon = icon(fish_type::icon, fish_type::icon_state, frame = 1, moving = FALSE)
cached_fish_icons[fish_type] = icon2base64(fish_icon)
@@ -35,6 +38,7 @@ PROCESSING_SUBSYSTEM_DEF(fishing)
cached_unknown_fish_icons[fish_type] = icon2base64(unknown_icon)
var/obj/item/fish/fish = new fish_type(null, FALSE)
spawned_fish += fish
var/list/properties = list()
fish_properties[fish_type] = properties
properties[FISH_PROPERTIES_FAV_BAIT] = fish.favorite_bait.Copy()
@@ -67,36 +71,35 @@ PROCESSING_SUBSYSTEM_DEF(fishing)
properties[FISH_PROPERTIES_BEAUTY_SCORE] = beauty_score
qdel(fish)
catchable_fish = list()
var/list/all_catchables = list()
for(var/source_type as anything in GLOB.preset_fish_sources)
var/datum/fish_source/source = GLOB.preset_fish_sources[source_type]
if(!(source.fish_source_flags & FISH_SOURCE_FLAG_SKIP_CATCHABLES))
all_catchables |= source.fish_table
for(var/thing in all_catchables)
if(!ispath(thing, /obj/item/fish))
continue
var/obj/item/fish/fishie = thing
var/fish_id = initial(fishie.fish_id)
var/fish_id
if(fish.fish_id_redirect_path)
var/obj/item/fish/other_path = fish.fish_id_redirect_path
if(!ispath(other_path, /obj/item/fish))
stack_trace("[fish.type] has a set 'fish_id_redirect_path' variable but it isn't a fish path but [other_path]")
continue
fish_id = initial(other_path.fish_id)
else
fish_id = fish.fish_id
if(!fish_id)
stack_trace("[fishie] doesn't have a set 'fish_id' variable despite being a catchable fish")
stack_trace("[fish.type] doesn't have a set 'fish_id' variable despite being a catchable fish")
continue
if(fish.fish_id_redirect_path)
continue
if(catchable_fish[fish_id])
stack_trace("[fishie] has a 'fish_id' value already assigned to [catchable_fish[fish_id]]. fish_id: [fish_id]")
stack_trace("[fish.type] has a 'fish_id' value already assigned to [catchable_fish[fish_id]]. fish_id: [fish_id]")
continue
catchable_fish[fish_id] = fishie
catchable_fish[fish_id] = fish.type
///init the list of things lures can catch
lure_catchables = list()
var/list/fish_types = subtypesof(/obj/item/fish)
for(var/lure_type in typesof(/obj/item/fishing_lure))
var/obj/item/fishing_lure/lure = new lure_type
lure_catchables[lure_type] = list()
for(var/obj/item/fish/fish_type as anything in fish_types)
if(lure.is_catchable_fish(fish_type, fish_properties[fish_type]))
lure_catchables[lure_type] += fish_type
for(var/obj/item/fish/fish as anything in spawned_fish)
if(lure.is_catchable_fish(fish, fish_properties[fish.type]))
lure_catchables[lure_type] += fish.type
qdel(lure)
QDEL_LIST(spawned_fish)
return SS_INIT_SUCCESS
+6 -6
View File
@@ -470,13 +470,13 @@
for(var/component_key in dc)
var/component_or_list = dc[component_key]
if(islist(component_or_list))
for(var/datum/component/I in component_or_list)
if(I.can_transfer)
target.TakeComponent(I)
for(var/datum/component/component in component_or_list)
if(component.can_transfer)
target.TakeComponent(component)
else
var/datum/component/C = component_or_list
if(C.can_transfer)
target.TakeComponent(C)
var/datum/component/component = component_or_list
if(!QDELETED(component) && component.can_transfer)
target.TakeComponent(component)
/**
* Return the object that is the host of any UI's that this component has
+2
View File
@@ -118,6 +118,7 @@
RegisterSignal(movable, COMSIG_ATOM_UI_INTERACT, PROC_REF(interact))
movable.AddElement(/datum/element/relay_attackers)
movable.AddComponent(/datum/component/fishing_spot, /datum/fish_source/aquarium)
for(var/atom/movable/content as anything in movable.contents)
if(content.flags_1 & INITIALIZED_1)
@@ -150,6 +151,7 @@
beauty_by_content = null
tracked_fish_by_type = null
movable.remove_traits(list(TRAIT_IS_AQUARIUM, TRAIT_AQUARIUM_PANEL_OPEN, TRAIT_STOP_FISH_REPRODUCTION_AND_GROWTH), AQUARIUM_TRAIT)
qdel(movable.GetComponent(/datum/component/fishing_spot))
REMOVE_KEEP_TOGETHER(movable, AQUARIUM_TRAIT)
/datum/component/aquarium/PreTransfer(atom/movable/new_parent)
+1 -1
View File
@@ -41,7 +41,7 @@
if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
return
if(!fish_source.has_known_fishes())
if(!fish_source.has_known_fishes(source))
return
examine_text += span_tinynoticeital("This is a fishing spot. You can look again to list its fishes...")
+3 -2
View File
@@ -1883,11 +1883,12 @@
return src
/// Checks if the bait is liked by the fish type or not. Returns a multiplier that affects the chance of catching it.
/obj/item/proc/check_bait(obj/item/fish/fish_type)
/obj/item/proc/check_bait(obj/item/fish/fish)
if(HAS_TRAIT(src, TRAIT_OMNI_BAIT))
return 1
var/catch_multiplier = 1
var/list/properties = SSfishing.fish_properties[fish_type]
var/list/properties = SSfishing.fish_properties[isfish(fish) ? fish.type : fish]
//Bait matching likes doubles the chance
var/list/fav_bait = properties[FISH_PROPERTIES_FAV_BAIT]
for(var/bait_identifer in fav_bait)
+1 -1
View File
@@ -836,7 +836,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
. = ..()
if(!fish_source || !HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
return
if(!GLOB.preset_fish_sources[fish_source].has_known_fishes())
if(!GLOB.preset_fish_sources[fish_source].has_known_fishes(src))
return
. += span_tinynoticeital("This is a fishing spot. You can look again to list its fishes...")
+42 -51
View File
@@ -88,13 +88,11 @@
rod.spin_frequency = null
///Called for every fish subtype by the fishing subsystem when initializing, to populate the list of fish that can be catched with this lure.
/obj/item/fishing_lure/proc/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/avg_size = initial(fish_type.average_size)
/obj/item/fishing_lure/proc/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
var/intermediate_size = FISH_SIZE_SMALL_MAX + (FISH_SIZE_NORMAL_MAX - FISH_SIZE_SMALL_MAX)
if(!ISINRANGE(avg_size, FISH_SIZE_TINY_MAX * 0.5, intermediate_size))
if(!ISINRANGE(fish.size, FISH_SIZE_TINY_MAX * 0.5, intermediate_size))
return FALSE
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish.fish_traits))
return FALSE
return TRUE
@@ -120,11 +118,16 @@
. += span_info("You can catch the following fish with this lure: [english_list(known_fishes)].")
///Check if the fish is in the list of catchable fish for this fishing lure. Return value is a multiplier.
/obj/item/fishing_lure/check_bait(obj/item/fish/fish_type)
/obj/item/fishing_lure/check_bait(obj/item/fish/fish)
var/multiplier = 0
if(is_type_in_list(/obj/item/fishing_lure, SSfishing.fish_properties[fish_type][FISH_PROPERTIES_FAV_BAIT]))
var/is_instance = istype(fish)
var/list/fish_properties = SSfishing.fish_properties[is_instance ? fish.type : fish]
if(is_type_in_list(/obj/item/fishing_lure, fish_properties[FISH_PROPERTIES_FAV_BAIT]))
multiplier += 2
if(fish_type in SSfishing.lure_catchables[type])
if(is_instance)
if(is_catchable_fish(fish, fish_properties))
multiplier += 10
else if(fish in SSfishing.lure_catchables[type])
multiplier += 10
return multiplier
@@ -133,12 +136,10 @@
desc = "A bigger fishing lure that may attract larger fish. Tiny or picky prey will remain uninterested."
icon_state = "plug"
/obj/item/fishing_lure/plug/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/avg_size = initial(fish_type.average_size)
if(avg_size <= FISH_SIZE_SMALL_MAX)
/obj/item/fishing_lure/plug/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(fish.size <= FISH_SIZE_SMALL_MAX)
return FALSE
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish.fish_traits))
return FALSE
return TRUE
@@ -148,17 +149,17 @@
icon_state = "dropping"
spin_frequency = list(1.5 SECONDS, 2.8 SECONDS)
/obj/item/fishing_lure/dropping/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
/obj/item/fishing_lure/dropping/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
var/list/sources = list(/datum/fish_source/toilet, /datum/fish_source/moisture_trap)
for(var/datum/fish_source/source as anything in sources)
var/datum/fish_source/instance = GLOB.preset_fish_sources[/datum/fish_source/toilet]
if(fish_type in instance.fish_table)
if(fish.type in instance.fish_table)
return TRUE
var/list/fav_baits = fish_properties[FISH_PROPERTIES_FAV_BAIT]
for(var/list/identifier in fav_baits)
if(identifier[FISH_BAIT_TYPE] == FISH_BAIT_FOODTYPE && (identifier[FISH_BAIT_VALUE] & (JUNKFOOD|GROSS|TOXIC)))
return TRUE
if(initial(fish_type.beauty) <= FISH_BEAUTY_DISGUSTING)
if(fish.beauty <= FISH_BEAUTY_DISGUSTING)
return TRUE
return FALSE
@@ -168,17 +169,15 @@
icon_state = "spoon"
spin_frequency = list(1.25 SECONDS, 2.25 SECONDS)
/obj/item/fishing_lure/spoon/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/avg_size = initial(fish_type.average_size)
if(!ISINRANGE(avg_size, FISH_SIZE_TINY_MAX + 1, FISH_SIZE_NORMAL_MAX))
/obj/item/fishing_lure/spoon/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(!ISINRANGE(fish.size, FISH_SIZE_TINY_MAX + 1, FISH_SIZE_NORMAL_MAX))
return FALSE
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish.fish_traits))
return FALSE
var/fluid_type = initial(fish_type.required_fluid_type)
var/fluid_type = fish.required_fluid_type
if(fluid_type == AQUARIUM_FLUID_FRESHWATER || fluid_type == AQUARIUM_FLUID_ANADROMOUS || fluid_type == AQUARIUM_FLUID_ANY_WATER)
return TRUE
if((/datum/fish_trait/amphibious in fish_traits) && fluid_type == AQUARIUM_FLUID_AIR)
if((/datum/fish_trait/amphibious in fish.fish_traits) && fluid_type == AQUARIUM_FLUID_AIR)
return TRUE
return FALSE
@@ -188,9 +187,8 @@
icon_state = "artificial_fly"
spin_frequency = list(1.1 SECONDS, 2 SECONDS)
/obj/item/fishing_lure/artificial_fly/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(/datum/fish_trait/picky_eater in fish_traits)
/obj/item/fishing_lure/artificial_fly/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(/datum/fish_trait/picky_eater in fish.fish_traits)
return TRUE
return FALSE
@@ -216,9 +214,8 @@
. = ..()
REMOVE_TRAIT(rod, TRAIT_ROD_IGNORE_ENVIRONMENT, type)
/obj/item/fishing_lure/led/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
/obj/item/fishing_lure/led/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(length(list(/datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish.fish_traits))
return TRUE
return FALSE
@@ -236,9 +233,8 @@
. = ..()
REMOVE_TRAIT(rod, TRAIT_ROD_ATTRACT_SHINY_LOVERS, REF(src))
/obj/item/fishing_lure/lucky_coin/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(/datum/fish_trait/shiny_lover in fish_traits)
/obj/item/fishing_lure/lucky_coin/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(/datum/fish_trait/shiny_lover in fish.fish_traits)
return TRUE
return FALSE
@@ -248,9 +244,8 @@
icon_state = "algae"
spin_frequency = list(3 SECONDS, 5 SECONDS)
/obj/item/fishing_lure/algae/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(/datum/fish_trait/vegan in fish_traits)
/obj/item/fishing_lure/algae/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(/datum/fish_trait/vegan in fish.fish_traits)
return TRUE
return FALSE
@@ -260,11 +255,10 @@
icon_state = "grub"
spin_frequency = list(1 SECONDS, 2.7 SECONDS)
/obj/item/fishing_lure/grub/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
if(initial(fish_type.average_size) >= FISH_SIZE_SMALL_MAX)
/obj/item/fishing_lure/grub/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(fish.size >= FISH_SIZE_SMALL_MAX)
return FALSE
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater) & fish_traits))
if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater) & fish.fish_traits))
return FALSE
return TRUE
@@ -274,9 +268,8 @@
icon_state = "buzzbait"
spin_frequency = list(0.8 SECONDS, 1.7 SECONDS)
/obj/item/fishing_lure/buzzbait/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(/datum/fish_trait/electrogenesis in fish_traits)
/obj/item/fishing_lure/buzzbait/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(HAS_TRAIT(fish, TRAIT_FISH_ELECTROGENESIS))
return TRUE
return FALSE
@@ -286,14 +279,13 @@
icon_state = "spinnerbait"
spin_frequency = list(2 SECONDS, 4 SECONDS)
/obj/item/fishing_lure/spinnerbait/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(!(/datum/fish_trait/predator in fish_traits))
/obj/item/fishing_lure/spinnerbait/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(!(/datum/fish_trait/predator in fish.fish_traits))
return FALSE
var/init_fluid_type = initial(fish_type.required_fluid_type)
var/init_fluid_type = fish.required_fluid_type
if(init_fluid_type == AQUARIUM_FLUID_FRESHWATER || init_fluid_type == AQUARIUM_FLUID_ANADROMOUS || init_fluid_type == AQUARIUM_FLUID_ANY_WATER)
return TRUE
if((/datum/fish_trait/amphibious in fish_traits) && init_fluid_type == AQUARIUM_FLUID_AIR) //fluid type is changed to freshwater on init
if((/datum/fish_trait/amphibious in fish.fish_traits) && init_fluid_type == AQUARIUM_FLUID_AIR) //fluid type is changed to freshwater on init
return TRUE
return FALSE
@@ -303,11 +295,10 @@
icon_state = "daisy_chain"
spin_frequency = list(2 SECONDS, 4 SECONDS)
/obj/item/fishing_lure/daisy_chain/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
if(!(/datum/fish_trait/predator in fish_traits))
/obj/item/fishing_lure/daisy_chain/is_catchable_fish(obj/item/fish/fish, list/fish_properties)
if(!(/datum/fish_trait/predator in fish.fish_traits))
return FALSE
var/init_fluid_type = initial(fish_type.required_fluid_type)
var/init_fluid_type = fish.required_fluid_type
if(init_fluid_type == AQUARIUM_FLUID_SALTWATER || init_fluid_type == AQUARIUM_FLUID_ANADROMOUS || init_fluid_type == AQUARIUM_FLUID_ANY_WATER)
return TRUE
return FALSE
+2
View File
@@ -170,6 +170,8 @@
* Once set, the value shouldn't be changed, so don't make typos.
*/
var/fish_id
///Used to redirect to another fish path so that catching this fish unlocks its entry instead.
var/obj/item/fish/fish_id_redirect_path
/obj/item/fish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
+3 -1
View File
@@ -61,7 +61,9 @@ GLOBAL_LIST_INIT_TYPED(chasm_detritus_types, /datum/chasm_detritus, init_chasm_d
var/default_spawn = pick(default_contents[default_contents_key])
return new default_spawn(fisher_turf)
return determine_detritus(chasm_contents)
var/atom/movable/detritus = determine_detritus(chasm_contents)
detritus.forceMove(fisher_turf)
return detritus
/datum/chasm_detritus/proc/get_chasm_contents(turf/fishing_spot)
. = list()
@@ -25,6 +25,7 @@
/obj/item/fish/goldfish/gill
name = "McGill"
desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case."
fish_id_redirect_path = /obj/item/fish/goldfish
stable_population = 1
random_case_rarity = FISH_RARITY_NOPE
fish_flags = parent_type::fish_flags & ~FISH_FLAG_SHOW_IN_CATALOG
@@ -61,6 +62,7 @@
/obj/item/fish/goldfish/three_eyes/gill
name = "McGill"
desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case. It looks kinda different today..."
fish_id_redirect_path = /obj/item/fish/goldfish/three_eyes
compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/three_eyes)
beauty = FISH_BEAUTY_GREAT
fish_flags = parent_type::fish_flags & ~FISH_FLAG_SHOW_IN_CATALOG
@@ -163,6 +165,7 @@
/obj/item/fish/tadpole
name = "tadpole"
fish_id = "tadpole"
desc = "The larval spawn of an amphibian. A very minuscle, round creature with a long tail it uses to swim around."
icon_state = "tadpole"
average_size = 3
@@ -64,6 +64,7 @@
/obj/item/fish/sludgefish/purple
name = "purple sludgefish"
fish_id = "purple_sludgefish"
desc = "A misshapen, fragile, loosely fish-like living goop. This one has developed sexual reproduction mechanisms, and a purple tint to boot."
icon_state = "sludgefish_purple"
random_case_rarity = FISH_RARITY_NOPE
+4 -4
View File
@@ -215,8 +215,8 @@
SIGNAL_HANDLER
REMOVE_TRAIT(rod, TRAIT_ROD_REMOVE_FISHING_DUD, REF(src))
/obj/item/fishing_hook/magnet/get_hook_bonus_multiplicative(fish_type, datum/fish_source/source)
if(fish_type == FISHING_DUD || ispath(fish_type, /obj/item/fish))
/obj/item/fishing_hook/magnet/get_hook_bonus_multiplicative(fish_type)
if(fish_type == FISHING_DUD || ispath(fish_type, /obj/item/fish) || isfish(fish_type))
return ..()
// We multiply the odds by five for everything that's not a fish nor a dud
@@ -275,9 +275,9 @@
return "The hook on your fishing rod wasn't meant for traditional fishing, rendering it useless at doing so!"
/obj/item/fishing_hook/rescue/get_hook_bonus_multiplicative(fish_type, datum/fish_source/source)
/obj/item/fishing_hook/rescue/get_hook_bonus_multiplicative(fish_type)
// Sorry, you won't catch fish with this.
if(ispath(fish_type, /obj/item/fish))
if(ispath(fish_type, /obj/item/fish) || isfish(fish_type))
return RESCUE_HOOK_FISH_MULTIPLIER
return ..()
+35 -10
View File
@@ -357,11 +357,12 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user)
if(win)
if(reward_path != FISHING_DUD)
playsound(location, 'sound/effects/bigsplash.ogg', 100)
if(ispath(reward_path, /obj/item/fish))
if(ispath(reward_path, /obj/item/fish) || isfish(reward_path))
var/obj/item/fish/fish_reward = reward_path
var/fish_id = initial(fish_reward.fish_id)
var/obj/item/fish/redirect_path = initial(fish_reward.fish_id_redirect_path)
var/fish_id = ispath(redirect_path, /obj/item/fish) ? initial(redirect_path.fish_id) : initial(fish_reward.fish_id)
if(fish_id)
user.client?.give_award(/datum/award/score/progress/fish, user, initial(fish_reward.fish_id))
user.client?.give_award(/datum/award/score/progress/fish, user, fish_id)
SEND_SIGNAL(user, COMSIG_MOB_COMPLETE_FISHING, src, win)
if(!QDELETED(src))
qdel(src)
@@ -397,7 +398,13 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user)
playsound(location, 'sound/effects/fish_splash.ogg', 100)
if(HAS_MIND_TRAIT(user, TRAIT_REVEAL_FISH))
fish_icon = GLOB.specific_fish_icons[reward_path] || FISH_ICON_DEF
var/possible_icon
if(isatom(reward_path))
var/atom/reward = reward_path
possible_icon = GLOB.specific_fish_icons[reward.type]
else
possible_icon = GLOB.specific_fish_icons[reward_path]
fish_icon = possible_icon || FISH_ICON_DEF
switch(fish_icon)
if(FISH_ICON_DEF)
send_alert("fish!!!")
@@ -455,11 +462,22 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user)
SIGNAL_HANDLER
interrupt()
/datum/fishing_challenge/proc/on_reward_removed(datum/source)
SIGNAL_HANDLER
send_alert("reward gone!")
interrupt()
/datum/fishing_challenge/proc/on_fish_death(obj/item/fish/source)
SIGNAL_HANDLER
if(source.status == FISH_DEAD)
win_anyway()
/datum/fishing_challenge/proc/win_anyway()
if(!completed)
//winning by timeout or idling around shouldn't give as much experience.
experience_multiplier *= 0.5
complete(TRUE)
if(completed)
return
//winning by timeout / fish death shouldn't give as much experience.
experience_multiplier *= 0.5
complete(TRUE)
/datum/fishing_challenge/proc/hurt_fish(datum/source, obj/item/fish/reward)
SIGNAL_HANDLER
@@ -506,13 +524,15 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user)
if(difficulty > FISHING_DEFAULT_DIFFICULTY)
completion -= MAX_FISH_COMPLETION_MALUS * (difficulty * 0.01)
var/is_fish_instance = isfish(reward_path)
/// Fish minigame properties
if(ispath(reward_path,/obj/item/fish))
if(ispath(reward_path,/obj/item/fish) || is_fish_instance)
var/obj/item/fish/fish = reward_path
var/movement_path = initial(fish.fish_movement_type)
mover = new movement_path(src)
// Apply fish trait modifiers
var/list/fish_traits = SSfishing.fish_properties[fish][FISH_PROPERTIES_TRAITS]
var/list/fish_traits = is_fish_instance ? fish.fish_traits : SSfishing.fish_properties[fish][FISH_PROPERTIES_TRAITS]
for(var/fish_trait in fish_traits)
var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
trait.minigame_mod(used_rod, user, src)
@@ -552,6 +572,11 @@ GLOBAL_LIST_EMPTY(fishing_challenges_by_user)
var/obj/item/fish/fish = reward_path
var/wait_time = (initial(fish.health) / FISH_DAMAGE_PER_SECOND) SECONDS
addtimer(CALLBACK(src, PROC_REF(win_anyway)), wait_time, TIMER_DELETE_ME)
else if(ismovable(reward_path))
var/atom/movable/reward = reward_path
RegisterSignal(reward, COMSIG_MOVABLE_MOVED, PROC_REF(on_reward_removed))
if(is_fish_instance)
RegisterSignal(reward, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_death))
start_time = world.time
///Throws a stack with prefixed text.
+43 -24
View File
@@ -180,16 +180,20 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
// Difficulty modifier added by the rod
. += rod.difficulty_modifier
if(!ispath(result,/obj/item/fish))
var/is_fish_instance = isfish(result)
if(!ispath(result,/obj/item/fish) && !is_fish_instance)
// In the future non-fish rewards can have variable difficulty calculated here
return
var/obj/item/fish/caught_fish = result
var/list/fish_properties = SSfishing.fish_properties[caught_fish]
//Just to clarify when we should use the path instead of the fish, which can be both a path and an instance.
var/result_path = is_fish_instance ? caught_fish.type : result
// Baseline fish difficulty
. += initial(caught_fish.fishing_difficulty_modifier)
var/list/fish_properties = SSfishing.fish_properties[result_path]
if(rod.bait)
var/obj/item/bait = rod.bait
//Fav bait makes it easier
@@ -204,7 +208,11 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
. += DISLIKED_BAIT_DIFFICULTY_MOD
// Matching/not matching fish traits and equipment
var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
var/list/fish_traits
if(is_fish_instance)
fish_traits = caught_fish.fish_traits
else
fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
var/additive_mod = 0
var/multiplicative_mod = 1
@@ -217,6 +225,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
. += additive_mod
. *= multiplicative_mod
///Comsig proc from the fishing minigame for 'roll_reward'
/datum/fish_source/proc/roll_reward_minigame(datum/source, obj/item/fishing_rod/rod, mob/fisherman, atom/location, list/rewards)
SIGNAL_HANDLER
@@ -265,8 +274,6 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
else if(istype(reward, /obj/effect/spawner)) // Do not attempt to forceMove() a spawner. It will break things, and the spawned item should already be at the mob's turf by now.
fisherman.balloon_alert(fisherman, "caught something!")
return
else // for fishing things like corpses, move them to the turf of the fisherman
INVOKE_ASYNC(reward, TYPE_PROC_REF(/atom/movable, forceMove), get_turf(fisherman))
fisherman.balloon_alert(fisherman, "caught [reward]!")
return reward
@@ -306,6 +313,10 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
/datum/fish_source/proc/spawn_reward(reward_path, atom/spawn_location, atom/fishing_spot)
if(reward_path == FISHING_DUD)
return
if(ismovable(reward_path))
var/atom/movable/reward = reward_path
reward.forceMove(spawn_location)
return reward
if(ispath(reward_path, /datum/chasm_detritus))
return GLOB.chasm_detritus_types[reward_path].dispense_detritus(spawn_location, fishing_spot)
if(!ispath(reward_path, /atom/movable))
@@ -317,7 +328,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
return reward
/// Returns the fish table, with with the unavailable items from fish_counts removed.
/datum/fish_source/proc/get_fish_table(from_explosion = FALSE)
/datum/fish_source/proc/get_fish_table(atom/location, from_explosion = FALSE)
var/list/table = fish_table.Copy()
//message bottles cannot spawn from explosions. They're meant to be one-time messages (rarely) and photos from past rounds
//and it would suck if the pool of bottle messages were constantly being emptied by explosive fishing.
@@ -336,8 +347,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
///Multiplier used to make fishes more common compared to everything else.
var/result_multiplier = 1
var/list/final_table = get_fish_table()
var/list/final_table = get_fish_table(location)
if(bait)
for(var/trait in weight_result_multiplier)
@@ -357,7 +367,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
final_table[result] *= rod.hook.get_hook_bonus_multiplicative(result)
final_table[result] += rod.hook.get_hook_bonus_additive(result)//Decide on order here so it can be multiplicative
if(ispath(result, /obj/item/fish))
if(ispath(result, /obj/item/fish) || isfish(result))
if(bait)
final_table[result] = round(final_table[result] * result_multiplier, 1)
var/mult = bait.check_bait(result)
@@ -384,7 +394,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
var/highest_fish_weight
var/list/collected_fish_weights = list()
for(var/fishable in table)
if(ispath(fishable, /obj/item/fish))
if(ispath(fishable, /obj/item/fish) || isfish(fishable))
var/fish_weight = table[fishable]
collected_fish_weights[fishable] = fish_weight
if(fish_weight > highest_fish_weight)
@@ -397,30 +407,38 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
table[fish] += round(difference**exponent, 1)
/datum/fish_source/proc/get_fish_trait_catch_mods(weight, obj/item/fish/fish, obj/item/fishing_rod/rod, mob/user, atom/location)
if(!ispath(fish, /obj/item/fish))
var/is_fish_instance = isfish(fish)
if(!ispath(fish, /obj/item/fish) && !is_fish_instance)
return weight
var/multiplier = 1
for(var/fish_trait in SSfishing.fish_properties[fish][FISH_PROPERTIES_TRAITS])
var/list/fish_traits
if(is_fish_instance)
fish_traits = fish.fish_traits
else
fish_traits = SSfishing.fish_properties[fish][FISH_PROPERTIES_TRAITS]
for(var/fish_trait in fish_traits)
var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
var/list/mod = trait.catch_weight_mod(rod, user, location, fish)
var/list/mod = trait.catch_weight_mod(rod, user, location, is_fish_instance ? fish.type : fish)
weight += mod[ADDITIVE_FISHING_MOD]
multiplier *= mod[MULTIPLICATIVE_FISHING_MOD]
return round(weight * multiplier, 1)
///returns true if this fishing spot has fish that are shown in the catalog.
/datum/fish_source/proc/has_known_fishes()
for(var/reward in fish_table)
if(!ispath(reward, /obj/item/fish))
/datum/fish_source/proc/has_known_fishes(atom/location)
var/show_anyway = fish_source_flags & FISH_SOURCE_FLAG_IGNORE_HIDDEN_ON_CATALOG
for(var/reward in get_fish_table(location))
if(!ispath(reward, /obj/item/fish) && !isfish(reward))
continue
var/obj/item/fish/prototype = reward
if(initial(prototype.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG)
if(!show_anyway && initial(prototype.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG)
return TRUE
return FALSE
///Add a string with the names of catchable fishes to the examine text.
/datum/fish_source/proc/get_catchable_fish_names(mob/user, atom/location, list/examine_text)
var/list/known_fishes = list()
var/show_anyway = fish_source_flags & FISH_SOURCE_FLAG_IGNORE_HIDDEN_ON_CATALOG
var/obj/item/fishing_rod/rod = user.get_active_held_item()
var/list/final_table
@@ -433,17 +451,18 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
var/list/rodless_weights = list()
var/total_rod_weight = 0
var/list/rod_weights = list()
for(var/reward in fish_table)
var/weight = fish_table[reward]
var/list/table = get_fish_table(location)
for(var/reward in table)
var/weight = table[reward]
var/final_weight
if(rod)
total_weight += weight
final_weight = final_table[reward]
total_rod_weight += final_weight
if(!ispath(reward, /obj/item/fish))
if(!ispath(reward, /obj/item/fish) && !isfish(reward))
continue
var/obj/item/fish/prototype = reward
if(!(initial(prototype.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG))
if(!show_anyway && !(initial(prototype.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG))
continue
if(rod)
rodless_weights[reward] = weight
@@ -472,7 +491,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
var/info = "You can catch the following fish here"
if(rod)
info = span_tooltip("boldened are the fish you're more likely to catch with your current setup. The opposite is true for smaller names", info)
info = span_tooltip("In bold are fish you're more likely to catch with the current setup. The opposite is true for the smaller font", info)
examine_text += span_info("[info]: [english_list(known_fishes)].")
///How much the explosive_fishing_score impacts explosive fishing. The higher the value, the stronger the malus for repeated calls
@@ -493,7 +512,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
for(var/i in 1 to (severity + 2))
if(!prob((100 + 100 * severity)/i * multiplier))
continue
var/reward_loot = pick_weight(get_fish_table(from_explosion = TRUE))
var/reward_loot = pick_weight(get_fish_table(location, from_explosion = TRUE))
var/atom/spawn_location = isturf(location) ? location : location.drop_location()
var/atom/movable/reward = simple_dispense_reward(reward_loot, spawn_location, location)
if(isnull(reward))
@@ -698,6 +698,46 @@
return data
#define RANDOM_AQUARIUM_FISH "random_aquarium_fish"
/datum/fish_source/aquarium
radial_state = "fish_tank"
fish_table = list(
FISHING_DUD = 10,
)
fish_source_flags = FISH_SOURCE_FLAG_NO_BLUESPACE_ROD|FISH_SOURCE_FLAG_IGNORE_HIDDEN_ON_CATALOG
fishing_difficulty = FISHING_EASY_DIFFICULTY - 5
#undef RANDOM_AQUARIUM_FISH
/datum/fish_source/aquarium/get_fish_table(atom/location, from_explosion = FALSE)
if(istype(location, /obj/machinery/fishing_portal_generator))
var/obj/machinery/fishing_portal_generator/portal = location
location = portal.current_linked_atom
var/list/table = list()
for(var/obj/item/fish/fish in location)
if(fish.status == FISH_DEAD) //dead fish cannot be caught
continue
table[fish] = 10
if(!length(table))
return fish_table
return table
/datum/fish_source/aquarium/spawn_reward_from_explosion(atom/location, severity)
return //If the aquarium breaks, all fish are released anyway.
/datum/fish_source/aquarium/generate_wiki_contents(datum/autowiki/fish_sources/wiki)
var/list/data = list()
data += LIST_VALUE_WRAP_LISTS(list(
FISH_SOURCE_AUTOWIKI_NAME = "Fish",
FISH_SOURCE_AUTOWIKI_DUD = "",
FISH_SOURCE_AUTOWIKI_WEIGHT = 100,
FISH_SOURCE_AUTOWIKI_NOTES = "Any fish currently inside the aquarium, be they alive or dead.",
))
return data
/datum/fish_source/hot_spring
catalog_description = "Hot Springs"
radial_state = "onsen"
+4 -2
View File
@@ -101,6 +101,7 @@
stable_population = INFINITY
breeding_timeout = 0
fish_flags = parent_type::fish_flags & ~(FISH_FLAG_SHOW_IN_CATALOG|FISH_FLAG_EXPERIMENT_SCANNABLE)
fish_id_redirect_path = /obj/item/fish/goldfish //Stops SSfishing from complaining
var/expected_num_fillets = 0 //used to know how many fillets should be gotten out of this fish
/obj/item/fish/testdummy/add_fillet_type()
@@ -368,6 +369,7 @@
/obj/item/fish/chasm_crab/instant_growth
fish_traits = list() //We don't want to end up applying traits twice on the resulting lobstrosity
fish_id_redirect_path = /obj/item/fish/chasm_crab
/datum/unit_test/fish_sources
@@ -419,10 +421,10 @@
/datum/fish_source/unit_test_profound_fisher
fish_table = list(/obj/item/fish/testdummy = 1)
fish_counts = list(/obj/item/fish/testdummy = 2)
fish_source_flags = parent_type::fish_source_flags | FISH_SOURCE_FLAG_SKIP_CATCHABLES
fish_source_flags = parent_type::fish_source_flags
/datum/fish_source/unit_test_all_fish
fish_source_flags = parent_type::fish_source_flags | FISH_SOURCE_FLAG_SKIP_CATCHABLES
fish_source_flags = parent_type::fish_source_flags
/datum/fish_source/unit_test_all_fish/New()
for(var/fish_type as anything in subtypesof(/obj/item/fish))
+2 -1
View File
@@ -54,4 +54,5 @@ Fish can grow in size and weight if you fed them somewhat frequently. Giving the
Feeding a fish mutagen can triple the probability of generating evolved offsprings, provided it has an evolution.
You can print fishing rods of different materials from an autolathe, which can inrease or decrease fishing difficulty, casting range, experience gained and can have other, special effects.
Albeit scarcely, it's possible to catch fish made of the same materials of a custom material fishing rod. Equipping a shiny fishing hook and the quality of the bait can improve your odds.
You can use a fishing rod to snatch random organs during the "manipulate organs" step of the "organ manipulation" surgery.
You can use a fishing rod to snatch random organs during the "manipulate organs" step of the "organ manipulation" surgery.
Aquariums are also potential fishing spots. Only useful for catching fish you couldn't find in the wild, as a personal achievement and nothing more.