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 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() # ── 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