mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-25 16:45:42 +00:00
## About The Pull Request I wanted to try and implement an easier way for people to fish out corpses from chasms, as I heard many tales of people trying to fish others out of chasms and it taking over one IRL hour, with some cases where it would take over two hours. Obviously, that's not really interesting gameplay, and it doesn't really give people an incentive to fish, it just turns it into an annoyance that people won't want to do for fun. Now, we don't want that, do we? As such, I've created the rescue hook, a special fishing hook that can only be used in chasms (as that's currently the only place you can find people into), which will only be able to fish out duds, skeleton corpses, any mob that's fallen into a chasm and hasn't been rescued yet, or rarely, a hostile monster lurking below. It has, at the time of writing this, a weight of 5 (50 without bait, lower with bait) for duds and a weight of 30 for chasm detritus, which themselves have a 50% chance to be a random skeleton corpse, or a lobstrosity, and the remaining 50% chance of fishing out a mob that's fallen into a chasm. I'm open to tweaking these values if we think it's too easy or too hard, but it's still a rather expensive item, so I'd consider it quite fine the way it is myself, as it's still not risk-free. It's currently only obtainable through buying it from cargo in the goodies section, at a default price of 600 credits (making it SIGNIFICANTLY more expensive than the rest of the fishing content, and making it something that assistants will have to put some elbow grease into if they want to be able to afford it). As it stands currently, it can't be used to recover the fallen's belongings that weren't on their person (i.e., their crusher if they were holding it in hands), ~*but* I'm down to make that easier to fish out using, for instance, the magnet hook, while also making it incompatible with fishing out bodies, which would make it a nice way to recover those lost items without spending over an hour fishing for them, if that's something that maintainers would want.~ Maintainers did want it, and as such... The Magnetic hook is now the go-to hook to retrieve objects from chasms! Not only does it inherently do a much better job at fishing out non-fishes, it also has a lesser chance of retrieving random junk from chasms, and an even lower chance of fishing out lobstrosities! I also improved the code for the fishing weights calculation so that the hooks and the rods can have an effect on the odds of certain types of rewards more easily, with the option of offloading a more of what's currently being calculated on `fishing_challenge` over on the rods or even the hooks themselves. I finished by fixing a handful of capitalization and punctuation issues in various fishing items, as that bugged me when I was testing my changes. ## Why It's Good For The Game Corpses being recoverable from chasms was a great idea, however making it so people would have to sink a major portion of their shift for a chance at recovering a corpse doesn't create a particularly interesting gameplay loop. However, being able to spend your hard-earned funds in order to streamline that process without really being able to use that to cheese other mechanics sounds like a great deal to me. ## Changelog 🆑 GoldenAlpharex add: Added a Rescue Hook, that will allow the fishing rod it's attached onto to become a lot more proficient at recovering corpses from chasms, at the expense of making it unusable for more traditional fishing. It isn't entirely lobstrosity-proof, however... balance: The magnetic hook can no longer fish out corpses from chasms, but will fish out items much more efficiently than any other hooks, while also being much less attractive to lobstrosities. Some still fall for it regardless, however. spellcheck: Fixed the capitalization and punctuation in the description of multiple fishing accessories. code: Improved the code for fishing weights, to allow for different hooks to have some more noticeable results on the weights without having to add to an already massive proc. /🆑
200 lines
8.3 KiB
Plaintext
200 lines
8.3 KiB
Plaintext
/// Keyed list of preset sources to configuration instance
|
|
GLOBAL_LIST_INIT(preset_fish_sources,init_fishing_configurations())
|
|
|
|
/// These are shared between their spots
|
|
/proc/init_fishing_configurations()
|
|
. = list()
|
|
|
|
var/datum/fish_source/ocean/beach/beach_preset = new
|
|
.[FISHING_SPOT_PRESET_BEACH] = beach_preset
|
|
|
|
var/datum/fish_source/lavaland/lava_preset = new
|
|
.[FISHING_SPOT_PRESET_LAVALAND_LAVA] = lava_preset
|
|
|
|
var/datum/fish_source/chasm/chasm_preset = new
|
|
.[FISHING_SPOT_PRESET_CHASM] = chasm_preset
|
|
|
|
/// Where the fish actually come from - every fishing spot has one assigned but multiple fishing holes can share single source, ie single shared one for ocean/lavaland river
|
|
/datum/fish_source
|
|
/// Fish catch weight table - these are relative weights
|
|
var/list/fish_table = list()
|
|
/// If a key from fish_table is present here, that fish is availible in limited quantity and is reduced by one on successful fishing
|
|
var/list/fish_counts = list()
|
|
/// Text shown as baloon alert when you roll a dud in the table
|
|
var/duds = list("it was nothing", "the hook is empty")
|
|
/// Baseline difficulty for fishing in this spot
|
|
var/fishing_difficulty = FISHING_DEFAULT_DIFFICULTY
|
|
/// How the spot type is described in fish catalog section about fish sources, will be skipped if null
|
|
var/catalog_description
|
|
/// Background image name from /datum/asset/simple/fishing_minigame
|
|
var/background = "fishing_background_default"
|
|
|
|
/// Can we fish in this spot at all. Returns DENIAL_REASON or null if we're good to go
|
|
/datum/fish_source/proc/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman)
|
|
return rod.reason_we_cant_fish(src)
|
|
|
|
|
|
/// DIFFICULTY = (SPOT_BASE_VALUE + FISH_MODIFIER + ROD_MODIFIER + FAV/DISLIKED_BAIT_MODIFIER + TRAITS_ADDITIVE) * TRAITS_MULTIPLICATIVE , For non-fish it's just SPOT_BASE_VALUE
|
|
/datum/fish_source/proc/calculate_difficulty(result, obj/item/fishing_rod/rod, mob/fisherman)
|
|
. = fishing_difficulty
|
|
|
|
if(!ispath(result,/obj/item/fish))
|
|
// In the future non-fish rewards can have variable difficulty calculated here
|
|
return
|
|
|
|
var/list/fish_list_properties = collect_fish_properties()
|
|
var/obj/item/fish/caught_fish = result
|
|
// Baseline fish difficulty
|
|
. += initial(caught_fish.fishing_difficulty_modifier)
|
|
. += rod.difficulty_modifier
|
|
|
|
if(rod.bait)
|
|
var/obj/item/bait = rod.bait
|
|
//Fav bait makes it easier
|
|
var/list/fav_bait = fish_list_properties[caught_fish][NAMEOF(caught_fish, favorite_bait)]
|
|
for(var/bait_identifer in fav_bait)
|
|
if(is_matching_bait(bait, bait_identifer))
|
|
. += FAV_BAIT_DIFFICULTY_MOD
|
|
break
|
|
//Disliked bait makes it harder
|
|
var/list/disliked_bait = fish_list_properties[caught_fish][NAMEOF(caught_fish, disliked_bait)]
|
|
for(var/bait_identifer in disliked_bait)
|
|
if(is_matching_bait(bait, bait_identifer))
|
|
. += DISLIKED_BAIT_DIFFICULTY_MOD
|
|
break
|
|
|
|
// Matching/not matching fish traits and equipment
|
|
var/list/fish_traits = fish_list_properties[caught_fish][NAMEOF(caught_fish, fishing_traits)]
|
|
|
|
var/additive_mod = 0
|
|
var/multiplicative_mod = 1
|
|
for(var/fish_trait in fish_traits)
|
|
var/datum/fishing_trait/trait = new fish_trait
|
|
var/list/mod = trait.difficulty_mod(rod, fisherman)
|
|
additive_mod += mod[ADDITIVE_FISHING_MOD]
|
|
multiplicative_mod *= mod[MULTIPLICATIVE_FISHING_MOD]
|
|
|
|
. += additive_mod
|
|
. *= multiplicative_mod
|
|
|
|
/// In case you want more complex rules for specific spots
|
|
/datum/fish_source/proc/roll_reward(obj/item/fishing_rod/rod, mob/fisherman)
|
|
return pick_weight(get_modified_fish_table(rod,fisherman))
|
|
|
|
/// Gives out the reward if possible
|
|
/datum/fish_source/proc/dispense_reward(reward_path, mob/fisherman)
|
|
if((reward_path in fish_counts)) // This is limited count result
|
|
if(fish_counts[reward_path] > 0)
|
|
fish_counts[reward_path] -= 1
|
|
else
|
|
reward_path = FISHING_DUD //Ran out of these since rolling (multiple fishermen on same source most likely)
|
|
if(ispath(reward_path))
|
|
if(ispath(reward_path,/obj/item))
|
|
var/obj/item/reward = new reward_path(get_turf(fisherman))
|
|
if(ispath(reward_path,/obj/item/fish))
|
|
var/obj/item/fish/caught_fish = reward
|
|
caught_fish.randomize_weight_and_size()
|
|
//fish caught signal if needed goes here and/or fishing achievements
|
|
//Try to put it in hand
|
|
fisherman.put_in_hands(reward)
|
|
fisherman.balloon_alert(fisherman, "caught [reward]!")
|
|
else //If someone adds fishing out carp/chests/singularities or whatever just plop it down on the fisher's turf
|
|
fisherman.balloon_alert(fisherman, "caught something!")
|
|
new reward_path(get_turf(fisherman))
|
|
else if (reward_path == FISHING_DUD)
|
|
//baloon alert instead
|
|
fisherman.balloon_alert(fisherman,pick(duds))
|
|
|
|
/// Cached fish list properties so we don't have to initalize fish every time, init deffered
|
|
GLOBAL_LIST(fishing_property_cache)
|
|
|
|
/// Awful workaround around initial(x.list_variable) not being a thing while trying to keep some semblance of being structured
|
|
/proc/collect_fish_properties()
|
|
if(GLOB.fishing_property_cache == null)
|
|
var/list/fish_property_table = list()
|
|
for(var/fish_type in subtypesof(/obj/item/fish))
|
|
var/obj/item/fish/fish = new fish_type(null)
|
|
fish_property_table[fish_type] = list()
|
|
fish_property_table[fish_type][NAMEOF(fish, favorite_bait)] = fish.favorite_bait.Copy()
|
|
fish_property_table[fish_type][NAMEOF(fish, disliked_bait)] = fish.disliked_bait.Copy()
|
|
fish_property_table[fish_type][NAMEOF(fish, fishing_traits)] = fish.fishing_traits.Copy()
|
|
QDEL_NULL(fish)
|
|
GLOB.fishing_property_cache = fish_property_table
|
|
return GLOB.fishing_property_cache
|
|
|
|
/// Checks if bait matches identifier from fav/disliked bait list
|
|
/datum/fish_source/proc/is_matching_bait(obj/item/bait, identifier)
|
|
if(ispath(identifier)) //Just a path
|
|
return istype(bait, identifier)
|
|
if(islist(identifier))
|
|
var/list/special_identifier = identifier
|
|
switch(special_identifier["Type"])
|
|
if("Foodtype")
|
|
var/obj/item/food/food_bait = bait
|
|
return istype(food_bait) && food_bait.foodtypes & special_identifier["Value"]
|
|
else
|
|
CRASH("Unknown bait identifier in fish favourite/disliked list")
|
|
else
|
|
return HAS_TRAIT(bait, identifier)
|
|
|
|
/// Builds a fish weights table modified by bait/rod/user properties
|
|
/datum/fish_source/proc/get_modified_fish_table(obj/item/fishing_rod/rod, mob/fisherman)
|
|
var/obj/item/bait = rod.bait
|
|
|
|
var/list/fish_list_properties = collect_fish_properties()
|
|
|
|
var/list/final_table = fish_table.Copy()
|
|
for(var/result in final_table)
|
|
if((result in fish_counts) && fish_counts[result] <= 0) //ran out of these, ignore
|
|
final_table -= result
|
|
continue
|
|
|
|
final_table[result] *= rod.multiplicative_fish_bonus(result, src)
|
|
final_table[result] += rod.additive_fish_bonus(result, src) //Decide on order here so it can be multiplicative
|
|
if(result == FISHING_DUD)
|
|
//Modify dud result
|
|
//Bait quality reduces dud chance heavily.
|
|
if(bait)
|
|
if(HAS_TRAIT(bait, GREAT_QUALITY_BAIT_TRAIT))
|
|
final_table[result] *= 0.1
|
|
else if(HAS_TRAIT(bait, GOOD_QUALITY_BAIT_TRAIT))
|
|
final_table[result] *= 0.3
|
|
else if(HAS_TRAIT(bait, BASIC_QUALITY_BAIT_TRAIT))
|
|
final_table[result] *= 0.5
|
|
else
|
|
final_table[result] *= 10 //Fishing without bait is not going to be easy
|
|
else if(ispath(result, /obj/item/fish))
|
|
//Modify fish roll chance
|
|
var/obj/item/fish/caught_fish = result
|
|
|
|
if(bait)
|
|
//Bait matching likes doubles the chance
|
|
var/list/fav_bait = fish_list_properties[result][NAMEOF(caught_fish, favorite_bait)]
|
|
for(var/bait_identifer in fav_bait)
|
|
if(is_matching_bait(bait, bait_identifer))
|
|
final_table[result] *= 2
|
|
break // could compound possibly
|
|
//Bait matching dislikes
|
|
var/list/disliked_bait = fish_list_properties[result][NAMEOF(caught_fish, disliked_bait)]
|
|
for(var/bait_identifer in disliked_bait)
|
|
if(is_matching_bait(bait, bait_identifer))
|
|
final_table[result] *= 0.5
|
|
break // same question as above
|
|
|
|
// Apply fishing trait modifiers
|
|
var/list/fish_traits = fish_list_properties[caught_fish][NAMEOF(caught_fish, fishing_traits)]
|
|
var/additive_mod = 0
|
|
var/multiplicative_mod = 1
|
|
for(var/fish_trait in fish_traits)
|
|
var/datum/fishing_trait/trait = new fish_trait
|
|
var/list/mod = trait.catch_weight_mod(rod, fisherman)
|
|
additive_mod += mod[ADDITIVE_FISHING_MOD]
|
|
multiplicative_mod *= mod[MULTIPLICATIVE_FISHING_MOD]
|
|
|
|
final_table[result] += additive_mod
|
|
final_table[result] *= multiplicative_mod
|
|
|
|
if(final_table[result] <= 0)
|
|
final_table -= result
|
|
return final_table
|