/// 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