- Air: direct steering bends existing velocity toward input without changing its magnitude (air_steer_rate), on top of Quake accelerate with raised gains (air_strafe_accel 55->90, per-tick cap 1.2->2.5). No-input air drag nearly removed so held momentum carries. Steering skips opposing input to avoid flipping through zero. - Dash: charge system on the machine — dash_charges (2) spend instantly, each recharges in dash_cooldown (2 s). get_dash_cooldown_remaining() now reports time to the NEXT charge (0 while one is banked) so existing HUDs keep working; get_dash_charges() added for pips. - Tests: dash tests updated to the charge API + new coverage for spend-2-then-recharge; 11/11 pass, spawn smoke clean. Co-Authored-By: Claude Fable 5 <[email protected]>
81 lines
2.4 KiB
GDScript
81 lines
2.4 KiB
GDScript
extends Node
|
|
class_name StateDash
|
|
|
|
## Short burst dash. Cooldown lives on the machine (per-player instance) and is
|
|
## checked by the states that enter dash, so entering this state always dashes.
|
|
## Momentum is fully preserved on exit — the dash ADDS speed to your run.
|
|
|
|
var machine: MovementStateMachine
|
|
var params: MovementParams:
|
|
get: return machine.params
|
|
|
|
var elapsed: float = 0.0
|
|
var direction: Vector3 = Vector3.ZERO
|
|
var _dash_speed: float = 0.0
|
|
|
|
|
|
func enter(_data: Dictionary = {}) -> void:
|
|
elapsed = 0.0
|
|
machine.consume_dash_charge()
|
|
|
|
var player := machine.player
|
|
if player.dash_player:
|
|
player.dash_player.play()
|
|
|
|
# Direction: use wish direction if available, else player forward
|
|
var wish := machine.wish_dir_world
|
|
if wish.length_squared() > 0.01:
|
|
direction = wish.normalized()
|
|
else:
|
|
direction = -machine.player.global_transform.basis.z
|
|
direction.y = 0.0
|
|
direction = direction.normalized()
|
|
|
|
machine.register_chain_mechanic("dash")
|
|
machine.movement_event.emit("dash", {})
|
|
|
|
var current_hvel := Vector3(machine.player.velocity.x, 0.0, machine.player.velocity.z)
|
|
var current_speed := current_hvel.length()
|
|
var proj := current_hvel.dot(direction)
|
|
|
|
var base_dash_speed = machine.get_effective_speed(params.dash_speed)
|
|
|
|
if proj >= 0.0:
|
|
# Dashing forward or diagonally forward: add base dash speed to the projected speed
|
|
_dash_speed = proj + base_dash_speed
|
|
else:
|
|
# Dashing backward or diagonally backward:
|
|
# Reflect current momentum into the new direction, smoothly scaling from
|
|
# base_dash_speed (at sideways) to current_speed (at exactly backward).
|
|
var backward_factor = -proj / current_speed if current_speed > 0.001 else 0.0
|
|
_dash_speed = lerpf(base_dash_speed, maxf(current_speed, base_dash_speed), backward_factor)
|
|
|
|
machine.player.velocity = direction * _dash_speed
|
|
machine.on_ground = false
|
|
|
|
|
|
func exit() -> void:
|
|
pass
|
|
|
|
|
|
func update(delta: float) -> void:
|
|
elapsed += delta
|
|
var player := machine.player
|
|
|
|
if elapsed > params.dash_duration:
|
|
# Keep the full dash velocity — the dash is a speed investment
|
|
player.velocity = direction * _dash_speed
|
|
if player.is_on_floor():
|
|
machine.on_ground = true
|
|
machine.switch_to("ground")
|
|
else:
|
|
machine.switch_to("air")
|
|
return
|
|
|
|
# Maintain dash velocity, flat trajectory
|
|
var vel := direction * _dash_speed
|
|
if player.is_on_floor():
|
|
vel.y = -0.5 # Snap to floor to handle ramps
|
|
player.velocity = vel
|
|
player.move_and_slide()
|