Files
Bubberstation/code/modules/fishing/fish_movement.dm

249 lines
11 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
/datum/fish_movement/proc/reset_difficulty_values()
short_jump_chance = initial(short_jump_chance)
long_jump_chance = initial(long_jump_chance)
///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
if(fish_idle_velocity)
var/idle_velocity = fish_idle_velocity
var/abs_idle_vel = abs(idle_velocity)
//Make sure idle velocity doesn't manage to halt fish to a grind and getting them unable to move.
//First, check if the directions of the two forces are oppositve
if((idle_velocity / abs_idle_vel) != (target_acceleration / abs(target_acceleration)))
//Then, calculate the ratio between absolute idle velocity and halved acceleration multiplier.
var/halved_ratio = (acceleration_mult * 0.5) / abs_idle_vel
/**
* If the idle velocity is more than half the acceleration,
* proceed to use powers, for diminishing loss of acceleration per additional unit of idle velocity.
* This way you never reach 0 acceleration while allowing more extreme values to keep lowering it.
*/
if(halved_ratio < 1)
var/power = min(halved_ratio + 0.5, 1)
target_acceleration *= 1 - (halved_ratio^power)
/**
* Otherwise we add the idle velocity (which we know is of opposite sign and
* has an absolute value between 0.ε and 0.5) to the target velocity
*/
else
target_acceleration += idle_velocity
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, -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/reset_difficulty_values()
. = ..()
if(is_plunging)
fish_idle_velocity -= plunging_speed
plunging_speed = initial(plunging_speed)
/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