Files
GhomandGitHub 659e6f0ca2 Fishing expansion 2: one-year later boogaloo (#85252)
## About The Pull Request
This PR mainly adds more fish and more fishing spots to the game, while
refactoring a few aspects of the fishing minigame.

Listing out with the new fish:
- Arctic char: mainly filler content for the ice hole fishing spot
- Sockeye Salmon: ditto but also provides better fillets that boost the
quality of resulting food items when cooked or used in recipes
- Soulfish: joke content, found by the cursed spring ruin
- Skin Crab: also a joke found by the cursed spring
- Bump-Fish: filler for the sand fishing spot
- Burrower Crab: ditto, reusing a fish sprite I made last year
- Sand Surfer: ditto
- Three-Eyed Goldfish: It's a reference, doh
- Stingray: A modestly weaponizable fish (whoops I've forgot to set the
hit sounds), it possess a few traits that make it deliver bits of venom
each time you hit someone with it
- Swordfish: Huge-ass fish that may require two hands to wield (or not,
if the RNG wants to make it smaller). Stats-wise, it's more or less the
equivalent of the captain sabre, if not stronger (and more unwieldy due
to size and weight). Becomes weaker when dead. Also gives better quality
fillets.
- Chainsawfish: A mutation of the goldfish with some size, weight and
traits requirements, but can also be found on emagged fishing portals.
Stronger than the swordfish, it behaves sort of like a chainsaw, with
the similar tool behaviour and var values. Also becomes weaker when
dead.

As for the fishing spots, you can now fish on sand turfs, at the cursed
springs or on ice. Rivers/jungle water now has its own fishing spot
datum, and no longer uses the generic fishing portal one. To fish on
ice, you first have to carve a hole with a pick or a shovel.

I've also refactored the fish "AI" hardcoded stuff used in the fishing
minigame into their own datums, which let me add a few fancier ways to
how the fish moves during the minigame (i.e. the soulfish moving at 1
FPS or the chainsawfish getting faster and faster).

As for the sword and chainsaw fish, their potential strength is balanced
out by the need of keeping them alive, as well as the potential
cumbersomeness, two-handed wielding and potential slowdown from the
excessive weight of the fish (Thank you Big Slappy for the inspiration).

Other minor changes include: Pufferfish giving better quality fillets
(too bad they're poisonous, I'll go and make a skillchip to let cooks
safely separate the poisonous liver from the fillets); McGill The
lawyer's goldfish) having a 15% of being three-eyed; the aforementioned
slowdown from fish weight and two-handed carry from fish size; a couple
new fish icons (the ones that hint you on what you're trying to catch)
for the fishing minigame; a few adjustments to prevent self-reproducing
fish from ignoring the population cap and let fish with a stable
population of 1 to crossbreed (also gotta make a different PR to let it
happen rarely without the crossbreeding trait).

This PR is still a WIP, gotta test it several times.

## Why It's Good For The Game
Fishing is something I've been working on for about a year now, but
there are still a few places where it's kinda lackluster, like there's
not enough diverse fishing spots or useful fish (I'll be working on a
separate PR to make the logistic of a carrying a fish around without
letting it die a tad easier). Also, look at these sprites:

![new
fish](https://github.com/user-attachments/assets/49c55065-32b0-44ec-ac12-74b3a1763f50)

Can you guess which is which?

## Changelog

For the sake of not dumping players with niche information 90% of the
players won't understand, I'll keep the CL pretty generic

🆑
add: Added twelve new fish types to the game. Some are cool, other are
not, some come with their own special traits and some are straight-up
weapons.
add: Added more fishing spots to the game. Sand, ice, rivers, the cursed
spring...
balance: A few fish like salmon, swordfish and pufferfish (poisonous
btw) now give better quality fillets when butchered, which can improve
the quality of food that uses them even further.
balance: Excessive fish weight will make the fish slowier to carry,
while excessive size may make it require two hands.
balance: Adjusted size, weight and cooldowns of several fish, for the
better.
/🆑
2024-08-20 06:46:11 +02:00

215 lines
9.2 KiB
Plaintext

/// Any lower than this, and the target position of the fish is considered null
#define FISH_TARGET_MIN_DISTANCE 6
/// The friction applied to fish jumps, so that it decelerates over time
#define FISH_FRICTION_MULT 0.9
/// Used to decide whether the fish can jump in a certain direction
#define FISH_SHORT_JUMP_MIN_DISTANCE 100
/// The maximum distance for a short jump
#define FISH_SHORT_JUMP_MAX_DISTANCE 200
///Fish movements are simple datums, generated by the fishing minigame, that represent how the fish moves suring the minigame.
/datum/fish_movement
/// The minigame that spawned us
var/datum/fishing_challenge/master
/// How many times move_fish() has been called
var/times_fired = 0
/// How likely the fish is to perform a standard jump, then multiplied by difficulty
var/short_jump_chance = 2.25
/// How likely the fish is to perform a long jump, then multiplied by difficulty
var/long_jump_chance = 0.0625
/// The speed limit for the short jump
var/short_jump_velocity_limit = 400
/// The speed limit for the long jump
var/long_jump_velocity_limit = 200
/// The current speed limit used
var/current_velocity_limit
/// The base velocity of the fish, which may affect jump distances and falling speed.
var/fish_idle_velocity = 0
/// A position on the slider the fish wants to get to
var/target_position
/// If true, the fish can jump while a target position is set, thus overriding it
var/can_interrupt_move = TRUE
/// The current speed the fish is moving at
var/fish_velocity = 0
/datum/fish_movement/New(datum/fishing_challenge/master)
src.master = master
/**
* Proc that adjusts movement values to the difficulty of the minigame.
* The operations can be a tad complex, but basically it ensures that jump
* chances with a probability higher than 1% increase in a smooth curve so that
* they still reach 100% prob when the difficulty peakes.
*/
/datum/fish_movement/proc/adjust_to_difficulty()
var/square_angle_rad = TORADIANS(90)
var/zero_one_difficulty = master.difficulty/100
if(short_jump_chance > 1)
short_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(short_jump_chance * 1/square_angle_rad))))*100
else
short_jump_chance *= master.difficulty
if(long_jump_chance > 1)
long_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(long_jump_chance * 1/square_angle_rad))))*100
else
long_jump_chance *= master.difficulty
///The main proc, called by minigame every SSfishing tick while it's in the 'active' phase.
/datum/fish_movement/proc/move_fish(seconds_per_tick)
times_fired++
/**
* The jump chances are meant to run every odd tick (each every decisecond)
* We cannot do it every tick because the fish would be jumpier than intended
* and we cannot cut the chances in half to fit on each tick, because the maximum probability
* would go from 100% to 75%.
*/
var/can_roll = times_fired % 2
var/long_chance = long_jump_chance * seconds_per_tick * (1/seconds_per_tick)
var/short_chance = short_jump_chance * seconds_per_tick * (1/seconds_per_tick)
// If we have the target but we're close enough, mark as target reached
if(abs(target_position - master.fish_position) < FISH_TARGET_MIN_DISTANCE)
target_position = null
// Switching to new long jump target can interrupt any other
if(can_roll && (can_interrupt_move || isnull(target_position)) && prob(long_chance))
/**
* Move at least 0.75 to full of the availible bar in given direction,
* and more likely to move in the direction where there's more space
*/
var/distance_from_top = FISHING_MINIGAME_AREA - master.fish_position - master.fish_height
var/distance_from_bottom = master.fish_position
var/top_chance
if(distance_from_top < FISH_SHORT_JUMP_MIN_DISTANCE)
top_chance = 0
else
top_chance = (distance_from_top/max(distance_from_bottom, 1)) * 100
var/new_target = master.fish_position
if(prob(top_chance))
new_target += distance_from_top * rand(75, 100)/100
else
new_target -= distance_from_bottom * rand(75, 100)/100
target_position = round(new_target)
current_velocity_limit = long_jump_velocity_limit
// Move towards target
if(!isnull(target_position))
var/distance = target_position - master.fish_position
// about 5 at diff 15 , 10 at diff 30, 30 at diff 100
var/acceleration_mult = get_acceleration(seconds_per_tick)
var/target_acceleration = distance * acceleration_mult * seconds_per_tick
fish_velocity = fish_velocity * FISH_FRICTION_MULT + target_acceleration
else if(can_roll && prob(short_chance))
var/distance_from_top = FISHING_MINIGAME_AREA - master.fish_position - master.fish_height
var/distance_from_bottom = master.fish_position
var/jump_length
if(distance_from_top >= FISH_SHORT_JUMP_MIN_DISTANCE)
jump_length = rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
if(distance_from_bottom >= FISH_SHORT_JUMP_MIN_DISTANCE && (!jump_length || prob(50)))
jump_length = -rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
target_position = clamp(master.fish_position + jump_length, 0, FISHING_MINIGAME_AREA - master.fish_height)
current_velocity_limit = short_jump_velocity_limit
fish_velocity = clamp(fish_velocity + fish_idle_velocity, -current_velocity_limit, current_velocity_limit)
set_fish_position(seconds_per_tick)
///Proc that returns the acceleration of the fish during the minigame.
/datum/fish_movement/proc/get_acceleration(seconds_per_tick)
return 0.3 * master.difficulty + 0.5
///Called at the end of move_fish(), for updating the position of the fish in the fishing minigame.
/datum/fish_movement/proc/set_fish_position(seconds_per_tick)
master.fish_position = clamp(master.fish_position + fish_velocity * seconds_per_tick, 0, FISHING_MINIGAME_AREA - master.fish_height)
///Generic fish movement datum that only performs slow, uninterrupted long jumps
/datum/fish_movement/slow
short_jump_chance = 0
long_jump_chance = 1.5
long_jump_velocity_limit = 150
can_interrupt_move = FALSE
///Generic fish movement datum with triple the short jump chance.
/datum/fish_movement/zippy
short_jump_chance = parent_type::short_jump_chance * 3
///fish movement datum that progressively gets faster until acceleration and velocity are double the starting ones.
/datum/fish_movement/accelerando
///The jump velocity to add each tick
var/short_jump_vel_add
///The long jump velocity to add each tick
var/long_jump_vel_add
///Time to reach full speed, in seconds.
var/accel_time_cap = 30
/datum/fish_movement/accelerando/move_fish(seconds_per_tick)
var/seconds_elapsed = (times_fired * seconds_per_tick)
if(seconds_elapsed >= accel_time_cap)
return ..()
if(!times_fired) //First tick, cache the initial jump velocities
short_jump_vel_add = short_jump_velocity_limit/accel_time_cap
long_jump_vel_add = long_jump_velocity_limit/accel_time_cap
return ..()
if(current_velocity_limit)
var/vel_add = current_velocity_limit == short_jump_velocity_limit ? short_jump_vel_add : long_jump_vel_add
current_velocity_limit += round(vel_add * seconds_per_tick, 0.01)
short_jump_velocity_limit += round(short_jump_vel_add * seconds_per_tick, 0.01)
long_jump_velocity_limit += round(long_jump_vel_add * seconds_per_tick, 0.01)
return ..()
/datum/fish_movement/accelerando/get_acceleration(seconds_per_tick)
var/acceleration = ..()
return acceleration + min(acceleration, acceleration * times_fired * seconds_per_tick / accel_time_cap)
/datum/fish_movement/accelerando/set_fish_position(seconds_per_tick)
fish_velocity = round(fish_velocity)
return ..()
///Fish movement datum that updates the fish position twice per second.
/datum/fish_movement/choppy
///We keep of the theorical fish position to eventually use
var/faux_position = 0
/datum/fish_movement/choppy/set_fish_position(seconds_per_tick)
faux_position = clamp(faux_position + fish_velocity * seconds_per_tick, 0, FISHING_MINIGAME_AREA - master.fish_height)
if(!((times_fired * SSfishing.wait) % (0.5 SECONDS)))
master.fish_position = faux_position
///Fish movement datum that weakly pushes the fish up and then down with greater force once it reaches the top of the minigame.
/datum/fish_movement/plunger
///Is the fish plunging to the bottom of the minigame area, or should it swim up?
var/is_plunging = TRUE
///The added idle velocity when plunging
var/plunging_speed = -22
/datum/fish_movement/plunger/adjust_to_difficulty()
. = ..()
//Adjust the fleeing velocity, up to five times the initial value.
plunging_speed += round(plunging_speed * master.difficulty * 0.03)
fish_idle_velocity += plunging_speed //so it can be safely subtracted if the fish starts at the bottom.
/datum/fish_movement/plunger/move_fish(seconds_per_tick)
var/fish_area = FISHING_MINIGAME_AREA - master.fish_height
if(is_plunging)
if(target_position > master.fish_position) //nothing should stop us from plunging.
target_position = null
var/dist_bot_percent = master.fish_position/fish_area
if(dist_bot_percent <= 0.04)
fish_idle_velocity -= plunging_speed
is_plunging = FALSE
else
var/dist_top_percent = (fish_area - master.fish_position)/fish_area
if(dist_top_percent <= 0.04)
fish_idle_velocity += plunging_speed
is_plunging = TRUE
return ..()
#undef FISH_TARGET_MIN_DISTANCE
#undef FISH_FRICTION_MULT
#undef FISH_SHORT_JUMP_MIN_DISTANCE
#undef FISH_SHORT_JUMP_MAX_DISTANCE