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 03:33:18 +00:00
committed by GitHub
parent ccc2533380
commit 1dff5f6de3
19 changed files with 221 additions and 130 deletions
+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"