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]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
198345177a
commit
2c7b5a1aca
@@ -36,6 +36,7 @@ func run_all() -> void:
|
||||
"test_wall_run_preserves_fast_entry_speed",
|
||||
"test_wall_run_entry_keeps_some_upward_momentum",
|
||||
"test_dash_cooldown_is_per_machine",
|
||||
"test_dash_has_two_charges_then_recharges",
|
||||
"test_dash_adds_speed_to_forward_momentum",
|
||||
"test_chain_bonus_soft_caps",
|
||||
"test_notify_landed_emits_scaled_event",
|
||||
@@ -149,9 +150,10 @@ func test_dash_cooldown_is_per_machine() -> void:
|
||||
fake_player.velocity = Vector3(10, 0, 0)
|
||||
sm.wish_dir_world = Vector3(1, 0, 0)
|
||||
sm.switch_to("dash")
|
||||
_expect(sm.dash_cooldown_timer > 0.0, "dash arms this machine's cooldown")
|
||||
_expect(sm.get_dash_charges() == params.dash_charges - 1, "dash spends one charge")
|
||||
_expect(sm.dash_recharge_timer > 0.0, "dash arms this machine's recharge clock")
|
||||
|
||||
# A second, independent machine must NOT share that cooldown (the old
|
||||
# A second, independent machine must NOT share that state (the old
|
||||
# implementation used a static var shared across all players).
|
||||
var player2 = load("res://movement/tests/fake_player.gd").new()
|
||||
add_child(player2)
|
||||
@@ -159,11 +161,36 @@ func test_dash_cooldown_is_per_machine() -> void:
|
||||
sm2.player = player2
|
||||
sm2.params = MovementParams.new()
|
||||
player2.add_child(sm2)
|
||||
_expect(sm2.dash_cooldown_timer == 0.0, "second player's dash must be ready")
|
||||
_expect(sm2.get_dash_charges() == sm2.params.dash_charges, "second player keeps full charges")
|
||||
remove_child(player2)
|
||||
player2.free()
|
||||
|
||||
|
||||
func test_dash_has_two_charges_then_recharges() -> void:
|
||||
params.dash_charges = 2
|
||||
sm.wish_dir_world = Vector3(1, 0, 0)
|
||||
fake_player.velocity = Vector3(10, 0, 0)
|
||||
|
||||
_expect(sm.can_dash(), "first dash available")
|
||||
sm.switch_to("dash")
|
||||
sm.switch_to("air")
|
||||
_expect(sm.can_dash(), "second charge still banked — no cooldown wait")
|
||||
_eq(sm.get_dash_cooldown_remaining(), 0.0, "cooldown reads 0 while a charge is banked")
|
||||
sm.switch_to("dash")
|
||||
sm.switch_to("air")
|
||||
_expect(not sm.can_dash(), "both charges spent → dash blocked")
|
||||
_expect(sm.get_dash_cooldown_remaining() > 0.0, "cooldown now counts to next charge")
|
||||
|
||||
# Simulate the recharge tick the machine runs each physics frame.
|
||||
# Blank the state so only machine bookkeeping runs (no state update /
|
||||
# move_and_slide outside a real physics frame).
|
||||
sm.current_state = ""
|
||||
sm.dash_recharge_timer = 0.0001
|
||||
sm._physics_process(0.016)
|
||||
_expect(sm.can_dash(), "one charge regained after recharge timer elapses")
|
||||
_expect(sm.dash_recharge_timer > 0.0, "recharge continues toward the second charge")
|
||||
|
||||
|
||||
func test_dash_adds_speed_to_forward_momentum() -> void:
|
||||
fake_player.velocity = Vector3(12, 0, 0)
|
||||
sm.wish_dir_world = Vector3(1, 0, 0)
|
||||
|
||||
Reference in New Issue
Block a user