Files
Papay-Shooter/movement/states/state_slide.gd
2026-08-02 02:20:02 -04:00

165 lines
6.0 KiB
GDScript

extends Node
class_name StateSlide
## Momentum slide. Friction is a flat deceleration on level ground but gravity
## projected along the floor accelerates you downhill, so slopes are the fast
## route. The capsule stays low via the machine's crouch handling (slide counts
## as crouched); no capsule fiddling here.
var machine: MovementStateMachine
var params: MovementParams:
get: return machine.params
var elapsed: float = 0.0
var _slide_direction: Vector3 = Vector3.ZERO
func enter(_data: Dictionary = {}) -> void:
elapsed = 0.0
machine.register_chain_mechanic("slide")
if machine.player.slide_player and not machine.player.slide_player.playing:
machine.player.slide_player.play()
# Slide in the direction of current velocity (momentum-based)
var hvel := Vector3(machine.player.velocity.x, 0.0, machine.player.velocity.z)
if machine.player.has_method("update_slide_audio"):
machine.player.update_slide_audio(hvel.length())
if hvel.length_squared() > 0.01:
_slide_direction = hvel.normalized()
else:
# Fallback: player forward
_slide_direction = -machine.player.global_transform.basis.z
_slide_direction.y = 0.0
_slide_direction = _slide_direction.normalized()
# Entry speed: keep momentum, add a flat boost when actually moving fast on
# the ground (rewards slide-cancelling sprints without making crouch a brake)
var entry_speed := hvel.length()
if machine.player.is_on_floor() and entry_speed > params.walk_speed * 0.8:
entry_speed += params.slide_boost
entry_speed = maxf(entry_speed, params.slide_speed * 0.75)
machine.player.velocity.x = _slide_direction.x * machine.get_effective_speed(entry_speed)
machine.player.velocity.z = _slide_direction.z * machine.get_effective_speed(entry_speed)
func exit() -> void:
machine.slide_cooldown_timer = params.slide_cooldown
if machine.player.slide_player:
machine.player.slide_player.stop()
func update(delta: float) -> void:
elapsed += delta
var player := machine.player
if player.slide_player and not player.slide_player.playing:
player.slide_player.play()
var vel: Vector3 = player.velocity
var hvel := Vector3(vel.x, 0.0, vel.z)
var hspeed := hvel.length()
if player.has_method("update_slide_audio"):
player.update_slide_audio(hspeed)
# ── Slope physics: gravity projected along the floor plane ────────────
var on_slope := false
if player.is_on_floor():
var floor_n := player.get_floor_normal()
if floor_n.y < 0.999:
# Downhill direction on this slope
var downhill := (Vector3.DOWN - floor_n * Vector3.DOWN.dot(floor_n))
if downhill.length_squared() > 0.0001:
downhill = downhill.normalized()
var steepness := 1.0 - floor_n.y # 0 flat .. ~0.3 steep
var slope_pull := downhill * params.slide_slope_accel * steepness * 8.0
hvel += Vector3(slope_pull.x, 0.0, slope_pull.z) * delta
on_slope = downhill.dot(_slide_direction) > 0.1
# ── Friction: flat decel on level ground, nearly free downhill ────────
if hspeed > 0.01:
var friction := params.slide_friction_flat
if on_slope:
friction *= 0.15
var new_speed := maxf(hvel.length() - friction * delta, 0.0)
if hvel.length() > 0.001:
hvel = hvel.normalized() * new_speed
# ── Steering: bend velocity toward input without changing speed ───────
var wish_dir := machine.wish_dir_world
if wish_dir.length_squared() > 0.01 and hvel.length_squared() > 0.01:
wish_dir = wish_dir.normalized()
var speed_now := hvel.length()
var steered := hvel.lerp(wish_dir * speed_now, params.slide_steer_rate * delta)
if steered.length_squared() > 0.01:
hvel = steered.normalized() * speed_now
_slide_direction = hvel.normalized()
vel.x = hvel.x
vel.z = hvel.z
# ── Gravity (for slopes / edges) ──────────────────────────────────────
if not player.is_on_floor():
vel.y -= params.gravity * delta
else:
vel.y = -2.0 # Strong floor snap
player.velocity = vel
player.move_and_slide()
# ── Update ground status ──────────────────────────────────────────────
if player.is_on_floor():
machine.on_ground = true
machine.current_jump_count = 0
else:
machine.on_ground = false
# ── End conditions ────────────────────────────────────────────────────
var out_speed := Vector3(player.velocity.x, 0.0, player.velocity.z).length()
# Jump out of slide: momentum-preserving hop
if machine.input_jump_just_pressed and machine.jump_cooldown_timer <= 0.0:
var hv := Vector3(player.velocity.x, 0.0, player.velocity.z)
if hv.length_squared() > 0.01:
var dir := hv.normalized()
var jump_speed := maxf(out_speed, minf(out_speed + params.slide_jump_speed_boost, params.bunny_hop_speed_cap))
player.velocity.x = dir.x * jump_speed
player.velocity.z = dir.z * jump_speed
player.velocity.y = params.jump_velocity
machine.current_jump_count = 1
machine.jump_cooldown_timer = params.jump_cooldown
machine.on_ground = false
machine.register_chain_mechanic("slide_jump")
machine.movement_event.emit("jump", {})
if player.jump_player:
player.jump_player.play()
machine.switch_to("air")
return
# Slide straight onto a wall → wall run (keeps the flow going)
if not player.is_on_floor() and machine.wall_cooldown_timer <= 0.0 and out_speed > params.slide_min_speed:
var wall_n := machine.detect_wall_horizontal()
if wall_n != Vector3.ZERO and abs(machine.wish_dir_world.dot(wall_n)) < 0.85:
machine.switch_to("wall_run")
return
# Dash out of slide
if machine.input_dash and machine.can_dash():
machine.switch_to("dash")
return
# Slide ended: too slow
if out_speed < params.slide_min_speed:
machine.switch_to("ground")
return
# Player released crouch
if not machine.input_crouch:
machine.switch_to("ground")
return
# Fell off edge
if not machine.on_ground and machine.coyote_timer <= 0.0:
machine.switch_to("air")
return