Files
Papay-Shooter/movement/states/state_dash.gd
Nicholas ButzkeandClaude Fable 5 2c7b5a1aca feat: lighter air control (steer + stronger strafe) and 2-charge dash
- 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]>
2026-07-17 13:32:01 -04:00

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